StopCoroutine() / StopAllCoroutines() not functioning properly

Archj

New member
Hey,

I have a Task that looks roughly like this (irrelevant bits cut out)

C#:
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class MoveTowardsTarget : Action
{
    [SerializeField]
    private SharedTransform target;

    [SerializeField]
    private SharedBool walker_;

    private bool inProgress_;
    private Transform target_;
    private NavMeshAgent agent_;

    public override void OnStart()
    {
        agent_                  = GetComponent<NavMeshAgent>();
        target_                 = target.GetValue() as Transform;
        inProgress_          = true;

        if (walker_.Value)
        {
            var pos = aiWalker_.getRandomPosition();
            agent_.SetDestination(pos);
            StartCoroutine(checkWalkerCompletion());
        }
        else
        {
            agent_.SetDestination(target_.position);
            StartCoroutine(checkCompletion());
        }
    }

    public override TaskStatus OnUpdate()
    {
        var status = inProgress_ ? TaskStatus.Running : TaskStatus.Success;
        if (status == TaskStatus.Success)
        {
            stopCoroutines();
        }
        return status;
    }

    public override void OnEnd()
    {
        stopCoroutines();
    }

    private void stopCoroutines()
    {
        if (walker_.Value)
        {
            StopCoroutine(checkWalkerCompletion());
        }
        else
        {
            StopCoroutine(checkCompletion());
        }
    }

    private IEnumerator checkCompletion()
    {
        while (true)
        {
            yield return new WaitForSeconds(0.5f);
            inProgress_ = agent_.pathPending ||
                          agent_.remainingDistance >= agent_.stoppingDistance ||
                          agent_.velocity.sqrMagnitude > 0.0f;
        }
    }

    private IEnumerator checkWalkerCompletion()
    {
        while (true)
        {
            yield return new WaitForSeconds(0.5f);
            inProgress_ = agent_.remainingDistance > 4; // Occasional crash due to disabled Behavior
        }
    }
}

The first issue was when I tried using StopAllCoroutines(). It threw exceptions, and I solved it by calling StopCoroutine() instead on the coroutine that was actually running. I interpreted this as StopAllCoroutines() will try and stop non-running coroutines and then crashing, but I'm not sure about this.

The second, and my current, issue is that occasionally a NullReferenceException is thrown at the line I commented far down in the file. The problem is that occasionally the agent_ has become disabled while the coroutine is running. I have tested and verified that the agent (behaviour) is in fact enabled right before and right after stopping the coroutine. To me it seems like the coroutine doesn't stop immediately. A quickfix is to simply check if the agent is in fact enabled before using it, but I feel that is just hiding the underlying problem.

Hoping for some clarification on this matter!
 
What version of Unity are you using? Also, are you able to send me a small repro scene that I can use to take a closer look?
 
Hi, I will get back to you with my version number when I get back home. Unsure if I'm able to send you a small scene, I may have to send the whole project along with instructions (it's not that big yet). I don't have exact repro steps, it just seems to happen every now and then.
 
My Unity version is 2018.3.10f1.

Here's the stack trace, that I forgot to add in the post:

Code:
NullReferenceException: Object reference not set to an instance of an object
BehaviorDesigner.Runtime.Behavior.StopTaskCoroutine (System.String methodName) (at <8273d6b105784beab3b1f76a8d030c0c>:0)
BehaviorDesigner.Runtime.Tasks.Task.StopCoroutine (System.String methodName) (at <8273d6b105784beab3b1f76a8d030c0c>:0)
MoveTowardsTarget.stopCoroutines () (at Assets/Code/AI/MoveTowardsTarget.cs:75)
MoveTowardsTarget.OnEnd () (at Assets/Code/AI/MoveTowardsTarget.cs:65)
BehaviorDesigner.Runtime.BehaviorManager.PopTask (BehaviorDesigner.Runtime.BehaviorManager+BehaviorTree behaviorTree, System.Int32 taskIndex, System.Int32 stackIndex, BehaviorDesigner.Runtime.Tasks.TaskStatus& status, System.Boolean popChildren, System.Boolean notifyOnEmptyStack) (at <8273d6b105784beab3b1f76a8d030c0c>:0)
BehaviorDesigner.Runtime.BehaviorManager.DestroyBehavior (BehaviorDesigner.Runtime.Behavior behavior, BehaviorDesigner.Runtime.Tasks.TaskStatus executionStatus) (at <8273d6b105784beab3b1f76a8d030c0c>:0)
BehaviorDesigner.Runtime.BehaviorManager.DisableBehavior (BehaviorDesigner.Runtime.Behavior behavior, System.Boolean paused, BehaviorDesigner.Runtime.Tasks.TaskStatus executionStatus) (at <8273d6b105784beab3b1f76a8d030c0c>:0)
BehaviorDesigner.Runtime.BehaviorManager.DisableBehavior (BehaviorDesigner.Runtime.Behavior behavior, System.Boolean paused) (at <8273d6b105784beab3b1f76a8d030c0c>:0)
BehaviorDesigner.Runtime.Behavior.DisableBehavior () (at <8273d6b105784beab3b1f76a8d030c0c>:0)
BehaviorDesigner.Runtime.Behavior.OnDisable () (at <8273d6b105784beab3b1f76a8d030c0c>:0)
 
I'm really sorry, but I managed to copy the wrong stacktrace >.< Here's the actual one, but I don't think this one is very helpful:

Code:
"GetRemainingDistance" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.AI.NavMeshAgent:get_remainingDistance()
<checkWalkerCompletion>d__13:MoveNext() (at Assets/Code/AI/MoveTowardsTarget.cs:107)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

I'll see if I can put together a test scene for you.
 
Top