As I learn more about how gaming engines work, I am struck by the parallels to microsimulation frameworks, particularly in the design challenges in aiming for both performance and extensibility.
![](https://static.wixstatic.com/media/101ec8_85b469f089764e42a728cb9bf75344bd~mv2.png/v1/fill/w_561,h_282,al_c,q_85,enc_auto/101ec8_85b469f089764e42a728cb9bf75344bd~mv2.png)
I've been particularly interested to delve more deeply into the Entity Component System (ECS) and Data-Oriented Technology Stack (DOTS) pattern that Unity is moving towards.
To that end, I've been playing around with a few simple test cases to see how some basic demographic microsimulation -- in this case on bunnies -- might be structured (GitHub).
![](https://static.wixstatic.com/media/101ec8_ead873eab8b748cb822c6adca3e78608~mv2.png/v1/fill/w_980,h_439,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/101ec8_ead873eab8b748cb822c6adca3e78608~mv2.png)
Some things I have learned along the way:
Generating entities from other entities within a system is not trivial, and most examples use a spawner GameObject to create entities. In my BirthSystem, I copied some patterns that allow prefabs to be dragged onto an object with a custom authoring script, but I've also put it into a SubScene which converts everything at compile time.
Changing translation/rotation/scale of entities in code was not as obvious from the documentation without a bit of trial and error in my GrowthSystem. Specifically, you don't get a non-uniform scale during conversion if the prefab scale is 1 in the editor. I also was not previously aware of how to use hierarchical objects to offset the pivot position -- something I required to grow the bunnies with their feet are on the ground.
It was also interesting to learn about the challenges in accessing thread-safe pseudorandom numbers within parallel jobs in ECS systems. For my BirthSystem, I followed this pattern to set up a RandomSystem that just stores an array of Unity.Mathematics.Random generators for each thread, which can be accessed inside jobs using some special named parameters in the Entities.ForEach pattern.
Setting Jobs > Burst > Enable Compilation and toggling GPU instancing on materials makes rendering much faster in my 20k-entity spawner test, and profiler seems to be limited by the rendering processes not my parallel system updates (?)
![](https://static.wixstatic.com/media/101ec8_a9989703e88a48afbadb3fb33387cfbc~mv2.png/v1/fill/w_980,h_526,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/101ec8_a9989703e88a48afbadb3fb33387cfbc~mv2.png)
We'll see if I can make some more computationally intensive system logic -- e.g. inefficient O(n^2) partner interactions -- that can change the balance to the worker jobs.
Summary
A few things I'm still excited about:
ease of serialization + deserialization
isolation + organization of the system logic from the model state
extensibility and configurability from emphasis on small component data
powerful query system for entity archetypes
super fast parallelized job system + automagical boost compiler
A few areas where I have more questions:
Preview packages in flux make a lot of tutorials obsolete
Syntax overhead to do simple tasks (although visibly improving by iteration)
Challenges with physics, event-based callbacks, storing arbitrary containers
Best pattern for configuring global parameters of systems or composing system logic from a scriptable configuration of sub-system modules.
Best practices for retrieving entity component data and saving to file
Comments