Roguelike grid based realtime navigation

Godot Version

4.5

Question

`Hi everyone I was following some tutorials on enemy grid based Ai issue I’m running into following tutorials is they all go in turn based order. Since I wanted to make it real time but still have them following one grid cell at a time how could I achieve this? Or what do you think is a good way to. - this is the tutorial I’m following and it’s perfect except the turn based issue. I know there’s probably a lot of different ways to get this but I don’t want to over complicate it so thought I’d ask!

https://youtu.be/3yMUStff-hg?si=HzrtWxx6TwDMp7nx `

How about doing everything like in the tutorial except you move the enemy regularly so it doesn’t feel like it’s turn-based?

So, instead of player_did_a_move emit a signal at some regular intervals from an ‘enemy manager’

1 Like

I’ll give that a shot I was messing with character turns and this and that but I’ll come back and see how that goes!

What do you think would be the best way to do the intervals, how would I keep a good consistent timer?

This is how I like to do it:

signal move()

const MOVE_INTERVAL_MSEC: int = 500

var last_move_msec: int = 0

func _update_enemies() -> void:
	# time in milliseconds
	var time_msec: int = Time.get_ticks_msec()

	if time_msec - last_move_msec < MOVE_INTERVAL_MSEC:
		return

	# increment last move instead of setting it to the actual time,
	# this prevents the timer from going out of sync because of lag
	last_move_msec += MOVE_INTERVAL_MSEC

	move.emit()

(Only works if all enemies move at the same time)

I have tried this a ton of ways and tried to implement the code above - feel like I am going crazy. How did you implement that into the enemy movement script?

A very minimal implementation (without the debug lines):

class_name GridAI
extends Node2D

const TILE_SIZE: int = 32
#const TURNS_TO_MOVE: int = 2

@export var tile_map_layer: TileMapLayer
@export var player: Node2D

var pathfinding_grid := AStarGrid2D.new()
var path: Array[Vector2] = []
#var turn_counter: int = 1

func _ready() -> void:
	var enemy_manager: EnemyManager = get_tree().get_first_node_in_group("EnemyManager")
	if not enemy_manager:
		push_error("Failed to get enemy manager. Self-destructing")
		queue_free()
		return

	enemy_manager.move.connect(_move_ai)

	pathfinding_grid.region = tile_map_layer.get_used_rect()
	pathfinding_grid.cell_size = Vector2.ONE * TILE_SIZE
	pathfinding_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
	pathfinding_grid.update()

func _move_ai() -> void:
	path.assign(pathfinding_grid.get_point_path(
			global_position / TILE_SIZE, player.global_position / TILE_SIZE))

	#if turn_counter < TURNS_TO_MOVE:
		#turn_counter += 1
		#return

	if path.size() <= 1:
		return

	path.remove_at(0)

	var target: Vector2 = path[0] + Vector2.ONE * TILE_SIZE / 2
	global_position = target

I added the script I posted before to a Node “EnemyManager” which calls _update_enemies every frame. It’s in a group called “EnemyManager” so that the enemy can easily get a reference to it.
Scene trees:


This is so helpful thank you I’m gonna give it a go and see how it turns out