• 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!

Legacy GM [SOLVED]Isometric pathfinding using mp_grid?

dazza_bo

Member
I have read a few times that the easiest way to implement pathfinding in an isometric game is to handle it all in regular 'top down' code then only use isometric for rendering. What I don't understand is how though.

I have a ds_grid which is filled with isometric sprite tiles that I can render on screen isometrically, change or delete them with a mouse click, that all works fine but how do I use mp_grid with the ds_grid? The mp_grid_create function takes xstart and ystart arguments whereas the ds_grid does not so do I create the mp_grid at the same x and y position as I begin rendering the isometric tiles?

Sorry if these are dumb questions, this is my first attempt at isometric.
 

JackTurbo

Member
Hi Dazza,

I'm using a custom A* implementation and not MP functions in my isometric project, so I havent actually tried this myself but here is my first thoughts regarding how to approach this.

First off you'll need to think about your 2D (non-isometric) grid. What size are the squares and where does it start?

I use 64px squares and 0,0 for the coords of the first cell for simplicity so I'll use that in my example.

For my actors (NPC's, player characters etc) I'd set their assigned sprite to a 64px square (if this causes issues might be better reducing it to 62px). This should ensure that they occupy a whole square and will be used by the MP grid for deciding if a cell is free or not.

Then I'd store the sprite that I want to draw in an instance variable, I'll call this isoSprite. In the object's draw event you do not use draw_self but instead use draw_sprite to render the desired sprite in the correct position. This should look something like this:
Code:
draw_sprite(isoSprite, -1, (x - y) * 2, (x + y) *1)
Now you should be able to create a mp_grid at 0, 0 coords with a cell size of 64 by 64 and it should use the object collision mask and actual x/y for that purpose, but what is actually being rendered to screen should look correct.

Now how do we go about using this with your ds_grid?
Well we can easily use a double for loop to iterate through the ds_grid and check if the cell contains something that should effect pathfinding and if it does, then use mp_grid_add_cell to make the relevant cells on the mp_grid non-passable.
 
Last edited:

dazza_bo

Member
Hi JackTurbo,

I think this is where I'm getting so easily confused:
First off you'll need to think about your 2D (non-isometric) grid. What size are the squares and where does it start?
I use an object as a controller that creates my ds_grid and updates as I add or change tiles but it's never actually in the room as a 2D grid. The controller object is just in the upper left somewhere so that the code will run. I then use the draw event to use nested for loops to iterate through the ds_grid and draw each tile isometrically. Am I doing it wrong then?
 

JackTurbo

Member
Hi JackTurbo,

I think this is where I'm getting so easily confused:

I use an object as a controller that creates my ds_grid and updates as I add or change tiles but it's never actually in the room as a 2D grid. The controller object is just in the upper left somewhere so that the code will run. I then use the draw event to use nested for loops to iterate through the ds_grid and draw each tile isometrically. Am I doing it wrong then?
Not really, that sounds like a reasonable approach to drawing the tiles themselves. The key thing is that the MP_grid needs to run in 2D.

you can translate between this 2D mp_grid and your ds_grid pretty easily by using grid coordinates rather than x/y coordinates.
 

dazza_bo

Member
Damn it, getting an error on that guy's dropbox link. Don't suppose you could host it somewhere for me?
 

YanBG

Member
Can't find it on my pc. Maybe @Juju can help you?

Do you know how to set a top-down mp_grid and path? I made it work with using only one path though(since i don't need the top-down switch as in the example).

In script "Navigate" you pass the x and y of the player's cell and the x and y of the mouse cell when you click to move. You get their cells by converting the world coordinates to grid positions.
Code:
///Define the path
var start_x=argument[0],start_y=argument[1],end_x=argument[2],end_y=argument[3],pace=argument[4];
if !mp_grid_path(map_grid,path,start_x,start_y,end_x,end_y,1){
    return false;
}else{
    mp_grid_path(map_grid,path,start_x,start_y,end_x,end_y,1);
    var pos=0;
    while(pos<path_get_number(path)){
        xx=path_get_point_x(path,pos);
        yy=path_get_point_y(path,pos);
        scr_x=(xx-yy);
        scr_y=(yy+xx)/2;
        path_change_point(path,pos,scr_x,scr_y,200);
        pos++;
    }
    path_start(path,pace,0,false);
    return true;
}
 
Last edited:

JackTurbo

Member
Converting mp_grid to isometric movement requires you to replace the path's points with the isometric offset.
Keeping all data level stuff in 2D and just applying isometric offsets to how things are drawn makes way more sense and keeps things much simpler in the long term.

The path and its points are data, they should stay in 2D. If you're applying the off set to how stuff is drawn it will still look correct.
 

dazza_bo

Member
Can't find it on my pc. Maybe @Juju can help you?
No worries mate. Thanks anyway.

After reading JackTurbo's post a few more times I ended up getting it working to a certain degree but can't figure out how to convert mouse clicks on the isometric tiles back to movement in the 2D mp_grid for player character movement etc.
In the object's draw event you do not use draw_self but instead use draw_sprite to render the desired sprite in the correct position. This should look something like this:
Code:
draw_sprite(isoSprite, -1, (x - y) * 2, (x + y) *1)
Would it just be the inverse of the code (x - y) * 2, (x + y) *1 posted above?

I have my mp_grid created at 0, 0 but I'm using an x and y offset for the origin of the rendering of the tile sprites so that the iso grid is in the middle of the room. So in my player character's draw event I have:
Code:
draw_sprite(sprite_index, 0, xorigin + ((x - y) * 2), yorigin + ((x + y) * 1));
 

JackTurbo

Member
Basically you'll want to run the isometric projection in reverse on the mouse x/y when interacting with stuff in the isometric perspective

Take a look at Yellow Afterlife's run down of isometric on his blog, the "Global to Local" section covers the reverse conversion (explanation of what the variables he mentions relate to is just above that section).
https://yal.cc/understanding-isometric-grids/

The way you're handling the grid offsets seems fine to me. I'd probably put them in macros (if they're constant) or global variables (if they change from room to room), as you'll likely be using those values in a lot of different objects.

While you're on Yellow Afterlife's site, I'd whole heartedly recommend downloading his example GMS project and taking a look through the code. I remember back when I was first grappling with isometric it was doing this that made me finally understand the whole concept and why the separation of data from visuals is so key to isometric games.
 
Last edited:

YanBG

Member
If you draw with offsets, how would you check the isometric distance between objects(other built in GM fuctions too), their x and y will be on the top-down grid?

Edit: I guess it's possible if you use the amount of cells between objects, instead of pixels. Like 1 cell distance would mean adjacent.
 

JackTurbo

Member
If you draw with offsets, how would you check the isometric distance between objects(other built in GM fuctions too), their x and y will be on the top-down grid?

Edit: I guess it's possible if you use the amount of cells between objects, instead of pixels. Like 1 cell would mean adjacent.

Why would you want to measure distances between objects "in the isometric perspective"?

Your results wouldn't be consistent with the perspective.

With isometric perspectives for example, vertical distances are covering twice as much "in game space" as the same distance horizontally.

Dealing with all the data in 2D and applying the perspective to how things are rendered automatically compensates for things like this.
 

YanBG

Member
For talking with NPCs and attacking enemies, but i guess if their x and y collide in top-down they'd still be drawn close to each other in isometric(now i'm interested how Diablo 2's code was written...).
 

JackTurbo

Member
You would just create all the logic as if the game was 2d top down and it would work perfectly as it should in isometric providing your 2D vs Iso grids were consistent.
 

YanBG

Member
Yeah that sounds perfect, this explanation should be pinned somewhere because most isometric tutorials for GM give bad advices.
 

dazza_bo

Member
Basically you'll want to run the isometric projection in reverse on the mouse x/y when interacting with stuff in the isometric perspective

Take a look at Yellow Afterlife's run down of isometric on his blog, the "Global to Local" section covers the reverse conversion (explanation of what the variables he mentions relate to is just above that section).
https://yal.cc/understanding-isometric-grids/

The way you're handling the grid offsets seems fine to me. I'd probably put them in macros (if they're constant) or global variables (if they change from room to room), as you'll likely be using those values in a lot of different objects.

While you're on Yellow Afterlife's site, I'd whole heartedly recommend downloading his example GMS project and taking a look through the code. I remember back when I was first grappling with isometric it was doing this that made me finally understand the whole concept and why the separation of data from visuals is so key to isometric games.
Working perfectly now. Many thanks guys, you helped me out a lot. :D
 
Top