• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GML Speeding up auto tile

D

Dracodino300

Guest
My game has procedurally generated rooms, which build structures like walls using an auto tile object system. There is a significant slowdown, most of which seems to be due to the auto tiling.

The relevant auto-tiling code is as follows:

Code:
if(newlyCreated){
    image_index = sprite_get_auto_index_16(objWall);
    newlyCreated = false;
}
Code:
///sprite_get_auto_index_16(object)

var objType = argument[0];
var returnVal = 0;

if(position_meeting(x+32,y,objType)) returnVal+=1;
if(position_meeting(x,y-32,objType)) returnVal+=2;
if(position_meeting(x-32,y,objType)) returnVal+=4;
if(position_meeting(x,y+32,objType)) returnVal+=8;

return returnVal;

I can't see how to make this faster without getting rid of the autotile entirely.
 

Kahrabaa

Member
If all blocks are 32x32 perhaps creating a grid and accessing its info might be faster than collision checking? I am not sure
Something like:

Code:
var type = argument[0];
var returnVal = 0;

var posx=x/32;
var posy=y/32;
if(ds_grid_get(objGridControl.grid, posx+1,posy)=type) returnVal+=1;
if(ds_grid_get(objGridControl.grid, posx, posy-1)=type) returnVal+=2;
if(ds_grid_get(objGridControl.grid, posx-1, posy)=type) returnVal+=4;
if(ds_grid_get(objGridControl.grid, posx, posy+1)=type) returnVal+=8;
 
D

Dracodino300

Guest
If all blocks are 32x32 perhaps creating a grid and accessing its info might be faster than collision checking? I am not sure
Something like:

Code:
var type = argument[0];
var returnVal = 0;

var posx=x/32;
var posy=y/32;
if(ds_grid_get(objGridControl.grid, posx+1,posy)=type) returnVal+=1;
if(ds_grid_get(objGridControl.grid, posx, posy-1)=type) returnVal+=2;
if(ds_grid_get(objGridControl.grid, posx-1, posy)=type) returnVal+=4;
if(ds_grid_get(objGridControl.grid, posx, posy+1)=type) returnVal+=8;
The problem with that is that my game room is potentially extremely large, and so far there isn't much slow down compared to initially even if i go out to 60000+. And there isn't any fixed limit or direction, so I don't think a ds_grid is really feasible for this.
 

Kahrabaa

Member
Oh, okay.
Another alternative, if your deactivating objects outside of screen then delay the autotiling a frame. So that the tiling doesnt happen when blocks spawn, they deactivate untiled and then autotile offscreen just as the player approaches different areas. This will put the proccessing weight into gameplay, so it depends on how taxing your gameplay is.
 

Simon Gust

Member
60000+ objects don't slow down the game but only a single collision function may slow your game down to a crawl. Be aware that when you call a collision function, you're making essentially 60000 checks wether coordinates match. The function position_meeting() makes 4 calls, that times 4 (for the whole script) that times 60000 for every object. Don't tell me your pc nor the engine can handle 1 million comparissons in a single step.

Deactivating is a good idea in a chunking manner where you deactivate objects that are far out of reach. But also then you have to be careful with it as it also taxes as well.
 
D

Dracodino300

Guest
Sorry, I guess I was being misleading: I do have a method for de/re-activating objects at a distance, which seems to be working fine. The slowdown occurs even if relatively few objects have been created (~100) and doesn't get worse from there.

Oh, okay.
Another alternative, if your deactivating objects outside of screen then delay the autotiling a frame. So that the tiling doesnt happen when blocks spawn, they deactivate untiled and then autotile offscreen just as the player approaches different areas. This will put the proccessing weight into gameplay, so it depends on how taxing your gameplay is.
I might try something like that: delaying the autotiling until it is almost on the game screen.
 

YanBG

Member
Yeah try to chunk it, each step you need to place only the closest row of tiles and discard the furthest, instead of essentially a for loop inside the same step checking all tiles.
 
D

Dracodino300

Guest
Chunking the auto-tiling, as well as putting in a ticker to only run ~10 objects per tick, seems to have improved it a lot. Still not completely smooth though.
 

NightFrost

Member
You're implying the world is infinite so yeah that'll put strain on the idea of using ds grids. You could tier them though, for example one grid holding info 500x500 tiles, then a master grid that holds IDs of those grids. It sounds odd how there's still performance issues, up to 10 objects running it should mean max 40 collision checks a step, which shouldn't cause any strain.
 
A

anomalous

Guest
So it's infinite world
procedural

Is the terrain dynamic or largely fixed once generated?
 
D

Dracodino300

Guest
You're implying the world is infinite so yeah that'll put strain on the idea of using ds grids. You could tier them though, for example one grid holding info 500x500 tiles, then a master grid that holds IDs of those grids. It sounds odd how there's still performance issues, up to 10 objects running it should mean max 40 collision checks a step, which shouldn't cause any strain.
The world size (so far at least) doesn't seem to be the problem. And having max 40 checks per step sped it up considerably but it's still not completely keeping up with the room speed.

@anomalous Haven't got too far into the area designs yet, but as of yet its mostly fixed. I actually though of something based on that AFK this weekend, that will reduce the number of checks for a room by about half for the time being.
 
M

MirthCastle

Guest
The slowdown is in the creation of the objects themselves too if you have so many.

A good speed up would be in the room creation - have all the objects store their neighbors in an array, list, static variable, enum, what-have-you. then check against a variable that is set which says what tile it is.

EG - During creation - I have my objects store what tile they are in a variable (though it seems you use a different object for each, so you technically have it stored already).
EG - After creation - (I usually use a separate event that kicks off after all the create events are done) have the objects loop all their neighbors and check what variable is set for the tile\sprite the neighbors have.

That would kick all the collision events out altogether. What @Simon Gust said is 100% true. So do it with the aforementioned data instead of collisions.

So instead of this:
if(position_meeting(x+32,y,objType)) returnVal+=1;
if(position_meeting(x,y-32,objType)) returnVal+=2;
if(position_meeting(x-32,y,objType)) returnVal+=4;
if(position_meeting(x,y+32,objType)) returnVal+=8;

You would have (assuming static variables):
if ( nodeRIGHT.tile = tileType ) returnVal+=1;
if( nodeUP.tile = tileType ) returnVal+=2;
if( nodeLEFT.tile = tileType ) returnVal+=4;
if( nodeDOWN.tile = tileType ) returnVal+=8;

If you would appreciate some unsolicited advice on a separate piece - dont go infinite - make it huge sure, but not infinite. Trying to account for the infinite tends to leave things... empty\devoid of creativity... plan for a specific size world(s) and divide\fill it up properly. Your players will thank you for it.
 
D

Dracodino300

Guest
The slowdown is in the creation of the objects themselves too if you have so many.

A good speed up would be in the room creation - have all the objects store their neighbors in an array, list, static variable, enum, what-have-you. then check against a variable that is set which says what tile it is.
I'm not quite sure how I'd detect what the neighbours are without having the same checks though. And there's also the issue that storing a list of all objects would make the code loop through deactivated objects, which I'm sure once the game space is large enough would become less efficient than point_meeting checks for only active objects.

If you would appreciate some unsolicited advice on a separate piece - dont go infinite - make it huge sure, but not infinite. Trying to account for the infinite tends to leave things... empty\devoid of creativity... plan for a specific size world(s) and divide\fill it up properly. Your players will thank you for it.
I'll be honest, I really started the idea with the desire to make a potentially-infinite game space. Maybe once it gets fleshed out I might decide that limiting the space works better from a gameplay perspective, but for now I wanna see how well it goes. I will keep that in mind though :)
 

Simon Gust

Member
I'm not quite sure how I'd detect what the neighbours are without having the same checks though. And there's also the issue that storing a list of all objects would make the code loop through deactivated objects, which I'm sure once the game space is large enough would become less efficient than point_meeting checks for only active objects.
Deactivated objects are reduced to a memory region and browsing that is not slow. It's the same speed as like looking up a normal variable as long as you know where it is in memory.
 
D

Dracodino300

Guest
Deactivated objects are reduced to a memory region and browsing that is not slow. It's the same speed as like looking up a normal variable as long as you know where it is in memory.
Ah ok. Still though, that would make the two methods about the same speed.
 
M

MirthCastle

Guest
They aren't even close to the same speed. The Method I listed above is similar to pathfinding with nodes. Its not the least bit slow.
 
D

Dracodino300

Guest
They aren't even close to the same speed. The Method I listed above is similar to pathfinding with nodes. Its not the least bit slow.
Why not? You still need to loop through every object, you just do it at creation instead of after creation.
 

GMWolf

aka fel666
Chunk your world, and use grids rather than instances.

It will save you sooo much trouble down the line!
 
Z

zendraw

Guest
you want infinite world and in the same time use a finite space? it doesnt matter the size, unless the player is utterly slow he will reach the end in 5 mins, 10 at the most. your way of doing it is id say wrong.

creating instances on the fly is a big no no in my book, unless its few at a time. what i do is i create max number of instances that i wuld use, for each type, and then just manage them, change theyr state, calculations, engage them in gameplay, etc. and if theyr not required i simply put them aside.

also i dont see the problem with a ds_grid? a grid is perfect for a big world.

Edit: altho i think your just showing off for how short the code is, altho its been around for a while and altho its incomplete.
 
D

Dracodino300

Guest
you want infinite world and in the same time use a finite space? it doesnt matter the size, unless the player is utterly slow he will reach the end in 5 mins, 10 at the most. your way of doing it is id say wrong.

creating instances on the fly is a big no no in my book, unless its few at a time. what i do is i create max number of instances that i wuld use, for each type, and then just manage them, change theyr state, calculations, engage them in gameplay, etc. and if theyr not required i simply put them aside.

also i dont see the problem with a ds_grid? a grid is perfect for a big world.

Edit: altho i think your just showing off for how short the code is, altho its been around for a while and altho its incomplete.
It's not a finite space.....
@YanBG Thanks, I'll give that a read.
 
D

Dracodino300

Guest
It has to be, computers don't have infinite memory.
Also you have precision issues.
If you want your game to be accurate to 1mm, then you cannot have a map larger than 16km
In practice yes, but the map could go in any direction, in any shape. It seems like it'd be wasteful to limit the game size to a fixed grid when the map most likely won't end up grid shaped.
 
D

Dracodino300

Guest
Again, USE CHUNKS.
That way you are not really wasting any memory.
Check out my rudimentary tutorials:
I am already using something similar, only generating the "rooms" when you get close and deactivating them when you move away from them. The issue here was that this is all happening in a single chunk.
 
Z

zendraw

Guest
well you would still have get the nieghbours ids in the first place. Unless you have a good idea, I see no way around using collision functions at least once.
global.grid[# x div grid_size, y div grid_size]=1; ????
tile=grid[# x+1, y]+(grid[# x+1, y-1]*2)+(grid[# x, y-1]*4)+.... ???????
???????????

2 lines and you got your autotilin code.
altho this is needed only if you cant set the tile in the generation itself. but id say its best you do that in the generation itself rather then doin 2 separate processes.
 

Simon Gust

Member
global.grid[# x div grid_size, y div grid_size]=1; ????
tile=grid[# x+1, y]+(grid[# x+1, y-1]*2)+(grid[# x, y-1]*4)+.... ???????
???????????

2 lines and you got your autotilin code.
altho this is needed only if you cant set the tile in the generation itself. but id say its best you do that in the generation itself rather then doin 2 separate processes.
sure thing, but we're evaluating the situation when we don't use grids and just objects. Don't ask me why.
 
Z

zendraw

Guest
sure thing, but we're evaluating the situation when we don't use grids and just objects. Don't ask me why.
no problem,
when u place the objects set theyr xstart/ystart to xstart div xstart and ystart div ystart and then in another WITH ask
if (other.xstart==xstart+1 && other.ystart==ystart) {tile++};
if (other.xstart==xstart+1 && other.ystart==ystart-1) {tile+=2};
... somthing like that. using xstart/ystart for optimisation purposes.... in case he says 2 much variables.
there are ways to avoid hardcoding it with collisions. he just dont know what he wants or is doing.
 
D

Dracodino300

Guest
sure thing, but we're evaluating the situation when we don't use grids and just objects. Don't ask me why.
Primarily because grids are not resizable, and the dimensions of the room are not predetermined.

@Vile Rack : I still don't understand how looping through a list of instance's xstart & ystart would be quicker than checking a single point in the room against the same list of instances.
 
Z

zendraw

Guest
im not sure but i think your still looping thru all the instances.

also that excuse makes no sense. 2700 div 256 is the same as 172 div 16. and you can just create another grid.
 

Simon Gust

Member
im not sure but i think your still looping thru all the instances.

also that excuse makes no sense. 2700 div 256 is the same as 172 div 16. and you can just create another grid.
yes if we start using grids, but if we use grids we might as well not use instances at all.
Maybe to clear up things, @Dracodino300 can tell us exactly what methods they want to use right now.
Only objects? Only grids? Objects + Grids? Something else?
 
D

Dracodino300

Guest
To be perfectly honest, I'm confused as to what objects we're even talking about.
 
Z

zendraw

Guest
do you even know what the code you posted does? whats it purpose? do you even have any base build on it?
 

GMWolf

aka fel666
I am already using something similar, only generating the "rooms" when you get close and deactivating them when you move away from them. The issue here was that this is all happening in a single chunk.
I fail to understand the issue.
 
D

Dracodino300

Guest
do you even know what the code you posted does? whats it purpose? do you even have any base build on it?
The code is made to check the instances adjacent to the object and change it's sprite accordingly. I'm not sure what you mean by "base build", but it's working well in-game, apart from that it's slow.
 

Simon Gust

Member
The code is made to check the instances adjacent to the object and change it's sprite accordingly. I'm not sure what you mean by "base build", but it's working well in-game, apart from that it's slow.
So, your current method is to create a billion instances next to each other and autotile them via collision functions, pretty clear.
Now, we realised that having a lot of objects slows down the process. Now to speed this up you are given solutions like using grids instead.
Or deactivate instances during the auto-tiling and what not.

Now, what do you want to do? Do you want to stick to instances or do you want to try grids?
 
D

Dracodino300

Guest
So, your current method is to create a billion instances next to each other and autotile them via collision functions, pretty clear.
Now, we realised that having a lot of objects slows down the process. Now to speed this up you are given solutions like using grids instead.
Or deactivate instances during the auto-tiling and what not.

Now, what do you want to do? Do you want to stick to instances or do you want to try grids?
I don't see how using grids would stop me needing multiple objects. The objects themselves are solid in-game, not just sprites. The collide with the player, enemies, projectiles etc. So as far as I understand they need to be there regardless.

If using a grid would improve the performance, usability etc. then I'm up for it, I'm just struggling to see the benefit.
 
Z

zendraw

Guest
grids wont remove instances, they will make your autotiling easyer so you dont need collision checks. ofcorse grids can remove instances from the world regardles if theyr solid or not but im against using grids like that. for the simple reason you cant draw a grid in a optimal way, maybe if you use surfaces, but then you open more space for bugs, etc. and you cant interact with a grid that much, unlike an instance that can store variables.
 

Simon Gust

Member
I don't see how using grids would stop me needing multiple objects. The objects themselves are solid in-game, not just sprites. The collide with the player, enemies, projectiles etc. So as far as I understand they need to be there regardless.

If using a grid would improve the performance, usability etc. then I'm up for it, I'm just struggling to see the benefit.
The grids would be soley for displaying the game world (and auto-tiling it), collisions for entities can also be done with grids.
And if you really feel like it, you can simply put all the objects inside the grid spaces to have the benefit of fast lookups and the functionality of objects.
 
M

MirthCastle

Guest
And if you really feel like it, you can simply put all the objects inside the grid spaces to have the benefit of fast lookups and the functionality of objects.
This is what I do: (can't wait for light objects and methods!!)

I have a grid that has references to deactivated objects that I use for containers of all sorts of information about the world (yes I can use maps and lists). The deactivated objects keep track of their neighbors, their tile data, actors standing on them, walkable, height, terrain costs, temperature, water level, etc and a bunch of other things -
I can reference any object at any time through the grid - and I can reference any information about any of their neighbors, neighbor data etc all through the same functions.. including collisions and line of sight all without a single collision function, at the speed of a grid\variable lookup :)

The above is what we are trying to get you to see @Dracodino300. If you don't need all the information that i track, then you just need the grid. :)
 
D

Dracodino300

Guest
I think I understand what you guys are saying now. Well, the concept at least, but I think I'd need to start from scratch to really understand how it all would work. Thanks for the help though :)
 
Z

zendraw

Guest
my idea is make your world on a grid and just use instances to draw things. like you cover a small area with these instances and just move them around. like imagine a projector that projects a map on a large surface, your grid, and then imagine a white board, your instances, and black background. so you just move the white surface around displayn parts of the map from the projector, large enough to cover the view.
 
Top