Compatible with Entities (ECS) and DOTS Framework?

Has anyone figured out how to do a navmesh cut in ECS? All the examples use the NavmeshClipper class, but it is a monobehaviour that needs to be attached to the object to be cut… which would normally be a gameobject, but for me, all the models that I need to cut around are entities. I traced it to the function call public void ForceUpdateAround(NavmeshClipper clipper), but again, the problem is that clipper needs to be a gameobject. Maybe there is a low-level API function to specify a bounds to cut? I suppose I could try attaching a hybrid game-object, but I’d rather not have a gameobject hanging around all my entities.

Yep, exactly.

I had to rewrite a lot of the scripts so that they could run in DOTS such as path modifiers but the overall concept is pretty simple.

I used the rvo controller as a base and take the output from that and inject it into the positions of the entities. When an agent is created I add and link a reference to the rvo controller, then no more game objects needed.

So I am running AstarPath.StartPath in a job and take the OnPathComplete and apply a component to tag the entity to apply the path modifiers. Right now I just use a start end modifier and a funnel modifier, but any of the modifiers could be used.

I will try to write up a high level summary of the main code pieces so you can use it if you need. Currently though I am very happy with the performance. I can get a few thousand units before performance becomes an issue. These are pretty heavy mesh models too so lots of room for improvement still:

For the navmesh cuts, I would assume you could do that without the need for gameobjects, and I will probably do that sometime soon because my plan is to get as much over to ECS as possible. Currently the objects I have that effect navmeshcuts are gameobjects anyways (the turrets and buildings) so I’ve been okay so far.

2 Likes

I will have to look into rewriting the scripts such as path modifiers so they can run in DOTS. I’ve gone over this thread a few times: LightweightRVO in ECS but I don’t quite see the big picture… not sure if I still need to rewrite a bunch of the scripts to be ECS compatible.

I tried running AstarPath.StartPath() in a job, but it fails if the job is bursted. ABPath.Construct complains that “Burst error BC1032: Using function pointers/delegates is not supported” and StartPath() complains that “get_isPlaying can only be called from the main thread”). A lot of the functions in A*PFP weren’t really developed for burst from the start so I suppose that’s to be expected. I had to move the path constructor and start-path functions to the main thread.

As for navmesh cutting, I finally gave up trying to figure it out… it seems like everything ties back to reliance on that NavmeshClipper game-object. I couldn’t find anything lower-level than that. Maybe there is a way to do it without game-objects, but I spent a long time searching and didn’t find anything.

I was able to get something working just by using UpdateGraph() and setting the boundary area to non-walkable. After setting the nodes to non-walkable, I walked through all the paths of existing agent and called a function DoesLineSegmentIntersectSphere() to determine if the particular agent intersects the building the player just dropped. If the agent’s current path intersects the new building, then I tell the agent to find another path (StartPath) and it routes around it. It seems to work okay.

One thing that I don’t like is that I’m using a grid graph, and I had the erosion value set to 1, but at 1, it leaves a large area around the newly placed building where the agents can’t go. If I set the erosion to 0, that cuts closer to the new building, which is good, but then the rest of the graph has problems because it places walking areas so close to the edge of cliffs that my agents fall off of them. I think I may try the recast graph again, although back when I first started, I wasn’t having much luck with it.

So… yeah… without the ability to navmesh cut, the recast graph is completely worthless.

I was able to get navmesh cutting “working”. Since there is no low-level API (most things are built-around and tied-to game objects and inheritance), I had to create a dummy gameobject. So whenever the player places a building onto the terrain, I create the dummy gameobject like this:

GameObject navMeshCutter = new GameObject("dummy");
Pathfinding.NavmeshCut cut = navMeshCutter.AddComponent<Pathfinding.NavmeshCut>();
// setup rectangle or circle cut here
// since I have force updates turned off, I force an update here
AstarPath.active.navmeshUpdates.ForceUpdate();
AstarPath.active.FlushGraphUpdates();
// since I have periodic path updates disabled, I reroute my entities here.

It’s hacky because now I have these game objects hanging around doing nothing. I tried deleting them, but doing that causes the graph to go back to normal (without the cuts). If a building gets destroyed, I’ll have to figure out which game-object to destroy to “undo” the navmesh cut (haven’t implemented this yet).

I figured out how to get the recast graph working so I don’t have to use the grid graph anymore. I had to take the functions for path modifiers for funnel, smooth, and radius and put them into a static class with static functions and form an “API” that doesn’t rely on gameobjects or OOP. Then whenever I calculate a new path, I apply the modifiers right after the path is returned, (mainly the funnel and radius ones). One benefit of navmesh vs grid graph is that each path calculation is much faster now <1ms vs 2-3ms. The main benefit is that the navmesh cuts fit perfectly around the dropped buildings.

I’m currently using physics to keep agents from occupying the same space, which is cool, and I like the fact that there is gravity so agents can fall off cliffs when there is an explosion or something. Unfortunately, it has some really bad side-effects: the agents get stuck on corners, slide down inclines, and accidentally fall off cliffs. I spent hours trying to tweak the physics parameters (friction, gravity, mass, damping, etc) to find a solution, but wasn’t able to find a combination that worked. The only thing that seems to work is giving the agents a high velocity to power up hills and avoid sliding down things or getting stuck, but I need my agents to walk slowly, so that is not a good solution.

I think the problem is because my movement script is too simple - I just took the “Writing a movement script” tutorial and put it in a system. It has a small loop to skip waypoints that are really close to each other, a couple of lines to rotate the agent in the direction of motion, and “pos.Value += vel * deltaTime;” to move it. I think my next step is to try and extract some of the code from AIPath (or RichAI), AIBase, Seeker, and Interpolator. Unfortunately, this won’t be easy… there is a lot of complicated code in those classes. RichAI is the most complicated of all, relies on UnityEngine.Transform, and could take a very long time to convert. AIPath looks like the best bet at this point. I’ll also need to create a CharacterControll equivalent, or figure out how to move rigid-bodies. It will probably take me a several days to convert it and get all the bugs worked out.

The only reason I was looking into RVO and Physics is because I want to keep agents from occupying the same space. From what I can tell, RVO will require everything I mentioned previously, AND converting RVOController to ECS. Because the data flow in RVO mode is even more complicated and requires even more classes to be rewritten to use ECS, I’m going to try the physics method first. If that doesn’t work, I’ll circle back to RVO.

My conclusion is that using APFP with ECS in the most basic of ways is fairly easy, but it’s very limited and doesn’t work very well. Using more advanced features like the AI classes, RVO, Navmesh cutting, etc requires a large amount of rewriting, reorganization, and hacking that is almost equivalent to starting from scratch. It requires becoming and expert in A-star navigation and could take months to implement a robust comprehensive ECS solution.

…EDIT…

I made a little bit of progress… I found out that one of the main reasons entities were getting stuck is because after applying the modifiers, the paths would go slightly underground in some places (the terrain has hills and valleys). With physics enabled, the path movement system was trying to set velocity or position to force the entities to go to an underground location that couldn’t be reached.

The solution was to make a new path modifier called “clamp-to-ground” which uses a physics raycast to make the path a specified offset above the terrain. It works quite well.

Before:

After:

I gave up on RVO… I’m not sure how you did it @HeroSyndromeTheGame , but it looks to me like rewriting the rvo controller, AIPath, AIBase, Seeker, Interpolator, and all that other stuff would take forever.

@Ph0t0n Sorry for the delay, I don’t get notifications about replies to this unless I am tagged, so I saw the last one.

For navmesh cutting I haven’t fully converted that piece yet, so I am doing the same thing as you, creating a simple dummy GO to cut out the area. For my use case this is fine because the only thing that cuts navmeshes are buildings and player walls, which won’t be more than a couple hundred, and don’t create any noticeable impact on performance.

Yeah, I feel your pain on getting the system as a whole over to ECS. I went through the same steps as you did. Ultimately I rewrote all of the pieces that I would be using: AIPath, funnel modifier, a few other modifiers. I also wanted the bots to have real time physics so I implemented it so that could happen. It’s actually really cool and I’ve had a ton of fun just messing around with the volume of enemies and how the system reacts with injecting more, adding explosion forces, etc. I can get a couple thousand with collision and pathfinding together all under a few ms.

another clip here of them with collision in big groups (neighbors turned way down so that they intentionally collide with each other) https://twitter.com/BigRookGames/status/1345884251061940229

I am in a crunch right now, trying to get into playtesting in the next two weeks, but after that, I can walk you through some of the code if you would like.

The clamp to ground is a good solution. I use mostly hovering robots so I haven’t encountered that issue yet, but I will have walking units so thanks for posting about that.

1 Like

@Ph0t0n Hey how is the project going? Any further progress on ecs side?

I’ve been using the implementation I discussed prior and the dots side of it hasn’t failed me yet (a few tweaks here and there). I also implemented other classes to use straight pathfinding calls for things like harvesters and the bots that don’t need rvo, and it works pretty well.

Would love to hear how your project is going though.

@HeroSyndromeTheGame . I was unable to get RVO working… it just seemed like too much work to rewrite everything. That RichAI/AIPath classes have their hands in a messy spider-web of classes. What I have right now works so-so… I’m moving entities by setting their physics velocity (using unity.physics). They still get stuck now and then when the terrain is uneven. On uneven terrain, the movement looks unnatural. Unless I can find a better path navigation method, I’ve resigned myself to the fact that I will need to keep the terrain mostly flat.

I hope your game is going well!

@HeroSyndromeTheGame How’re things going so far with your pathfinding? Did you ever try to convert the navmesh cutter or are you still using gameobjects for that? I saw some of your videos on reddit - awesome stuff! Our games are pretty similar - combination of base defending, factorio-style automation, and TD, except yours is first person and way cooler.

I’ve been working on other stuff… animation, weapons, damage, AI, etc., but in a few weeks I’ll be going back into pathfinding and trying to get something acceptable running.