Unity’s failure as an Entity System, example 1: Selecting things

Entity Systems in Unity… some examples of the problems

This is a new series of blog posts, where I’m going to document specific ways + concrete examples in which Unity fails (sometimes spectacularly) as an “ECS” game engine.

I like Unity; but the core architecture (which is very old) is a half-assed ECS, and if we’re to upgrade it into a really good, modern, architecture … we first need to understand exactly where it’s failing, and why.

So, let’s start with Selecting Things…

Background

Game

A 2-player card game where each player normally draws a card each turn, and then plays one or more cards. Sometimes (e.g. when discarding because “too many cards in hand”) they have to select more than one card at once.

Initial version of game will be 1-player versus computer. Very soon: want to upgrade to OPTIONAL 2-players on-screen, with one using the mouse, other using gamepad. Ultimately, want to also add over-the-internet multiplayer (which ends up interfacing with the codebase in a very similar way to the original 1-player-vs-computer, so we can ignore for now).

Code Situation

Player taps card. Now … we must inform many other scripts and independent systems, so they can choose to eg.

  • ignore (if invalid for current state of game)
  • react (e.g. zoom to display the card more clearly)
  • “select” internally (side-effects include: deselect other things)
  • advance the game (if it was waiting-for-input)
  • ..etc

Problem

What/where do you send the “player clicked on a card; can some piece(s) of code PLEASE deal with this??!!?” ?

Addendum

In Unity, only the low-level “card” object can sensibly detect it has been clicked on.

Both Unity’s Physics (old) and EventSystem (new) effectively force this via their core design. Both require you to attach scripts to the physical objects that will be clicked.

In practice, this is bad for OOP (and bad for ECS too). When there’s e.g. 100 cards all of which must be separately clickable, your code is really in the wrong place. You don’t want cards (which sometimes are in a deck, sometimes on the table, sometimes “virtual” (perhaps in a virtual, unopened booster pack etc)) … to be containing all the GAME logic required to know what to do when they’re touched!

Classic Entity System / ECS solution

  1. Create a SelectedByPlayer component
  2. Add it to the card
  3. Sit back, and relax. Code that cares about input will scan “get me all SelectedByPlayer components” on each frame, and react accordingly

Everything works automatically; any System/Processor that’s “waiting for a selection”, or “making render changes when selections add/remove”, etc … will pick up what it needs, with no work.

You can add new input-handling routines simply by adding them. That’s all. No other changes needed.

Attempting to solve this in Unity

UNITY 4.6/5.0

Maybe … NB: I’ve only just started using the new GUI/EventSystem in Unity 4.6+ .. create a custom Input event, and a custom InputModule that can understand that, and then put all the code for ALL affected systems/processors into one monolithic ugly, hard-to-maintain script from Hell.

I suspect that this code will be quite maintainable w.r.t. adding new Input hardware in future – e.g. allowing mouse vs gamepad. But it’s going to suck at the rest, all the business-logic and handling. Which is going to be > 95% of the maintenance cost.

You get one small benefit: you can separate-out different inputs (click versus drag). Sadly, in reality: 95% of game actions will be simple clicks. This is one of those “the code architecture sounded great in academic situation, but reality is so unbalanced, it works out less well in practice” situations.

This is a classic OOP solution, and has the downsides. The only significant benefit I can think of is that it’s a “known” Hell: if you’ve done a lot of OOP game coding, you’ll be familiar with the pain you’re going to run into.

UNITY CLASSIC

Make a new class “CardClickManager” whose sole purpose is to reference all the possible bits of OOP code that “might” need to react, and which has to be updated by hand EVERY TIME you modify, add, or remove some input-handling code ANYWHERE else in the codebase.

Pretty much the same as above, except it:

  • … is even more simplistic (no event-dispatch systems)
  • … making it even harder to maintain + debug
  • … is slightly more proprietary

Conclusions / Improving Unity

So far, I cannot think of any sane, maintainable solution here other than “suck it down and use OOP, and suffer forever”, or “throw away Unity GameObject/Component, and implement a proper ECS”.

That’s fine, though. That’s the point of these posts – to hilight situations where there’s no good middle-ground, where we must create the data-centric, cleanly-separated architecture of a modern ECS.

Counter-ideas very welcome! Comment away, guys…

17 thoughts on “Unity’s failure as an Entity System, example 1: Selecting things

  1. Jos

    Why can’t you use some event broadcaster on the GameObject, which would announce that it has been clicked/dragged/whatever. You’d then have a bunch of scripts (somewhere else) which listen for that and then do what they need to do. Do you not get the decoupled advantages there? The event could include the GO itself, if they need to deal with its position or translation or whatever.

    Please note, i agree that the Unity system is not a very good ECS, but is probably easier for people to wrap their heads around, because of its architecture.

  2. adam Post author

    Sure, you can build an entire publish/subscribe event system – could try piggybacking the one Unity is using in the new EventSystem.

    But that’s introducing another paradigm to work around the fact your code is in the wrong place, and Unity has no real support for this.

    (Real support = editor tools. A debug view of the event queue, and the pub/sub chains. Etc)

  3. Lior Tal

    What prevents you from creating the “classic ECS” example you gave?
    You could create a SelectedByPlayer component and attach it to whatever objects that require it (e.g: cards).

    Is the problem with who handles the clicks? well why is a global system/processor any better than the card itself containing the logic ?

  4. Adam

    If you go that route, you end up with massive source files that are impossible to maintain. I’ve seen a shipped Unity game with more than 30,000 lines in a single source file.

    Any C# file, using the vile and shitty Mono IDE, that goes over 100 lines is already noticeably hard to maintain. If you’re windows-only, and can use MSVC, then great – but still you’re in dangerous ground if you get much over 700 per file.

    “Puttng the code in the wrong place” was one of the practical things that OOP’s predecessors cured; I don’t want to go back to the hell of 1980’s codebases :).

  5. Adam

    …or you end up with huge numbers of pointless code-components everywhere and you have NO IDEA which code is where or why or what it’s doing. This appears to be very common in Unity codebases :(.

    (sorry, did cut/paste and accidentally hit submit instead of paste)

  6. Marson

    Hi Adam:
    Between `2. Add it to the card` and `3. Sit back, and relax`, are we still have to add some codes to process those cards?
    For example, I’m using the component/system architecture by Artemis Framewoek now (I think that’s the good ECS architecture), so I think I need to add several systems, like `RenderEffectOnSelectedCardSystem`, `DoSomeMagicOnSelectedCardSystem`, etc.
    I just want to make sure that, before I can `Sit and Relax`, I still have to do some works, but that should be a lot easier than adding functionality in Unity’s way.
    Would like to know if my understanding is correct, thanks!

  7. adam Post author

    Those are extra features you add later.

    To get the basic setup working, you don’t need them. But they are much cheaper to add in an ECS than in Unity

  8. Amir

    Bit late for this party; wanted to chime in on how I handle Unity (mostly because it’s been directly related to reading your blog).
    Disclaimer; I don’t claim that this solution is optimized for memory/speed other than making my life as a programmer easier.

    I basically don’t use any of Unity’s methods in my behaviours. In fact I don’t even call my behaviours “behaviours” but rather “components” . Each component is a simple data container with only public fields.

    The only real behaviour I have is a single script called a “Driver” which sits on the root entity of the scene. The driver implements the Awake(), Start() and Update() methods. And has two lists called “UpdateSystems” and “StartupSystems”. Startup systems are stepped through during the Start() method where each system contains code to initialize the game/scene requirements. And the ‘UpdateSystems’ are stepped during each Update() call (gets a bit more involved since systems can be added/removed during runtime).

    A system is usually an object with one method [Perform] which queries the driver for the components it needs then transforms the data in those components somehow.

    Again, probably not the most optimized solution (threshing the memory etc) but the games I’m currently working on are not AAA graphics/physics heavy so I’m not too worried and haven’t noticed a significant slowdown.

  9. adam Post author

    @Amire – how do you interface with Unity’s engine features – e.g. rendering, physics?

    How does the code work for that? And the editing?

  10. Amir

    I’m not sure how you mean “interfacing with rendering/physics”?

    I don’t touch Unity’s rendering at all to be honest as I’m making 2d games and there’s nothing fancy going on beyond sprites. And I don’t need control over what gets rendered when. As far as physics, I don’t use Unity’s physics so that’s an empty Box or maybe a dodged Bullet? :P

    I mainly use Unity as a data entry tool for my designer and a deployment service for different platforms. When/if heavy optimization will be required most of my code is decoupled enough from Unity’s ‘engine’ to be lifted and re/attached to another engine (if required).

    As far as editing goes; since all the component data is public the designer can pretty much control all aspects of it.

  11. adam Post author

    Ah, OK. Yes, a much easier problem domain.

    If I were to discard that much of Unity, it would be useless to me – for the kind of games I do, there are much better solutions for cross-platform deployment, in-game editing, etc. Unity’s attraction is that it’s 5-10x more than that.

    But it’s harder to integrate with.

  12. Amir

    If you don’t mind me asking, what features of Unity do you require that make Unity specifically attractive over MonoGame for example?

  13. adam Post author

    Using events for this is generally a slow, unwieldy, boilerplate-coding solution. It’s not fun, and causes problems as your project scales up.

    However, it may well be the best practical way for user code to bridge between Unity and a real Entity System. I’d be interested to see how well it works, but their website doesn’t really explore this. It covers basic concepts, but doesn’t give much/any info on how well that works in practice, how well it works compared to alternatives, etc. (alternatives seem to get largely ignored. They have a way they like, they aren’t interested in other ways?)

    Ultimately, though, Unity does not support event-driven programming so it feels like this is swapping one unsupported paradigm (ES) for another (Events). A real solution IMHO needs to either use Unity-supported programming approaches, or else needs to hack Unity apart and force it to support things.

  14. adam Post author

    That doesn’t answer any of the issues I raised.

    I have written events systems before, and their blog post has nothing new or interesting in it. if you want to write an events driven framework, I’d say there are much better frameworks and much better tutorials out there.

    If they’re doing something new and useful here, they need to jilight it better, because I can’t see it!

  15. gl33mer

    I’m not sure it is much more than an events driven framework.

    So – we’re left with the “Unity way” ?

Leave a Reply

Your email address will not be published. Required fields are marked *