MMF is a great tool that simplifies a hell of a lot of things when creating a game, I guess everybody around here knows that. But MMF also has its problems when it comes across some particular situations. One of these problems is what I call MMF's object focus: When something happens to a specific object, MMF checks if some conditions are true and then somehow performs actions that influence the object. But it's not always that easy.
But let's start with a simple example. E.g. the player's sprite... If the sprite is overlapping a background, it should stop falling. Or something like that. It's a simple cycle: Something happens to an object, MMF checks if some conditions are true and then (well, if they are actually true) performs the actions that influence the object.
But what happens if there are several instances of the same object? Everybody who uses Clickteam products usually stumbles across this question sooner or later (usually sooner, but who cares...). Usually the first time this question comes up, it's about enemies that should have several hitpoints, but that doesn't really matter. MMF has a solution for this problem: It remembers which instance of that object fulfills the conditions and performs the specified actions for that single object only. An example would be an enemy that has limited ammunition: After it has fired 20 bullets (counted by one of its alterable values), it has to reload. So if that single enemy has fired 20 bullets, that single enemy (and no other one) will reload.
However, life wouldn't be life if everything was that easy. And a computer wouldn't be a computer if it does immediately what the user wants it to do... MMF usually becomes a nasty little bitch when it's supposed to focus on the right objects in events that have more complex conditions. Personally, I came across this problem when I wanted to do a movement for my platform game enemies: I wanted to keep the level design as simple es possible. My enemies should act like a fire and forget missile: Just put them into there and let them walk. I didn't want to do any AI masterpieces, but I wanted them to do basic stuff like firing at the player, not falling down abysses, not walking through walls etc. etc.. And I wanted them to do it without any path nodes or waypoints or dummies or whatever. MMF 1.5 has a great collision mask feature that checks (without using any active objects or something) for the level structure at a particular x-y coordinate. All I wanted the enemies to do is to check if there is an obstacle at e.g. x=128; y=192 - and if not, to turn around (because there would be an abyss or something similar). But my event (negate: collision mask at coordinates relative to enemy is an obstacle) simply didn't work as it was supposed to. The reason was simple: There aren't many conditions that make MMF remember the object it has tested. And that collision mask condition didn't do it. MMF checked for the first enemy and didn't run the actions. It checked for the second and it checked for the third and so on. And suddenly, when one enemy (be it number 13 or whatever), it had to run those actions. But it didn't know what object should be used, it couldn't focus on the right object. And therefore, the actions were run for all objects. Every last enemy in that level turned around.
I was thinking and trying and thinking again. And finally found a solution that does not only work for some rare examples but one that should fix any object focus bug in MMF's games.
Alterable values are one of the few values that are checked for every single instance seperately. So if I could just run the same events for every single instance again, it should work. MMF comes with the spread value function: Every instance of an object gets a number, but unlike fixed values which are pretty much random numbers, the spread values enables you to count through the instances: The first object starts with 0, the next gets 1, then 2, 3, 4, 5, 6, 7 and so on (okay, it doesn't have to start with 0, but that doesn't matter now). All that has to be done for checking the conditions for each instance on its own is to spread a value in one of its alterable values and to add a counter that is raised one by one. Then, one more condition has to be added for the events that make MMF's object focus refuse to work: The object's alterable value has to have the same value as the counter has.
That'd be fine, but MMF is slow as hell. It only runs its code 50 times per second, so if there were 50 enemies, every enemy would only be checked once in a second which might be not enough. But there is the amazing fast loop object everybody should know how to use: It can run events several times within one single MMF-beat. So all that is left is add a new loop that runs as many times as there are instances of the object that is tested in the frame (simply select number of objects). Voilà, it works.
So what events do we have now? The first one should be a set counter event - the enemies should be checked over and over again, so the counter has to be set to 0 before MMF enters the loop.
always: Set counter to 0
Then, we start the loop.
always: Start loop number 0 for [number of object object] times
Within that loop, each object has to get its number...
loop trigger 0: Spread value 0 in alterable value a of object
... so we can check for something that should influence that particular object alone (e.g. the collision mask or whatever).
loop trigger 0 + alterable value a of object = value of counter + collision mask (x of object / y of object + 32) is an obstacle
And finally, the counter's value has to be raised... After all, all objects should be checked, not object number 0 several times while the other ones aren't checked at all
loop trigger 0: add 1 to counter
Of course, all numbers and names and whatever can be changed...
All in all, the example described here was, as the name suggests, only an example. Nothing more. MMF's focus doesn't really always work as it should, but this is a working solution for practically every problem that might come up concerning that problem - and it isn't that much work. Of course, it's a new counter and it's one more loop, but apart from that it's not that much more than before. On the other hand, this is only three more events: start the loop, spread the value and add 1 to the counter (if you put the last two actions into one event, it's even merely two events) - and you can put as many other events into that loop as you want (like checking for four points - obstacle left, obstacle right, no obstacle bottom left, no obstacle bottom right) etc. etc.. So this really keeps the code clean and enables you to do some things that MMF refused to do before.
Good article - covers an important topic.
If you do extension programming you learn a lot about this.
This is a well written article but I wouldn't have expected a non-extension programmer to know this - it's for your reference and help:
The 'object focus' is called object selection by MMF. Selected objects are the ones that apply to the event. For example, alt val A = 3 will select out of all duplucates, the ones with alt val A equalling 3. This could be all of them, none, or some. Then, the ACTIONS are repeated for each individual duplicate. This happens for ALL conditions, objects, and actions - but MMF will only go so far to find which objects you want.
I could write a whole other article on this (and did, at some point, somewhere) - but in brief:
MMF will not test every combination of events for you, or it would take loads of processing time. Take this for example:
Val A of active = Val A of active 2
MMF will look through every Active to find the one that matches the FIRST instance of active 2. It won't search further, because complex events could require tens of thousands of combination testing with duplicates.
Also if MMF doesn't know which object you want specifically, it either takes the first instance or all of them, e.g.:
(Compare 2 gen values) collision mask backdrop at X("Active") + 32, Y("Active")
MMF doesn't know which active you want here, so it only takes the first one. If you had selected some objects previously with val A = 3 for example, in an above condition, then it would check the first selected Active and apply the actions to that one object. I haven't fully explored this yet myself, but it seems you should avoid compare 2 general values if you want things to work with duplicates. Compare X position etc. keeps everything selected nicely. It's only the expressions where MMF is forced to choose.
Basically MMF's object selection works exactly as it should, but this sometimes isn't what you want, but the change is deliberate, for efficiency. I had figured out about as much as you have now before I started programming, this extra info should be useful though.
"loop trigger 0 + alterable value a of "object" = value of "counter" + collision mask (x of "object" / y of "object" + 32) is an obstacle"
there is just one thing i want to say
as explained :
'alterable value a of "object" = value of "counter"'
make MMF scan all object to find the one who have an alterable value = counter
there is just one probleme about speed
when MMF will execute the line above , if there are N object , he will search through N object in this line , but this line is in a loop so MMF will scan N*N object , so if you have 10 object it will be executed 100 times , if you have 25 object it will be executed 625 times (!!) , if you have 100 object it will be executed 10000 times , for just 1 event !
we can do nothing about it , the simple way to speed up it to limit this condition to one event and replacing the other condition by a faster one , one of the faster I found is to compare with the fixed value , MMF must have a short way to do it
but this way is pretty good solution to this problem
@Tigerworks: There are some points I already knew (believe it or not ) and some that I didn't, very informative, thanks. Of course I could have made this article twice or thrice as long, but all I wanted to explain is how to keep events focused on one object that usually aren't and I was afraid that it probably would have gotten too long and too complicated if I had added more... But nonetheless, thanks
@Batchman: I know a loop increases the events to run by [Number of Objects] * [Number of loop events]... And it might seem like a lot. But it works - and it works fine, trust me
i have already used this , i know it solve lots of problem , but when I give this solution without remplacing the comparison of the alterable value by the comparison of the fixed value , poeple say me it is slow on their 300Mz-400Mz
in one of my games (team puzzle) I have the wrong idea to put a other loop inside an loop , and each time I repeated
compare with X position = counter
+ compare with Y position = counter
Result : the game is slow on 600 Mz computer and the 2 player mode were not released because it was slow on my own 1700Mhz comp
"1. A fastloop is always finshed before MMF moves on to the next event."
Well, yes, it is... But I don't really see why it should be mentioned here. This article isn't about the fast loop object
"2. The counter is not needed as you can use the "loop step" expression to get the loop number."
Yepp, didn't know that.
"3. You don't need to spread the Alt Val at the start of each loop, once at the start of the frame is enough, but then you'll need to "re-spread" every time an enemy dies/is created."
In theory, yes. However, it didn't work for me when I tried it. I used an always-event just before the loop and the whole thing got messed up...
VERY nicely explained for those who just dont get it, but there is still no answer to the same problem backwards (well noit exactly):
what if MMF thinks you want it to focus on a certain object and you dont want it to? the answer to this came to me eventually tho
the get loopstep isnt like having the counter there cause for example: if the loop is on its 5 step then it could return 13(i know this because i made a level editor save stuff in an ini and each item was named like the loopstep and everything got mixed up. but when i used a counter it didnt got mixed up)
MMF's problem is with expressions in conditions - it only runs each condition once, but the difference with Alterable Value comparison and Collisions etc. is the way they are programmed. If one of the parameters is an OBJECT (not an expression, which may or may not get data from an object) all instances of that object are checked. Expressions are only evaluated once, regardless of any object data retrieval, so all objects will default to the first (selected) instance. That's why you have to loop through all objects manually if you're using the object in an expression.