Fast Loops
Before we can understand how a fast loop works, we must first understand how MMF2 works...
The default frame rate for Multimedia Fusion is 50 frames per second. In each frame, MMF2 completes one loop of your code. Each event is read one at a time from top to bottom and each action within that event is executed one at a time from top to bottom. Once the final event has been executed, the screen is updated or "redrawn" and the entire process is repeated, 50 times per second.
When MMF2 executes a Start loop action, the main event loop is paused right there and then and the fast loop begins. Until that fast loop is over, all events outside that fast loop will be ignored. A fast loop can be run as many times consecutively as you wish (within reason) and be stopped at any time. Let's take a look at the various fast loop actions and expressions.
Start Loop The Start loop action is created by right clicking Special Conditions in the Event Editor and selecting Fast Loops. First, specify the name of the loop and then how many times you wish it to run. For example...
- Upon pressing "Spacebar"
- : Start loop "Add" 200 times
- On loop "Add"
- : Add 1 to Counter
Stop Loop Depending on the complexity of the fast loop, MMF2 is capable of running it many thousands of times. Setting a fast loop to run for -1 times will make it run forever. However, running a loop forever will crash your program, so you must stop the loop at some point with a Stop loop action. For example:
- Upon pressing "Spacebar"
- : Start loop "Add" -1 times
- On loop "Add"
- : Add 1 to Counter
- On loop "Add"
- = 10000
- : Stop loop "Add"
Comparing to the Loop Index Alternatively, you can refer to the loop index. The loop index is an invisible counter which increases by 1 every time the loop repeats and resets to 0 once the loop is over. To compare to the loop index you must create a Compare two general values condition. For example:
- Upon pressing "Spacebar"
- : Start loop "Add" -1 times
- On loop "Add"
- : Add 1 to Counter
- On loop "Add"
- LoopIndex("Add") = 10000
- : Stop loop "Add"
Retrieving the Loop Index Another extremely useful feature of the loop index is one's ability to retrieve it. For example:
- Upon pressing "Spacebar"
- : Start loop "Add" -1 times
- On loop "Add"
- LoopIndex("Add") = 10000
- : Stop loop "Add"
- : Set Counter to loopindex("Add")
Setting the Loop Index Another important (and mostly unknown) fast loop feature is the Set loop index action. First, you specify the name of the loop and then the loop index number you wish to change to. For example:
- Upon pressing "Spacebar"
- : Start loop "Move Enemy" 100 times
- On loop "Move Enemy"
- : Set X position to X( "" )+1
- On loop "Move Enemy"
- LoopIndex("Add") = 50
- : Set loop ("Move Enemy") index to 60
Subsequent Actions Ever the economists that we are, we will always try to perform our various programming tasks in as few lines of code as possible. One way we can do this is by strategically ordering our events and actions. When a fast loop is over, MMF2 will resume executing actions where it left off. For example:
- Upon pressing "Spacebar"
- : Start loop "Move Enemy" 100 times
- : Destroy
- On loop "Move Enemy"
- : Set X position to X( "" )+1
To the user, it will be as if pressing Spacebar instantly destroyed the Enemy, never knowing that it actually moved 1 pixel 100 individual times before it's inevitable destruction. Obviously, the code above is pointless. For a more practical application, check out the Text Blitting Part 2 tutorial.
Fast Loops and Spread Values The Spread Value action assigns each instance of an object, a different ID value (stored in the alterable value you specify). These values are assigned sequentially, starting with the most recently created instance, which is set to the value you choose to spread, while older instances are set to progressively higher values.
By comparing this value to the loop index, you can loop through each instance individually. Consider this hypothetical scenario/problem: There are an unknown amount of Enemies, each carrying an unknown amount of gold, how much gold are they carrying in total? This is how we find out:
- Always
- : Spread value 0 in ID
- Always
- : Set Total Gold to 0
- : Start loop "Find Total Gold" NObjects( "" ) times
- : Set Counter to Total Gold
- On loop "Find Total Gold"
- ID of = LoopIndex("Find Total Gold")
- : Add Gold ( "" ) to Total Gold
Due to a bug in MMF2, strange things happened if you a spread value in the same event that starts a fast loop. A second Always event is required. This event resets the global value Total Gold to 0 and runs a fast loop which loops through each Enemy, adding it's Gold value to Total Gold. Once the loop is over, a Counter is set to this value.
The loop is run as many times as there are Enemies. Say we have 3 Enemies, the loop is run 3 times, with the loop indexes being 0, 1 and 2, matching the 3 Enemy ID values, which also begin at 0. The first loop isolates the first Enemy and adds it's Gold value to the Total Gold value, the second loop isolates the second Enemy, and so on...
When comparing the loop index to an alterable value, be sure to use Compare to one of the alterable values and not Compare two general values. For an in depth explanation why, read the article, Compare to one of the alterable values VS Compare two general values .
Nested Fast Loops Also known as recursion, nested fast looping is the simple task of running one set of fast loops inside another. Demonstrated with another hypothetical scenario/problem: There are an unknown amount of Marines and an unknown amount of Aliens. We wish to constantly update each Alien with the distance horizontal to the closest Marine...
- Always
- : Spread value 0 in ID
- : Spread value 0 in ID
- : Set Closest Distance to 99999
- Always
- : Start loop "Phase 1" NObjects( "" ) times
- On loop "Phase 1"
- : Start loop "Phase 2" NObjects( "" ) times
- On loop "Phase 2"
- ID of = LoopIndex("Phase 1")
- ID of = LoopIndex("Phase 2")
- : Set Closest Distance to Min(Closest Distance( "" ), Abs(X( "" )-X( "" )))
The second Always event starts the first loop, Phase 1, once for each Alien, which then starts the second loop, Phase 2, once for each Marine. Now say we have 3 Aliens and 2 Marines, this means there we will be a total of 6 loops. 3 Phase 1 loops, each starting 2 nested Phase 2 loops.
Each Phase 2 loop will compare the distance between the currently isolated Alien (the loop index of Phase 1) and the currently isolated Marine (the loop index of Phase 2). Using the Min() function, we test if that value is lower than the current value stored in Closest Distance and if it is, replace it. Hence the high starting value.
Optimising your Fast Loops As you can see, fast looping can get pretty complicated and running too many complicated fast loops per frame will reduce your framerate. By nature, fast loops are actually quite slow. One way to speed them up a little is to place each loop's events inside a separate group and only activate that group when you call the fast loop. For example:
- Always
- : Activate group "Loop 1"
- : Start loop "Calculate Complex Math" 1000 times
- : Deactivate group "Loop 1"
Until next time, my friend.