{"id":3587,"date":"2015-04-29T14:37:16","date_gmt":"2015-04-29T13:37:16","guid":{"rendered":"http:\/\/t-machine.org\/?p=3587"},"modified":"2015-04-29T15:02:13","modified_gmt":"2015-04-29T14:02:13","slug":"replacing-unity3ds-core-architecture-structs","status":"publish","type":"post","link":"http:\/\/new.t-machine.org\/index.php\/2015\/04\/29\/replacing-unity3ds-core-architecture-structs\/","title":{"rendered":"Replacing Unity3d&#8217;s core architecture: Structs"},"content":{"rendered":"<p>(One of a series of posts about converting Unity3D into a 1st-class Entity Component System. <a href=\"http:\/\/t-machine.org\/index.php\/category\/entity-systems\/\">Read others here<\/a>)<\/p>\n<h2>How to store game data: GameObject? MonoBehaviour? ScriptableObject?<\/h2>\n<p>Those three pillars of Unity&#8217;s architecture are the curse of game implementation and performance. Architecturally, they&#8217;re great: they&#8217;re a big part of what makes Unity easy for newcomers to pick up and understand within hours, instead of taking weeks.<\/p>\n<p>On large Unity game projects, they&#8217;re hated for being slow and hard to work with when you get to hundreds of thousands (or millions, tens of millions, &#8230;) in a single scene. Unity provides zero tools and support for this situation &#8211; it just gets harder and harder to work with, and slower and slower to run.<\/p>\n<p>In Entity Systems world, we hate them because they are inherently slow and buggy &#8211; no matter how great Unity&#8217;s coders, they&#8217;ll never make them work as well as our preferred approach (See below).<\/p>\n<p>But &#8230; replacing them forces you to replace <strong>most of the Unity Editor<\/strong>!<\/p>\n<p>Most of the features of our Entity System need new Editor GUI anyway. If we&#8217;re going to be adding big chunks to the editor, replacing GO\/MB\/SO is going to be much cheaper than normal. And it potentially has huge benefits that are independent of the Entity System.<\/p>\n<p>If we&#8217;re going to replace them, what will we replace them with? Custom versions that have almost the same name and work the same way? Or something very different?<\/p>\n<h3>What is Unity&#8217;s &#8216;GameObject&#8217; anyway?<\/h3>\n<p>As far as I can tell, the real meaning of GameObject is:<\/p>\n<blockquote><p>\n<strong>def. GameObject:<\/strong> Something that exists inside a Scene, and has a Position, Rotation, and Scale. It can be &#8220;embedded&#8221; inside any other GameObject, meaning that any change to the other object&#8217;s Position, Rotation, or Scale gets merged onto this one. It&#8217;s the ONLY thing that can have Unity &#8220;Components&#8221; (MonoBehaviours) attached to it. Anything that is not a GameObject, and is not attached to a GO, gets deleted every time you load\/reload\/play\/resume a Scene.\n<\/p><\/blockquote>\n<p>From a code perspective, looking at the backend systems, there&#8217;s a lot more to it. The GameObject (GO) has a slightly weird name, when you think about it. You use it so much during Unity dev that you stop noticing it. &#8220;Game &#8230; Object&#8221; &#8230; wha?<\/p>\n<h3>Other purposes GameObject serves<\/h3>\n<p>Here&#8217;s a few obvious ones (I&#8217;ll edit later if I think of more):<\/p>\n<ol>\n<li>Guarantees everything has the GetInstanceID() method\n<ol>\n<li>Needed for Unity&#8217;s Serialization system to work (because of how it&#8217;s been designed, not because serializing requires this &#8211; C# has built-ins that would have done the same job)\n<li>Enables the Editor to &#8220;select&#8221; anything in any tab\/window, and that thing is ALSO selected in all other tabs\/windows (e.g. click in Scene, and the Inspector updates to show the components on that thing)\n<li>&#8230;probably loads of other things. It&#8217;s so commonly used I hardly notice it\n<\/ol>\n<li>When doing in-Editor things, Unity can often &#8220;ignore&#8221; classes that are &#8220;not part of the game, because they don&#8217;t extend GameObject&#8221;\n<ol>\n<li>Reading between the lines of official docs, and simplifying vastly, this is what happens behind the scenes. Especially with Serialization, I suspect.\n<li>This can be a huge pain. It&#8217;s hard to debug code when &#8220;GameObject subclasses&#8221; magically behave differently to &#8220;all other classes&#8221; with no indication of why\n<\/ol>\n<li>Since every GO has a Transform &#8230; you can do automatic, general-purpose, high-quality, Occlusion Culling\n<ol>\n<li>NB: for this to work perfectly, you&#8217;d also want every GameObject to have a .bounds variable. Why did Unity&#8217;s original architects leave that out? Dunno, sorry.\n<\/ol>\n<\/ol>\n<h2>How to store game-data: the Entity Systems ideal way<\/h2>\n<p>We know this, it&#8217;s been discussed to death: if your programming language supports it, <em>use structs<\/em>.<\/p>\n<p>By definition, structs are the most memory-efficient, CPU-efficient, multi-thread-safe, typesafe way of storing data, bar none. There is no better way to do this in mainstream languages. C# and C++ both support structs; I think we have a winner!<\/p>\n<p><em>There are probably some awesomely clever advanced Math ways of doing things better, or by using raw assembler and code optimized for each individual CPU on the market. Or, or &#8230; but: in terms of what&#8217;s &#8216;normal&#8217;, and commonly used, this is the best you&#8217;re going to get<\/em><\/p>\n<p>But how will this work in practice? What about all the &#8220;other&#8221; roles of GO etc in Unity?<\/p>\n<h3>Structs in &#8230; Identity (GetInstanceID)<\/h3>\n<p>Simple ECS implementations often hand-around raw pointers to their Components; that gets messy with Identity.<\/p>\n<p>But most (all?) advanced implementations have some kind of indirection &#8211; a smart pointer, or an integer &#8220;index&#8221; that the Component Storage \/ Manager maps internally to the actual point in memory where that lives.<\/p>\n<p>If we turn that &#8220;advanced&#8221; feature into a requirement, we should be fine.<\/p>\n<h3>Structs in &#8230; Serialization<\/h3>\n<ol>\n<li>Start writing your own version of the Unity Serialization system that only uses structs\n<li>Finish it the same day\n<li>Discover it runs 10 times faster than Unity&#8217;s built-in one\n<li>&#8230;with fewer bugs\n<li>PROFIT!\n<\/ol>\n<p>Structs are the easiest thing you could possibly try to serialize and deserialize. Unity coders probably wish that they could restrict us all to structs &#8211; it would make their live much easier in some ways.<\/p>\n<h4>User-written structs: special rules?<\/h4>\n<p>Do we need to add special &#8220;rules&#8221; to user-written structs that appear in the game?<\/p>\n<p>Nope! It turns out that C#&#8217;s built-in Reflection system is able to inspect every struct, shows us every field\/property\/etc, and lets us easily read and write to them. Both private and public.<\/p>\n<h4>CAVEAT: structs break C# .SetValue()<\/h4>\n<p>Yeah, <a href=\"http:\/\/stackoverflow.com\/questions\/6608368\/why-doesnt-reflection-set-a-property-in-a-struct\">this sucks &#8211; and it&#8217;s nothing to do with Unity, it&#8217;s core C#<\/a>.<\/p>\n<p>A little care is needed when we implement the Entity System internals. But it has no effect on user code \/ game code.<\/p>\n<h3>Structs in &#8230; SceneView (Transforms)<\/h3>\n<p>No! Just &#8230; NO!<\/p>\n<p>This is one of the architectural mistakes of Unity we&#8217;re going to fix in passing: we <em>won&#8217;t<\/em> require everything to have a Transform.<\/p>\n<p>However, it foreshadows a more subtle problem that we&#8217;ll eventually have to deal with:<\/p>\n<blockquote><p>\nEntity Systems are a table-based\/Relational\/RDBMS way to store and access data; such systems are very inefficient at storing and retrieving tree-structured data (e.g. OOP classes + objects)\n<\/p><\/blockquote>\n<p>The transform hierarchy in a Unity scene is a massive tree. As it turns out, it&#8217;s bigger than it needs to be (because of that law of Unity that everything must have a Transform) &#8211; but we uses trees <em>all the damn time<\/em> in game-development. So it&#8217;s a problem we&#8217;ll have to come back to.<\/p>\n<h2>Bigger storage: where do the structs live?<\/h2>\n<p>Fine; at the lowest level, the individual ECS Components, we&#8217;ll use structs.<\/p>\n<p>But how do we store those?<\/p>\n<p><a href=\"http:\/\/t-machine.org\/index.php\/2014\/03\/08\/data-structures-for-entity-systems-contiguous-memory\/\">Choosing the right aggregate data-structures for your ECS<\/a> is a major topic in itself. C# gives us (almost) total control of how we&#8217;d like to do it &#8211; plenty of rope to hang ourselves with.<\/p>\n<p>At this point: I&#8217;m not sure.<\/p>\n<p>As a temporary solution, I&#8217;m going to focus on making the storage-system swappable, so that I can implement a crappy version quickly, and then easily swap-it-out with something much better &#8211; without having to rewrite anything else.<\/p>\n<h2>Does it work?<\/h2>\n<p>Yes. I tried it &#8211; I wrote a complete replacement for the Unity Inspector tab that only works on Entities-with-Components, where &#8220;Component&#8221; is any struct. Ugly, but it works:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2015-04-29-at-14.41.23.png\" alt=\"Screen Shot 2015-04-29 at 14.41.23\" width=\"278\" height=\"100\" class=\"aligncenter size-full wp-image-3594\" srcset=\"https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2015-04-29-at-14.41.23.png 278w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2015-04-29-at-14.41.23-150x53.png 150w\" sizes=\"(max-width: 278px) 100vw, 278px\" \/><\/p>\n<p>Everything there is auto-generated (including the colour; every struct gets a globally unique background colour, which I find makes it much easier when you have hundreds or thousands of Component types).<\/p>\n<p>The only tricky bit was the problem with C# FieldInfo.SetValue(&#8230;), as mentioned above.<\/p>\n<h2>Further Reading<\/h2>\n<p>&#8230;some things I might want to pick up on in future posts:<\/p>\n<ul>\n<li><a href=\"http:\/\/stackoverflow.com\/questions\/9335455\/mutable-struct-vs-class\">Worked example of converting struct to immutable struct, and why\/how to do it<\/a>\n<li><a href=\"http:\/\/csharp.2000things.com\/2013\/03\/07\/795-rules-for-creating-an-immutable-class\/\">Guidelines for implementing immutable structs<\/a>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>(One of a series of posts about converting Unity3D into a 1st-class Entity Component System. Read others here) How to store game data: GameObject? MonoBehaviour? ScriptableObject? Those three pillars of Unity&#8217;s architecture are the curse of game implementation and performance. Architecturally, they&#8217;re great: they&#8217;re a big part of what makes Unity easy for newcomers to [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60,67],"tags":[],"_links":{"self":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/3587"}],"collection":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/comments?post=3587"}],"version-history":[{"count":8,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/3587\/revisions"}],"predecessor-version":[{"id":3596,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/3587\/revisions\/3596"}],"wp:attachment":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/media?parent=3587"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/categories?post=3587"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/tags?post=3587"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}