• Hey! Guest! The 37th GMC Jam will take place between May 28th, 12:00 UTC and June 1st, 12:00 UTC. Why not join in! Click here to find out more!

(SOLVED) Serious lag when having many tree objects

Hi everyone!

For a while now, I've had serious lag issues, to the point where the game becomes unplayable.
This only happens when I have a lot of tree objects in my game.

Trees have a chance to spawn a new one each day in my game. They also grow each day.
In order for a tree to be placed, it must first check if that is allowed (if no other object is present at that location).

When I have like 50 trees, it's fine. But once the numbers go up by a lot, it causes massive lag.

I can't pinpoint why. The other weird thing is , when I move to another room, the lag endures. Rooms are persistent by the way.

So since I'm out of solutions, let me sum up what I have:

-Many tree objects, 6 types of trees.
-They only turn to solid if the player is in a collision_rectangle around the tree.
-Collision check happens from the player object, not in each individual tree.
-Trees have a 1 in 4 chance each new day to spawn a seed.
-When a seed spawn triggers, it has a 1 in 12 chance to choose direction. Basically,
1 in 4 chance to spawn, and then spawns randomly in 1 out of 12 places around the original tree.
-Seeds can only be spawned in areas that are allowed (no other object present)
-There is no parent object.
-All together, it's a couple of hundred lines of code in the tree object (which causes tree types, tree spawns, tree growth, tree cut by player,... It really works great in low amounts of trees)
-Depth of trees is just a single number in their create event.
-Depth of player is updated in player step event

It leaves me wondering what the cause could be.
Is it the collision checks?
Is it the amount of code (hundreds of trees = hundreds x hundreds lines of code)?
Is it each individual draw event? (not drawing the tree reduces lag a lot, but not completely).
Why does the lag carry over to other rooms (also persistent) where there are only a couple of trees?
Is it just the amount of objects and there's no way around it?

It really comes down to "how to reduce lag when working with a large amount of objects" really. I probably have to start from scratch with my trees, however I already learned a lot about player interaction, growth, spawns,... So the knowledge doesn't go to waste.

I'm missing something here... Something NOT TO DO when having a lot of objects.
 
You want to deactivate any trees not being shown on your camera view in order to reduce lag. And then reactivate the trees once your camera view comes close to it. Having many objects out and running is going to cause serious lag.
A yes, a question I forgot to add in that list.

I was thinking about that as well. But what about moving to another room?
I'd think, since I'm in another (persistent) room, the draw events of the hundreds of objects from the previous room no longer run?
I will definitely try and add code to stop the draw event when the objects move out of view.
 
I haven't found a use for persistent rooms for myself and so I have never used them, so I can't really say in that matter.

I don't think stopping the draw event is going to do too much. Objects are heavy and so your best bet is to activate/deactivate somehow. Or, you can make the trees a part of a tile set (or tile set prop) and just brush the trees in that way, so they aren't heavy objects.
 
I haven't found a use for persistent rooms for myself and so I have never used them, so I can't really say in that matter.

I don't think stopping the draw event is going to do too much. Objects are heavy and so your best bet is to activate/deactivate somehow. Or, you can make the trees a part of a tile set (or tile set prop) and just brush the trees in that way, so they aren't heavy objects.
Well here's my limitation in understanding the engine. If the issue is that you can't have many objects in the game, I have a problem :D
 
Also, I need them to be objects. The player needs to interact with them in a variety of ways. Having them be "just images" on a tile layer won't work for me. Also, that would screw up their depth values.
 

Amon

Member
Well here's my limitation in understanding the engine. If the issue is that you can't have many objects in the game, I have a problem :D
Yep, I agree. The issue is having many objects in your game with your understanding of coding. Try object pooling, deactivating instances when not in view etc.
 
Yep, I agree. The issue is having many objects in your game with your understanding of coding. Try object pooling, deactivating instances when not in view etc.
I suggested that and he says he can't do that due to persistent rooms, so I don't know.
 

Joe Ellis

Member
You can have thousands of instances with no lag at all, I tested it with an empty object that does nothing. So its not instances themselves causing the problem. But there could be a few things that each of the instances is doing that adds up to alot of processing power. I tbh would like to see your actual project out of interest, and there might be something I find that's like "Oh hell no!"
But also, the tree branching thing can be done alot more efficiently with arrays. Arrays containing other arrays mainly. And I know you said you need them to be instances, but you don't. You can just make a special collision script or whatever you need to do. Look at how people handle per triangle collision systems in 3d. They don't make every triangle a separate instance, instead each triangle is an array. I contains all the coordinates and everything needed for instances to interact and collide with. The same thing can apply to your tree branches.
 

Joe Ellis

Member
I've never used debug mode, I always feel like I'd be cheating if I used that haha. I preferred to work out what it was instead, sure, I could've saved hours of time, but I also learned alot. I wouldn't recommend this approach for the general humans though, I'm a genius, so it's easier for me
 

RangerX

Member
ZombieSquirrel, maybe you could let us see your trees' code. We could maybe see something to optimise.
Here's some other generic advice:

- Use a parent object here. Its the perfect situation.
- Deactivate the trees in a "outside view" event and have your main character or whatever camera activate objects
- Does all your checks absolutely needed every step? Only run code when its reasonably necessary.
 
ZombieSquirrel, maybe you could let us see your trees' code. We could maybe see something to optimise.
Here's some other generic advice:

- Use a parent object here. Its the perfect situation.
- Deactivate the trees in a "outside view" event and have your main character or whatever camera activate objects
- Does all your checks absolutely needed every step? Only run code when its reasonably necessary.
Here's the code in the step event (don't worry about create event, that(s just initialisation of the variables you see here, and the draw event is literally only a couple of lines of code "draw_sprite(....)"


STEP EVENT:
------------------------------------

//After hit with axe; run treecut script
SCR_TreeCut();
//a script that changes tree HP ONLY IF the player hit's it with an axe, this is triggered in the player object.

//Set trees to solid once player moves out of the collision box area when planting.
///////////////////////////////////////////////////////////////////////////////////
if (TreeStage >= 1)
&& ((collision_rectangle(x,y,x+15,y+15, OBJ_Player, false, true))) //does not use mask! just checks if player solid is in this area!!
{
if (solid != true) {solid = true;}
}
else if (solid = true)
{
solid = false;
}
//Make trees transparent if player moves behind them (tree stage specific)
if (TreeStage = 2)
{
if (OBJ_Player.x >= x-15 && OBJ_Player.x <= x+15) && (OBJ_Player.y >= y-16 && OBJ_Player.y <= y+1)
{
TreeTransp = 0.40;
} else {TreeTransp = 1;}
}
if (TreeStage = 3)
{
if (OBJ_Player.x >= x-15 && OBJ_Player.x <= x+15) && (OBJ_Player.y >= y-32 && OBJ_Player.y <= y+1)
{
TreeTransp = 0.40;
} else {TreeTransp = 1;}
}
if (TreeStage = 4)
{
if (OBJ_Player.x >= x-24 && OBJ_Player.x <= x+24) && (OBJ_Player.y >= y-74 && OBJ_Player.y <= y-3)
{
TreeTransp = 0.40;
} else {TreeTransp = 1;}
}

//Grow tree only if no other tree is present in a square of tiles around it.
if (DayGrow < TimeTotalDays) && (TreeDead = false) && (TreeTopAlive = true)
{
DayGrow += 1;
if (TreeStage < 4)

//When the player leaves this map, and comes back many days later, the trees need to catch up and know how many times to grow.
//Hence, DayGrow +1, it does it thing, DayGrow again +1,... untill DayGrow = TimeTotalDays (TimeTotalDays = current day).
{
if !(collision_rectangle(x-16, y-16, x+31, y+31, OBJ_Tree, false, true))
{TreeStage += 1;}
}
if (TreeStage = 4)
{
SpawnSeedChance = irandom_range(1,3)
{
if (SpawnSeedChance = 1)
{
SCR_TreeSpawnSeed(TreeType, DayGrow);
}
} //end of: SpawnSeedChance = irandom_range(1,6)
} //end of: if (TreeStage = 4)
} //end of: if (DayGrow < TimeTotalDays) && (TreeDead = false)




//Spawn shadow only once (shadows always appear below player, depth of them is set in shadow object)
if (TreeStage >= 3) && (ShadowNotSpawnedyet = true)
{
with (instance_create_depth(x, y, depth, OBJ_Tree_Shadow)) {ShadowStage = other.TreeStage;}
ShadowNotSpawnedyet = false;
}
if (position_meeting(x, y, OBJ_Tree_Shadow))
{
with (instance_position(x, y, OBJ_Tree_Shadow)) {ShadowStage = other.TreeStage;}
}




DRAW EVENT
----------------------------------
if (TreeStage < 3)
{
draw_sprite_ext(TreeStumpSprite, TreeStage, x+8, y+11, 1, 1, TreeSwivel*1.5, c_white, 1);
draw_sprite_ext(TreeSprite, TreeStage, x+8, y+9, 1, 1, TreeSwivel*1.5, c_white, TreeTransp);
}
if (TreeStage >= 3)
{
draw_sprite_ext(TreeStumpSprite, TreeStage, x+8, y+11, 1, 1, StumpRot, c_white, 1);
if (TreeTopDrop = false)
{
draw_sprite_ext(TreeSprite, TreeStage, x+8+DropOffSet, y+9-abs(DropOffSet/5), 1, 1, TreeSwivel, c_white, TreeTransp);
//use drop offset to move tree away from stump when cut!
}
}
 
You can have thousands of instances with no lag at all, I tested it with an empty object that does nothing. So its not instances themselves causing the problem. But there could be a few things that each of the instances is doing that adds up to alot of processing power. I tbh would like to see your actual project out of interest, and there might be something I find that's like "Oh hell no!"
But also, the tree branching thing can be done alot more efficiently with arrays. Arrays containing other arrays mainly. And I know you said you need them to be instances, but you don't. You can just make a special collision script or whatever you need to do. Look at how people handle per triangle collision systems in 3d. They don't make every triangle a separate instance, instead each triangle is an array. I contains all the coordinates and everything needed for instances to interact and collide with. The same thing can apply to your tree branches.
Look at my previous reply :) Sorry for the late response, I'm in Europe and it was almost 3 at night when I first posted this thread, I went to sleep xD
 
I should also mention that seeds are separate objects with a depth always below player, just like the shadow. Basically the "SCR_TreeSpawnSeed(TreeType, DayGrow);" script
determines through the TreeType and the DayGrow variable what kind of seed needs to be spawned, and make the seed know at which day it was spawned.
Then the seed goes through a random number each day to see if it can grow or not. If it can, it destroys itself and spawns a tree in its first stage.
 

RangerX

Member
Ok it seems there's not much wrong in your code there. What should take the most time in your code there is the "collision" family of functions. That being said, try to optimise your logic for the "shortcircuit evaluation" function of game maker. (see your global game settings). This function make so when an evaluation is false, it automatically go to your "then" or "else" without checking the other conditions in your "if " line. I suppose you know where am going there but you can try and never have a collision function as the first check. This way it will never be run if not necessary. Do that with any costly check in your code logic.

Second thing, I doubt you need to do all of this every step. Trees could try to spawn seeds only every 2 or 4 steps and same thing for the growing. You could count the steps with a variable and have the seed part never being read in the same step as the grow part.

Last thing, does this axe script really needs to be read every step?
 
Ok it seems there's not much wrong in your code there. What should take the most time in your code there is the "collision" family of functions. That being said, try to optimise your logic for the "shortcircuit evaluation" function of game maker. (see your global game settings). This function make so when an evaluation is false, it automatically go to your "then" or "else" without checking the other conditions in your "if " line. I suppose you know where am going there but you can try and never have a collision function as the first check. This way it will never be run if not necessary. Do that with any costly check in your code logic.

Second thing, I doubt you need to do all of this every step. Trees could try to spawn seeds only every 2 or 4 steps and same thing for the growing. You could count the steps with a variable and have the seed part never being read in the same step as the grow part.

Last thing, does this axe script really needs to be read every step?
I've just turned off that collision check and the lag remains. However, yeah that axe script... It handles how the tree "swivels" when the player hits the tree, it handles the animation when the tree is destroyed, and handles how wood is spawned, depending on tree type. It's also quite substantial.
However, in that script, NOTHING HAPPENS if the player doesn't hit the tree.
Basically, all of the code in that script lies within an if statement:
if (TreeHit = true)
{
… script code
}

However, you were right! I removed that script from the code, and the lag is gone... I feel a bit like an idiot now xD
But that leaves me to wonder... Does GM run through code that should not be executed? I mean, TreeHit = false all the time, unless I specifically let my player stand next to a tree, and attack it.
And since that variable is false, all of that script's code should not run right? It can only run in one tree at a time.
But if GM reads it every step, without executing it, it might be the cause of the lag?

I love these forums. Each time i have a major hurdle to overcome, I always find the cause and solution to the problem here, but it seems like it's never one of the more complex things I'm doing xD It's usually something minor, and it leaves me feeling that I need to learn even more :p
 
Second thing, I doubt you need to do all of this every step.

Last thing, does this axe script really needs to be read every step?
I made it so the script no longer runs every step in the Tree object step event. Basically I added one more "check" that is turned on when the player hits the tree.
Now the the script only runs if that "check" is turned on.
The lag reduction is enormous!

Thanks so much, I couldn't immediately see this as the problem. That simple question you asked me opened my eyes to the problem. Thanks!
 

RangerX

Member
Calling a script takes time. Using a script is good (or a parent depending the situation) if you have multiple different objects needing the same code.
If you have tons of the same object, too many scripts are going to be called.
Always continue coding with the mindset "do I need to run this every step". Use parents, use scripts when it makes sense. Never have activation, deactivation, or collision functions as the first comparison in you "if" lines.
 
UPDATE!!!!
The lag is almost completely gone now!

I did 2 "tricks", as I noticed that the ENGINE has issues with having so many instances PROCESSED.

Trick 1: minimizing step event code. Basically, I locked long blocks of code behind a couple booleans, making sure that code that DOES NOT NEED TO RUN EVERY STEP, doesn't run every step.
Basically, each instance now only runs a minimal of code each step. This reduced lag a lot.

Trick 2: Have my CAMERA control if these instances are visible or not. If the object is out of the camera's view, they are turned to not visible. If they come into view, they are turned back to visble.
BUT!!! This didn't completely remove the lag.

basically, in my draw event I now have something like
if (TreeOnCamera = true)
{
draw code
}
(the camera switches this to false or true)

I then realized where the "engine processing" comes into play. So even when having only those
"in the camera's view" be visible, those will still run code a lot right?
Well now I did:

if (TreeOnCamera = true)
{
shader_set(SHD_DrawTree) //a simple passtrhough shader, nothing fancy and really beginner-level

draw code

shader_reset();
}


Lag? Almost none. I can go to a massive amount of instances, more than needed and it actually now looks ugly with so many overlapping, so I'm building in an instance limit on my map.


I never imagined I'd get this result, but I thought "why have GM and the processor work through all that draw code if the videocard could do it for them through a shader?"

This is my very first attempt at a shader, I don't understand how any of it really "works" yet, but the result is much, much better o_O

I'm not even sure it's completely because of the shader, you know, sending the draw stuff to the graphics card to handle.
I'm pretty sure it's a combination:

-Minimize code wherever you can
-Only objects inside the view are needed to be visible
-Shaders can handle a lot of graphics processing power it seems
 

obscene

Member
Haven't read all your code but just wanted to throw this in the mix... alarms are a good substitute for step events when it's Ok if the code runs every other frame, every third frame, etc. Evenly distribute the time the alarms begin so that each frame only a fraction of the trees are updating.
 
Top