Legacy GM Instance_nearest - when to use it? performance impact?

Wile94

Member
Hi, I'm working on a platformer (if you saw my posts i'm always working on a platformer :rolleyes:). Currently I'm looking the best way to perform some collisions without running the place_meeting and instance_place function constantly. I coded a moving platform using the next lines of code:

Code:
movingPlatformID = noone;
phyMovPlatform = false;

Code:
//Save half sprite sizes
var midw = sprite_width >> 1;
var midh = sprite_height >> 1;

if (onGround)
{
    if (!phyMovPlatform) movingPlatformID = noone; else check_movplat_position();
}
else
{
    //Check for moving platform distance
    if (!phyMovPlatform) check_movplat_nearest();
}

//Check Y Collision
var vdir = sign(verVel);
var vvround = math_round_dir(verVel * DT,vdir);
if (vvround != 0)
{
    //{
    //ds_grid collision code here
    //}
    //Check for moving platform
    if (!phyMovPlatform) if (vvround > 0)
    {
        var land_on_movplat = place_meeting_movplat();
        //Land on platform
        if (land_on_movplat)
        {
            y = movingPlatformID.bbox_top - midh;
            verVel = 0;
            phyGround = true;
            phyMovPlatform = true;
        }
    }
}

Code:
//PLATFORMS ALREADY MOVED IN BEGIN STEP EVENT

//Save platform movement
var platform_hmove = (movingPlatformID.moveType == 0 || movingPlatformID.moveType == 2);
var platform_vmove = (movingPlatformID.moveType == 1 || movingPlatformID.moveType == 2);
//horizontal
if (platform_hmove)
{
    //Match last object position with platform
    var xadd = movingPlatformID.x - movingPlatformID.xprevious;
    x += xadd;
    //Check ds_grid wall collision
    var check_wall = ds_grid_get_cell_at(bbox_left,
                                         bbox_top,
                                         bbox_right,
                                         bbox_bottom,
                                         gridID,CELL_WALL);
    //Collide with walls
    if (check_wall)
    {
        x = math_round_dir(x,-sign(xadd));
        x -= xadd;
    }
}

//vertical
if (platform_vmove)
{
    //Match last object position with platform
    var yadd = movingPlatformID.y - movingPlatformID.yprevious;
    y += yadd;
    //Check ds_grid wall collision
    var check_wall = ds_grid_get_cell_at(bbox_left,
                                         bbox_top,
                                         bbox_right,
                                         bbox_bottom,
                                         gridID,CELL_WALL);
    //Collide with walls
    if (check_wall)
    {
        //If movement is circular, avoid top collision bugging
        if(movingPlatformID.moveType == 2)
        {
            //most likely to hit on top
            var ydir = movingPlatformID.pathYDir;
            if (!ydir)
            {
                var cell_to_snap = (bbox_top div CELL)+1;
                y = ds_grid_snap(y,ydir,cell_to_snap,sprite_height >> 1);
            }
        
        }
        y = math_round_dir(y,-sign(yadd));
        y -= yadd;
    }
}

//Check if still on top of the platform
var on_platform = check_box_on_box(bbox_left,bbox_right,
                                   bbox_bottom+1,bbox_bottom+1,
                                   movingPlatformID.bbox_left,
                                   movingPlatformID.bbox_right,
                                   movingPlatformID.bbox_top,
                                   movingPlatformID.bbox_top);

//Continue sticking to platform / Reset parameters
phyMovPlatform = on_platform;
if (!phyMovPlatform) movingPlatformID = noone;
exit;

Code:
//Check for nearest moving platform and get an ID
movingPlatformID = instance_nearest(x,y,objMovPlatform);
if (movingPlatformID)
{
    //Check if a minimum distance is met
    var min_dist = CELL * (movingPlatformID.image_xscale+1);
    var dist_to_platform = (point_distance(x,y,movingPlatformID.x,movingPlatformID.y) < min_dist);
    if (!dist_to_platform) movingPlatformID = noone;
}
exit;

Code:
//Check if desired moving platform is on meeting range
var r = false;
if (movingPlatformID)
{
    //Check if object is beneath platform
    var platform_below = bbox_bottom < movingPlatformID.bbox_top+1;
    //Already touchig the platform?
    if (place_meeting(x,y,movingPlatformID))
    {
        //check if platform was already below in the previous step
        var ydiff = (movingPlatformID.y - movingPlatformID.yprevious);
        platform_below = bbox_bottom < (movingPlatformID.bbox_top+1)-ydiff;
    }
    //Parameters are met
    if (platform_below)
    {
        //Check the collision
        if (place_meeting(x,y+(verVel * DT + 1),movingPlatformID))
        {
            //Return ID just in case (this line would eventually not be needed?)
            movingPlatformID = instance_place(x,y+(verVel * DT + 1),movingPlatformID);
            //Confirm collision
            if (movingPlatformID.canLand) r = true;
        }
    }
}
return r;
exit;

Is heavy coded but is working really well so far. As you can see, is the Player/Instance checking for the nearest platform and not the other way around. But now I wanna code a static fading platform (those that when you land on top will fade after a short period of time and you will fall) and I don't know if make the player/instance check when is near a platform or the platform do the checking with those instance.
I have this concern because the player/instance won't be colliding with that platform per se, in fact it will detect in that region of the ds_grid that there are cells with a "PLATFORM" value and land on top of them.

So my question is, how much running instance_nearest cost performance wise? It would be better to just use with() and make a distance check instead? If I do it that way my script would end up like this

Code:
//Check for nearest moving platform and get an ID
movingPlatformID = noone;
with(objMovPlatform)
{
    var min_dist = CELL * (image_xscale+1);
    var dist_to_instance = (point_distance(x,y,other.x,other.y) < min_dist);
    if (dist_to_instance)
    {
        other.movingPlatformID = id;
        break;
    }
}
exit;

I've already tested it and I didn't see any difference in my fps.
 
Last edited:

CloseRange

Member
I've already tested it and I didn't see any difference in my fps.
that's because there isn't really a difference.
EDIT: just wrote about how rectangle collisions work and realized i read the post wrong. If you're curious here you go
Assuming you are using square collision masks there are much better ways to perform collision checking.
This is why game maker's collision masks have differant options like ellipse, diamond, precice, and rectangle by default.
Rectangles are the easiest to calculate using the function rectangle_in_rectangle function, or doing something like this:
Code:
/// rectCollision(x1, y1, x2, y2, ox1, oy1, ox2, oy2)
return x1 < ox2 && x2 > ox1
      && y1 < oy2 && y2 > oy1;
where x1, y1, x2, y2 is the points of the first mask and ox1, oy1, ox2, oy2 is the points of the other mask
you did a circle collision in your example by using point distance:
Code:
/// distance(x1, y1, x2, y2)
var dx = x2 - x1;
var dy = y2 - y1;
return sqrt(dx*dx   +   dy*dy);
aka Pythagorean's therome
Note that doing a square root function takes about (if I remember correctly) around 60 times longer than a mulitply and a simple compare operator

as for instance_nearest?
well it's faster than what you wrote.
point_distance uses sqrt to get the exact distance.
instance_nearest does the same thing except because it just needs to know what's closer it's able to fully skip square root:

That being said.....
it does not matter.....
As far as I'm aware every plat-former, including 3d platformers and AAA games do constant collision checking.
If your game gets big (and I mean really really really big, to the point where it's triple A size) then you do something called broad phase collision checking.
This is just cutting down of how many things you need to do Narrow Collision Checking (this stuff)
but for a simple plat-former you are very very unlikely to need to do this.

The exception might be if you have a lot of precise collision checking then you might want to first do a check to see if the player is in a square box surrounding it and if so then you can do the precise collision check.
 
Last edited:

Wile94

Member
EDIT: just wrote about how rectangle collisions work and realized i read the post wrong. If you're curious here you go
Assuming you are using square collision masks there are much better ways to perform collision checking.
This is why game maker's collision masks have differant options like ellipse, diamond, precice, and rectangle by default.
Rectangles are the easiest to calculate using the function rectangle_in_rectangle function, or doing something like this:
Code:
/// rectCollision(x1, y1, x2, y2, ox1, oy1, ox2, oy2)
return x1 < ox2 && x2 > ox1
      && y1 < oy2 && y2 > oy1;
where x1, y1, x2, y2 is the points of the first mask and ox1, oy1, ox2, oy2 is the points of the other mask
I see, actually I used exactly that function when the instance is already on top of the platform (my script is called check_box_on_box() but is exactly the same) because i didn't want to use place_meeting(x,y+1,id) constantly.

you did a circle collision in your example by using point distance.
aka Pythagorean's therome
Note that doing a square root function takes about (if I remember correctly) around 60 times longer than a mulitply and a simple compare operator

as for instance_nearest?
well it's faster than what you wrote.
point_distance uses sqrt to get the exact distance.
instance_nearest does the same thing except because it just needs to know what's closer it's able to fully skip square root.
Mmm so maybe instead of checking two points i should check an area kind of like this?

Code:
//Check for nearest moving platform and get an ID
movingPlatformID = noone;
with(objMovPlatform)
{
    if (!canLand) continue;
    var min_dist = CELL * 2;
    var check_area = check_box_on_box(bbox_left,bbox_right,
                                      bbox_top-min_dist,bbox_bottom,
                                      other.bbox_left,other.bbox_right,
                                      other.bbox_top,other.bbox_bottom);
    if (check_area)
    {
        other.movingPlatformID = id;
        break;
    }
}
exit;
or

Code:
//Check for nearest moving platform and get an ID
movingPlatformID = instance_nearest(x,y,objMovPlatform);
if (movingPlatformID)
{
    //Check if a minimum distance is met
    if (!movingPlatformID.canLand)
    {
        movingPlatformID = noone;
        exit;
    }
    var min_dist = CELL * 2;
    var check_area = check_box_on_box(bbox_left,bbox_right,
                                      bbox_top,bbox_bottom,
                                      movingPlatformID.bbox_left,
                                      movingPlatformID.bbox_right,
                                      movingPlatformID.bbox_top-min_dist,
                                      movingPlatformID.bbox_bottom);
    if (!check_area) movingPlatformID = noone;
}
exit;
EDIT: I've tested it these two codes and the one with instance_nearest is more performance friendly for what I can tell. My FPS are still pretty large (around 1800-2300) but the one with the with() loop frop my fps down to 1300-1200.
 
Last edited:

CloseRange

Member
(my script is called check_box_on_box() but is exactly the same) because i didn't want to use place_meeting(x,y+1,id) constantly.
That's like me saying I don't want to eat store bought cookies because I'm on a diet, then go home and make my own to eat.

Looking at the code I'm unsure of the reason for 'min_dist'
it looks like a top bounding offset but if so then you should just go into the sprite and modify the bounding box accordingly.
I assure you whatever the reason for min_dist is there are better alternatives
Then just do this:
Code:
movingPlatformID = instance_place(x, y, objMovPlatform)
There is no reason not to use the built in function in this case.
It's not faster and will not cause slowdown in this case.
Sometimes you shouldn't reinvent the wheel
 

Wile94

Member
Looking at the code I'm unsure of the reason for 'min_dist'
it looks like a top bounding offset but if so then you should just go into the sprite and modify the bounding box accordingly.
I assure you whatever the reason for min_dist is there are better alternatives
That code is for detecting the nearest platform below the player/instance and getting the ID, not for colliding with it. I use this to give some error margin and avoid falling through the platform specially the ones with vertical movement.
I'm no expert programmer what so ever, I just like trying new ways to code. I've already made this mechanic with the collisions functions and they work well, but I'm making a Big Game and all I read here and In reddit is how innefficient and slow are place_meeting and instance_place. So I don't know...
 
Last edited:

CloseRange

Member
Trying new things is fine I'm just telling you that it's not as big of a deal as people say.
As a test I opened up the project I was working on and just put this in a draw event
Code:
repeat(100000) {
    if(rectangle_in_rectangle(random(100), random(100), random(100), random(100), random(100), random(100), random(100), random(100))) {
       
    }
}
it ran 100% fine at 10,000 runs but slowed down at 100,000

So what is a big game to you? If you put 100,000 wall objects in one room (in a single room, not in the whole game) then you can worry.
At this point though you want to look into loading chunks instead of small optimizations.
 

Joe Ellis

Member
The code I use for all this stuff is way smaller and not slow at all, I never use instance nearest, and for moving platforms I simply do a collision latching process, where the player first collides with it, and it's position is moved by the same amount the platform does, then the next step it checks whether the player is still sitting on top of it, if not then the process is ended, if so it simply repeats the thing where it moves with it
 

TheouAegis

Member
Could always consider Castlevania's method. For moving platforms, you only check for one when falling. As soon as you land on one, you save its id in the player (or whatever). From that point on, the only collisions you worry about are horizontal collisions. If you jump, you clear the saved id. If bbox_left > saved's bbox_right, you clear the id. If bbox_right < saved's bbox_left, you clear the id.
 

GMWolf

aka fel666
Using your own collision detection methods will be slower than using the built in systems.

If you use rectangle_in_rectangle for every collision pair, GM has no choice but to evaluate that.

But by using instances, GM is able to build acceleration structures (probably bvh) to greatly speed up your collision checking.

I really wouldn't worry about performance at this point.

If you map is big, look into instance deactivation.
You don't want to activate/deactivate instances every frame because that will kill performance (having to rebuild structures etc).
Instead try using a chunk system, where chunks are loaded in and out rather than dealing with the whole world.


And don't forget tilemaps!!!
Collision detection against tilemaps is very cheap, no matter how large the tilemap is!
 
D

Deleted member 16767

Guest
A good way to use instance_nearest is if you have a parent for all enemy objects and they need to seperate from eachother. But I don't know how that would work on a platformer.
 

DBenji

Member
...
I've already tested it and I didn't see any difference in my fps.
This indicates to me that you don't yet know how to do profiling - where you run the game in debug mode and click the profile button and then look at how much time each function is actually taking.

Believe it or not, it is highly possible that all the code you wrote to avoid place_meeting could in fact be much slower than a simpler place_meeting implementation - but you won't know until you actually profile it.

I also see you are using the sprite halfwidth algorithm probably recommended by this article. I found in my project that using place_meeting with precise collision detection (with large 512x512 sprites) was actually 3-4 times faster (!) than my own gml implementation of "simple" bbox collision and it still perplexes me. It seems that in many cases, if you really wanted to gain performance from bbox collision recommended by that article, you would be better off writting a c++ dll that handles those calculations for you rather than gml code. Gm's place_meeting function may be "slow", but in many cases it can still beat alternative solutions using pure gml because it's built-in.
 

Wile94

Member
Thank you for all your feedback, I'll look into it. This helps me a lot, I've been coding in gml for about 2 years and I'm doing it only for the love of coding and game making, the only kind of "programming" I did was in MUGEN by Elecbyte when I was a teenager.
I'll have to admit I became kind of a performance maniac because of all the post I've read, but my rooms are quite small and only a few objects inside it and I tend to eventually mistrust every little thing I code despite is working well, so I should start to let things be :confused:.

If you map is big, look into instance deactivation.
You don't want to activate/deactivate instances every frame because that will kill performance (having to rebuild structures etc).
Instead try using a chunk system, where chunks are loaded in and out rather than dealing with the whole world.

And don't forget tilemaps!!!
Collision detection against tilemaps is very cheap, no matter how large the tilemap is!
It's really cool to read a message from you, I've watch a lot of your videos! My game is based on room swapping (actuallly it all happens in one room), so no big maps at all. Anyway I coded in the past a instance_deactivation funtion using an array and checking the view position so It only runs whenever a "cell" from the room enters or leaves the view.
I've already replaced static platforms, spikes and walls with a ds_grid (since I have GMS 1.4 I don't have tilemap functions) and It's pretty fast anyway, I've also made it check for any size the bbox might have. I always make sure to free them from memory as well.

Could always consider Castlevania's method. For moving platforms, you only check for one when falling. As soon as you land on one, you save its id in the player (or whatever). From that point on, the only collisions you worry about are horizontal collisions. If you jump, you clear the saved id. If bbox_left > saved's bbox_right, you clear the id. If bbox_right < saved's bbox_left, you clear the id.
Already done, I always check for platforms only when the instance has a positive vertical velocity, check when the bounds are outside the platforms box or when going up (jump) and check whenever the repositioning of the object on top of the platform is colliding with a wall (horizontally and vertically, when hits a wall above it falls down, eventually getting crushed and die if I make somekind of solid platform).

This indicates to me that you don't yet know how to do profiling - where you run the game in debug mode and click the profile button and then look at how much time each function is actually taking.

Believe it or not, it is highly possible that all the code you wrote to avoid place_meeting could in fact be much slower than a simpler place_meeting implementation - but you won't know until you actually profile it.
You are right, when I tested I've only check for my visual from show_debug_overlay() but then tested using the profiler and there was a difference (fps count from the profiler going from 5500-5800 down to 3500). I'm still learning how to use it properly, but I'm beggining to understand the information is giving me. Basically, I should trust more the built in collision functions and just search for an alternative when I really see some kind of performance issue.

The time I struggle most with my code is when I'm adding enemies, basically because I want them to have almost the same kind of capability to move through the map as the player and It's a pain in the butt to code the AI with my limited knowledge.

Thank you all :D
 
Last edited:

DBenji

Member
You are right, when I tested I've only check for my visual from show_debug_overlay() but then tested using the profiler and there was a difference (fps count from the profiler going from 5500-5800 down to 3500). I'm still learning how to use it properly, but I'm beggining to understand the information is giving me. Basically, I should trust more the built in collision functions and just search for an alternative when I really see some kind of performance issue.
I can see you're confused. Clicking the "start profiling" button isn't meant to directly improve your fps. It will show you the actual time in ms each function is taking on a chart. You can refer to this timestamped video here to see quickly how it's done.
 

Wile94

Member
I can see you're confused. Clicking the "start profiling" button isn't meant to directly improve your fps. It will show you the actual time in ms each function is taking on a chart. You can refer to this timestamped video here to see quickly how it's done.
I've been using the profiler and for now, with around 8-10 objects in the room, my total average time is around 0,3+/- ms so it's practically nothing? I guess? My game use delta timing but I have my room locked on 60fps and I should be worried when that time goes above 16 ms right? I end up using instance_nearest instead of with(), the script runs slightly faster according to the profiler.
Anyway, I should start coding more stuff and start populating the room to really know how much and what is having an impact in performance.
 
Top