This year, one of my goals was to build a sane, powerful, viable Entity System / Entity-Component System within Unity3D. Something I could build current and future games on, and save a lot of dev-time (both in ease-of-use and in easily adding missing features that I wish Unity had).
@t_machine_org Did you end up using some kind of ECS system with your unity experiences, some other patterns or just the standard way?
— Carib◔u (@caribouloche) October 4, 2014
Update: Autumn 2014
- Built and shipped an Editor Extension to the Unity Asset Store (http://SnapAndPlug.com – snaps any object to any object, without using crappy Grids)
- Delved deep into Unity’s Serialization – found some bugs, learnt/understood some things I’d never really understood in Unity before, played with the new ISerializ… callbacks that were added this year to core Unity
- Spent a lot of time doing custom EditorGUIs for the above plugin
Let’s have a quick look at what I’m considering next. First-up, some GUI mockups.
GUI: Processors (nee “Systems”)
A recurring frustration in all Unity projects beyond a certain size is that it’s EXTREMELY hard to find the damn source code that implements a given behaviour. Unity-architecture’s “splatter it in a mess all over the place, put it anywhere and everywhere” is great for getting started without worrying about “planning” – but terrible for keeping you to good practice. Soon you forget where things are.
ECS Processors are a great (automatic) solution to this: they centralise your code by purpose. At its simplest, I’d like a custom EditorWindow (Unity’s term for a dockable pane) which lists them, gives me some basic global controls, etc:
- You can see “at a glance” what Processors you have
- …and how many Entities each is currently ticking over
- (helps enormously when you screw-up your Component-matching/filtering, and one of your Processors is finding “no entities”!)
- Critical info like “how many FPS is this running at” is visible / editable
- Dynamically enable/disable/replace entire code-stacks at runtime
- (helps enormously with debugging, refactoring, and general game-testing/balancing/design fiddling)
- Some “global, simple” features: save the entire state of all Processors – or load it live, at runtime. Or pause the entire Entity System! (while leaving Unity3D running, so you can still interact with the game-world, do live cheating / re-balancing, etc)
This is mostly a replacement for the Unity3D “Hierarchy” window. With an Entity System, a few things change at an architectural level:
- Each “Component” is much simpler – data-only, no buttons possible! – so you can even do in-line editing
- Filtering-by-Component becomes relevant and sensible (like Unity’s new “search”/filters, but more powerful and easier to use)
- Unity’s antiquated “everything has a Transform” can be ditched, saving designers from going mad while editing large levels
…these are simple changes, IMHO nothing to get excited about. The most important is that it should become MUCH easier and faster for humans to browse the list of all gameobjects and find what they want (to edit, to delete, to debug) at runtime.
- We have a dropdown for showing “all entities” or only “entities that match the filter”. Changing back and forth between these is likely to be used/needed a lot
- Alternative: I thought instead: maybe I’d like this window to remember the “currently selected Processor” – or have another option on that dropdown – which dynamically changes the Entity list to whichever Processor is current / most recently selected. HUGE timesaver!
- At the top-right, we have fast filtering based on “which entities have some/all of these components”
- Entities are auto-coloured to show if they match/partially match/don’t match the filter
- The fast-filtering WILL NOT SCALE: this will have to become a dropdown when there’s more than a few ECS Components in the game (i.e. almost always). This mockup is misleading!
- Perhaps: mouseover that area, and it does a popup showing tickboxes for ALL ECS Components?
- The “index” column is a debugging aid: rather than memorize the Entity ID, the Entity Manager class can auto-track how many instances there are of each templated-Entity, and give them indexes (which can decrease if earlier-created Entities get destroyed).
- I don’t really like this. I think I’ll delete this column in practice.
Looking forwards … pre-GUI
WARNING! Stream-of-conscious follows. Here be dragons.
(in particular, please note: I mentally draw a strict line between “Unity’s architecture” – e.g. the concept of GameObjects, or the need for Inspectors, or the Update/LateUpdate rendering – and “Unity’s implementation” – i.e. what works today, or doesn’t. Cool features *within* the Inspectors, etc)
Integrating an ECS into Unity
Here’s a shortlist of features we MIGHT want when doing a “good” integration of a full ECS into Unity:
- all code stored outside the Component (please, please, oh please yes!)
- all data stored reasonably efficiently in raw memory / Arrays (impl in C# = ??)
- efficient getComponent (we can do this many times faster than Unity’s built-in, using the above)
- cool features on Entities that aren’t possible with standard Unity
- See the “history” of this Entity: which Components were added or removed, at what time, since game-start
- hot-save/hot-load any Entity without messing-up any of Unity’s Serialization
- create an “EntityTemplate” from this Entity
- create and edit EntityTemplates (like Unity prefabs – but on steroids: more powerful, more efficient, and less prone to bugs)
- In particular: we’ll add some essential features that are missing from Unity prefabs, without having to do dangerous changes to core Unity (IMHO: prefabs still suck in Unity because improving them is a HARD task, not because Unity staff are happy with them! We get to sidestep any legacy problems)
- live views of what’s happening inside the Entity System itself
- How many entities are live?
- Which Components does EntityID 234324324832 currently have?
- Which Processors are taking how much CPU time?
- How many instances are there of Component X? (more than expected? fewer?)
- How many threads are running, and which Processors are on which thread? How much CPU are we saving by forcing Unity to be a multi-threaded application? (GRR!)
- How many writes are happening per type-of-Component? How many reads?
- custom Inspector that automatically understands how to quick-jump between “this Component, this Entity, and the Entity-Template that created it”
- quick controls for Entity-System-wide actions
- quick-Save/Load the entire game-state
- delete any entity, any component – temporarily or permanently
- rewind / fast-forward the game (not possible in standard Unity, but easy with a full ECS)
- custom EditorWindow for the entity-manager / component-manager
- custom EditorWindow for each Processor
- custom EditorWindow for ECS stats: mem-usage, max IDs, etc
- multi-threading support: run any Processor partially (or fully) on background threads
- custom “rapid” serialization via callbacks
NB: I don’t consider that a complete list – it’s merely the obvious ones that came to mind based on recent experiences. Got some I missed? Post it in comments!
If you’re already hooked on an ECS, this goes without saying. But it’s worth answering this to be clear what we (might) gain – and to illuminate the many Unity users that haven’t used a full ECS yet.
Again – STREAM OF CONSCIOUSNESS notes here – not authoritative!
developer’s attention span
In Unity, we spend way too much time in the code-editor, b/c Unity doesn’t separate Code from Data. Thousands of developers have run-away to PlayMaker et al; I would prefer a middle-road where I don’t need to write code (Unity forces me to create a class, open in IDE, and re-compile Unity project) every time I want to create some data!
creating new components
inventing a new component must be EVEN FEWER keystrokes than Unity’s (Choose folder, Create Script, Name Script, Edit Script, Compile/Build, Select Script, Drag/Drop script to GameObject) … we can do so much better than that, given that ECS’s use pure data, no code.
enable/disable FEATURES not GAMEOBJECTS
one of Unity’s least appreciated (because it has so many flaws?) yet most powerful features is the little tickbox on every component+ every gameobject that “disables” that item: completely removes it from runtime as if it didn’t exist. But Unity only allows you to do this on individual objects at a time; it’s massively more useful/powerful to do it on “the script itself, on ALL objects” – trivial to do with an ECS
fix Unity’s overloaded “enable/disable”
related to previous point: enabling a component in Unity no longer does what the name implies; Unity overloaded this feature with too many incompatible uses a while back. We can “fix” that by adding discrete features for each of the different reasons that enable/disable exists in Unity today.
in Unity, medium/large projects are difficult to read/edit/expand: the source is spread across hundreds or thousands of meaningless source files. It is VERY VERY hard to find “where is the line of code that is causing X to happen?”.
Unity callbacks are great for simple cases, but weak for medium/large games: too many callbacks, they break C# rules (why no “override” guys? many times that has bitten us when someone typoed “onSomething()”!!), and you end up with massive, long source files with many, many methods you have to read to find the one you’re looking for – or even check “does this file have callback X, and even if it does, does its version of X do anything I need to know about?”.
.. make “all of Unity’s rendering callbacks” into a single ECS Processor. Make “all of Unity’s lifecycle callbacks” into a different ECS Processor. Repeat until all callbacks are covered, and split into a sensible number of smaller, better encapsulated source interfaces/files.
…just don’t work in Unity any more. Unity got many feature upgrades over the years, but prefabs did not. Today, prefabs cause lots of problems: code that works 100% fine breaks in bizarre ways when prefabs, or part prefabs, or nested prefabs … etc … are passed-in as variables.
(if you’re careful, you can fix most of those problems. But not all: some of the callbacks / events have subtly different meanings based on whether items are prefab or gameobject, IME)
We can fix all that, quite easily – in a way that Unity can’t: because Entities are pure data, our equivalent of Prefabs are MUCH easier to reason about (and write code for).
Lots of things we can do here. Unity scripts serve too many functions – code, data, marking – and have “magic” behaviour based on parenting (try adding Colliders to a child object where the parent has none. This changes the meaning of the parent object EVEN THOUGH you haven’t edited it. It’s a useful feature, but can be very confusing (and painful)).
…so many improvements in fact that I’m still brainstorming them!
multiple people edit one Scene / cross-project data sharing
Unity has MANY painful bugs when you have multiple people in a team trying to edit the same levels/assets/etc. We can fix most of those trivially using ECS and storing in standards-compatible data foramts.
(and make it so that git merging “actually works” etc)
There are a bunch of import/export/preview/”view differences”/”view other person’s commit”/etc features we can add too. Because ECS is pure data, it’s much better at holding “two different versions of the Scene in memory, at the same time, without corrupting either”.
(That’s usually a “hard” problem without an ECS…)
Some other things I wanted to reference, but I’m out of time for this post:
Forge, which tries to bring some modern ECS features into Unity – does some good things, but also falls a long way short for most of my needs. Worth playing with if you’re interested in putting Entity Systems into Unity3D (also: it’s open-source!)
Ash framework being used in Unity – Ash is the main Entity System implementation for Flash / ActionScript. Unity’s similar enough to Flash that this is (apparently) not too difficult to do, but different enough that it has its own flaws. Again, worth exploring. Especially if your background is AS.