Jump to content
A 2021 backup has been restored. Forums are closed and work in progress. Join our Discord server for more updates! ×
SoaH City Message Board

MMF2/CF2.5 Optimization Superthread


Recommended Posts

EDIT: Oh wait, this isn't about object selection, what you are talking about is setting values on individual instances of a single object. That's cool, but it still doesn't help with optimization since that can be done in MMF with spread value and a table. It's just really awkward and shitty in MMF.

Link to comment
Share on other sites

Something I've wondered. In The Construct, they prefer the use Function instead of using Loop. Are Functions faster, or better for optimization? I know MMF has Function object, but I think it's rather dated.

Link to comment
Share on other sites

Functions and Loops are just two totally different things that can be used similarly and actually act similarly when you look at the way they are handled in terms of memory and flow control. The primary purpose of a loop is iteration, where you repeat an action many times. A function on the other hand... the primary ideas in that case are reusing code and being able to use the function to get some kind of return value. If you recall from math class, a function has specific input and specific output... though in the case of programming this isn't always true. You can have functions that take nothing and produce nothing, they just aren't especially useful (actually, that's not true, they are, just for the purpose of organization).

In MMF, loops tend to see a lot of use because they work a lot like functions... though more accurately they should be called subroutines since they aren't especially designed to work as functions. You can get around the fact that they don't directly take input and produce output by using a proxy object, which you'll see a lot of that out of me in my MMF projects.

In theory, a loop is basically just a goto statement with termination conditions. It shouldn't be very costly. In practice within MMF, I'm not so sure. The event system makes everything rather complicated in regards to cost, and it's a black box. There is really no knowing what MMF has to do under the hood to enter a loop or to evaluate what events are part of a loop... it could be almost no cost or it could be a totally convoluted system with a large incurred cost. It's difficult to say.

Link to comment
Share on other sites

  • 1 month later...

My next topic of experimentation is going to be use of the 'collision mask'. Basically you pick a pixel on the map and MMF will tell you whether it's defined as obstacle or ladder... for some reason platform is strangely absent. Basically it's a great way of checking collisions on a single pixel without using sensor objects. Sounds exciting eh? I'm looking at this condition and the ideas I'm having for using it to improve various things are quite staggering.

I'm not entirely sure how MMF does its collision mask, but this should prove quite faster than using proxy objects, so look forward to some more projects by me from me over SAGE.

Link to comment
Share on other sites

DW, Damizean has used collision masks since the early releases of Worlds (collisions for moving rings). I've only ever used it as a simple way for an object to check collisions around it without using a bunch of sensors (the pushable objects in Worlds for example), until I figured out an easy way to set up sensors for multiple objects, which I then trashed collision masks because they're not very flexible nor much of a speed up in my testing.

Azu and Serephim, there is no way for a collision mask to detect another object. The only real work around is to use a multiple object sensor system instead. IIRC I submitted an example that uses a somewhat automated process to make the task easy, though it lacks the object iteration optimization DW discussed earlier.

Link to comment
Share on other sites

Hmmmm? You sure? According to the code that's in the ring bounce events in the level collab build of Worlds, it just looks for collision against the background with the whole ring object rather than using the collision mask conditionals. Technically it still uses the collision mask of course since I'm sure it was already employed for normal background collision detection, but it sin't what I'm talking about.

I still intend to test them for speed. You were right I think to use the collision mask to check for the floor with the moving boxes. That seems very sane to me. Depending on how the checks work with multiple instances before constraining the objects you are using though, I can totally imagine the bottle neck being object selection if using the spread value method instead of collision detection.

Link to comment
Share on other sites

You know i just now recently actually opened the Chain exe you posted DW. Shit's sexy.

I recently got into playing around with Lark's iteration engine, but stopped after i put a few enemies into it because it was going to be more work than i was willing to sacrifice for better AI collisions (seeing as im pretty much able to put however many enemies on the screen i want currently with actives set on the stage to count as walls for sensorless enemies, at the expense of solidity)

Combining the two methods, however, would help solve so many MMF2 issues..... being able to iterate shit without MMF2 choking with only 40 sensors on the screen would be godlike.

Link to comment
Share on other sites

I'd personally like to see a non laggy or no slow level up system using loops. You know, one that has instant level up. Like If I get 1,000,000 EXP, I'd instantly level up to level 300, or it I set my EXP to 1,000,000 I'd get to level 300. Instead of the exp increasing every so slightly. If I still had the source, I'd post. I might be able get one up though.

Link to comment
Share on other sites

I think another good thing to know when it comes to removing collision detectors are the following expressions: X Left, X Right, Y Bottom, and Y Top. You could use these expressions to test whether a specific edge of the object in question is overlapping an obstacle. If you wanted to make a floor detector, for example, you might do something like this for a condition:

- Y Bottom + 1 is an obstacle

- X Left is an obstacle

OR

- Y Bottom +1 is an obstacle

- X Right is an obstacle

It's definitely a handy shortcut that I find myself using occasionally. ^_^ Of course, it does little to solve the issue of making active objects behave like obstacles. Goodness knows I'd like to find a good way to do the same thing with moving platforms and such.

Link to comment
Share on other sites

I'd personally like to see a non laggy or no slow level up system using loops. You know, one that has instant level up. Like If I get 1,000,000 EXP, I'd instantly level up to level 300, or it I set my EXP to 1,000,000 I'd get to level 300. Instead of the exp increasing every so slightly. If I still had the source, I'd post. I might be able get one up though.

That has little to do with this, and more to do with the way you program the system that handles that kind of thing. Don't program the system to display the "level up" rituals once you gain the amount of EXP for the next level. You need to design the system to always keep track of all the experience you've gained, and instead base everything that happens on that.

Since im about to do a level up system myself:

- Make a system that keeps track of your total experience, but have events that activate once the EXP number reaches a certain amount. (This is the "Level Up.")

- Don't program the visual"level up" events to activate when you reach a specific level up point; instead, design a function that does these events that 1) triggers only when you level up, and 2) adds everything you've gained at once.

So, assume Lv 1-100 took 10 EXP each to level up (1000 EXP total). If you killed some super powerful boss and gained 1000 EXP in one go, instead of getting:

>Lv 2

>STR+ 5

>INT+ 2

>HP + 95

>Lv 3

>STR +4

>INT+ 2

>HP+ 43

and so on and so on displayed on the screen until level 100 (which would take years), you'd get something more like:

>Lv 100

>STR+ 592

>INT+ 304

>HP + 9999

Im eventually going to write one in MMF2 later, and i'll probably make a tutorial or something sometime soon.

Link to comment
Share on other sites

  • 2 weeks later...

Over the past few days, I've learned that when it comes to object selection in MMF2, fastloops are not friendly towards huge numbers of active objects. D:

I'm currently trying to retool my action RPG's engine, and I used DW's fixed value array trick to snap monsters to a single collision mask (which is their shadow, allowing me to display each monster in 3 dimensions such that I can have the player hurl them into the air with attacks if necessary). However, even with this trick, I can only spawn about 95 monsters before the frame rate cuts down from 60 to 30. I've made it my goal to figure out a way to get at least 300 monsters in the frame without any noticeable lag.

EDIT: Hmm... I think I might be doing something wrong... *goes to tweak some events*

Woot, I've got it up to 200 monsters! I could potentially double that and reach my goal if there's a feasible way to embed collision detectors. The detectors would need to be able to change positions dynamically. I could try and implement what DW said earlier about shuffling a single detector through every monster in the frame.

Link to comment
Share on other sites

Alright, folks, I'd like to bring your attention to the Select Object extension:

http://ext.neatwares.ath.cx/ext/Select_Object

This extension has the ability to select objects in the frame based on their index value. When I ran a single fastloop that attaches collision masks to a parent object, I discovered that DW's fixed value array and the Select Object produce roughly the same framerate, with the Select Object being far simpler to execute (as in, no arrays required).

However, when I increase the number of fastloop events from 1 to 10, the framerate drops about 33% more with the Select Object than it does with the fixed value array. A bit disappointing, but it does hold promise.

On a different note, I've discovered an interesting article that explains how using an alterable string is more effective than spread values when communicating between objects and their collision detectors, on account of the way MMF2 handles object pairings:

http://www.create-games.com/article.asp?id=1960

I'm going to test this later to see how effective it is.

Link to comment
Share on other sites

The problem with relying on MMF2's built in object linking weirdness is that it's very unreliable. It works for instance when you are changing position, but the instant you want to do something a little more elaborate like messing with alterable values, all bets are off as to what objects it actually selects.

Also, using strings to set states is just wrong. These aren't constant strings, and there are no fast ways to do evaluations with them, so basically you are tying the performance of your game to the length of these strings and string comparison is already a many step operation that you just shouldn't be doing in real time.

So again, the best... cheapest way to do stuff like this is to just iterate through all your objects one by one and act that way. Of course, it isn't quite that simple since you never know what confounders MMF introduces, but at least for the purpose of what is logical, it is best if you can just do one evaluation per object.

Link to comment
Share on other sites

Hmm....Okay, so i was thinking of using this for enemy sensors.

Is there any kind of easy, effective way to sort this when it comes to creating objects without the array getting dirty?

i notice that in your example, it works great when you're creating the objects through the loop, but what about objects created and destroyed during runtime? With no spread value, i guess you have to just check the array for the specific value?

Link to comment
Share on other sites

Objects that are created and destroyed during runtime can be a little tricky, but that's really unavoidable when you are working with multiple instances of objects.

The easiest way to handle it is to handle your destruction events in a loop... like say:

"<objectname>_destructor"

Then as you go through that loop, you start by setting your fixed values in your array to -1 (guaranteed to fail the selection since negatives aren't valid for fixed numbers). If you have anything fancy going on after the initial destruction, that's up to you to manage... but anyway, once you are done unsetting your references.

By now, you probably should not be using "count of <object name> to start your loop iterator. Instead, use a second value to track how many objects have been created in order to start the loop. It might be a good idea to make sure that the reference for a particular object is not -1 before starting the actual activity loop. That way you play things more safely.

Now, if you want to get fancy, you can track deleted objects in a data structure known as a stack, and every time you want to create a new object that needs to have stuff put into an array, you can first look to see if you have an available spot in the array already. A stack is basically just an array with a value telling it where it is pointing now, and you only have two options for modifying the stack... push and pop. Push puts a value at the end of the array and moves your "where I'm at in the stack" value to the top of the stack. Pop just removes the top value and puts it back. Anyway, whenever you delete an object, you push the array position it came from onto the stack, and whenever you want to create an object, you start by seeing if your stack has any elements in it, and if it does you put the values into that position in the array. That way you don't have ever increasing numbers of gaps in your array which would slowly make your game run worse.

If the object in question isn't going to be getting new objects of its type created though, I'd actually recommend against implementing that kind of system since it does incur some overhead and a few gaps isn't going to matter if the array in question only stores references to 30 objects or so. And in my case, it's generally a lot less. I tend to have no more than 10 instances of a single enemy in my levels so far. Madcap Grotto in particular had like 6 plasmoles, 3 rollingrockers, and maybe 7 bat-eggs, and in order to handle the deletion I actually just moved them off screen and blanked their references.

It's funny you mentioned that though, because I had a nasty nasty nasty glitch involving the plasmoles being destroyed where I overlooked something... basically I would iterate through my list of plasmoles and set a value to their fixed value if their reference were equal to some number (this was with some weak optimization that wasn't as ideal as what I'm doing more recently), I'd set an argument that I used for object selection to their fixed value and then operate on them from there.

Unfortunately, if I didn't encounter a plasmole in that iteration, I'd still run the action events, they were just happening on the last selected plasmole because I forgot to reset the value in the proxy object I was using to hold my arguments. So basically, I'd have one guy performing additional actions for each destroyed badnik that came before him in the order. This led to a bug where some of my moles would start exhibiting really freaky behavior. In addition to running around twice as fast, shooting twice as fast, and doing basically everything twice as fast, he'd also randomly teleport for no apparent reason right in front of the player and start shooting him in the face. I have no idea why that last part happened and it almost seemed like an impossible bug, but the bug was easily fixed just by blanking the argument before selecting the next fixed value using references.

With arrays, that sort of thing should really never be a problem though.

Link to comment
Share on other sites

The teleportion glitch was just cheating on their part and it was also causing slowdown for some reason. Believe me, the level was a lot better off without that glitch.

But yeah, I considered going back and manually changing values for the plasmoles each time one was killed. But they were tough enough as is I think. I'll consider something like that when I remake the level in Gamemaker for Time Twisted.

Link to comment
Share on other sites

  • 4 weeks later...

Hi guys, I'm new to this forum but I was directed to this thread from a link on one of the other communities that uses MMF.

One feature that helps a lot when speeding up MMF2 is Group Management. Because MMF processes the whole event list every time it renders, having many events - even if most of them return false and never actually process their actions - will still slow down your application.

Group Management is where you divide your code into groups and subgroups and activate/deactivate them as necessary.

Suppose, for example's sake, that we have a bad guy called Bob who has a complex AI. We might have a group called 'Bob', which is always active. However, inside that group we have a subgroup called 'Bob - AI'. An event inside 'Bob' checks whether there are any Bobs within, say, 200px of the window's edge. If there aren't, it deactivates the 'Bob - AI' subgroup, and perhaps any other subgroups that are unnecessary.

This way, instead of checking all the conditions in that group (which would likely have returned false anyway), MMF is told it can just forget that whole process until further notice.

If it's done well, you can get some pretty big improvements on performance by intelligently switching off redundant code at runtime in this way.

------

Also, although MMF's Object Selection gets a lot of bad press, you need to understand it, because it's equally as important as fast loops. The two don't replace each other, they complement one another.

Fast Loops have the advantage of control, but they're crippled by the fact that they have to set up a new event and check all the conditions on every iteration. This gives them an unfortunate overhead.

Meanwhile, Object Selection is blisteringly fast, but it's far more limited in what you can do on each iteration.

You'll usually find that the key to processing many objects is a marriage of the two. One example of this was Line of Sight tests, where you test multiple objects to see whether they can see the player or not. Many people make the mistake of fast looping the whole process, which is very slow.

The quicker method is to use a fast loop which triggers an object loop. The object loop selects all your enemies and moves them a step closer to the player. The fast loop then returns and iterates this process again, checking whether the objects have collided with the player yet and setting their flags accordingly.

In this way you're using Object Selection to do the object-iteration and a fast loop to call this process many times.

  • Like 1
Link to comment
Share on other sites

This is correct. There is a significant penalty incurred from having an event in the first place, and this makes it more taxing to iterate through all of an object when an event can simply be applied to all of an object... In fact, I'm throwing down an experimental mfa file which demonstrates that for a thousand objects going through 200 events (which is more than enough to make a rather complicated enemy actually, so a thousand instances of a complicated enemy... not bad), it's roughly three times as taxing to iterate through them all as it is to simply apply an event to the whole selection (I made sure the actual events were the limiting factor in this case).

The example is pretty simple... just enable the style being tested and disable the one not being tested... the cost from iterating through all of them comes from the fact that instead of running 200 events, it has to run 200 per frame events 1000 times per frame... in other words, it incurs the event execution penalty (mentioned below) 200000 times per frame, which is pretty significant, and all things considered it still isn't running that poorly.

But really, this would have to be a huge bane for MMF and not a positive about its esoteric object selection scheme. It isn't like MMF doesn't still have to iterate through its selected objects to apply the changes requested in an event, the performance hit is caused instead by the natural overhead of having executing an event in the first place.

The reason it is slower to iterate through the object isn't because applying the action to each object is just naturally slow... it's because MMF has a huge overhead to apply every time it executes an event. When you are iterating through an object, instead of incurring the event overhead once to execute an action on 30 objects, you have to incur that overhead 30 times. If I had to guess, a single event in MMF involves many different object selection structures all of which must be continually built up and purged on the fly. It's really quite obnoxious, especially considering that you generally need a lot of events to get anything of substance done since MMF doesn't support proper nesting. Ideally once I've selected an object, I'd be able to tell it to perform all of its actions in a single event rather than needing to split it up into a dozen different reselections of the same item which I already know will be selected just to accomplish what should really be performable in a single step.

Most sensible engines work on a pretty simple concept. You have a collection of objects with some kind of capacity to act and you iterate through them and they act. There isn't the kind of overhead generated by an event system and as a side affect of this, programs tend to be less prone to ridiculous sphagetti code where seemingly unrelated events can be strewn about together willy-nilly.

If down the road, a later version of MMF allowed object selection to occur independently of actual logic dictating what an object does... so that all the event overhead wouldn't have to occur just so that you could have an object check various variables to perform various actions... then the affect of simply having to perform one event for each instance of an object would probably be negligible... and in that case, it would generally be better to just abandon non-iterative binding for all purposes... not that it isn't already. Even with the triple cost, unless you have a thousand objects acting simultaneously it isn't a huge burden compared to everything else going on. Also, almost all the cool stuff demands iteration anyway.

Actually, rather than disabling event groups entirely, I prefer to keep as many events locked behind loops as possible. That way they only run if the loop is called and they are actually needed anyway.

Anyway Dines, I presume you are DeadmanDines of DailyClick right? I read a couple of your articles while working on some of these examples... not sure they always made the most sense to me, but this concept was basically what encouraged me to start looking at this stuff in the first place... and benefits greatly from the use of arrays and fixed values, so thanks. Your work has gotten me eyeing this stuff from an analytically angle.

iterativeVsAllSelect.mfa

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...