Handle lots of units

  • Hello everyone,

    It's been a while I didn't post in the forums cause I find Construct 2 very complete and I'm used to workarounds. But I'm struggling with one thing, handling many units with behaviors and states updated every tick. (Native application, not mobile)

    Basically it's the For Each loop on a family with some calculations and checks inside that are CPU intensive and tend to kill performance.

    I've read all Ashley posts about optimization and how to rightly use Construct 2 but in some game design you can't pass the For Each loop.

    I encounter difficulties to supress the stuttering when it comes to deal with 40-50 units ingame. With 100 the game is playable but regular small stutters are annoying and low end machines can't sustain. I saw the benefits of collision check optimization that have been done but how could loops can be so demanding ? I tried various things to understand and ran into the case of function calls vs inline events but stress tests were not so relevant.

    Anyone has a solution or an idea to organise the code or something to handle (not so) large number of units ? I mean, 200 with no stutter would be cool !

    Thanks

  • Nabu0001

    I was thinking about this same issue the other day; this might be a solution:

    https://www.dropbox.com/s/35vfsi9j0j8gw ... t.c3p?dl=0

    Each tick, the modulo (%) 10 of each sprite.IID is checked against the modulo 10 of the current tickcount and those with the same value are picked ((e.g. if the tickcount is 29, pick all the sprites with an IID that ends in 9).

    This means that each tick only 10% of the sprites are selected. You can pick less sprites per tick by increasing the value after the % sign.

    In the demo I've set it up so that the sprite's frame changes when picked - if you run the demo you can see the horizontal white band indicating the sprites that are picked.

    Now, I haven't stress-tested this to see how it fares when you have a lot of code affecting the picked sprites, but I'm guessing it should at least reduce some of the stutter.

  • Sorry, that was a C3 demo; here's a C2 one:

    https://www.dropbox.com/s/1igxvu9v9d2kn ... .capx?dl=0

  • I don't know if this work for you, but I sometimes use dictionary to store picked UID's. That way the for each loops doesn't have to loop through a whole family, but only the ones stored in the dictionary. That might work when you have many units in the same family, but let's say 20 out of 100 selected, as you don't have to loop through the 80 that are not selected. I found it to have some performance benefit in some cases.

    For me the picking itself seems to use a lot of cpu in some cases, so i try to find ways to use some more lightweight way of picking objects. Restructuring the condition order can help a lot too.

    For example:

    For each unit (loops through all units)

    is Selected. (then picks and filters the ones selected)

    insead use.

    Is selected (pick the selected ones)

    For each unit. (loops through only the selected ones)

    Small things like that can help a lot, but you probably know that already. Trying to filter down with conditions as much as possible before running any for each loops and actions, seems to work pretty well in most cases.

  • I have the same problem and still looking for a solution I hope someone can give us a good hint, I got 16 objects checking distances between them so when is in a empty project with just that 16 objects works ok and when I put the same code on my main project where they are like 150 instances of the same object the CPU goes double and triple doesn't matter how much I filter it with boleans and conditions and all kind of tricks is not working and this is just to check distance between 16 instances on the screen

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • I don't know if this work for you, but I sometimes use dictionary to store picked UID's. That way the for each loops doesn't have to loop through a whole family, but only the ones stored in the dictionary. That might work when you have many units in the same family, but let's say 20 out of 100 selected, as you don't have to loop through the 80 that are not selected. I found it to have some performance benefit in some cases.

    For me the picking itself seems to use a lot of cpu in some cases, so i try to find ways to use some more lightweight way of picking objects. Restructuring the condition order can help a lot too.

    For example:

    For each unit (loops through all units)

    is Selected. (then picks and filters the ones selected)

    insead use.

    Is selected (pick the selected ones)

    For each unit. (loops through only the selected ones)

    Small things like that can help a lot, but you probably know that already. Trying to filter down with conditions as much as possible before running any for each loops and actions, seems to work pretty well in most cases.

    Hi tunepunk I show you did an interesting test on this very good test Thanks

    Did you manage to find any good results or ways how to improve ?? I will be very interested in that

    do you know if is possible example you have 20 objects on the screen the idea is to take the UID of all those 20 objects and just loop between them checking distances between them and depends the distances do some actions those objects, is it possible without looping through the whole 150 instances that are not on the screen

  • > I don't know if this work for you, but I sometimes use dictionary to store picked UID's. That way the for each loops doesn't have to loop through a whole family, but only the ones stored in the dictionary. That might work when you have many units in the same family, but let's say 20 out of 100 selected, as you don't have to loop through the 80 that are not selected. I found it to have some performance benefit in some cases.

    >

    > For me the picking itself seems to use a lot of cpu in some cases, so i try to find ways to use some more lightweight way of picking objects. Restructuring the condition order can help a lot too.

    >

    > For example:

    > For each unit (loops through all units)

    > is Selected. (then picks and filters the ones selected)

    >

    > insead use.

    > Is selected (pick the selected ones)

    > For each unit. (loops through only the selected ones)

    >

    > Small things like that can help a lot, but you probably know that already. Trying to filter down with conditions as much as possible before running any for each loops and actions, seems to work pretty well in most cases.

    >

    Hi tunepunk I show you did an interesting test on this how-do-i-pick-multiple-by-uid-string_t190930 very good test Thanks

    Did you manage to find any good results or ways how to improve ?? I will be very interested in that

    do you know if is possible example you have 20 objects on the screen the idea is to take the UID of all those 20 objects and just loop between them checking distances between them and depends the distances do some actions those objects, is it possible without looping through the whole 150 instances that are not on the screen

    In some cases i found that using LOS behaviour could be a bit better, it's pretty much a range check as well, if you set a 360 cone, and they can also utilize the "use collision cells" option.

    Another way that is way less CPU intensive is to use pick nearest/furthest, if something is within the range you set a boolean, and loop through that a few times per tick. It updates slowly but uses less cpu, so can be good for something that doesn't need to be instantly updated, or if you have a lot of instances but most of them are outside the viewport, in other areas of the layout.

    Code looks like this:

    Edit: Added link to C3 project.

    https://www.dropbox.com/s/96e7y0njoqyqd ... e.c3p?dl=0

  • do you know if is possible example you have 20 objects on the screen the idea is to take the UID of all those 20 objects and just loop between them checking distances between them and depends the distances do some actions those objects, is it possible without looping through the whole 150 instances that are not on the screen

    Forgot to answer this one.

    For this you can just use the "is on screen" condition. I think that's the best way to shortlist.

    Sprite is on screen

    check distance

    That way you first pick only the instances that are on screen, and then you can run your distance calculations on just those.

  • Thanks a lot tunepunk for the capx and the tips

    I do use at the moment all the ones that you mentioned the (Line of sight, Is on screen, boolean is Active) etc... but the cpu still goes high when I'm checking distances just between 16 objects on the screen, the only one that I didn't try yet is like the test you did putting all 16 UID in a list and just keep checking distances from that list but I'm not sure if this is even possible to do, I been playing with your capx to see if I can make it work but no luck, I see there is a new feature in c3 that it looks interesting that I didn't know existed the one you use in your capx the plugin "Picked" I may this can help to put the picking in list and just loop through that list I will have to keep testing.

    For my purpose, I need to be able to check distances every thick between the objects on screen and depends on what distances are then apply some actions to them so I need to refer to them one by one independently is quite complicated probably to make it work with a list

    And I agree with you the best results I had from all the Tests that I did, the Line of sight was the winner all the time is very optimized

  • tarek2

    16 objects doesn't sound like a lot. Care to explain how it works?, maybe I can come up with something more efficient in your case.

    Are they all checking distance too eachother and get picked if colliding?

  • tarek2

    16 objects doesn't sound like a lot. Care to explain how it works?, maybe I can come up with something more efficient in your case.

    Are they all checking distance too eachother and get picked if colliding?

    That's what I thought is not many and the problem they will be more on the gameplay, at the moment I'm testing just to see where are the limitations to have an idea.

    Yess that's right 16 objects on the screen and say like (150 of the screen) they are all instances of the same object even I stop everything off the screen, I use a family to check distances between them and make sure they don't check distances with their selfs by filtering the UID of each one and them when picking the family don't pick this Uid wich mean himself so they check distance against all the other ones on the screen.

    To give you a perfect Example of the mechanics is like (Agar.io) when you split and has to stop the overlap between your cells you can be split up into 16 pieces and all of them they cant overlap they have to move next to each other

    I'm not sure if I explained properly haha

    so say 170 instances of one object is the total

    from those 170 instances, 16 are the player and the rest are the enemy

    I filter the 16 instances for the player by instance Variable say Var = "Player" to identify them

    Then check the distances between this 16 instances

    For what I see the problem can be when I filter pick the ones with Var = "Player" c2 will have to loop through the whole 170 if I'm not mistaking and all of this every thick, even if I put another condition first like (is on screen) or (is Active)

  • tarek2

    Here's a capx I did that has 160 in layout and checking distances between everything on screen very low CPU usage. The trick is to use nearest, you dont have to check distance of every single one every tick. if the nearest one is not even within range there's no point to check all the rest of the instances either.

    Hope that can help you out.

    https://www.dropbox.com/s/3mc7dmbnfs8o8 ... .capx?dl=0

  • tunepunk Thanks a lot for the capx that's a great example it definitely improves performance a lot but in my case what I win in performance I lose it on accuracy ending behaving a bit jumping as they can be 4 or 5 famili1 instances at the same time touching one sprite. is a bit complicated and demanding I will keep testing

    Thanks anyway for your help take care

  • Hi, thank you all for replies,

    @mekonbekon, I found a similar solution than yours to avoid checking every object every tick. But I don't get enough performance improvements with it cause you can't abuse this trick, if you do so then many objects won't be updated every tick and can alter the gameplay. For things like weapons positionning on characters, it has to be every tick instead you got a lag. And if I filter onscreen/not on screen to position things, it creates a lag between what's on screen and what's not. So I ca use the trick to alleviate CPU usage just a bit.

    @tunepunk, same here, I already use a lot of filter condition. By the way your ideas on picking are maybe a kind of solution. Your test works well but I do more calculation per object in my project and that causes more CPU usage.

    Maybe a solution would be to separate states that require to be updated every tick from states that doesn't and store objects UID in different arrays to loop through at different frequence. But again, after testing things like this the gain is very little. At some moment, I can't prevent the player to make all his units attack and switch a lot of units to array that require update every tick.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)