This should be a simple but thorough article to explain exactly how Spread Value works, and how you can use it. In case anyone didn't know.


Basic Background Info for Newbies (skip if you're used to MMF/TGF)

All Active Objects, from TGF to MMF to MMF2, all have what we call Alterable Values. If you create one monster object, and place several copies of him throughout the level, these Alterable Values allow you to make each copy unique.

A classic example is a damage counter. We may do this:

- Player colides with Enemy
+ Player is doing animation 'Attack'
--- Add '5' to Enemy value A

In MMF, we can also rename the value, so we may call it 'Damage' instead of 'Value A'

Then we could do:

- Enemy 'Damage' is greater than 20
--- Destroy Enemy

This selects ONLY those enemies that have received more than 20 points of damage, and destroys them. Objects with less damage don't match the conditions, so don't get destroyed.

Spread Value allows us to take this concept a step further.

The Basic Idea of Spreading

The 'Spread Value' action takes a value and uses it as an ID for all the copies of an object. So in the case of our enemy, suppose we do this action:

- START OF FRAME
--- Spread '1' in Alterable Value A of Enemy

If you had a look at Alterable Value A for each of our Enemy objects, you would find they go like this:


Copy of the Enemy | Value A
-------------------|-----------
1st Enemy | 1
2nd Enemy | 2
3rd Enemy | 3
4th Enemy | 4


-
...and so on. It causes that alterable value to act as an ID number - so that if there are 5 enemies, each one will have an ID number between 1 and 5.

Since we can rename Alterable Values, it would be a good idea to rename Value A so that it now has a name like 'ID' - just so we know at a glance what we're using it for.

We could have used any Alterable Value for this, and as you'll see later, there are some situations where you could use several at the same time!

A note about spreading

At this point, just a little note. Remember when we told it to spread the enemies (to give them an ID number), we gave it the number '1'? Well that's just what we call the Index, or the Starting Number. It means the enemies will be numbered 1, 2, 3, 4, etc. If we typed '0', then the enemies would be numbered 0, 1, 2, 3, and so on. If you type '5', they get numbered 5, 6, 7, 8.

Normally, I just use either '1' or '0'.

Spreading and Object Selection

Do you remember in our first example, for the newbies, where we had the following condition:

- Enemy 'Damage' is greater than 20
--- Destroy Enemy

...where ONLY the enemies whose damage is greater than 20 got destroyed?

This is because MMF has a kind of 'object selection' system in its events. Only the objects that match the conditions will be affected by the actions you choose.

This is also true for the Spread Value action.

When an event first starts, it selects every object by default. Your conditions then work to narrow down or exclude objects from that list. So if you have no specific conditions (for example, just a START OF FRAME condition, or an ALWAYS condition) then all objects will be selected.

This is why, when we did:

- START OF FRAME
--- Spread '1' in Alterable Value A of Enemy

...every enemy was given an ID number. Because they are all selected by default, and there was nothing in the conditions to narrow that focus down.

But suppose we draw a line halfway down the screen (at a position of, say, 320px), having some enemies on the left and some on the right. And we do this:

- ENEMY's X Pos is greater than '320'
--- Spread '1' in ID of Enemy

The result is that all enemies on the LEFT will have an ID value of 0. But the ones on the RIGHT will have ID numbers going through 1, 2, 3, 4, 5, etc.

This is because, like everything else, Spread Value only affects the objects that match the conditions.

Object Matching

Okay, so let's see what we can actually do with this thing.

There's a quirk in TGF/MMF which brings Spread Values into their own:

Suppose you create two objects - a tank body, and a tank turret. You're keeping them as separate objects so that the tank can aim in one direction but drive in another.

You create 5 copies of the Tank Body, and 5 copies of the Tank Turret, and you do this:

- Always
--- Set position of Tank Turret to Tank Body

This selects every tank, and every turret, as we were saying earlier. But the 'set position' action places just one Turret at each Tank Body.

The result is that every Tank has a Turret neatly placed over it - and all in just one event!

If we give ID numbers to the tanks and turrets like this:

- Start of Frame
--- Spread '1' in ID of Tank Body
--- Spread '1' in ID of Tank Turret

...then we will notice that the tanks automatically match up with their ID numbers!

So Turret#3 will follow Tank#3, and Turret#2 will follow Tank#2.


Now suppose we want to make it so that when the player shoots a turret, the tank associated with that turret will take damage. We can do this:

- Bullet Collides with Tank Turret
+ ID of Tank Body = ID of Tank Turret
--- Add 5 to Damage of Tank Body
--- Destroy Bullet

Let's go through that.
At the start, all objects are selected.
The first condition narrows it down and out of all the Tank Turrets, selects only the one that collided with the bullet. And of all the bullets, it selects only the one that collided with the turret.
The SECOND condition selects whatever Tank Body has the same ID number as the Turret.

This way, we're tying the objects together using a property that they have in common (the ID number).

So when we get to the actions, only the Tank Body with the same ID as the Turret that was hit by the bullet will be damaged! And only the bullet that hit the Turret will be destroyed.

Nifty!

Another very common example of this is using Detector Objects. Suppose we want an enemy to spot ammo if it's near him, and run to it. We draw a big solid circle and insert it as an Active Object (we'll name it 'Detector').

We give it a spread value:

- Start of Frame
--- Spread '1' in ID of Enemy
--- Spread '1' in ID of Detector

...and we make the detectors follow the enemies:

- Always
--- Set position of 'Detector' to 'Enemy'

Now we can do this:

- Detector is Overlapping Ammo
+ ID of Enemy = ID of Detector
--- Enemy looks at Ammo

Again, we start selecting every copy of every object.
Then, only the detector that overlaps Ammo will be selected, and only the Ammo that overlaps a detector will be selected.
In the second condition, only the Enemy that has the same ID as the Detector will be selected.

So the result is that only the Enemy whose ID is the same as the Detector that overlaps the Ammo will be affected. And he will only look at the Ammo that was overlapping the Detector.


This concept of tying objects together using Spread Values is a fundamental key to getting the power out of any Click product.

Extending the Above

Just to illustrate the usefulness of this, let's play with another example:

How about having an enemy that shoots at us... but when the bullet hits us, we get a message saying You were shot by enemy number 5!

Or even better, how about we give our enemies NAMES. And we make it so that it says You were shot by Bernard! or Steve! or Fred!


We'll create an Enemy, and place 5 copies of him on the level (often, we call these copies 'Instances' - just in case you didn't know).

We then create a List Object with 5 lines in it:


Bob
Fred
Bill
Bernard
Steve


-
We do the code to assign each enemy an ID (I won't repeat it again, you know it by now. Start of frame, etc).

Then we do some more code to make the enemies shoot. Now, suppose every 5 seconds, the enemies shoot at you:


- Every 5 Seconds
--- Enemy shoots Bullet towards Player
--- Set ID of Bullet to ID of Enemy

This means that each bullet is carrying the signature of its owner. So if Enemy#3 fires a bullet, that bullet will have an ID of 3 as well.

Then we do this:

- Bullet colides with Player
--- Display message: You were shot by...

...and this is where it gets more interesting.
We use an expression to look at the line in the List Object that refers to the enemy that shot you.

So if you were shot by Enemy#3, then the bullet will have an ID of 3. So you look up the line in the List Object that corresponds with the ID of the bullet (in this case, '3'). You would use an expression like this:


List Line Text$( List Object, ID(Bullet))

-
And that would return the 3rd line of the list object. So we end up with a message that reads:

You were shot by Bill!

As you can hopefully see, with more work you could use this to keep track of how many times a certain enemy shot you, and create a kind of scoreboard for the AI. You could also test to see if an enemy shot another enemy, and create a kind of revenge killing thing between your AI characters (Bob shoots Fred, so Fred chases and attacks Bob).

Looping! (Yes, there's more)

Unfortunately, Object Selection in MMF is not without its problems. You can't do EVERYTHING just by binding things together with IDs, or testing which objects overlap other objects, that overlap other objects, that are in a certain direction, and that overlap yet more objects.

You reach a point of stupidity where you really wish you could just do events for each object one after the other.

That's where Spread Values can help, if you bind them with Fast Loops. (Note; in TGF, fast loops are done with an extension)

If you want to go through every object, one by one, performing your actions on each of them, you can do this:

- Always
--- Start Loop 'My Loop' 'nObjects(Enemy)' times

That literally means start a loop for every Enemy.
Then, in the loop conditions, do this:

- On Loop 'My Loop'
+ ID of Enemy = Loopindex(My Loop)
--- Do whatever actions you had in mind

This binds the loop index (the ID number of the current loop that you're doing) with the ID of the Enemy.

In a situation like this, it's wise to either Spread a value of '0' in your Enemies, or to say 'Loopindex(My Loop)+1', since fastloops start with 0.

And if you do that, then you know none of your enemies are being missed. It also gives you greater control, at a certain cost in speed.


More Advanced Loops

Remember how I said you could Spread a value in objects that only matched certain conditions? Well there's also an object (a plugin for MMF) that can tell you exactly how many objects were selected by certain conditions (how many enemies it selected, how many bullets, whatever you want).

We can combine this to fastloop through only the objects that match a set of conditions (which obviously reduces the workload on the Processor).

For example, suppose we have a fairly intense AI routine, and we only want it to run on the enemies that are visible on-screen.

We can do this:

- Enemy is within -32px of the Window's Edge
--- Spread '1' in ID of Enemy

- Enemy is within -32px of the Window's Edge
--- Start Loop AI 'Selected(Selected Object Counter, Enemy)' times

Important note! There's a reason we've divided the above event into TWO events, and that's explained in Appendix 1 at the bottom

For all objects, there's an 'Is Object within a certain distance of the window edge' condition. If you type a minus figure, it tests if an object is only just outside the window. So we use this to select objects which are in, or almost in, the window area.

We then Spread them.

Then we use an extension called the Selected Object Counter to select how many Enemies were picked up by that condition, and run a loop that many times.

- On Loop AI
+ ID of Enemy = 'Loopindex(AI)+1'
--- Do some AI actions...

Now we simply loop through the objects.

Since we spreaded a value of '1', any that weren't touched by the spread will stay at '0'. So they won't be looped at all.

Now finally, just some notes about little things to do with object selection and Spread Values. I hope you're not bored by this point!

Appendix 1 - Fastloops mess with Object Selection

In our AI example above, where we only wanted to loop through enemies that were on-screen, we used a condition to select and Spread only those objects.

However, we then copied that condition and made an entirely separate event when starting the loop!?

Why not just do this:

- Enemy is within -32px of the Window's Edge
--- Spread '1' in ID of Enemy
--- Start Loop AI 'Selected(Selected Object Counter, Enemy)' times

...using just one event?

The reason is because FastLoop messes with object selection.

When any event runs, it runs for every copy of the objects you selected. So if there are 5 enemies on-screen, the above event actually repeats itself 5 times (once for each object). It's like a mini-fastloop all of its own.

Fastloops cause MMF to forget which objects were selected by the conditions, so after the loop finishes, MMF can't complete the rest of the Spread Value action. The result is that it only applies it to one object, no matter how many you selected. Bummer.

So we simply split them into two events. Select the objects and spread them FIRST. Then let MMF go to the *next* event which will run the fast loop.


Appendix 2 - How Spread Value Numbers Stuff

Spread value gives the ID numbers according to the order the objects were created in, starting with the newest.

So if you create 5 objects, and you later create a 6th, then object #1 becomes object #2. Object #2 becomes Object #3, and so on. All the IDs shift along one.

This can be a pain if you have specific conditions that you want to run for a certain object.

One example is a game I made a while ago where there are several signposts. Depending on their ID number, a different message pops up when you touch them (it works the same way as the 'Naming' example we did above).

But if I created a new Signpost, all the IDs for the previous ones shifted and became wrong!

You can get around this using a simple formula when you spread a value. I'll do it in words first, then as you would do it in an expression, then as a whole event:


Number of Signposts - ID of Signpost + Starting Value

-
So if we're spreading a value of '1' in our signposts, the expression would be:


nObjects(Signpost) - ID(Signpost) + 1

-
Wooyay. Now we apply this in our spreading condition like this:

- Start of Frame
--- Spread '1' in ID of Signpost
--- Set ID of Signpost to 'nObjects(Signpost) - ID(Signpost) + 1'

And that successfully reverses the order of your Spread Values, so they don't shift when a new object is added later. Because it spreads a value according to normal rules, and then inverts it straight afterwards with the formula we did.

Appendix 3 - Adding Objects at Runtime

Using 'Start of Frame' is best for giving ID numbers, but only if you don't plan to create new enemies during gameplay.

If you DO (like if you have enemy spawn points or something), there are two simple ways you can get around it.

OPTION 1

- Always
--- Spread '1' in ID of Enemy

This just runs constantly, updating the ID numbers for every object all the time. But it's a bit wasteful.

OPTION 2

- Start of Frame
--- Start Loop 'Give IDs' 1 time.

- Enemy ID = '0'
--- Start Loop 'Give IDs' 1 time.

- On Loop 'Give IDs'
--- Spread '1' in ID of Enemy

This is more efficient. We use a fastloop as if it was a function. We only need to run one loop, no matter how many objects there are.

We run the loop at the start, so all objects are spreaded at the beginning.

But also, since we're spreading a value of '1', it makes sense that any objects with a value of '0' must not have been spreaded yet. In other words, they're newly created objects.

So if MMF can find an Enemy with a value of '0', this means there are enemies who were created that have yet to be spreaded. So it runs the Spread function again. Because we're calling a fastloop, MMF loses object focus and spreads ALL enemies, not just the newly created ones.


Well... I said it was gonna be thorough...