A* Pathfinding Project

Can the agent's movement be driven by physics?


#1

Hello,

I started using Unty’s NavMesh, but noticed that agents can only be moved kinematically.
I’d like my units to be moved by applying accelerations to the gameObject, not set its transform.

Does Aaron pathfinding support physics driven movement ?


#2

Hi

Currently this is not possible (without a custom movement script). I think it might be possible by changing a few lines of code though, but I haven’t tried it myself. I can show you how to do it if you want.


#3

Hi,

Thank you for your answer.
I am definitely into writing a custom script !
If you can put me in the right direction, I will try to implement it, and get back to you.


#4

Hi

I was suggesting it was possible without writing a custom movement script. Are you writing your own anyway for other purposes?


#5

Can you show me how to do this? I’m making a pseudo2D Top down Space Shooter (everything is 3D but the camera angle is fixed so it looks 2d and everything moves on the XZ plane). My ships are made of different modules that can be blown off, the engines too. Each engine applies Force to the main rigidbody when receiving a thrust float. Turning is done simply with Quaternions, setting a Target with the mouse and turning. The target could just as well be a steering target or next waypoint. How exactly would I do that and apply the correct amount of thrust? No braking needs to be done, I’ve set drag on all Rigidbodies to 1 so they slow down eventually.


#6

Hi

To calculate the correct amount of thrust for that is way outside the scope for this package. You’ll want to have a look at some physics textbooks or wikipedia I think.


#7

Maybe using the term “correct amount of thrust” was wrong. I don’t want it to be that exact just move to wherever the ship is pointed and stop applying thrust when it arrives. I’ve got max speed and acceleration curves set up on the ship.


#8

For the player thrust is pretty much 0 or 1 unless you use an analog controller.


#9

Well, then you should be able to look at this tutorial page, I think it should be reasonably straightforward to modify that to work with rigidbodies and forces: https://arongranberg.com/astar/docs/custom_movement_script.html


#10

Sorry to bother again. I’ve tried modifying the script and it works, kinda. The ship points to the target and follows the waypoints but never slows down and just keeps on circling the targetpoint. Here’s my code:

using UnityEngine;
// Note this line, if it is left out, the script won't know that the class 'Path' exists and it will throw compiler errors
// This line should always be present at the top of scripts which use pathfinding
using Pathfinding;
using SpaceShooter;

public class AStarAI : MonoBehaviour
{
    public Transform targetPosition;

    private Seeker seeker;

    private Ship ship;

    public Path path;

    public float nextWaypointDistance = 3;

    private int currentWaypoint = 0;

    public float repathRate = 0.5f;
    private float lastRepath = float.NegativeInfinity;

    public bool reachedEndOfPath;


    public void Start()
    {
        seeker = GetComponent<Seeker>();
        ship = GetComponentInChildren<Ship>();

        // Start a new path to the targetPosition, call the the OnPathComplete function
        // when the path has been calculated (which may take a few frames depending on the complexity)
        seeker.StartPath(transform.position, targetPosition.position, OnPathComplete);
    }

    public void OnPathComplete(Path p)
    {
        Debug.Log("A path was calculated. Did it fail with an error? " + p.error);

        // Path pooling. To avoid unnecessary allocations paths are reference counted.
        // Calling Claim will increase the reference count by 1 and Release will reduce
        // it by one, when it reaches zero the path will be pooled and then it may be used
        // by other scripts. The ABPath.Construct and Seeker.StartPath methods will
        // take a path from the pool if possible. See also the documentation page about path pooling.
        p.Claim(this);
        if (!p.error)
        {
            if (path != null) path.Release(this);
            path = p;
            // Reset the waypoint counter so that we start to move towards the first point in the path
            currentWaypoint = 0;
        }
        else
        {
            p.Release(this);
        }
    }

    public void Update()
    {
        if (Time.time > lastRepath + repathRate && seeker.IsDone())
        {
            lastRepath = Time.time;

            // Start a new path to the targetPosition, call the the OnPathComplete function
            // when the path has been calculated (which may take a few frames depending on the complexity)
            seeker.StartPath(transform.position, targetPosition.position, OnPathComplete);
        }

        if (path == null)
        {
            // We have no path to follow yet, so don't do anything
            return;
        }

        // Check in a loop if we are close enough to the current waypoint to switch to the next one.
        // We do this in a loop because many waypoints might be close to each other and we may reach
        // several of them in the same frame.
        reachedEndOfPath = false;
        // The distance to the next waypoint in the path
        float distanceToWaypoint;
        while (true)
        {
            // If you want maximum performance you can check the squared distance instead to get rid of a
            // square root calculation. But that is outside the scope of this tutorial.
            distanceToWaypoint = Vector3.Distance(transform.position, path.vectorPath[currentWaypoint]);
            if (distanceToWaypoint < nextWaypointDistance)
            {
                // Check if there is another waypoint or if we have reached the end of the path
                if (currentWaypoint + 1 < path.vectorPath.Count)
                {
                    currentWaypoint++;
                }
                else
                {
                    // Set a status variable to indicate that the agent has reached the end of the path.
                    // You can use this to trigger some special code if your game requires that.
                    reachedEndOfPath = true;
                    break;
                }
            }
            else
            {
                break;
            }
        }


        // Slow down smoothly upon approaching the end of the path
        // This value will smoothly go from 1 to 0 as the agent approaches the last waypoint in the path.
        var speedFactor = reachedEndOfPath ? Mathf.Sqrt(distanceToWaypoint / nextWaypointDistance) : 1f;
        // Direction to the next waypoint
        // Normalize it so that it has a length of 1 world unit
        Vector3 dir = (path.vectorPath[currentWaypoint] - transform.position).normalized;
        ship.TargetPoint = transform.position + dir;
        Debug.Log(dir);
        // Set Thrust
        ship.Thrust = speedFactor;

    }
}

#11

I’m guessing that you are just applying a force in the direction of dir. This will lead to a circular orbital motion (think gravity applying a force on planets).

You will need to apply a braking force as well.


#12

I just need the Thrust value to go down to 0 once it reaches the end of the path or ideally have a slowdownDistance like in the RichAI script. Drag is set to 1 on all ships so they slow down eventually. The speedFactor float seems to be always 1.

Here’s a video I made showing how the AI acts: https://youtu.be/Wdq_vVETcCk

BTW the thrust value can go negative, the ships are a bit slower backing up though.


#13

Well, it’s pretty hard to get it to reach the end of the path by applying forces like that. I think you will be much better off faking the movement, not simulating it using real forces.


#14

Hello araon,
I just came across your A* pathfinding project and was wondering if i could implement it in my 2D platformer game.
I would like to use your script to find the shortest path between the player and end goal of a level.
The issue is that the player has a dynamic rigidbody and so gravity will affect him.
The player possesses his own movement controller and can move left, right and jump.
Would it be useful to use your path finding project in my specific case?


#15

Hi @jpax

This package has no official support for 2D platformers. However you can create a point graph with your level using code, that might work for you.


#16

Thank you for the tip,
Do you know any point graph tutorials that would be useful for me?


#17

Hi

https://arongranberg.com/astar/docs/accessingdata.html
https://arongranberg.com/astar/docs/writinggraphgenerators.html

Really though, this package will not give you that much which is useful for a 2D platformer unfortunately.
It will give you the core pathfinding part, but not much else.