NodeCanvas Forums › Support › Action doesn't wait for Coroutine end (IEnumerator) to finish
Hi,
First, I want to thank you for your amazing work!
I’m implementing a turn-by-turn tactical game with NodeCanvas, and I’m having a bug:
I have an Action “Execute Function” with an IEnumerator, the Coroutine is working well but the Action doesn’t wait for the Coroutine end to finish. I tested it with both TransitionEvaluations (CheckContinuously and CheckAfterStateFinished), same issue.
The action have 2 transitions: If '$success' == 'True'
and If '$success' == 'False'
.
$success is set to False if an error occurs in my IEnumerator (before a yield break;
)
$success is set to False at the end of the IEnumerator (after all of the yield return ...;
)
I don’t know if it’s a bug of NodeCanvas or a problem of my code, but if you cann help me with that, it would be great π
Thanks!
Hello,
Thanks for your kind words! I’m glad you like NC π
So, it was actually a silly bug on my side I did when refactoring some code. Here is the fix:
Please open up ExecuteFunction.cs and at line #92, you should see that the EndAction() is called.
Simply move the EndAction call within the bracket just above (within the else).
From this:
1 2 3 4 5 6 |
} } EndAction(); } |
To this:
1 2 3 4 5 6 |
} EndAction(); } } |
—-
Cheers and thanks for the bug post π
Join us on Discord: https://discord.gg/97q2Rjh
Hi Gavalakis,
Thanks for the help, it’s working!
I’m experiencing this in v2.0 now. Do I need to make the same change? If so, which function should be changed?
Is there a proper way to call and execute coroutine? Do I need to be doing anything special in the coroutine?
Thanks.
I’m having a similar issue where a somewhat complex coroutine that worked great in NC1 now exits prematurely.
So I’ve been trying to get this to work and I’m having no luck at all. Hopefully I’m missing something obvious but I fear that NC is pretty broken with coroutines and C# events in it’s current state.
So I have the below test script attached to a cube as well as the FSM below. The script runs and executes the initial wait action and then moves to the next action but simply executes the method and immediately goes green, never catching the event that should cause the FSM to change states. In NC1, the state would remain yellow in the graph until the coroutine finished. Then it would move on if any conditions had been met.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
using System; using UnityEngine; using System.Collections; public class TestScript : MonoBehaviour { public delegate void EmptyEventHandler(); public event Action UnDocked; public event Action Docked; public event Action StartUp; public float _timer; public void Update() { _timer = _timer + Time.deltaTime; } public IEnumerator Test2SecondCoRoutine() { while (_timer <= Time.deltaTime + 2f) { //yield return null; } Debug.Log("2 seconds have passed."); _timer = 0f; if (Docked != null) Docked(); yield return null; } public IEnumerator Test1SecondCoRoutine() { while (_timer <= Time.deltaTime + 1f) { //yield return null; } Debug.Log("1 second has passed."); _timer = 0f; if (UnDocked != null) UnDocked(); yield return null; } } |
Here is the FSM export.
{
“version”: 2.0,
“type”: “NodeCanvas.StateMachines.FSM”,
“name”: “FSM”,
“comments”: “”,
“translation”: {
“x”: -5171.998046875,
“y”: -4822.998046875
},
“nodes”: [
{
“_actionList”: {
“executionMode”: “ActionsRunInParallel”,
“actions”: [
{
“waitTime”: {
“_value”: 2.0,
“_name”: null
},
“finishStatus”: “Success”,
“_isActive”: true,
“overrideAgent”: null,
“$type”: “NodeCanvas.Tasks.Actions.Wait”
}
],
“_isActive”: true,
“overrideAgent”: null
},
“_repeatStateActions”: false,
“_transitionEvaluation”: “CheckContinuously”,
“_collapsed”: false,
“_position”: {
“x”: 5430.0,
“y”: 5040.0
},
“_name”: null,
“_tag”: null,
“_comment”: null,
“_isBreakpoint”: false,
“$type”: “NodeCanvas.StateMachines.ActionState”,
“$id”: “1”
},
{
“_actionList”: {
“executionMode”: “ActionsRunInParallel”,
“actions”: [
{
“functionWrapper”: {
“result”: {
“_value”: null,
“_name”: “”
},
“_targetMethod”: {
“_baseInfo”: “TestScript|Test2SecondCoRoutine”,
“_paramsInfo”: “”
},
“$type”: “NodeCanvas.Framework.Internal.ReflectedFunction1[[System.Collections.IEnumerator, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]”
1[[System.Collections.IEnumerator, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]”
},
“_isActive”: true,
“overrideAgent”: null,
“$type”: “NodeCanvas.Tasks.Actions.ExecuteFunction”
}
],
“_isActive”: true,
“overrideAgent”: null
},
“_repeatStateActions”: false,
“_transitionEvaluation”: “CheckContinuously”,
“_collapsed”: false,
“_position”: {
“x”: 5580.0,
“y”: 5220.0
},
“_name”: null,
“_tag”: null,
“_comment”: null,
“_isBreakpoint”: false,
“$type”: “NodeCanvas.StateMachines.ActionState”,
“$id”: “5”
},
{
“_actionList”: {
“executionMode”: “ActionsRunInParallel”,
“actions”: [
{
“functionWrapper”: {
“result”: {
“_value”: null,
“_name”: “”
},
“_targetMethod”: {
“_baseInfo”: “TestScript|Test1SecondCoRoutine”,
“_paramsInfo”: “”
},
“$type”: “NodeCanvas.Framework.Internal.ReflectedFunction
},
“_isActive”: true,
“overrideAgent”: null,
“$type”: “NodeCanvas.Tasks.Actions.ExecuteFunction”
}
],
“_isActive”: true,
“overrideAgent”: null
},
“_repeatStateActions”: false,
“_transitionEvaluation”: “CheckContinuously”,
“_collapsed”: false,
“_position”: {
“x”: 5835.0,
“y”: 5370.0
},
“_name”: “sdfsdf”,
“_tag”: null,
“_comment”: null,
“_isBreakpoint”: false,
“$type”: “NodeCanvas.StateMachines.ActionState”,
“$id”: “11”
}
],
“connections”: [
{
“_condition”: null,
“_infoExpanded”: true,
“_sourceNode”: {
“$ref”: “1”
},
“_targetNode”: {
“$ref”: “5”
},
“_isActive”: true,
“$type”: “NodeCanvas.StateMachines.FSMConnection”
},
{
“_condition”: {
“targetType”: “TestScript”,
“eventName”: “Docked”,
“_invert”: false,
“_isActive”: true,
“overrideAgent”: null,
“$type”: “NodeCanvas.Tasks.Conditions.CheckCSharpEvent”
},
“_infoExpanded”: true,
“_sourceNode”: {
“$ref”: “5”
},
“_targetNode”: {
“$ref”: “11”
},
“_isActive”: true,
“$type”: “NodeCanvas.StateMachines.FSMConnection”
},
{
“_condition”: {
“targetType”: “TestScript”,
“eventName”: “UnDocked”,
“_invert”: false,
“_isActive”: true,
“overrideAgent”: null,
“$type”: “NodeCanvas.Tasks.Conditions.CheckCSharpEvent”
},
“_infoExpanded”: true,
“_sourceNode”: {
“$ref”: “11”
},
“_targetNode”: {
“$ref”: “5”
},
“_isActive”: true,
“$type”: “NodeCanvas.StateMachines.FSMConnection”
}
],
“primeNode”: {
“$ref”: “1”
},
“canvasGroups”: null,
“localBlackboard”: {
“_name”: “Local Blackboard”,
“_variables”: [],
“$type”: “NodeCanvas.Framework.Internal.BlackboardSource”
}
}
Hello guys,
For the coroutine fix, please open up ExecuteFunction.cs and replace the whole contents of OnExecute at line #67 with this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
protected override void OnExecute(){ if (targetMethod == null){ EndAction(false); return; } if (targetMethod.ReturnType == typeof(IEnumerator)){ StartCoroutine( InternalCoroutine( (IEnumerator)((ReflectedFunctionWrapper)functionWrapper).Call() )); return; } if (targetMethod.ReturnType == typeof(void)){ ((ReflectedActionWrapper)functionWrapper).Call(); } else { ((ReflectedFunctionWrapper)functionWrapper).Call(); } EndAction(); } |
Thanks
Join us on Discord: https://discord.gg/97q2Rjh
That fixed it. Thanks.
Side question, this fix will be released (along with cumulative fixes) sometime in the future?
Hello,
Yes, of course this fix along with all other posted around here and more, are in the new update submited π
Cheers!
Join us on Discord: https://discord.gg/97q2Rjh
Woot!
Thanks! Just out of curiosity, how long does it take from submission to approval for Unity assets?
Hey,
It usualy takes 3-5 businness days for updates of current assets π
Join us on Discord: https://discord.gg/97q2Rjh
Hi ! Again, thanks for the amazing work on NodeCanvas, it’s a great tool and a tremendous help to us. Unfortunately we’re facing the exact same problem as mentioned before:
We’re using an ExecuteFunction with a Coroutine. The Coroutine works well but the Action doesnβt wait for it to finish to evaluate the transition.
TransitionEvaluations is set to CheckAfterStateFinished and the possible transition are
If '$success' == 'True'
and If '$success' == 'False'
$success is set to true if everything went well and the FSM can move on to the next State from that moment or roll back if we have an error and $success is set to false.
Is the problem back ?
Hey,
Thanks! π
I have just retried using ExecuteFunction with a coroutine and works as expected. The Execute Function task does not finish before the Coroutine is finished. Can you please post some screen of the FSM? There might be something going in that I’m not aware.
Thanks.
Join us on Discord: https://discord.gg/97q2Rjh
Hey, sorry for the late reply. As you can see on the screenshot, the state of the $success
variable should be evaluated after the coroutine has ended, but the FSM goes directly to the next state (a character is walking and the next state should be triggered once he stops but is currently triggered when the character starts moving).