using godot 4.3
I’m creating an enemy ranged AI in a top-down rougelite, and since I wanted to make sure that my enemy had line of sight before firing (to avoid randomly firing at the wall when it was in desired distance) I added a Raycast 2D and some line-of-sight-code, but when I hide behind a wall that has a corner to it the enemy stops following the pathfind and randomly jetters in place.
Here’s the code I have:
extends CharacterBody2D
const SPEED : float = 40
@export var target = CharacterBody2D
@onready var ray_cast_2d: RayCast2D = $RayCast2D
@onready var navigation_agent_2d: NavigationAgent2D = $NavigationAgent2D
func _ready() → void:
call_deferred(“SeekerSetup”)
func SeekerSetup():
await get_tree().physics_frame
if target:
navigation_agent_2d.target_position = target.global_position
func _physics_process(delta: float) → void:
ray_cast_2d.target_position = target.global_position - self.global_position
ray_cast_2d.force_raycast_update()
if target:
navigation_agent_2d.target_position = target.global_position
if navigation_agent_2d.is_navigation_finished() and has_line_of_sight():
return
var CurrentAgentPosition = global_position
var NextPathPosition = navigation_agent_2d.get_next_path_position()
velocity = CurrentAgentPosition.direction_to(NextPathPosition) * SPEED
move_and_slide()
func has_line_of_sight():
if navigation_agent_2d.is_target_reachable() == false:
print(“fuck”)
get_tree().quit()
if ray_cast_2d.is_colliding():
print(“is colliding with” + str(ray_cast_2d.get_collider()))
if ray_cast_2d.get_collider() == target:
return true
else:
return false
I can’t figure out how to fix it, and it’d be really nice to have my ai not buzzing around like a baby on coffee. here’s a screen shot of the glitch
Screenshot 2025-05-09 164119
Screenshot 2025-05-09 164119
853×542 10.8 KB
the enemy is the icon in the top right corner. if you could help that’d be great!
sry for posting again I couldn’t figure out how to switch my last posts topic to navigation
If you put 3 backticks (```) on lines above and below your code, it will format it much more nicely here. I’d advise editing your first post and adding those.
Without it, it’s hard to determine the scope of statements, particularly which things are inside if
statements.
Edit: Your screenshots aren’t linked.
1 Like
using godot 4.3
I’m creating an enemy ranged AI in a top-down rougelite, and since I wanted to make sure that my enemy had line of sight before firing (to avoid randomly firing at the wall when it was in desired distance) I added a Raycast 2D and some line-of-sight-code, but when I hide behind a wall that has a corner to it the enemy stops following the pathfind and randomly jetters in place.
Here’s the code I have:
extends CharacterBody2D
const SPEED : float = 40
@export var target = CharacterBody2D
@onready var ray_cast_2d: RayCast2D = $RayCast2D
@onready var navigation_agent_2d: NavigationAgent2D = $NavigationAgent2D
func _ready() → void:
call_deferred(“SeekerSetup”)
func SeekerSetup():
await get_tree().physics_frame
if target:
navigation_agent_2d.target_position = target.global_position
func _physics_process(delta: float) → void:
ray_cast_2d.target_position = target.global_position - self.global_position
ray_cast_2d.force_raycast_update()
if target:
navigation_agent_2d.target_position = target.global_position
if navigation_agent_2d.is_navigation_finished() and has_line_of_sight():
return
var CurrentAgentPosition = global_position
var NextPathPosition = navigation_agent_2d.get_next_path_position()
velocity = CurrentAgentPosition.direction_to(NextPathPosition) * SPEED
move_and_slide()
func has_line_of_sight():
if navigation_agent_2d.is_target_reachable() == false:
print(“potty word”)
get_tree().quit()
if ray_cast_2d.is_colliding():
print(“is colliding with” + str(ray_cast_2d.get_collider()))
if ray_cast_2d.get_collider() == target:
return true
else:
return false
Blockquote
The first thing that strikes me is you’re assuming target
is valid in _physics_process()
at the very beginning (using target.global_position
raw), but immediately after test to see if target
is valid.
The second thing is, if you have a target, navigation is finished, and you can’t see the target, you’re bailing out of _physics_process()
. Which says to me that if your enemy has got to the end of their path and the player is behind a wall, they’re going to do nothing. I think they’ll continue to do nothing until the player comes into view, at which point it will update its navigation and actually get to the move_and_slide()
.
You might want to consider an explicit state variable driven by an enum like:
enum Action { searching, moving, attacking }
That might help you think about what the enemy is supposed to be doing at any given point in the code, and therefore what it should do next.
Yeah, but the problem isn’t the Line Of Sight. if I’m hiding in a corner, the navigation breaks and it stops moving, and instead looks like it’s constantly trying to decide where to go.
The best thing to do in this case is usually print everything; all the decisions, what state things are in, positions, the lot. It’s easier to figure out what’s going on if you have those details.
At a guess, it might be that get_next_path_position()
returns the location of the parent node if the path is complete, and you’re only acting on navigation_finished()
if target
exists and has_line_of_sight()
returns true.
1 Like