GMS 2 [Grid-Based] What is the most efficient way to create a ghost/preview of potential movement?

S

syntheticdream57

Guest
I am currently using IDE V2.2.2.413
Hello everyone!
I am quite new to GML, and doing a lot of experimentation, and toying around to understand how all the moving parts work. I have spent quite a lot of time in the past few days doing my best to problem solve, and find solutions to my roadblocks on my own, reading the manual, and posts of others encountering similar issues. I have found many posts relating to what I am trying to achieve, but I did not seem to locate one that tackles the exact issue I'm hung up on.

My long term goal is to create a functional, and efficient grid-based movement system that runs in a turn-based framework. The closest comparison I can make to what I want to achieve, is Fire Emblem/Advanced Wars movement style. Each unit has a set distance of cells in the grid that it can move per turn. I have the movement, and pathing already working via an mp_grid, through lots of research, and forum hunting. What I am really trying to nail down right now, is a visual preview of where you can potentially move while your unit is selected.

Ss_fe05_orsin's_range.png
The effect I aim to achieve.

Here is what I currently have:


obj_game (Create)
Code:
global.grid_size = 32;
global.grid = mp_grid_create(global.grid_size/2, global.grid_size/2, room_width / global.grid_size, room_height / global.grid_size, global.grid_size, global.grid_size);


Next, I have a cursor object that follows the grid, and snaps to the cell that the mouse is currently in. It both draws the cursor, and handles checks on what you are clicking, and is animated.

cursor_move.gif cursor.gif

obj_cursor (Create)
Code:
// hide the actual cursor
cursor_sprite=-1;
window_set_cursor(cr_none);

// initializes the timer for the cursor animation
alarm[0] = 60;

// start with the player deselected.
global.p_select = false;
obj_cursor (Draw)
Code:
// draw the custom cursor, animated at 2fps
if alarm[0] >= 30 {
var xx=global.grid_size*(window_mouse_get_x() div global.grid_size);
var yy=global.grid_size*(window_mouse_get_y() div global.grid_size);
draw_sprite(spr_cursor, 1, xx, yy);
}
else {
var xx=global.grid_size*(window_mouse_get_x() div global.grid_size);
var yy=global.grid_size*(window_mouse_get_y() div global.grid_size);
draw_sprite(spr_cursor, 2, xx, yy);
}
obj_cursor (alarm0)
Code:
alarm[0] = 60;

obj_cursor (Global Left Pressed)
Code:
var mx = global.grid_size*(window_mouse_get_x() div global.grid_size);
var my = global.grid_size*(window_mouse_get_y() div global.grid_size);
var px = global.grid_size*(obj_player.x div global.grid_size);
var py = global.grid_size*(obj_player.y div global.grid_size);

if mx = px && my = py {
    show_debug_message("PLAYER SELECTED");
    global.p_select = true;
    with obj_player {
    //when the player is clicked, generate a path from current location.
    mp_grid_path(global.grid, path_up,x,y,x,y-mv_dist,0);
    mp_grid_path(global.grid, path_down,x,y,x,y+mv_dist,0);
    mp_grid_path(global.grid, path_left,x,y,x-mv_dist,y,0);
    mp_grid_path(global.grid, path_right,x,y,x+mv_dist,y,0);
    mp_grid_path(global.grid, path_up_left_diag,x,y,x-mv_diag,y-mv_diag,1);
    mp_grid_path(global.grid, path_up_right_diag,x,y,x+mv_diag,y-mv_diag,1);
    mp_grid_path(global.grid, path_down_left_diag,x,y,x-mv_diag,y+mv_diag,1);
    mp_grid_path(global.grid, path_down_right_diag,x,y,x+mv_diag,y+mv_diag,1);
    }
}
else {
    show_debug_message("PLAYER NOT SELECTED");
    global.p_select = false;
}

Now comes the part where I am not sure the best course of action.
I currently have the obj_player scripts setup to create 8 different paths, one in each possible direction at creation.

obj_player (Create)
Code:
//mv_spd is the control for how many grid spaces the player is allowed to move, in an integer based on cells.
mv_spd = 3;
//Initializes variable that stores how many movement spaces are left for the player in the current turn.
mv_ap = mv_spd;

//mv_dist is the grid size, multiplied by the current move speed modifier to get a true loc variable based on
//grid size
mv_dist = 32 * mv_spd;

//variable for storing if the player is currently moving or not
p_moving = false;

//Initialize the paths required for movement later, and for ghosting the potential movement grids to the player.
path_up = path_add();
path_down = path_add();
path_left = path_add();
path_right = path_add();
path_up_left_diag = path_add();
path_up_right_diag = path_add();
path_down_left_diag = path_add();
path_down_right_diag = path_add();[/SIZE]
I'm unsure if creating so many paths is truly necessary, but this is the way I have currently designed it.

obj_player (Draw)

Code:
//This is here to ensure the player remains visible, while code exists inside the Draw event.
draw_self();

//subtracts one cell during diag movement, because diag movement will not be allowed.
mv_diag = mv_dist - global.grid_size;

//Checks to see if the player is currently selected.
if global.p_select = true {
    //when the player is selected, draw the paths from current location.
    draw_path(path_up,x,y,0);
    draw_path(path_down,x,y,0);
    draw_path(path_left,x,y,0);
    draw_path(path_right,x,y,0);
    draw_path(path_up_left_diag,x,y,1);
    draw_path(path_up_right_diag,x,y,1);
    draw_path(path_down_left_diag,x,y,1);
    draw_path(path_down_right_diag,x,y,1);
}

This sort of does what I want, as when the player is selected, it creates a path in all directions out based on the amount of cells I want the player to be able to move.

path.PNG

My question is:
Is the code I have here the most efficient way to generate those paths? My plan is to then draw sprites in some manner of either green or red with 0.5 alpha at each cell, or along each point of the path, indicating if it is accessible or not (exactly like the Fire Emblem screenshot,) and I planned to utilize mp_grid_get_cell to determine which color is painted.

Is there a simpler way to get all the potential cells that could be reached within a 3 cell radius in every direction, and then color those based on if they are vacant or blocked? And what would be the best manner of actually generating a sprite at those cells? I know that creating a non-solid instance at each location would be inefficient on resources. I considered draw_rect as well, except it appeared that you could not alter the alpha of those rectangles when I tried it.

Thanks so much in advance for any help! I've tried to make my goal as clear as possible, but I'd be happy to give any more details if required. It's clear in my head.. It may not be as clear in this post! :potato:
 

SeraphSword

Member
So I have an old project, long abandoned, that uses a similar system. If I recall correctly, you don't create paths for each direction, you just use the mp_grid to check your character's range and draw the squares first, then you create the path once you've chosen a valid destination.
You can grab the project here: https://www.dropbox.com/s/c3mhkeujcd3auvs/pathofsteel2b.yyz?dl=0
It's not very well commented, but with a little study you should be able to figure out what's going on.
Feel free to use any of the code you see there, although you might have to modify some to fit what your project is doing (I don't think mine had diagonal movement for example).

Just click on the character who has AP (I believe it's the dude in the middle), and the boxes that pop-up will be move, attack, and cancel from left to right.

Hope that helps. Good luck with your game.
 
S

syntheticdream57

Guest
Hope that helps. Good luck with your game.
This is awesome! Thank you so much!
The entire structure of how you have the characters setup is really helpful, and you have the exact system I was trying to work out on display here. I really appreciate you taking the time to share it with me; it's definitely going to help.
 
Top