How To Make A Character Stop After Navigation is Complete (Navigation2D)

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

I am referring to this Navigation2D Tutorial .

Although I have got my enemy character to chase the Player when it is in sight but I want the Enemy AI to complete its last 2D path generated and then stop whenever my player hides behind a wall or something.

This is the code for Generating The Path for the Enemy AI to follow :

func generate_path():
if levelNav != null and player != null:
	path = levelNav.get_simple_path(global_position, player.global_position, false)
	line2d.points = path

This is the code for the Enemy AI to move towards the player :

func navigate():
if path.size()>0:
	if path.size() == 1:
		velocity = global_position.direction_to(path[0]) * speed
	elif path.size() > 1:
		velocity= global_position.direction_to(path[1]) * speed
	if global_position == path[0]:
		path.pop_front()

And this is how I call them in my physics_process(delta):

func _physics_process(delta):
    ...
    player_detect()		
	if player_spotted:
		generate_path()
	navigate()
		
move()
  • Player detect is nothing but a function to check if the raycast from the Enemy AI is colliding with the player or a wall

However whenever player_spotted == false no more path is generated (which is what I want) but in the code where Enemy move towards the player the line of code if global_position == path[0] never gets executed and the enemy AI keeps twitching at a point specified in the path array


This is what it looks like :

Any help is appreciated,
Thank you for your time.

:bust_in_silhouette: Reply From: TimiT

It looks like error is in navigate() function. When you use direction_to(...) , you get path to dot.

Example:

  • Start: Vector2(10, 10)
  • Dot: Vector2(20, 15)
  • Direction: Vector2(10, 5)

If you multiply it on speed, your direction may me incorrect if speed != 1.
And every if global_position == path[0] call will returns false because direction will not be like required Vector2 and enemy gets stuck while it trying to get path[1] position.

To fix it, you should to use other ways to go to dot with required speed or comparison with intveral (if val1 < pos and pos < val2)

Hope it helps. Write me your result

I understand the fact that the direction could be different but in the code I referred and experimented with takes care of that using look_at() and works fine until I add the concept of stopping the enemy AI. But I’ll try to implement what you have suggested none the less
When my script spots the player and stops creating any additional paths this bug takes place but without that it (stopping path generation) the AI works fine.

You can find the GD project here

1MochaChan1 | 2021-06-22 12:33

Okay, look

When your AI see player, his way recreates. Every tick. And path[1] .x and .y changes (you can check it with print(...))

And your AI can move to path[1] all the time.

But when you disappear for the AI, path will not creates again. And path[1] will one all time.

And there we have this roblem

For example, we can abstragate from vector and use only one dimension.
Now you have way_size = 13 and speed = 5

Next tick AI will move by 5 (speed) units and way_size will be 8. Next tick 3. And… -2.

Okay, what next? AI will return to get this position. And distation will be 3 again.
And againg and again way_size will be 3 and -2. So your AI stuck on path[1] dot.

This is your problem. Solution: not one

My ideas.

  1. Interval comparsion: (path[1] - enemy_position).length() <= speed
  2. Other ways to go by this path with required speed

TimiT | 2021-06-22 12:56

I kind of got it to work better now, with your first suggestion, but I was failing to understand your reasoning about the speed and direction. I’ve understood that (I hope I’ve understood it correctly)

Whenever the path is stopped generating the AI may go beyond the desired destination due to its speed being miscalculated

I updated the navigate() function and in theory made a type of range for the Vector2(). So it works something like

If global_position is near path[0] then path.pop[0] and this will allow the enemy to set the next value : path[0] as the new destination and will continute following until the path becomes empty

However I don’t know how right my solution is although it does the job but needs some tweaking, here is the result :
Godot Navigation 2D - Album on Imgur

and this is the updated project

And thanks for taking the time to help me out, appreciate it :slight_smile:

1MochaChan1 | 2021-06-22 15:28

Yes, I told you about this :slight_smile:

Glade that your code is working now. If you have more questions, I can try to answer it. Good luck!

TimiT | 2021-06-23 16:17