Support Forum

Compatible with Entities (ECS) and DOTS Framework?

Im only using the free version of the A*Pathfinding project, so I know I cannot jobify the calculations, but can I use it to calculate paths and move EntityComponentSystem (ECS) Entities? If yes, how would I do that?

Hi

You can request paths directly from the AstarPath component. If you want to use modifiers you could use a shared Seeker component (only one for the entire scene) to later post-process the path (with the Seeker.PostProcess call).
See https://arongranberg.com/astar/docs/callingpathfinding.html#calling-directly
You could write a custom movement script that uses ECS code to move the entities: https://arongranberg.com/astar/docs/custom_movement_script.html

1 Like

Thanks! Ill go check those out.

1 Like

@aron_granberg Is requesting paths directly from the AstarPath component a Pro feature? If it is, there will be debate in purchasing it, but it would also be neat if there was a free+ version for students (although I have no idea how bad verification of “active student status” would be, so that’s more an idle thought than an actual recommendation).

EDIT: I was trying to use AstarPath.active. I figured it out now.

1 Like

Are there any example on using ECS with A* Pathfinding Project?

Hi

@Ph0t0n Not at the moment. With ECS you probably want to use the raw path calculation api as described here: https://arongranberg.com/astar/docs/callingpathfinding.html#calling-directly

This works fine in ECS as long as you’re calling StartPath() from the main thread. I you try to call it from a worker thread, it’ll cough up a unity exception complaining that "if (!Application.isPlaying) { BlockUntilCalculated(path); } must be called from the main thread. That line is located in AstarPath.cs in the StartPath() function.

I attempted to comment out that line, but I’m not sure if it’s because of something in the package manager or whatever, but when that line is commented out the unity editor crashes.

Hi

I think just commenting that line out should allow you to call it from different threads.
It should already be thread safe.

Are you sure the crash is not due to something else?

I’m not sure why it was crashing. I had restarted the Unity Editor several times and the editor would always crash when hitting the play button. I finally did get it working though!

For anyone else wondering how to do it… here’s what I did:

  1. Update to the latest version of A* Pathfinding Project via the package manager (4.3.27). I don’t have any evidence that this is needed, but it’s one of the few things I changed before getting it working.

  2. Close Unity Editor

  3. Open C:\Users\Me\AppData\Local\Unity\cache\packages\arongranberg.com\packages\8fbb73c301f50f24d5a3eef8ed9e27185eb38da58a90e\com.arongranberg.astar@4.3.27\Core\AStarPath.cs . (Substituting your username and different hex numbered folder names.)

  4. Find the StartPath() function. Comment out the “if (!Application.isPlaying) { BlockUntilCalculated(path); }” section (3 lines).

  5. Open Unity Editor again. It should reload the package,

Now you can run AstarPath.StartPath() outside of the main thread… like in an Entities.ForEach().ScheduleParallel() for example.

1 Like

Hey Ph0t0n

Did you manage to get the A star pathfinding working on and entity?
Is so would you give and example of how you changed the A* to work with Entitys?

Regards

Well… yes and no. The only changes to APFP are what I wrote about in my previous post, which allows it to calculate paths in threads other than the main thread. Although I would love to use more of the APFP with ECS, I was not able to get any of the AI classes working (AIPath, RichAI, AILerp, etc). I think if one had enough time, he/she could rewrite those classes to be burst/ECS compatible.

The only part of A*PFP I’m using is the static method for calculating a path. So in my “SpawnSystem”, I spawn enemies and give them a path. Each enemy entity stores their own path in a PathData component that contains a FixedList4096. The spawner calls this function:

// calculates a path and copies it from A*PFP's vectorPath to a FixedList4096
protected static void MakePath(ref FixedList4096<float3> destPath, float3 spawnerPos, float3 destPos)
    {
        ABPath path = null;
        path = ABPath.Construct(spawnerPos, destPos);
        AstarPath.StartPath(path);
        path.BlockUntilCalculated();
        for (int i = 0; i < path.vectorPath.Count; i++) { destPath.Add(path.vectorPath[i]); }
    }

This will break if paths have more than 340 points (because of FixedList4096). You could use a DynamicBuffer if your map is huge and you need more points, but the fixed list method is super easy to use.

Then, I have a PathMovementSystem that queries for any entities with a path and uses a movement script very similar to the one Aaron talked about here: https://arongranberg.com/astar/docs/custom_movement_script.html , except instead of using CharacterController, it just sets the rotation and translation component values (example: position.Value += velocity * deltaTime; rotation.Value = lookRotation; ) It’s kinda crappy compared to AIPath or RichAI, but it runs really fast with Burst and for my simple purposes it does the trick.

If you need entities to repath because something blocks their path, then you can call MakePath() again. I don’t have it implemented yet, but when I have some time, I’m hoping to find a way to query the grid/navmesh and determine which enemy paths were affected by the obstacle so that paths only need to be recalculated for affected enemies instead of all enemies. I don’t know if that’s even possible though. If you have moving targets that the enemies are heading towards, you’ll need to periodically recalculate paths for each enemy (in my case the targets are fixed so I don’t need to do that).

Maybe in the future when I have some spare time I’ll look into getting RVO local avoidance working with ECS… I think some other people may have gotten it working. Currently I’m using unity.physics for local avoidance, but I suspect it’s a lot more expensive than A*PFP’s RVO.

1 Like

So your not using obstacles that will be placed?
I mean are you pathfinding just a road the AI should follow?

I cant seem to get my head around this ECS, only thing i know is when i convert my Enemys to Entitys, then i lose the A Star Pathfinding cause its not optimized for ECS/DOTS.

I actually just wanted the Seeker and AI DestinationSetter to work with IAstarAI.
Then everything would be nice and work just as i have it now :D!

Yeah, some of my obstacles are placed by the user (I haven’t added that functionality yet though), and others are hand placed on the map and static. Currently all of my enemies get spawned, but it would be trivial to place them manually if I wanted to.

I too wanted to use Seeker and AI DestinationSetter, but without converting all of those scripts, I couldn’t find a way. It would take me months to figure out how to convert them and I can’t afford to do that right now.

So… yes, like you said, you can’t attach any A* Pathfinding stuff to your enemies. I’m not an expert by any means, but perhaps if I list the steps I took it might help you:

  1. First I created a terrain with roads, static obstacles, etc. Just like any normal non-ECS project.

  2. I added an empty game object called A* and added the PathFinder script to it and setup my navigation grid just like you would in an non-ECS project.

  3. I converted my terrain to a mesh and gave it a static physics body+shape and convert-to-entity script. This step is optional. I had to do this because I want to use unity.physics - it does things like allowing you to put physics bodies on enemies and detect bullet collisions with the ground.

  4. I made enemy prefabs and added a convert-to-entity script to them. They’re similar to any normal monobehaviour prefab with a mesh renderer + material. I also added a MoveSpeedData component and a few other things like HealthData or whatever.

  5. The only unique thing about these enemy prefabs is that when they get spawned, I add two extra components WaypointIndexData, and PathData. If you’re not spawning enemies, you could use [GenerateAuthoringComponent] and add them to your prefab instead of doing it in the spawner. They look like this:

public struct PathData : IComponentData
{
    public FixedList4096<float3> path;
}
public struct WaypointIndexData : IComponentData
{
    public int index; // current waypoint index
}

Notice that there is nothing in these components that use A*PFP code. The paths are there in an ECS appropriate form though… so the movement system can propel the enemies forward.

  1. I made a spawning system, that spawns the prefabs (there are lots of tutorials on this). Right before it spawns them, it uses that MakePath() function I mentioned earlier to find the path from the newly spawned enemy to the destination. This is the only place that I use A*PFP. My spawn system’s query roughly like this:
Entities.ForEach((ref PathData spawnPath, in SpawnOptionsData spawnOpts, in LocalToWorld spawnLtw) => { }).Run();
  1. Then I made a PathMovementSystem that sets the velocity and rotation of the enemy to the next point in the path until the path completes (the path was precomputed in the spawner). (See my previous comment about the PathMovementSystem) The query looks something like this:
Entities.WithNone<DeathData>().ForEach((ref Translation pos, ref Rotation rot, ref WaypointIndexData waypoint, in MoveSpeedData speed, in PathData path) => { }).ScheduleParallel();
  1. For games with moving targets (like ones that chase the player for example), you would need to create another system that would query all entities with path and waypoint data components and recalculate the path. I wouldn’t suggest doing it every frame because it’ll likely be way to slow… maybe every 1/2 to a couple of seconds depending on how many enemies there are.
1 Like