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

GameMaker get location of point in max range (grid based)

T

trentallain

Guest
Okay so I got the AI working thanks to the kind people in my other post, however I have a new issue now. What my AI is currently doing, is moving next to the closest opposing unit. What I want to do is make it move so that that the AI unit is attacking from it's furthest attack range distance, as this makes more sense.

Obviously if the AI unit's attack range is only 1, then it can keep moving up to the target, however I don't know how to do it if the range is greater than that. The unit's can not move diagonally.

Here's a diagram I made (with made up symbols):
example_diagram.png

The red square is the AI unit. The blue square is it's target that it wants to attack. The pink cross is the location I want to know how to find. The dotted line is just an example path of how to get there. The green squares are the locations that are within range at the point I want to find.

How do I find the ideal location (pink cross's location) so that the AI unit is moving to it's maximum range that it can attack from? Also, the AI unit could be anywhere on the grid, not just the location in the diagram.

Not sure on how to approach this.
 
I would just use
Code:
if (point_distance(...) =< MaxRange)
 {
  //execute attack
 }
maybe thats what you want? or you want grid based checking?

Edit: you can draw the range to see it in game with "draw_circle(x,y,MaxRange,true);" in a Draw Event
 
T

trentallain

Guest
I would just use
Code:
if (point_distance(...) =< MaxRange)
 {
  //execute attack
 }
maybe thats what you want? or you want grid based checking?
Oh oops, grid based
Edit: And I need the location to move to
Edit 2: and if it is already in range, it should move away from its target (so that the target is in range but it is as far away as it can go)
 
Grid based point_distance should still work fine. Instead of pixels, imagine the input to the point_direction as grid squares. Just make sure your max range is based off of the grid distance, not the pixel distance.
Code:
if (point_distance(grid_x,grid_y,target_grid_x,target_grid_y) >= max_range) {
   // movement here
}
 
T

trentallain

Guest
Grid based point_distance should still work fine. Instead of pixels, imagine the input to the point_direction as grid squares. Just make sure your max range is based off of the grid distance, not the pixel distance.
Code:
if (point_distance(grid_x,grid_y,target_grid_x,target_grid_y) >= max_range) {
   // movement here
}
That just finds whether something is in range. How can I find a position that is the furthest square away from a target and within its own attack range?
 

JackTurbo

Member
Simplest approach would be to path find directly to the enemy performing a rage check after each square of movement and stop as soon as you are in range
 
T

trentallain

Guest
Simplest approach would be to path find directly to the enemy performing a rage check after each square of movement and stop as soon as you are in range
Thanks, that concept is easier than what I was thinking!
Here's what I came up with to try and do this, however none of the units move.

Key words:
MP = movement points (who many squares you can move)
Range = the radius that you can attack from

Here's code:
Code:
// If there are AI units to be controlled
if ds_priority_size(ai_unit_priority) > 0 {
    // Get the AI unit that is the closest to the player
    var _ai_unit = ds_priority_find_min(ai_unit_priority);
    // If AI can input
    switch (ai_state) {
        case menu.inputting:
            // Create AI mp grid
            var _grid_w = ds_grid_width(global.grid);
            var _grid_h = ds_grid_height(global.grid);
            var _ai_mp_grid = mp_grid_create(0,0,_grid_w,_grid_h,grid.size,grid.size);
            // Find closest player unit
            var _ai_target = ai_get_closest(_ai_unit.x,_ai_unit.y);
            // If the unit isn't trying to move to it's current cell
            if _ai_unit.grid_x != _ai_target.grid_x && _ai_unit.grid_y != _ai_target.grid_y {
                // Convert global.grid to an mp grid
                for (var u = 0; u < _grid_w; u++) {
                    for (var v = 0; v < _grid_h; v++) {
                        // If the grid position isn't free
                        if global.grid[# u, v] != -1 {
                            // Set that position as taken in the mp grid
                            mp_grid_add_cell(_ai_mp_grid, u, v);
                        }
                    }
                }
                // Clear starting position and clear target position
                mp_grid_clear_cell(_ai_mp_grid,_ai_unit.grid_x,_ai_unit.grid_y);
                mp_grid_clear_cell(_ai_mp_grid,_ai_target.grid_x,_ai_target.grid_y);
                // If there is a path
                with (_ai_unit) {
                    // Tell the unit that AI is controlling it
                    ai_selected = id;
                    // Tell the unit its target
                    ai_target = _ai_target;
                    // Clear path points
                    path_clear_points(ai_path);
                    // Create the path regardless if you can fully traverse it
                    mp_grid_path(_ai_mp_grid, ai_path, grid_x * grid.size + 10, grid_y * grid.size + 10, _ai_target.grid_x * grid.size + 10, _ai_target.grid_y * grid.size + 10, false)
                    // Delete end point of the path (because this is where the target is)
                    path_delete_point(ai_path, path_get_number(ai_path) - 1);       
                    // Delete points that are either exceeding MP or are blocked
                    var _path_end = -1;
                    // Find where/if the path breaks
                    for (var i = 0; i < path_get_number(ai_path); ++i) {
                        // Get path's grid coordinates at this position
                        var _xpos = path_get_x(ai_path, i) div grid.size;
                        var _ypos = path_get_y(ai_path, i) div grid.size;
                        // Check if target is in range yet
                        var _in_range = point_distance(grid_x, grid_y, ai_target.x, ai_target.y) < range;
                        // If the path is larger than MP, or the position is taken on the grid, or the target is now within range
                        if path_get_number(ai_path) > mp || global.grid[# _xpos, _ypos] != -1 || _in_range {
                            // Set this point as the point to end the path at
                            _path_end = i;
                            break;
                        }
                    }
                    // If the path has broken somewhere
                    if _path_end != -1 {
                        for (var i = _path_end; i < path_get_number(ai_path); ++i) {
                            // Delete all remaining points
                            path_delete_point(ai_path, i);
                        }
                    }
                    other.ai_state = menu.paused;
                    // Update grid coordinates
                    global.grid[# grid_x, grid_y] = -1;
                    // Get the end point of the path in grid coordinates
                    var _end_x = path_get_point_x(ai_path, path_get_number(ai_path) - 1) div grid.size;
                    var _end_y = path_get_point_y(ai_path, path_get_number(ai_path) - 1) div grid.size;
                    global.grid[# _end_x, _end_y] = id;
                    grid_x = _end_x;
                    grid_y = _end_y;
                    // Follow the path
                    path_start(ai_path, 3, path_action_stop, false);   
                }
            }
            // Clean up
            mp_grid_destroy(_ai_mp_grid);
            ds_priority_delete_min(ai_unit_priority);
            break;
    }   
}
else {
    if ai_state == menu.inputting {
        // Change turns and clean up
        global.turn = "ally";
        global.menu_state = menu.inputting;
        global.created_ai_controller = false;
        ds_priority_destroy(ai_unit_priority);
        instance_destroy();
    }
}
And here's the original code that worked, but only moved the units without taking into account MP, Range, or getting as close as possible even if a target unit is surrounded already
Code:
// If there are AI units to be controlled
if ds_priority_size(ai_unit_priority) > 0 {
    // Get the AI unit that is the closest to the player
    var _ai_unit = ds_priority_find_min(ai_unit_priority);
    // If AI can input
    switch (ai_state) {
        case menu.inputting:
            // Create AI mp grid
            var _grid_w = ds_grid_width(global.grid);
            var _grid_h = ds_grid_height(global.grid);
            var _ai_mp_grid = mp_grid_create(0,0,_grid_w,_grid_h,grid.size,grid.size);
            // Find closest player unit
            var _ai_target = ai_get_closest(_ai_unit.x,_ai_unit.y);
            // If the unit isn't trying to move to it's current cell
            if _ai_unit.grid_x != _ai_target.grid_x && _ai_unit.grid_y != _ai_target.grid_y {
                // Convert global.grid to an mp grid
                for (var u = 0; u < _grid_w; u++) {
                    for (var v = 0; v < _grid_h; v++) {
                        // If the grid position isn't free
                        if global.grid[# u, v] != -1 {
                            // Set that position as taken in the mp grid
                            mp_grid_add_cell(_ai_mp_grid, u, v);
                        }
                    }
                }
                // Clear starting position and add target position
                mp_grid_clear_cell(_ai_mp_grid,_ai_unit.grid_x,_ai_unit.grid_y);
                mp_grid_clear_cell(_ai_mp_grid,_ai_target.grid_x,_ai_target.grid_y);
                // If there is a path
                with (_ai_unit) {
                    // Tell the unit that AI is controlling it
                    ai_selected = id;
                    // Tell the unit its target
                    ai_target = _ai_target;
                    // Clear path points
                    path_clear_points(ai_path);
                    // If there is a path
                    if mp_grid_path(_ai_mp_grid, ai_path, grid_x * grid.size + 10, grid_y * grid.size + 10, _ai_target.grid_x * grid.size + 10, _ai_target.grid_y * grid.size + 10, false) {
                        // Delete end point of the path (because this is where the target is)
                        path_delete_point(ai_path, path_get_number(ai_path) - 1);                   
                        other.ai_state = menu.paused;
                        // Update grid coordinates
                        global.grid[# grid_x, grid_y] = -1;
                        // Get the end point of the path in grid coordinates
                        var _end_x = path_get_point_x(ai_path, path_get_number(ai_path) - 1) div grid.size;
                        var _end_y = path_get_point_y(ai_path, path_get_number(ai_path) - 1) div grid.size;
                        global.grid[# _end_x, _end_y] = character;
                        grid_x = _end_x;
                        grid_y = _end_y;
                        // Follow the path
                        path_start(ai_path, 3, path_action_stop, false);   
                    }
                }
            }
            // Clean up
            mp_grid_destroy(_ai_mp_grid);
            ds_priority_delete_min(ai_unit_priority);
            break;
    }   
}
else {
    if ai_state == menu.inputting {
        // Change turns and clean up
        global.turn = "ally";
        global.menu_state = menu.inputting;
        global.created_ai_controller = false;
        ds_priority_destroy(ai_unit_priority);
        instance_destroy();
    }
}
 
T

trentallain

Guest
I realised that I was using path_get_x instead of path_get_point_x for one part, however still can't find why nothing is moving.

Code:
// If there are AI units to be controlled
if ds_priority_size(ai_unit_priority) > 0 {
    // Get the AI unit that is the closest to the player
    var _ai_unit = ds_priority_find_min(ai_unit_priority);
    // If AI can input
    switch (ai_state) {
        case menu.inputting:
            // Create AI mp grid
            var _grid_w = ds_grid_width(global.grid);
            var _grid_h = ds_grid_height(global.grid);
            var _ai_mp_grid = mp_grid_create(0,0,_grid_w,_grid_h,grid.size,grid.size);
            // Find closest player unit
            var _ai_target = ai_get_closest(_ai_unit.x,_ai_unit.y);
            // If the unit isn't trying to move to it's current cell
            if _ai_unit.grid_x != _ai_target.grid_x && _ai_unit.grid_y != _ai_target.grid_y {
                // Convert global.grid to an mp grid
                for (var u = 0; u < _grid_w; u++) {
                    for (var v = 0; v < _grid_h; v++) {
                        // If the grid position isn't free
                        if global.grid[# u, v] != -1 {
                            // Set that position as taken in the mp grid
                            mp_grid_add_cell(_ai_mp_grid, u, v);
                        }
                    }
                }
                // Clear starting position and clear target position
                mp_grid_clear_cell(_ai_mp_grid,_ai_unit.grid_x,_ai_unit.grid_y);
                mp_grid_clear_cell(_ai_mp_grid,_ai_target.grid_x,_ai_target.grid_y);
                // If there is a path
                with (_ai_unit) {
                    // Tell the unit that AI is controlling it
                    ai_selected = id;
                    // Tell the unit its target
                    ai_target = _ai_target;
                    // Clear path points
                    path_clear_points(ai_path);
                    // Create the path regardless if you can fully traverse it
                    mp_grid_path(_ai_mp_grid, ai_path, grid_x * grid.size + 10, grid_y * grid.size + 10, _ai_target.grid_x * grid.size + 10, _ai_target.grid_y * grid.size + 10, false)
                    // Delete end point of the path (because this is where the target is)
                    path_delete_point(ai_path, path_get_number(ai_path) - 1);       
                    // Delete points that are either exceeding MP or are blocked
                    var _path_end = -1;
                    // Find where/if the path breaks
                    for (var i = 0; i < path_get_number(ai_path); ++i) {
                        // Get path's grid coordinates at this position
                        var _xpos = path_get_point_x(ai_path, i) div grid.size;
                        var _ypos = path_get_point_y(ai_path, i) div grid.size;
                        // Check if target is in range yet
                        var _in_range = point_distance(_xpos, _ypos, ai_target.x, ai_target.y) < range;
                        // If the path is larger than MP, or the position is taken on the grid, or the target is now within range
                        if path_get_number(ai_path) > mp || global.grid[# _xpos, _ypos] != -1 || _in_range {
                            // Set this point as the point to end the path at
                            _path_end = i;
                            break;
                        }
                    }
                    // If the path has broken somewhere
                    if _path_end != -1 {
                        for (var i = _path_end; i < path_get_number(ai_path); ++i) {
                            // Delete all remaining points
                            path_delete_point(ai_path, i);
                        }
                    }
                    other.ai_state = menu.paused;
                    // Update grid coordinates
                    global.grid[# grid_x, grid_y] = -1;
                    // Get the end point of the path in grid coordinates
                    var _end_x = path_get_point_x(ai_path, path_get_number(ai_path) - 1) div grid.size;
                    var _end_y = path_get_point_y(ai_path, path_get_number(ai_path) - 1) div grid.size;
                    global.grid[# _end_x, _end_y] = id;
                    grid_x = _end_x;
                    grid_y = _end_y;
                    // Follow the path
                    path_start(ai_path, 3, path_action_stop, false);   
                }
            }
            // Clean up
            mp_grid_destroy(_ai_mp_grid);
            ds_priority_delete_min(ai_unit_priority);
            break;
    }   
}
else {
    if ai_state == menu.inputting {
        // Change turns and clean up
        global.turn = "ally";
        global.menu_state = menu.inputting;
        global.created_ai_controller = false;
        ds_priority_destroy(ai_unit_priority);
        instance_destroy();
    }
}
 
Last edited:
Top