- Joined
- Sep 6, 2013
- Messages
- 6,742
How-to Loop In GUI!
1. Introduction top
Welcome to this tutorial where we'll have a look at the loop functionality.
We will learn about the loop's basic usage, and how they work together with counters.Also, with the loop, a common, but a dangerous interfering risk comes along that should be definitly known!
2. What is a Loop? top
A loop helps you to perform some actions multiple times without duplicating your code.
To achieve this, we will have 3 numbers (integers):- start-value
- end-value
- counter
- Counter starts with value of start-value
- Check if counter is greater than the end-value. If yes, the loop will directly exit, if not, proceed to step 3
- Perform all actions inside the loop ( loop run )
- Increase Counter by +1
- Go back to step 2
Inside the loop run we always have access to our counter, the current index of the loop, which might become handy for many actions we plan to do.
3. Simple Loop Example top
Let's have a look at a practical example. We want to print all names from Player_1 to Player_6 on screen. The code might look like this:
-
For each (Integer A) from 1 to 6, do (Actions)
-
Loop - Actions
-
Game - Display to (All players) the text: (Name of (Player((Integer A))))
-
-
- Our start-value is 1
- Our end-value is 6
- As counter/current index we use IntegerA
4. Why a Loop? top
What our loop in the example above does, is actually the very same as:
-
Game - Display to (All players) the text: (Name of (Player(1)))
-
Game - Display to (All players) the text: (Name of (Player(2)))
-
Game - Display to (All players) the text: (Name of (Player(3)))
-
Game - Display to (All players) the text: (Name of (Player(4)))
-
Game - Display to (All players) the text: (Name of (Player(5)))
-
Game - Display to (All players) the text: (Name of (Player(6)))
The difference becomes more clear, once we want to change logics, like for example to print now all names from Player_7 to Player_16. Without a loop we have to touch all single action we created above. We or need to modify them, create new ones, or we need to remove them. But with the loop, all what we have to do is to change two numbers in the loop definition:
-
For each (Integer A) from 7 to 16, do (Actions)
-
Loop - Actions
-
Game - Display to (All players) the text: (Name of (Player((Integer A))))
-
-
So, with using the loop you end up having more efficient code which is easier to maintain, and to read. You reduce code duplicates, and prevent making same changes, or even same mistakes at many places!
5. Loop Interfering - Problem top
In our loop we usually expect our counter to be increased by +1 after each loop run. Always. We expect it to start at start-value, and to go all steps through upto end-value.
But what happens if we unwillingly change the loop counter during a loop run to an arbitary value? - Our loop would break, or at least have undefined behaviour. Let's have a look at a code example:We run following trigger, Trigger1, at map init, loop from 1-3, and we want to print "Loop_1" in each loop run.
Inside the actions we do also run some action to execute an other trigger. So in each loop run of Loop_1 we run Trigger2.
-
Trigger1
-
Events
-
Map initialization
-
-
Conditions
-
Actions
-
For each (Integer A) from 1 to 3, do (Actions)
-
Loop - Actions
-
Game - Display to (All players) the text: Loop_1
-
Trigger - Run Trigger2 <gen> (ignoring conditions)
-
-
-
-
-
Trigger2
-
Events
-
Conditions
-
Actions
-
For each (Integer A) from 1 to 3, do (Actions)
-
Loop - Actions
-
Game - Display to (All players) the text: Loop_2
-
-
-
-
But it only prints the following:
(indention is only cosmetics)Loop_1
Loop_2
Loop_2
Loop_2
What has happened, and why has "Loop_1" only printed once?
What is going on is that both triggers share the same counter for the loop iteration. They both use IntegerA, which is just one variable.
When Loop_2 has finished the IntegerA variable already holds a value larger than the end-value of Loop_2 "4".
Trying to continue the "Loop_1" will just fail now because IntegerA is already 4, which is higher than the end-value of Loop_1!
So the Loop_1 has ended prematurely, and we had only one single loop run. This is definitly not wanted in this case, and it is a potential problem in all loops you create.
Also, this interfering problem does not only happen with the obvious "Trigger - Run" action, but it can occur inside other, less transparent scenarios, too! Let's see...
In general, each action in the loop that will cause an other trigger to run has a risk to break the loop, if the loop counter gets changed!
Here are examples of actions that may unwillingly fire other triggers:
- Killing a unit
-> fires all trigger registered with event "Unit - A unit Dies"
- Creating a unit
-> fires all triggers registered with event "Unit - A unit enters Region", if the unit gets created in respective region / Entire Map
- Damaging a unit
-> fires all triggers where the unit is registered with event "A unit gets damaged", like for example in all DamageDetection systems!
- Ordering a unit
-> fires all triggers with registered with event "Unit - A unit is issue Point Order/Target Order/..."
- Kicking a player
-> fires all triggers registered with "Player - A Player leaves Game"
6. Loop Interfering - Solution top
How can we face the problem, when we just need actions that will cause other triggers to run, that themselves will need a loop counter, too?
The solution is actually pretty simple! For all triggers that may fire other triggers you need an own integer variable as loop counter.We need to switch from IntegerA/IntegerB-loop to a loop that now uses our own variable:
Let's look at our "Problem Example" from before, and let's also create now two new integer variables:
- LoopCounter_Trigger1
- LoopCounter_Trigger2
-
Trigger1
-
Events
-
Map initialization
-
-
Conditions
-
Actions
-
For each (Integer LoopCounter_Trigger1) from 1 to 3, do (Actions)
-
Loop - Actions
-
Game - Display to (All players) the text: Loop_1
-
Trigger - Run Trigger2 <gen> (ignoring conditions)
-
-
-
-
-
Trigger2
-
Events
-
Conditions
-
Actions
-
For each (Integer LoopCounter_Trigger2) from 1 to 3, do (Actions)
-
Loop - Actions
-
Game - Display to (All players) the text: Loop_2
-
-
-
-
As result, Loop_1 will not end prematurely and both loops work as expected. The final result looks like:
... for each run in Loop_1, Trigger2 is fired and Loop2 will correctly run!Loop_1
Loop_2Loop_1
Loop_2
Loop_2
Loop_2Loop_1
Loop_2
Loop_2
Loop_2
Loop_2
Loop_2
Last edited: