[Tutorial series] Making an RTS with pure ECS

Hey all!

I am starting a new tutorial series that ill try cover the most “updated” way to make a game with ECS. I felt like there might not have been enough examples for making easy-to-understand systems. I’m going to target RTS as I feel it suits very well to something like this (alot of units, various types and things happening, etc). I have the full code on GitHub and will probably try to keep it updated as much as possible. Looking for feedback and if any of the code looks wrong!

Link to the repository that will be updated: GitHub - skhamis/Unity-ECS-RTS: Trying to recreate a simple RTS game using Unity and pure ECS
Link to the playlist where I’ll add new videos
https://www.youtube.com/watch?v=36Q6HO19O6U

12 Likes

Didn’t watch video (I’m personally just not a fan of video tutorials) but I had a read of your code and it looks solid.

I look forward to watching it progress.

2 Likes

I appreciate reading the code! I definitely know there are some people who like to see everything piece-by-piece and some people who prefer to just be given code and decipher what they need from there. Hopefully I can help people from both groups!

2 Likes

I read the code
I wonder do we need to remove and add component to Entity. Just to tag it as selected.

As far as I understand how ECS work, add/remove component from entity move it to different chunk since it is a new archetypes. And Command Buffer is not consider cheap.

Would it be better if Selected Component have bool type or using SharedComponentData.

It’s actually unlikely to move it to a new chunk in this situation as there will likely only be a single ‘input’ entity so the chunk will just be redefined. (And when the next build drops with singletons it’ll probably made into that.)

It’s definitely a valid point though. Better off setting than adding/removing, I do not agree with the SharedComponentData suggestion though.

This right

This not right :slight_smile: Using SCD do same things, because it’s per chunk basis based on SCD value, it’s similar as add tag - it’s moving entities in different chunks.

Agree with the commenters that ShareComponentData shouldn’t be used for anything that’s changing often (like selecting units). From the docs:

As for SelectedComponent being a component on all PlayerUnits and then just set the value to true/false. I felt like other systems that need to do anything with the selected units (move, attack, see available stats, etc) can filter easier rather than having to check component.isTrue. Though the more I think about it, it definitely might be more performant – will have to run some tests!

Surely we don’t want every system running through thousands of entities, to check true false, if we dont change state often. Hence component taging can be useful.
Probably wont be changing state of all units per same frame anyway. So taging can be naturally distributed over the time. This way, reducing number of unnecessary entities iterations on other systems.

On contrary, I think iterate over 100000 every entities to check if-else is much cheaper than component tag different kind of archetype.
Like if we pick different kind of minions with different components then each of them will become new archetype type and each in seperate chunks or redefine like tertle say.

1 Like

For one system may be.
Likely project specific, what requires.

But if you have few system, which checks bool value, instead filtered entities by components?
So lets say having 5 systems, which iterates through 100k each.
Instead of having maybe 10k per system, per frame.
And if you don’t change status every frame, as I said earlier, that massive performance saving.

Some nice comparison benchmark would be welcome.

Then what would you think of the hierarchy tree sub-system design? Main system pass interest data over to sub-system.

Starting with a tag system process 100k check if-else tag entities and pass 10k tag entities to sub-tag-system and 90k to sub-notag-system. This way we can handle different type of tag to different system in one go (may not optimal when made new data to each system)

I think if design this way, more control over tag-system specific. Would it better than make system query for different component tag?

Edit: this is for game that need lots of tag like minion type need different solution. Not simple isclick tag like above

You need weight, where you would use tag, and where bool of course.

IsAlive tag, is a good example of use tags, to reduce count of entities for system (s).
ReadyToAttack can be another use.
Or retreat tag.

But in case of fire now, if firing / attack is rapid and often bool iteration can be suitable.
However, my system probably will be already filtered, by some form tag, as CanAttack / CanFight etc, since these are not need be toggled every single frame.

Again, last thing I want, is having multiple system iterating through all entities, when not needed.

They both have a place as Anytpodish showed good examples but polling the many for the few isn’t a good strategy to use by default. It could take a toll over time as your game grows and would be harder to identify and fix later on as a performance issue.

Tagging has it’s downsides too though and polling has increased benefits vs tags within the job system.

There is a thirdly option which is reference entities where you spawn a new entity and leave the target unaffected.

Selected : IComponentData { Entity target; }

You spawn a new Entity with Selected component and then the processing system just updates the target Entity position via ComponentDataFromEntity.
This could be useful if you have ordered entities which can’t move or heavy entities (dozens of components) which could be overkill to move for the sake of small operations.

And not to forget, that you would also only need to iterate through changed chunks. I would also love to see some benchmarks around this, to derive at some guiding design principles

The most important aapect of tagging to remember is in how often the tag is added/removed. Richard Fabian’s book on Data-Oriented Design talks of the concept as existence based processing.

His example is that a health recharge system that only does processing when some entity is damaged meaning that if the entity is at full health it’s health component is removed and the same for when the entity is dead. This removes the need to check if the entity is at max health thus allowing the CPU to only focus on processing what data actually exists.

It does depend on your use case though so it’s most important to understand what your data is doing.

Instead of making a new post I will just bump this one with my part 2! I also show how to update to 0.0.21 package (and update MeshInstanceRenderer to use a ISharedComponent)

4 Likes

Another great tutorial video! Thank you very much for creating these and helping to educate us poor souls who are still struggling with the concept of how to use ECS. I did notice 2 things though. First, your github link GitHub - skhamis/Unity-ECS-RTS: Trying to recreate a simple RTS game using Unity and pure ECS is out of date and not actually representative of your current code in the video (namely the “Unit Spawn” component and system are missing). Second, I think you missed a few areas that should have had [BurstCompile] on the Jobs. :wink:

Hey Garmbrael,

Thanks for watching! Also thanks for the feedback!

  1. GitHub updated, I hope sometime this weekend to start having branches per-video as opposed to just a dump on master once the “core” mechanics are done.

  2. I am actually saving the [BurstCompile] decorator, and burst compiling as a topic in general, for an entirely separate video where I can deep dive into the why and when to use it! Though thanks for keeping me on my toes :stuck_out_tongue_winking_eye:

Part 3 released! I try to switch it up and use IJobProcessComponentDataWithEntity to get selecting working and we start creating a custom NavAgent to process smoothly moving from position to final position!

updated github: GitHub - skhamis/Unity-ECS-RTS: Trying to recreate a simple RTS game using Unity and pure ECS

3 Likes

Your navagent not use Navmesh is kind of misleading. But anyway, great start up sample.