Forums › 💬 NodeCanvas › ⚙️ Support › Iterate gets stuck when list Is changed
I noticed that Iterate gets stuck when the last list element is removed while being executed.
The reason is, that the following if-condition is not entered as currentIndex > list.Count-1
.
1 2 3 4 5 6 |
if ( currentIndex == list.Count - 1 || currentIndex == maxIteration.value - 1 ) { if ( resetIndexAfterIteration ) { currentIndex = 0; } return status; } |
The following calls to Iterate’s OnExecute
method skip the whole for-loop as currentIndex > list.Count
. The list is therefore forever stuck in Status.Running
.
As I simple fix, I changed the if check to a greater or equal check:
1 2 3 |
if ( currentIndex >= list.Count - 1 || currentIndex == maxIteration.value - 1 ) { ... } |
I know this is an edge case but it is also an edge case that is easy to fix 😉
Thank you for the fix! I will do that as well 😉
I just noticed that the iterator gets stuck if list.Count
gets reduced after the iterator was executed.
E.g. Iterator was executed -> element is removed from list -> interator is executed again.
I added
1 2 3 4 |
if ( currentIndex > list.Count - 1 || (maxIteration.value >=0 && currentIndex > maxIteration.value - 1) ) { if ( resetIndexAfterIteration ) { currentIndex = 0; }} |
infront of the iterator’s for loop.
made some small modification to currentIndex==maxIteration.value -1
aswell. MaxIteration could be changed during runtime. If currentIndex > maxIteration, I would expect the loop not to continue with its normal operation.
Hey,
Do you mind posting the full Iterator.cs code just to double check your changes please? 🙂
Thank you!
This is the iterator as we are currently using it.
Note:
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
[Name("Iterate Interface List")] [Category("Decorators/Custom")] [Description("Enables to iterate any generic type of interface list and execute the child node for each element in the list. Keeps iterating until the Termination Condition is met or the whole list is iterated, in which case the last interation's child status is returned.")] [Icon("List")] public class CustomIterator : BTDecorator { public enum TerminationConditions { None, FirstSuccess, FirstFailure } [RequiredField] [BlackboardOnly] [Tooltip("The list to iterate")] public BBParameter targetList; [BlackboardOnly] [Name("Current Element")] [Tooltip("Store the current element")] public BBObjectParameter current; [BlackboardOnly] [Name("Current Index")] [Tooltip("Store the current index")] public BBParameter storeIndex; [Tooltip("The maximum count of iterations. Leave at -1 to iterate the whole list")] public BBParameter maxIteration = -1; [Tooltip("The condition when to terminate the iteration and return status")] public TerminationConditions terminationCondition = TerminationConditions.None; [Tooltip("Should the iteration start from the begining after a node reset?")] public bool resetIndexOnReset = true; [Tooltip("Should the iteration start from the begining after end of iteration has been reached?")] public bool resetIndexAfterIteration = true; private int currentIndex; private IList list { get { return targetList != null ? targetList.value : null; } } protected override Status OnExecute(Component agent, IBlackboard blackboard) { if ( decoratedConnection == null ) { return Status.Optional; } if ( list == null || list.Count == 0 ) { return Status.Failure; } if ( currentIndex > list.Count - 1 || (maxIteration.value>=0 && currentIndex > maxIteration.value - 1) ) { if ( resetIndexAfterIteration ) { currentIndex = 0; } } for ( var i = currentIndex; i < list.Count; i++ ) { current.value = list[i]; storeIndex.value = i; status = decoratedConnection.Execute(agent, blackboard); if ( status == Status.Success && terminationCondition == TerminationConditions.FirstSuccess ) { return Status.Success; } if ( status == Status.Failure && terminationCondition == TerminationConditions.FirstFailure ) { return Status.Failure; } if ( status == Status.Running ) { currentIndex = i; return Status.Running; } if ( currentIndex >= list.Count - 1 || (maxIteration.value>=0 && currentIndex >= maxIteration.value - 1) ) { if ( resetIndexAfterIteration ) { currentIndex = 0; } return status; } decoratedConnection.Reset(); currentIndex++; } return Status.Running; } protected override void OnReset() { if ( resetIndexOnReset ) { currentIndex = 0; } } //////////////////////////////////////// ///////////GUI AND EDITOR STUFF///////// //////////////////////////////////////// #if UNITY_EDITOR protected override void OnNodeGUI() { var leftLabelStyle = new GUIStyle(GUI.skin.GetStyle("label")); leftLabelStyle.richText = true; leftLabelStyle.alignment = TextAnchor.UpperLeft; GUILayout.Label("Anakin For Each " + current + "nIn t" + targetList, leftLabelStyle); if ( terminationCondition != TerminationConditions.None ) GUILayout.Label("Break on " + terminationCondition.ToString()); if ( Application.isPlaying ) GUILayout.Label("Index: " + currentIndex.ToString() + " / " + ( list != null && list.Count != 0 ? ( list.Count - 1 ).ToString() : "?" )); } protected override void OnNodeInspectorGUI() { DrawDefaultInspector(); if ( GUI.changed ) { var argType = targetList.refType != null ? GetEnumerableElementType(targetList.refType) : null; if ( current.varType != argType ) { current.SetType(argType); } } } public static Type GetEnumerableElementType(Type type) { if ( type == null ) { return null; } if ( !typeof(IEnumerable).RTIsAssignableFrom(type) ) { return null; } if ( type.HasElementType || type.RTIsArray() ) { return type.GetElementType(); } if ( type.RTIsGenericType() ) { //These are not exactly correct, but serve the purpose of usage. var args = type.RTGetGenericArguments(); if ( args.Length == 1 ) { return args[0]; } //This is special. We only support Dictionary<string, T> and always consider 1st arg to be string. if ( args.Length == 2 ) { return args[1]; } } var interfaces = type.GetInterfaces(); for (var i = 0; i < interfaces.Length; i++){ var iface = interfaces[i]; if (!iface.RTIsGenericType()){ continue; } var genType = iface.GetGenericTypeDefinition(); if (genType != typeof(IEnumerable<>)){ continue; } return iface.RTGetGenericArguments()[0]; } return null; } #endif } |