Pick_random() not working?

Godot Version

4.3

Question

i have setted up an enemy that targets the player and moves via tilemap and astargrid, however i have 1 problem

i want the enemy to move to a random tile (only a walkable tile) when it goes into a certain state, luckily path (a variable i made) had an indentifier called “pick_random”, so i tried it and the enemy isnt going to random tiles, it just freezes until it switches to another state that allows it to chase the player

is it because of something i forgot to type? or something else? here’s where “pick_random()” is located in:

func chase():
	print("CHASE")
	is_moving = true
	var path = astar_grid.get_id_path(
		tile_map.local_to_map(global_position),
		tile_map.local_to_map(player.global_position)
	)
	
	path.pop_front()
	
	if path.size() == 0:
		print("arrived at pacman")
		return
	
	if path.is_empty():
		print("cant find path")
		return
	
	var original_position = Vector2(global_position)
	
	global_position = tile_map.map_to_local(path[0])
	body.global_position = original_position
	
	var movement_direction = global_position - body.global_position
	update_direction(movement_direction)
	
	is_moving = true
	
	
	target.global_position
	
	current_state = GhostState.CHASE
	
	if current_state == GhostState.RUN_AWAY:
		is_moving = true
		path.pick_random()
	elif current_state == GhostState.EATEN:
		pass

and also heres the full code of the enemy:

extends Node2D

class_name Ghost

enum GhostState {CHASE, RUN_AWAY, EATEN}

signal frightened_timeout

@onready var player: Sprite2D = $"../Player" as PacMan
@onready var tile_map: TileMap = $"../TileMap"
@onready var body = $Sprite2D as BodySprite
@onready var target: Sprite2D = $Target
@onready var eyes = $Sprite2D/eyes as EyesSprite
@onready var ghostsiren: AudioStreamPlayer2D = $"../SFX/ghostsiren"
var current_state: GhostState
@onready var frightened_timer: Timer = $"../Frightened_Timer"
@export var color: Color
@onready var area_2d: Area2D = $Sprite2D/Area2D
@onready var frightenedghost: AudioStreamPlayer2D = $"../SFX/frightenedghost"
@onready var gate_tile: Sprite2D = $"../GateTile"
@onready var ghosteaten: AudioStreamPlayer2D = $"../SFX/ghosteaten"






var is_blinking = false



@export var move_speed: float = 1




var direction = null


var astar_grid: AStarGrid2D
var is_moving: bool


func _ready() -> void:
	move_speed = 0.9
	ghostsiren.play()
	is_blinking = false
	area_2d.set_collision_mask_value(1, true)
	body.normal()
	eyes.show()
	astar_grid = AStarGrid2D.new()
	astar_grid.region = tile_map.get_used_rect()
	astar_grid.cell_size = Vector2(8, 8)
	astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
	astar_grid.update()
	
	
	var region_size = astar_grid.region.size
	var region_position = astar_grid.region.position
	
	for x in region_size.x:
		for y in region_size.y:
			var tile_position = Vector2i(
				x + region_position.x,
				y + region_position.y,
			)
			
			var tile_data = tile_map.get_cell_tile_data(0, tile_position)
			
			if tile_data == null or not tile_data.get_custom_data("walk_tiles") or tile_data.get_custom_data("gate"):
				astar_grid.set_point_solid(tile_position)


func _process(_delta: float) -> void:
	if is_moving:
		return
	
	states()
	
	if !frightened_timer.is_stopped() && frightened_timer.time_left < frightened_timer.wait_time / 5 && !is_blinking:
		start_flashing()

func chase():
	print("CHASE")
	is_moving = true
	var path = astar_grid.get_id_path(
		tile_map.local_to_map(global_position),
		tile_map.local_to_map(player.global_position)
	)
	
	path.pop_front()
	
	if path.size() == 0:
		print("arrived at pacman")
		return
	
	if path.is_empty():
		print("cant find path")
		return
	
	var original_position = Vector2(global_position)
	
	global_position = tile_map.map_to_local(path[0])
	body.global_position = original_position
	
	var movement_direction = global_position - body.global_position
	update_direction(movement_direction)
	
	is_moving = true
	
	
	target.global_position
	
	current_state = GhostState.CHASE
	
	if current_state == GhostState.RUN_AWAY:
		is_moving = true
		path.pick_random()
	elif current_state == GhostState.EATEN:
		pass



func _physics_process(_delta: float) -> void:
	if is_moving:
		body.global_position = body.global_position.move_toward(global_position, move_speed)
		
		if body.global_position == global_position:
			is_moving = false

func update_direction(movement: Vector2) -> void:
	if movement.x > 0:
		direction = Vector2.RIGHT
	elif movement.x < 0:
		direction = Vector2.LEFT
	elif movement.y > 0:
		direction = Vector2.DOWN
	elif movement.y < 0:
		direction = Vector2.UP
	
	# Now, update the sprite texture based on the direction
	eyes.change_texture_based_on_direction(direction)





func _on_area_2d_body_entered(_body = player) -> void:
	if current_state == GhostState.RUN_AWAY:
		print("EATEN")
		get_eaten()
	elif current_state == GhostState.CHASE:
		print("DEATH")
		area_2d.set_collision_mask_value(1, false)
		get_tree().quit()

func frightened_mode():
	print("FRIGHTENED")
	if frightened_timer.is_stopped():
		ghostsiren.stop()
		body.frightened()
		eyes.hide_eyes()
		frightened_timer.start()
		is_blinking = false
		move_speed = 0.6
	current_state = GhostState.RUN_AWAY



func _on_frightened_timer_timeout():
	print("BACK TO CHASE")
	frightened_timeout.emit()
	is_blinking = false
	eyes.show_eyes()
	body.normal()
	chase()
	ghostsiren.play()
	frightenedghost.stop()
	move_speed = 0.9
	current_state = GhostState.CHASE

func get_eaten():
	if current_state != GhostState.EATEN:
		print("EATEN")
		ghosteaten.play()
		body.eaten()
		eyes.show_eyes()
		Global.score += 200
		frightened_timer.stop()
		frightenedghost.stop()
		frightened_timeout.emit()
		current_state = GhostState.EATEN
		$"200Points".show()
		await get_tree().create_timer(1).timeout
		$"200Points".hide()
		is_moving = true
		var eaten_path = astar_grid.get_id_path(
			tile_map.local_to_map(global_position),
			tile_map.local_to_map(gate_tile.global_position)
		)
		
		eaten_path.pop_front()
		move_speed = 5

func start_flashing():
	body.flashing()

func states():
	if current_state == GhostState.CHASE:
		chase()
	elif current_state == GhostState.RUN_AWAY:
		frightened_mode()
	elif current_state == GhostState.EATEN:
		get_eaten()

func start_chase_after_eaten():
	chase()
	body.normal()
	body.show()
	move_speed = 0.9

because current_state = GhostState.CHASE just before, this if statement will never be run.

In addition, pick_random() returns a random element from the array, it does no path finding for you. This seems like a bad use of the path array as it contains IDs of points to the player, not neighboring tiles.

oh, ok

but is there any other way for the enemy to move to random tiles?
@gertkeno

I’m not super familiar with tile maps, my first instinct is to pick a randomized position within the map bounds then clamp it to the navigatable region wth NavigationServer.map_get_closest_point, then navigate to that position.

alright

which function could pick a randomized position within the map bounds? any suggestions?

randi_range with your map bounds for the X and Y axis

understood, but what are map bounds exactly?