Hi all! 🙂

I have a problem with an enemy path-finding.
My goal is to have a path with few points on the map and make enemy to follow every path point continuously.
When the player will be in the enemy area, the enemy will start following a player instead of path points. (I still didn't get here because the basic path follow not working)

What I have:
• Enemy node (kinematic body) with script
• Enemy path node with few points in the 3D space (map)

What's the actual problem:
• Enemy doesn't follow the path correctly and continuously.
• Enemy is not placed correctly on the ground when following a path, he's flying a little in the air.

I tried to do this in different ways but still without the result. I'm still new to 3D so I'm just trying to do things in multiple ways and learning which one is the best.. 😃 But here, no one of them works..

Here how the path looks like:

Here's my enemy code to start following a path:

First I init the path and get the first point of the path curve:

func path_init():
	var _count = cPath.get_point_count();
	var _pointTo = cPath.get_point_position(0) + get_parent().get_node("ReddiePath").global_transform.origin;
	move_to(_pointTo);
	init = true;

Finding the points from the curve works fine. I tried to spawn a mesh on the position where I tried to go and that works correctly.

move_to func:

func move_to(_location):
	var _target = _location;
	path = nav.get_simple_path(global_transform.origin, _target, true)

And finally _physics_process:

var goal = Vector3()
	var step_size = delta * speed
	
	if path.size() > 0:
                #move to point when the path exists
		var destination = path[0]
		goal = destination - translation
		
		if step_size > goal.length():
			step_size = goal.length()
			path.remove(0)
			
		goal = goal.normalized()
		move_and_slide(goal * speed, Vector3.UP)
	else:
                #if he reach the point find next one
		if init:
			path = []
			var _count = cPath.get_point_count();
			pathPos += 1;
			if pathPos >= _count:
				pathPos = 0
			var _pointTo = cPath.get_point_position(pathPos) + get_parent().get_node("ReddiePath").global_transform.origin;
			move_to(_pointTo);

Yeah.. The result is not as I expect.. Enemy just randomly moving and behave really strenge.
Can anyone give me an impulse what I made wrong? I'm new to 3D. Thanks a lot!

Have a nice day!

  • MartinDan98 I'm going to try and do a proper write up on how to bake navmeshes correctly at some point to help people but there's a github issue I opened up on the topic and the contributors were really helpful in detailing everything out. It's nice that I'm not the only one that's been constantly running into problems lol.

    https://github.com/godotengine/godot/issues/67670

    It's not just you, it's pretty frustrating working out the correct settings to bake the navmesh, it does work fine, but it's fiddly.

For waypoints movement, you need to store a list of waypoints or a list of waypoint indices to know the index or the actual position of each waypoint in your 'path' that contains one or more waypoings.

Your character or whatever movable object should store a current index of waypoint moving towards and index of waypoint/starting point move from, then you have set up a threshold to test against, so you can tell your movable object is close enough to certain waypoing to 'clear' that waypoint and move towards the next waypoint in the 'path' list.

In the code you posted I didn't see any of the logics I described above, so I guess your movable object is actively changing its course(current moving-to target), so it looked like it moved randomly or bugly.

That's just my 2 cents, correct me if I am wrong. =)

    MagickPanda

    Yeah.. My wrong.. I didn't include this variable declarations here but I have it here already.
    This is the whole code:

    extends KinematicBody;
    
    onready var anim = $AnimationPlayer;
    onready var nav = get_owner().get_node("Navigation");
    onready var cPath = get_parent().get_node("ReddiePath").curve;
    export var speed = 1
    
    var init = false;
    var pathPos = 0;
    var path = [];
    
    func _ready():
    	anim.play("Walk", 0.5, 2)
    
    func _physics_process(delta):
    	var goal = Vector3()
    	var step_size = delta * speed
    	
    	if path.size() > 0:
    		var destination = path[0]
    		goal = destination - translation
    		
    		if step_size > goal.length():
    			step_size = goal.length()
    			path.remove(0)
    			
    		goal = goal.normalized();
    		move_and_slide(goal * speed, Vector3.UP)
    	else:
    		if init:
    			path = []
    			var _count = cPath.get_point_count();
    			pathPos += 1;
    			if pathPos >= _count:
    				pathPos = 0
    			var _pointTo = cPath.get_point_position(pathPos) + get_parent().get_node("ReddiePath").global_transform.origin;
    			move_to(_pointTo);
    	
    func move_to(_location):
    	var _target = _location;
    	path = nav.get_simple_path(global_transform.origin, _target, true)
    
    func path_init():
    	var _count = cPath.get_point_count();
    	var _pointTo = cPath.get_point_position(0) + get_parent().get_node("ReddiePath").global_transform.origin;
    	move_to(_pointTo);
    	init = true;

    Basically I store the actual point index in the variable: pathPos
    When this is greater than path points pathPos will set to 0 and he should follow the first point again.

    This is my logic which is here:

    1. Run the path_init function from timer to init a Path.
    2. Start a path with function move_to(_location) function
    3. Move the body towards the path in _physics_process while path size is greater than 0
    4. When he reach the point and the path size is less than 0 incerase the pathPos by one and find the next point on the path which the body should follow. When the pathPos index is greater than the number of the points I will reset it and he should follow first point again.

    I tried multiple times edit this post and make code appears in the code tag or spoiler tag but it just didn't work for me so I will post it here normally

      MartinDan98
      I don't think it's optimal to actively recompute path to current waypoint position every " physics frame", which might cause performance problem or unpredictable result/behaviors.
      I am still new to godot and I am not sure what " move_and_slide" helper function does, so correct me if I am wrong.
      imo move_and_slide shouldn't be use as per–frame action to continuously alter orientation and speed velocity vector in every frame.In my previous 2d car traffic simulation app, I implement ed a " move_towards(vector3)" to process movable's facing and velocity. There might be out of box function in godot to do move toward certain position, though due to my limited knowledge in godot, you have to figure it out yourself.

      That's just my speculations though, others who are more familiar with godot might explain beeter than me.

        MagickPanda

        Well.. its kinematic body in 3D space and it needs to collide with ground,move around obstackles, apply gravity so I dont think it can be fixed by move towards. This is why I used move and slide..

        And I dont recompute a path every step I just checking if he reach the path point or not.. if not he will move in a direction.

          MartinDan98
          hmmm I don't think checking against exact position of node will work.
          You will have to define a acceptable distance to waypoint exact position to test against, since the object wont match exact coords of target node when updating position based on calculated velocity.

          BTW here is what I did for waypoint:

          1. Store a list of waypoints in movable
          2. Make a variable that stores current target waypoint index and a variable to store last waypoint or origin index ( you need target pos – original pos to get direction of motion )
          3. Make movable moving towards target node position, updating turnrate, orientation and velocity each frame
          4. define a mininal constant range to target, when movable within [range ] to targe node, waypoint target index = current index + 1
          5. update last waypoint

          edit: fixed typos on cellphone -.-

          So whole logic was good.. Baked NavMesh making issues here. Because the navmesh is baked above the ground enemy starts fly because the navmesh is in the air and then doing odd things because the navmesh has wrong configuration..
          I still don't know how to set it correctly but I played a little with it and now its kinda works and looks much better.. It is still above the ground a few but not too much.
          So if anyone will have same problem try to play with your navmesh configuration.

            MartinDan98 I'm going to try and do a proper write up on how to bake navmeshes correctly at some point to help people but there's a github issue I opened up on the topic and the contributors were really helpful in detailing everything out. It's nice that I'm not the only one that's been constantly running into problems lol.

            https://github.com/godotengine/godot/issues/67670

            It's not just you, it's pretty frustrating working out the correct settings to bake the navmesh, it does work fine, but it's fiddly.

              Lethn Yeah.. exactly..
              I need the navmesh bake my terrain like with this option..

              This option bake terrain perfectly but without obstacles.. Everything is walkable.. But the terrain perfectly fit the ground. There is no way how to get same result with the navmesh configuration and baking to make navmesh avoid obstacles.