How to use astar(a*) without tilemap

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By HorseGorilla

Hello,

I created a room out of sprites and randomly placed obstacles and enemies through an array. Here, the enemies use astar to chase the player for the shortest distance.

But sometimes PoolVector2Array will be null and it won’t chase the player even if a point is captured.

I’ll leave a link for a simple project. Thank you for your help.

link: https://drive.google.com/drive/folders/1W2FLBN4FF_Fj_8YzlBUweoOdC3VPAfcI?usp=sharing

:bust_in_silhouette: Reply From: anuke
extends Node2D


onready var astar_node = AStar.new()
var map = {"width": 3, "height": 3}

func _ready():
    var previous = null;
    for y in range(3):
        for x in range(3):
            var point = Vector2(x, y)
            var point_index = calculate_point_index(point)
            astar_node.add_point(point_index, Vector3(point.x, point.y, 0.0))
            if previous == null:
                previous = point
            else:
                astar_node.connect_points(calculate_point_index(previous), point_index, true)
                previous = point
            if point_index > map.get("width"):
                astar_node.connect_points(point_index - map.get("width"), point_index, true)

    var path = astar_node.get_point_path(0, 8)
    print("Are points connected? (5, 8) = ", astar_node.are_points_connected(5, 8))
    print(path, astar_node)


func calculate_point_index(point):
    return point.x + map.get("width") * point.y

It’s a “dirty” code based on godot demo pathfind_astar.
I create a map 3x3 and connect vertically and horizontally and just find the path. Only Node2d is used

Thank you! I’ll test it.

HorseGorilla | 2021-08-04 20:30

[hopefully an informative late reply for anyone inquiring the same, like I did]

I’m not using a TileMap and I implemented a simple solution using a built-in Godot’s AStarGrid2D object.
I just followed the first example you find in its Godot Docs page:
Documentation for AStarGrid2D class

Basically, all you need to do to set it up is:

var astar_grid = AStarGrid2D.new()                                       # instantiates the AStarGrid2D object
astar_grid.region = Rect2i(0, 0, your_grid_width, your_grid_height)      # defines the size of the navigatable grid
astar_grid.cell_size = Vector2(your_cell_width, your_cell_height)        # defines the size of the navigatable grid's cell size 
astar_grid.update()                                                      # updates the AStarGrid2D object with new grid parameters

To get a calculated path between 2 positions, just add:

var id_path = astar_grid.get_id_path(starting_position, destination_position)
# get_id_path() returns an array of Vector2i, with each element being the next valid cell position.

· The first element of the array will always be the starting position.
If you want to exclude the starting position, you may instead:

var id_path = astar_grid.get_id_path(starting_position, destination_position).slice(1)

Additionally:
astar_grid.set_point_solid(Vector2i(cell_x, cell_y)) # sets the cell as 'solid' (blocked/not navigatable/impassable)

astar_grid.update() # needs to be called every time you change the AStarGrid2D grid parameters

An example of code:

@export var grid_width: int
@export var grid_height: int
@export var tile_width: int
@export var tile_height: int

var id_path

func _ready():
	setup_astar_grid_2d()

func _physics_process(delta):
	if id_path:
		navigate_path()

func setup_astar_grid_2d():
	astar_grid = AStarGrid2D.new()

	astar_grid.region = Rect2i(0, 0, grid_width, grid_height)
	astar_grid.cell_size = Vector2(tile_width, tile_height)
	astar_grid.default_compute_heuristic = AStarGrid2D.HEURISTIC_OCTILE
	astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES
	astar_grid.update()

func get_path(current_position: Vector2i, target_position: Vector2i):
	id_path = astar_grid.get_id_path(current_position, target_position)