using System; using System.Collections; namespace Unity.VisualScripting { /// /// Loops between a first and last index at a specified step. /// [UnitTitle("For Loop")] [UnitCategory("Control")] [UnitOrder(9)] public sealed class For : LoopUnit { /// /// The index at which to start the loop (inclusive). /// [PortLabel("First")] [DoNotSerialize] public ValueInput firstIndex { get; private set; } /// /// The index at which to end the loop (exclusive). /// [PortLabel("Last")] [DoNotSerialize] public ValueInput lastIndex { get; private set; } /// /// The value by which the index will be incremented (or decremented, if negative) after each loop. /// [DoNotSerialize] public ValueInput step { get; private set; } /// /// The current index of the loop. /// [PortLabel("Index")] [DoNotSerialize] public ValueOutput currentIndex { get; private set; } protected override void Definition() { firstIndex = ValueInput(nameof(firstIndex), 0); lastIndex = ValueInput(nameof(lastIndex), 10); step = ValueInput(nameof(step), 1); currentIndex = ValueOutput(nameof(currentIndex)); base.Definition(); Requirement(firstIndex, enter); Requirement(lastIndex, enter); Requirement(step, enter); Assignment(enter, currentIndex); } private int Start(Flow flow, out int currentIndex, out int lastIndex, out bool ascending) { var firstIndex = flow.GetValue(this.firstIndex); lastIndex = flow.GetValue(this.lastIndex); ascending = firstIndex <= lastIndex; currentIndex = firstIndex; flow.SetValue(this.currentIndex, currentIndex); return flow.EnterLoop(); } private bool CanMoveNext(int currentIndex, int lastIndex, bool ascending) { if (ascending) { return currentIndex < lastIndex; } else { return currentIndex > lastIndex; } } private void MoveNext(Flow flow, ref int currentIndex) { currentIndex += flow.GetValue(step); flow.SetValue(this.currentIndex, currentIndex); } protected override ControlOutput Loop(Flow flow) { var loop = Start(flow, out int currentIndex, out int lastIndex, out bool ascending); if (!IsStepValueZero()) { var stack = flow.PreserveStack(); while (flow.LoopIsNotBroken(loop) && CanMoveNext(currentIndex, lastIndex, ascending)) { flow.Invoke(body); flow.RestoreStack(stack); MoveNext(flow, ref currentIndex); } flow.DisposePreservedStack(stack); } flow.ExitLoop(loop); return exit; } protected override IEnumerator LoopCoroutine(Flow flow) { var loop = Start(flow, out int currentIndex, out int lastIndex, out bool ascending); var stack = flow.PreserveStack(); while (flow.LoopIsNotBroken(loop) && CanMoveNext(currentIndex, lastIndex, ascending)) { yield return body; flow.RestoreStack(stack); MoveNext(flow, ref currentIndex); } flow.DisposePreservedStack(stack); flow.ExitLoop(loop); yield return exit; } public bool IsStepValueZero() { var isDefaultZero = !step.hasValidConnection && (int)defaultValues[step.key] == 0; var isConnectedToLiteralZero = false; if (step.hasValidConnection && step.connection.source.unit is Literal literal) { if (Convert.ToInt32(literal.value) == 0) { isConnectedToLiteralZero = true; } } return isDefaultZero || isConnectedToLiteralZero; } } }