(Beginner) Coroutine crashing unity. Not sure why

I'm not sure what's going on but one of my coroutines is completely crashing unity. This is the coroutine in question.

IEnumerator attack() 
{
    currentState = state.attack;
    pathFinder.enabled = false;

    Vector3 origin = transform.position;
    Vector3 attackPos = target.position;
    float percent = 0;
    float attackSpeed = 3;

    while(percent <= 1) 
    {
        percent += Time.deltaTime * attackSpeed;
        float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
        transform.position = Vector3.Lerp(origin, attackPos, interpolation);

        yield return null;
    }
    currentState = state.chase;
    pathFinder.enabled = true;
}

A lot of code here might seem bad. I'm following a tutorial, probably heard that one a lot here, and on their end, it seems to be working fine but right when I press play it freezes unity forcing me to close it. Here's the entire script if that would give a better understanding of what could be going on

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
 
[RequireComponent(typeof(NavMeshAgent))]
public class Enemy : LivingEntity 
{
    public enum state { idle, chase, attack};
 
    private state currentState;
    private NavMeshAgent pathFinder;
    private Transform target;
    private float attackThreshhold = .5f;
    private float timeBetweenAttacks = 1;
    private float attackTimer;
    private float myCollisionRadius;
    private float targetCollisionRadius;
    
    // Start is called before the first frame update
    protected override void Start() 
    {
        base.Start();
        currentState = state.chase;
        pathFinder = GetComponent<NavMeshAgent>();
        target = GameObject.FindGameObjectWithTag("Player").transform;
        myCollisionRadius = GetComponent<CapsuleCollider>().radius;
        targetCollisionRadius = GetComponent<CapsuleCollider>().radius;
 
        StartCoroutine(updatePath());
    }
 
    // Update is called once per frame
    void Update() 
    {
        if(Time.time > attackTimer)
        {
            float sqrDistanceToTarget = (target.position - transform.position).sqrMagnitude;
            if (sqrDistanceToTarget < Mathf.Pow(attackThreshhold + myCollisionRadius + targetCollisionRadius, 2)) {
                attackTimer = Time.time + timeBetweenAttacks;
                StartCoroutine(attack());
            }
        }
    }
 
    IEnumerator attack() 
    {
        currentState = state.attack;
        pathFinder.enabled = false;
 
        Vector3 origin = transform.position;
        Vector3 attackPos = target.position;
        float percent = 0;
        float attackSpeed = 3;
 
        while(percent <= 1) 
        {
            percent += Time.deltaTime * attackSpeed;
            float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
            transform.position = Vector3.Lerp(origin, attackPos, interpolation);
 
            yield return null;
        }

        currentState = state.chase;
        pathFinder.enabled = true;
    }
 
    IEnumerator updatePath() 
    {
        float refreshRate = .25f;
        
        while(target != null) 
        {
            if(currentState == state.chase) 
            {
                Vector3 dirToTarget = (target.position - transform.position).normalized;
                Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
                if (!dead) pathFinder.SetDestination(targetPosition);
                yield return new WaitForSeconds(refreshRate);
            }
        }
    }
}

3 answers

  • answered 2021-02-23 04:11 user15119845

    Because at some point percent goes negative. It also never goes above 1. I would say your math is incorrect. Try removing the negative infront of Mathf.Pow

  • answered 2021-02-23 08:38 SeLeCtRa

    Math function has nothing to do with crash. Unity crashes because, @SOPMOD is running while loop infinetly. That causing a overflow. Your code tells it should break while loop when percent value is smaller or equal to 1 but you are yielding it null everytime inside the while loop. yield return null tells to compiler, coroutine will run in next frame. So, coroutine start again and percent value is set to 0 again. While loop run again and coroutine again will yield null. Infinite coroutine loop is crashing your app. Move

    float percent = 0;
    

    decleration to outside of function. Move it to class level and you will be fine. Because if it is in the class level, it will hold previous value and it will able to reach >1.

    Here is How Coroutine yields work

  • answered 2021-02-23 12:49 derHugo

    Your problem is in the other routine in updatePath.

    while(target != null) 
    {
        if(currentState == state.chase) 
        {
            Vector3 dirToTarget = (target.position - transform.position).normalized;
            Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
            if (!dead) pathFinder.SetDestination(targetPosition);
            yield return new WaitForSeconds(refreshRate);
        }
    }
    

    Problem: In the case your are not in the state state.chase you never yield => you have an infinite while loop!


    It should probably rather be

    while(target != null) 
    {
        if(currentState == state.chase) 
        {
            var dirToTarget = (target.position - transform.position).normalized;
            var targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius / 2);
            if (!dead) pathFinder.SetDestination(targetPosition);
            yield return new WaitForSeconds(refreshRate);
        }
        // In other cases wait one frame
        else
        {
            yield return null;
        }
    }