In this blog post I will demonstrate how to use Unity’s EntityComponentSystem as well as Burst compiled Jobs to build a dungeon builder game like the good old DungeonKeeper.
This awesome old game turned out to be the perfect example to show of some techniques that will hopefully help you to get into the mindset of DOTS and EntityComponentSystems.
In the previous Part of this series we already covered how to load a GameMap and first steps of user input handling. If you have not read it yet I would strongly suggest to read it first and then come back to this post. https://goodwillshippingagency.com/
In this blog post we will cover the topics of:
– Creating and spawning creatures
– Implementing a PathfindingSystem (A-Star in Burst)
– Implementing a PathfindingAgentSystem (let creatures follow a path)
– Implementing an idle behavior for our creatures
This project is only for demonstration of usage of the Unity Data Oriented Tech Stack. Neither InnoGames nor I do own any rights on DungeonKeeper. This project is not developed by InnoGames.
Since the last blog post I updated the project to the latest Unity version and also updated some packages. Fortunately there were no major changes required to get the project running on the new versions. But the performance improvements are significant!
Using: Unity 2020.1.10f1
– Entities 0.14.0 preview.19
– Hybrid Renderer 0.8.8 preview.19
– Unity Physics 0.5.0 preview.1
– Universal RP 9.0.0 preview.55
Creating and spawning creatures
Every unit that is able to walk through our dungeon will need some way to store information about its speed, target position and its current path. For this we will create the PathfindingAgent:IComponentData. Since a path consists of a list of positions with a varying amount of steps we will use a DynamicBuffer<PathStep> attached to every Agent.
We will use the GameObjectConversion flow to convert a Prefab of a creature into an Entity so we can make use of a AuthoringComponent that will be added to the Prefab of every creature.
Lets create the Prefab for our first unit – the Imp.
Its a simple GameObject with a child containing only a MeshFilter and a MeshRenderer (a simple sphere in this case). The root GameObject will have our PathfindingAgentAuthoring component.
Now we need a System that allows us to spawn those Imps into the gameworld. In order to keep things clean i will create a new CheatSystem:JobComponentSystem. This system will be used to implement various cheats and commands useful for debugging.
For it will do three things:
– It will change a diggable MapTile to an empty MapTile when clicking on it.
– It will spawn imps at the mouse cursors position when we press the “I” button.
– It will set the TargetPosition of all PathfindingAgents in the Map to the cursors position when we press the “p” button.
Pathfinding Grid System
Every unit in the game needs to be able to find its path through our dynamically growing dungeon. Since this game is build on a tile map, we will write our own A-Star pathfinding algorithm implementation for DOTS. My implementation consists of two Systems: The PathfindingGridSystem and the PathfindingSystem.
The PathfindingGridSystem will be responsible for creating and updating the Pathfinding Grid. When there are MapTiles that recently received a UpdateTileView tag, it will check if the tile changed its status from a “non walkable” to a “walkable state” (or vice versa), and adjust the grid nodes accordingly. Every pathfinding node stores information about connections to its neighbors and a roomindex.
The roomindex is the same for all MapTiles that are potentially connected to each other, this is very useful for quick checks if a certain MapTile can be reached by checking the start and destination tiles roomindices. If they are the same we can be sure a path will be found, otherwise we don’t even need to try finding a path.
Room 2 and 19 are not connected
As soon as we connect both rooms the higher roomnumber will be assigned
The PathfindingSystem will handle all path requests for the game. It will update all entities that have a PathRequest:IComponentData attached by doing pathfinding and assigning the found path back to the Entity. For that it uses the Pathfinding Grid from the PathfindingGridSystem and do some A-Star to find paths.
Implementing path following AI
With the data and pathfinding systems prepared we can write our PathfindingAgentSystem. It will iterate over all PathfindingAgents and request a path if it needs to move to a target position. if a path is assigned it will move the agent along the path until the target position is reached
Implementing a simple Idle Behavior AI
For demonstration purposes, lets create our first simple Idle Behavior.
Every unit with this behavior will move to a random position within the dungeon and wait there for a while, before it searches the next random position.
Since we have a roomindex, we can pick one random tile from the room where the creature is currently located. We can assign this behavior to all Imps we spawn in the CheatSystem
I included the whole project in this unitypackage