<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Rinaldi (TenaciousDan)</title>
    <description>The latest articles on DEV Community by Daniel Rinaldi (TenaciousDan) (@tenaciousdan).</description>
    <link>https://dev.to/tenaciousdan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4018344%2F1a9755e5-d536-447c-80df-757686e3e053.png</url>
      <title>DEV Community: Daniel Rinaldi (TenaciousDan)</title>
      <link>https://dev.to/tenaciousdan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tenaciousdan"/>
    <language>en</language>
    <item>
      <title>Unity Foundational Architecture: Loose Coupling, Interfaces &amp; Events</title>
      <dc:creator>Daniel Rinaldi (TenaciousDan)</dc:creator>
      <pubDate>Sat, 08 Aug 2026 00:29:24 +0000</pubDate>
      <link>https://dev.to/tenaciousdan/unity-foundational-architecture-loose-coupling-interfaces-events-1bio</link>
      <guid>https://dev.to/tenaciousdan/unity-foundational-architecture-loose-coupling-interfaces-events-1bio</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;Table of Contents&lt;/em&gt;:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
The Power of Interfaces

&lt;ul&gt;
&lt;li&gt;The Unity Serialization Wall&lt;/li&gt;
&lt;li&gt;Building a Serializable Unity Object Reference&lt;/li&gt;
&lt;li&gt;Serializing Plain C# Object References&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Decoupling Communication (Events)

&lt;ul&gt;
&lt;li&gt;Building a Simple Generic Global Event Bus&lt;/li&gt;
&lt;li&gt;The Instance Specific Event Problem&lt;/li&gt;
&lt;li&gt;Instance Specific EventBus&lt;/li&gt;
&lt;li&gt;Serializing Events&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the last few posts we covered Project Scaffolding, Bootstrapping Logic, and Global State Management. The next problem that we will tackle is how to tie our code and scene objects together without creating a tangled and unmanageable web of dependencies (Spaghetti).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Interfaces
&lt;/h2&gt;

&lt;p&gt;To decouple our systems, we need to stop relying on concrete classes. If a Turret script needs to deal damage, it shouldn't hold a reference to &lt;code&gt;PlayerHealth&lt;/code&gt;, &lt;code&gt;EnemyHealth&lt;/code&gt;, or &lt;code&gt;DestructibleCrate&lt;/code&gt;. Doing so creates strict dependencies.&lt;/p&gt;

&lt;p&gt;Instead, the Turret just needs to know that the thing it's hitting can take damage. As such we can define an generic interface for "things that can take damage". Let's call it &lt;code&gt;IDamageable&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDamageable&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TakeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the Turret only relies on the &lt;code&gt;IDamageable&lt;/code&gt; contract. It doesn't care what the object is, as long as it signs that contract. This is textbook &lt;strong&gt;loose coupling&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Unity Serialization Wall
&lt;/h3&gt;

&lt;p&gt;However, if you try to expose this interface in the inspector so that you can inject the dependancy into your monobehaviour scripts connecting the scene objects, you'll hit a wall.&lt;/p&gt;

&lt;p&gt;Unity's standard serializer does not serialize interfaces. It can only handle concrete classes that derive from &lt;code&gt;UnityEngine.Object&lt;/code&gt;. Now, you can work around this by using GetComponent() in Awake() essentially hooking things up on initialization manually through code, but that forces you to hardcode scene hierarchy paths or rely on slow runtime lookups, and defeating the purpose of a highly configurable architecture that uses dependancy injection. &lt;/p&gt;

&lt;h3&gt;
  
  
  Building a Serializable Unity Object Reference
&lt;/h3&gt;

&lt;p&gt;To get the best of both world, clean interfaces in our code and drag-&amp;amp;-drop support in the inspector, we can build a generic wrapper. &lt;/p&gt;

&lt;p&gt;By utilizing the &lt;code&gt;ISerializationCallbackReceiver&lt;/code&gt; interface, we can trick Unity into serializing a standard UnityEngine.Object, while casting it to our interface behind the scenes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ISerializationCallbackReceiver&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This field will store the serialized reference to the Unity Object that the inspector can serialize (The True Value)&lt;/span&gt;
    &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;UnityObject&lt;/span&gt; &lt;span class="n"&gt;_reference&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// This field will store the serialized reference casted to T which may or may not be a serializable type.&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Gets and Sets the current runtime value of this object. Note that if the value is set at runtime to something that is not a Unity Object,&lt;/span&gt;
    &lt;span class="c1"&gt;/// then the backing reference will be null.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;set&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;UnityObject&lt;/span&gt; &lt;span class="n"&gt;valueAsUnityObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;valueAsUnityObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;HasValue&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;ValueType&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// The backing reference value of this object (The True Value). If null, then return the stored runtime value of this object without modifying it.&lt;/span&gt;
    &lt;span class="c1"&gt;/// This differs from the &amp;lt;see cref="Value"/&amp;gt; property getter which will adjust the stored value to match the backing reference if the value is null and the reference is not.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;ValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;serializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializableUnityRef&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;serializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;serializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;implicit&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="nf"&gt;T&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;serializableRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;serializableRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;implicit&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="n"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ISerializationCallbackReceiver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnBeforeSerialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;ValidateReferenceObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ISerializationCallbackReceiver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnAfterDeserialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// do nothing&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ValidateReferenceObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_reference&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;GameObject&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// find first component that inherits from T&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponents&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This now shows up in the inspector!&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDamageable&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Attack&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;TakeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fim5yppveeck5fczdex0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fim5yppveeck5fczdex0i.png" alt=" " width="567" height="49"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;While this solution "works" it doesn't look very nice in the inspector and is missing some use cases that still makes it difficult to use properly.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;What we have so far:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A wrapper to serialize a reference to a Unity Object in the inspector. Works for Monobehaviour and ScriptableObject derived classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What we are missing:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A better look and feel in the inspector. Requires a custom property drawer.&lt;/li&gt;
&lt;li&gt;A way to drag and drop a GameObject and reference a specific component on that GameObject. If there are multiple components of the same reference type then we need some sort of dropdown in the inspector to help distinguish between them so we know which one we are injecting. Also requires a custom property drawer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's build a custom property drawer so that we can fix the above problems. You will need to create a &lt;code&gt;SerializableUnityRefPropertyDrawer&lt;/code&gt; script inside an &lt;code&gt;Editor&lt;/code&gt; folder. We will also have to modify our &lt;code&gt;SerializableUnityRef&lt;/code&gt; script and add an interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ISerializableUnityRef&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ISerializationCallbackReceiver&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetSiblingComponentsOfTypeFromReferenceObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;ValueType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// change the class so that it inherits from our custom interface&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ISerializableUnityRef&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// all other fields and methods remain the same ...&lt;/span&gt;

    &lt;span class="c1"&gt;// add the GetSiblingComponentsOfTypeFromReferenceObject method to respect our interface:&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// If the referenced object is a Component, then this gets all components attached &lt;/span&gt;
    &lt;span class="c1"&gt;/// to the same gameObject that are of type &amp;lt;typeparamref name="T"/&amp;gt;. &amp;lt;br /&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Otherwise, returns a single element list with just the reference object.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// A non-empty list of all components attached to the same gameObject as the &lt;/span&gt;
    &lt;span class="c1"&gt;/// referenced object that are of type &amp;lt;typeparamref name="T"/&amp;gt;.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="nc"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetSiblingComponentsOfTypeFromReferenceObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;siblingComponents&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;siblingComponents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// we must cast the value and set the reference if value was set via code&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;UnityObject&lt;/span&gt; &lt;span class="n"&gt;valueAsUnityObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;valueAsUnityObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Component&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;siblingComponents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponents&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;siblingComponents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_reference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;siblingComponents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok so the next part is just custom PropertyDrawer logic using the &lt;code&gt;EditorGUI&lt;/code&gt; API. We will essentially check if the reference is a gameobject and if it is then we will use the function we just created to get all the components attached that inherit from the same type and let the user select the correct component via a dropdown. If it's not a gameobject then we just make it look like a normal single line serialized property field. If you don't understand what is going on here it's ok to just copy paste the following code (this article is not going to cover Unity Editor Custom Tool programming). Just be sure to include this next bit of code inside an &lt;code&gt;Editor&lt;/code&gt; folder so that your build will not crash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;CustomPropertyDrawer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&amp;gt;))]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SerializableUnityRefPropertyDrawer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PropertyDrawer&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;REFERENCE_OBJECT_FIELD_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"_reference"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_selectedReferenceIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;GetPropertyHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SerializedProperty&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GUIContent&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EditorGUIUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleLineHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnGUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SerializedProperty&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GUIContent&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;EditorGUI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GUIContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;DrawReferenceField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;EditorGUI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndProperty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DrawReferenceField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SerializedProperty&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GUIContent&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ISerializableUnityRef&lt;/span&gt; &lt;span class="n"&gt;serializableRef&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PropertyDrawerUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetFieldValue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ISerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializableRef&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializableRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSiblingComponentsOfTypeFromReferenceObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;propertyFieldWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;propertyFieldWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mathf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propertyFieldWidth&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.7f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;EditorGUIUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;labelWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mathf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CalcSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_originalLabelWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;SerializedProperty&lt;/span&gt; &lt;span class="n"&gt;spReferenceObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindPropertyRelative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REFERENCE_OBJECT_FIELD_NAME&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;drawRect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;propertyFieldWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EditorGUIUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleLineHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;horizontalSpacing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;horizontalSpaceUsed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;horizontalSpacing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;horizontalSpaceUsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;horizontalSpaceUsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;EditorGUI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PropertyField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spReferenceObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;EditorGUIUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;labelWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_originalLabelWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;displayedOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;displayedOptions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spReferenceObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objectReferenceValue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="n"&gt;_selectedReferenceIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;drawRect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EditorGUIUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleLineHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;horizontalSpaceUsed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;horizontalSpacing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;horizontalSpaceUsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;horizontalSpaceUsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;EditorGUI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginChangeCheck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;_selectedReferenceIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EditorGUI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_selectedReferenceIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;displayedOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorGUI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndChangeCheck&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;spReferenceObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objectReferenceValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_selectedReferenceIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;UnityObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Helper functions contained inside a utility class:&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PropertyDrawerUtility&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;The actual value of the given &amp;lt;paramref name="property"/&amp;gt;. Handles arrays/lists as well.&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;GetFieldValue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;SerializedProperty&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;object&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetTargetObjectOfProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;objType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;objType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsGenericType&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;objType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetGenericTypeDefinition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&amp;gt;);&lt;/span&gt;

        &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;actualObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsArray&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;isList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;propertyPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;actualObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;IList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;actualObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;actualObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;actualObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;the object the property represents.&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nf"&gt;GetTargetObjectOfProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SerializedProperty&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;propertyPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".Array.data["&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"["&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;targetObj&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serializedObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;targetObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;elements&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"["&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;elementName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"["&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"["&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"["&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="n"&gt;targetObj&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetEnumerableObjectValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elementName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;targetObj&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetObjectValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;targetObj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nf"&gt;GetObjectValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FieldInfo&lt;/span&gt; &lt;span class="n"&gt;fieldInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NonPublic&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Public&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fieldInfo&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fieldInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;PropertyInfo&lt;/span&gt; &lt;span class="n"&gt;propertyInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NonPublic&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Public&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propertyInfo&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;propertyInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nf"&gt;GetEnumerableObjectValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;IEnumerable&lt;/span&gt; &lt;span class="n"&gt;enumerable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetObjectValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enumerable&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;IEnumerator&lt;/span&gt; &lt;span class="n"&gt;enumerator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnumerator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;enumerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MoveNext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enumerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the following Inspector GUI. If there is only one possible reference then there will be no dropdown and the singular reference will be serialized. If there is more than one reference to choose from because we dragged a gameobject into the field, it automatically serialize the first reference it finds and will show the dropdown to let the user switch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvubxgr18f3uow6ach4t4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvubxgr18f3uow6ach4t4.png" alt="SerializableUnityRef with reference dropdown" width="555" height="92"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;We now have the ability to create serializable unity object references but through an interface type. This is a very powerful tool and a big step towards helping us build cleaner software architecture and reduce spaghetti in our game. &lt;/p&gt;

&lt;h3&gt;
  
  
  Serializing Plain C# Object References
&lt;/h3&gt;

&lt;p&gt;While we may be able to now serialize references in which the object inherits from &lt;code&gt;UnityEngine.Object&lt;/code&gt;, what about plain C# object references? Our &lt;code&gt;SerializableUnityRef&lt;/code&gt; will not work for those.&lt;/p&gt;

&lt;p&gt;You may already know about this, but Unity will serialize plain C# objects and struct types using &lt;code&gt;[SerializeField]&lt;/code&gt; as long as you mark the non-abstract class or struct with &lt;code&gt;[System.Serializable]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- This is needed for Serializing this type in the inspector&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ability&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Quick tip: you can target the backing field of C# properties using "field:"&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Cost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Ability&lt;/span&gt; &lt;span class="n"&gt;InherentAbility&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Ability&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Abilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the following in the inspector:&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwug7qvq7ys7yqypc1xj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwug7qvq7ys7yqypc1xj.png" alt=" " width="571" height="247"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;This is nice but we are not utilizing the power of interfaces here. Let's modify our Ability class and the references in our component so that we use interfaces/abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// I used an abstract class but this could also be an interface&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ability&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Cost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlyAbility&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ability&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;MaxHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExtraJumpsAbility&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ability&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ExtraJumps&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Ability&lt;/span&gt; &lt;span class="n"&gt;InherentAbility&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Ability&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Abilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not so great anymore, we lost our serialization:&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F81ti4jh3rd1ekxa1weh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F81ti4jh3rd1ekxa1weh9.png" alt=" " width="569" height="54"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In order to serialize plain C# objects by reference, we need to use Unity's &lt;code&gt;[SerializeReference]&lt;/code&gt; attribute. It even works on &lt;code&gt;List&lt;/code&gt; types allowing for serializing lists of different concrete element types. However, with this new attribute alone we still have a problem, the editor does not know how to serialize unset values because it obviously does not know what the concrete types we want.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffxk7zz7i4zazg7uitp6i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffxk7zz7i4zazg7uitp6i.png" alt=" " width="568" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Short of using &lt;code&gt;[ContextMenu]&lt;/code&gt; to add options to the "three dots" component menu to add specific instances of abilities to the list or set the InherentAbility field, Unity expects you to use the &lt;code&gt;[SerializeReference]&lt;/code&gt; attribute in conjunction with a custom editor script. Well, luckily for us someone has already implemented this for us so we don't need to do it ourselves. Simply grab the &lt;code&gt;[SubclassSelector]&lt;/code&gt; attribute from this github user's repo : &lt;a href="https://github.com/mackysoft/Unity-SerializeReferenceExtensions" rel="noopener noreferrer"&gt;https://github.com/mackysoft/Unity-SerializeReferenceExtensions&lt;/a&gt; (see url for installation steps).&lt;/p&gt;

&lt;p&gt;With the package installed, the &lt;code&gt;[SubclassSelector]&lt;/code&gt; attribute allows us to use the same code we wrote above and easily add support for a search menu to assign (and even swap out) the concrete instances for &lt;code&gt;[SerializeReference]&lt;/code&gt; fields directly in the inspector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SubclassSelector&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Ability&lt;/span&gt; &lt;span class="n"&gt;InherentAbility&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SubclassSelector&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Ability&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Abilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzz1jatnvg0no5abh3evg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzz1jatnvg0no5abh3evg.png" alt=" " width="568" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have successfully serialized our plain C# objects via interface/abstract class reference and we have a neat little tool to assign them in the inspector! The only thing we still cannot serialize via interface is structs but that's ok.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decoupling Communication (Events)
&lt;/h2&gt;

&lt;p&gt;Smart references solve the dependency problem when you strictly need to interact with a specific object (like a Turret shooting an &lt;code&gt;IDamageable&lt;/code&gt;). But what about events?&lt;/p&gt;

&lt;p&gt;When the player dies, the &lt;code&gt;UIManager&lt;/code&gt; needs to show the game over screen, the &lt;code&gt;AudioManager&lt;/code&gt; needs to play &lt;code&gt;sad_trombone.mp3&lt;/code&gt;, and the &lt;code&gt;AchievementManager&lt;/code&gt; needs to log a death. If your &lt;code&gt;PlayerHealth&lt;/code&gt; script holds references to all three of those systems, it's now coupled to all of them. The player shouldn't know or care that these other systems exist. &lt;/p&gt;

&lt;p&gt;This is where an Event Bus comes in. An Event Bus is a centralized hub for communication. Instead of objects talking directly to each other, they speak to the bus and the bus broadcasts those events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a Simple Generic Global Event Bus
&lt;/h3&gt;

&lt;p&gt;We can create a lightweight, type-safe Event Bus using C# delegates and a static dictionary. This allows any system to subscribe to a specific type of event without knowing who triggers it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// an empty interface so we can constrain what qualifies as event data&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEventData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;PlayerDamagedEventData&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEventData&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;DamageAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;CurrentHealth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we build the Event Bus itself. We'll use a static generic class. The compiler will generate a unique static instance for every distinct type of &lt;code&gt;EventBus&amp;lt;T&amp;gt;&lt;/code&gt;, meaning we don't need to manage a massive dictionary of types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventBus&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEventData&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnEventRaised&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;OnEventRaised&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;OnEventRaised&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Raise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;eventData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;OnEventRaised&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this architecture, the &lt;code&gt;PlayerHealth&lt;/code&gt; script just raises the event on the &lt;code&gt;EventBus&amp;lt;PlayerDamagedEventData&amp;gt;&lt;/code&gt; channel when it takes damage. Over in the other systems that need to wait for a damage event, you simply subscribe to the event. Each system only knows about the &lt;code&gt;EventBus&amp;lt;PlayerDamagedEventData&amp;gt;&lt;/code&gt; event, but they don't have any direct dependencies on each other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HealthUI&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnEnable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;EventBus&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerDamagedEventData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EventBus_PlayerDamagedEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnDisable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;EventBus&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerDamagedEventData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EventBus_PlayerDamagedEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;EventBus_PlayerDamagedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlayerDamagedEventData&lt;/span&gt; &lt;span class="n"&gt;eventData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Player took &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eventData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DamageAmount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; damage! Health is now &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eventData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentHealth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Instance Specific Event Problem
&lt;/h3&gt;

&lt;p&gt;This is a great solution for a event bus system that deals with &lt;strong&gt;global&lt;/strong&gt; events however, as you may have already surmised, it falls apart completely if you have 50 Goblins in a scene and the UI only needs to track the health of the specific one you currently have targeted. If you use a global &lt;code&gt;CharacterDamagedEvent&lt;/code&gt;, every &lt;code&gt;CharacterHealthBarUI&lt;/code&gt; element in the game will update when any goblin takes damage.&lt;/p&gt;

&lt;p&gt;As we can see, our event bus is better suited for global events. What we actually want is instance specific events. Fortunately, because of the work we did in the first half of the article, we have an incredibly clean way to handle this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDamageable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The event is now part of the contract&lt;/span&gt;
    &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;OnDeath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TakeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your concrete implementation (like &lt;code&gt;CharacterHealth&lt;/code&gt;) implements the event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CharacterHealth&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDamageable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;OnDeath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TakeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_health&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="n"&gt;OnDeath&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, over in the &lt;code&gt;CharacterHealthBarUI&lt;/code&gt; script, you use the &lt;code&gt;SerializableUnityRef&lt;/code&gt; we built earlier to inject the specific instance via the inspector, and subscribe directly to its interface event(s).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CharacterHealthBarUI&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Drag the CharacterHealth, BossHealth, CarHealth or a any other IDamageable here!&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;SerializableUnityRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDamageable&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_damageable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnEnable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_damageable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_damageable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnDamageTaken&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;UpdateFillBar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnDisable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_damageable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_damageable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnDamageTaken&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;UpdateFillBar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Damageable_OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;newHealth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// update health bar fill amount ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this is powerful:&lt;/strong&gt; The UI is listening to a specific instance, but it is still completely decoupled from the concrete class. It doesn't know exactly what it's referencing only that it's an IDamageable and adheres to that contract. &lt;/p&gt;

&lt;h3&gt;
  
  
  Instance Specific EventBus
&lt;/h3&gt;

&lt;p&gt;We know that things like &lt;code&gt;CharacterAnimator&lt;/code&gt; or &lt;code&gt;CharacterAudio&lt;/code&gt; need to subscribe to the &lt;code&gt;IDamageable&lt;/code&gt; instance &lt;code&gt;OnDamageTaken&lt;/code&gt; event (maybe to play a sound on damage or death). However, &lt;code&gt;CharacterAudio&lt;/code&gt; might also need to listen to &lt;code&gt;CharacterMovement&lt;/code&gt; events or other events specific to the character instance and in a complex gameobject hierarchy it might be annoying to hook up all the references in the inspector. You might find yourself saying "I need a centralized hub (a bus) that is specific to the gameobject instance so that I don't have to manage many different dependencies on a complex prefab hierarchy. I'll just use the event bus pattern I learned earlier but localize it to my gameobject instance." and implementing a solution like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CharacterEventBus&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RaiseDamageTaken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;OnDeath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RaiseDeath&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDeath&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// keep adding events here as they come up in development ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your character's components just require the CharacterEventBus reference and they just need to ask the CharacterEventBus to fire their events out into the void and whoever is listening will hear them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;The hidden problem with this approach&lt;/u&gt;:&lt;/strong&gt; While it may seem that we cleaned up our dependencies even further, all we did is move them all into a single class &lt;code&gt;CharacterEventBus&lt;/code&gt;. In my professional opinion, this is the equivalent of a teenager "cleaning" his room by shoving everything in the closet.&lt;/p&gt;

&lt;p&gt;For instance based events, using the interface design from earlier and letting the classes responsible for those events fire their own events is the better approach. It may be annoying to hook up all the dependencies on the gameobject/prefab but this is one time setup work and will save you time in the long run by increasing dependency visibility and allow you to easily see dependencies for a given object at a glance. &lt;/p&gt;

&lt;p&gt;Instead of the teenager shoving everything inside the closet, as he gets new stuff he will keep things logically together (video games and film near the TV, books near the bed or desk where they are read, shoes near the door, etc). &lt;/p&gt;

&lt;p&gt;A complex hierarchy with lots of component dependencies is like a room with a lot of stuff, it's not necessarily messy because there's lots of stuff everywhere, it's messy because of a lack of architecture and logic in where the stuff is placed (clothes on the floor, video games far from the TV, etc).&lt;/p&gt;

&lt;h2&gt;
  
  
  Serializing Events
&lt;/h2&gt;

&lt;p&gt;While C# Action delegates are incredibly fast and type-safe, they exist entirely in code. If a level designer wants a door to open when a boss dies, they would have to ask a programmer to write a custom script to subscribe to the boss's &lt;code&gt;OnDeath&lt;/code&gt; event. This slows down development. If only the designer could subscribe and tie the event to the specific Door's &lt;code&gt;Open()&lt;/code&gt; function without needing the developer to step in. Well luckily for us you can do that in Unity by using the &lt;code&gt;UnityEvent&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;Unlike standard C# events, UnityEvent can be serialized and displayed in the Inspector. This allows designers to drag and drop object references and select methods from a dropdown menu, wiring up complex interactions visually.&lt;/p&gt;

&lt;p&gt;Let's look at an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BigBadBoss&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;UnityEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_onDamageTaken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;UnityEvent&lt;/span&gt; &lt;span class="n"&gt;_onDeath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TakeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_onDamageTaken&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_health&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="n"&gt;_onDeath&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance you might think that this is great and that you can just change all your events to use &lt;code&gt;UnityEvent&lt;/code&gt; instead of &lt;code&gt;Action&lt;/code&gt;. &lt;strong&gt;Don't Do This!&lt;/strong&gt; While using &lt;code&gt;UnityEvent&lt;/code&gt; is incredibly powerful for letting designers and programmers to quickly hook up event logic without any code changes, they come with a few trade-offs that you should keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; &lt;code&gt;UnityEvent&lt;/code&gt; is slightly slower than a standard C# &lt;code&gt;Action&lt;/code&gt; because it relies on reflection under the hood. For events that fire every frame (like in the &lt;code&gt;Update()&lt;/code&gt; loop), you should stick to C# &lt;code&gt;Action&lt;/code&gt;. For infrequent events (like &lt;code&gt;OnDeath&lt;/code&gt;), &lt;code&gt;UnityEvent&lt;/code&gt; is perfectly fine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traceability:&lt;/strong&gt; If you use C# &lt;code&gt;Action&lt;/code&gt;, you can easily use "&lt;em&gt;Find All References&lt;/em&gt;" in your IDE to see exactly what is listening to that event. With &lt;code&gt;UnityEvent&lt;/code&gt;, the connections are hidden inside scene or prefab files, which reduces code visibility and can make debugging a little harder if you overuse them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a separate note, you might have noticed that using &lt;code&gt;UnityEvent&lt;/code&gt; means that you would have to replace the C# &lt;code&gt;Action&lt;/code&gt; members in your interfaces. This is not true, instead you should be using &lt;code&gt;UnityAction&lt;/code&gt; instead of C# &lt;code&gt;Action&lt;/code&gt;. Under the hood, &lt;code&gt;UnityAction&lt;/code&gt; is just a thin wrapper or alias for a standard C# delegate, meaning they compile down to the same underlying code and perform identically during invocation.&lt;/p&gt;

&lt;p&gt;What if you want to use &lt;code&gt;UnityAction&lt;/code&gt; for code subscriptions and still leverage &lt;code&gt;UnityEvent&lt;/code&gt; for serializing the same events. You can do this using the following pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDamageable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;UnityAction&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;UnityAction&lt;/span&gt; &lt;span class="n"&gt;OnDeath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TakeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CharacterHealth&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDamageable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;UnityAction&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// we subscribe using the UnityAction which adds it to the UnityEvent and we always Invoke the UnityEvent.&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;UnityEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IInteractor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_onDeath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;UnityAction&lt;/span&gt; &lt;span class="n"&gt;OnDeath&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;add&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_onDeath&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;AddListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;remove&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_onDeath&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;RemoveListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TakeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;OnDamageTaken&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_health&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_health&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="n"&gt;_onDeath&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way we have the best of both worlds. We keep the &lt;code&gt;event UnityAction OnDeath&lt;/code&gt; which follows our contract and we can subscribe through code. We also get the ability to add serialized event callbacks to the same event in the inspector!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rule:&lt;/strong&gt; Use &lt;code&gt;UnityAction&lt;/code&gt; (not &lt;code&gt;Action&lt;/code&gt;) for when systems need to talk to each other through code. Use &lt;code&gt;UnityEvent&lt;/code&gt; for exposing design-specific logic to your design team and avoid using it for event callbacks that are called from performance critical code paths (like &lt;code&gt;Update()&lt;/code&gt;). Use both if you need both but avoid using serialized &lt;code&gt;UnityEvent&lt;/code&gt; for performance critical code paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By using interfaces for dependencies, we have went from tightly coupled architecture to a more loosely coupled one. We can listen to events fired from concrete implementations without needing a reference to each concrete type and what's even better is that we can do this and also take advantage of Unity's built in inspector serialization for dependency injection.&lt;/p&gt;

&lt;p&gt;This strict isolation is the exact prerequisite needed to break your game down into independent modules. In the next post, we will discuss Assembly Definitions (asmdefs) and how you can "break off" a part of your game and reuse it as it's own separate module.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>architecture</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Unity Foundational Architecture: Managing Global State</title>
      <dc:creator>Daniel Rinaldi (TenaciousDan)</dc:creator>
      <pubDate>Thu, 23 Jul 2026 03:28:01 +0000</pubDate>
      <link>https://dev.to/tenaciousdan/unity-foundational-architecture-managing-global-state-1mlp</link>
      <guid>https://dev.to/tenaciousdan/unity-foundational-architecture-managing-global-state-1mlp</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;Table of Contents&lt;/em&gt;:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Constants&lt;/li&gt;
&lt;li&gt;Singletons &amp;amp; Services&lt;/li&gt;
&lt;li&gt;Singleton&lt;/li&gt;
&lt;li&gt;Service Locator&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Every Unity developer eventually hits the exact same wall: &lt;em&gt;how do I get my UI script to talk to my Game Manager without turning my codebase into a tangled web of dependencies?&lt;/em&gt; Managing global state is a fundamental challenge in game architecture, and the internet is full of conflicting, often dogmatic advice on how to handle it.&lt;/p&gt;

&lt;p&gt;In this article, we are going to look at some popular approaches to managing global state: Static Constants, Singletons and Service Locator.&lt;/p&gt;

&lt;p&gt;Before we start though, I encourage you to read some of my previous blog posts in this series on &lt;a href="https://dev.to/tenaciousdan/unity-foundational-architecture-project-scaffolding-4gni"&gt;project scaffolding&lt;/a&gt; or, even more crucial to some sections in this article, the &lt;a href="https://dev.to/tenaciousdan/unity-foundational-architecture-smart-bootstrapping-52pf"&gt;bootstrapping process&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Constants
&lt;/h2&gt;

&lt;p&gt;Not every piece of global data needs a instance to live in, interfaces, or an initialization phase. Some data is constant and never changes at runtime (constants). These constants are usually defined with &lt;code&gt;static readonly&lt;/code&gt; or &lt;code&gt;const&lt;/code&gt; (at least they should be if they never change). &lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MathConstants&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;MilesToKm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1.60934f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CharacterAnimationController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// we must use static readonly instead of const here because we need to generate the SpeedHash from the string literal.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;SpeedHash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Animator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StringToHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Speed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Constants are also tied to the type they are defined in. So to keep things neat, you should only define constants where appropriate. Meaning if you have a constant for the max networked inventory slot capacity, this shouldn't be defined in a class called &lt;code&gt;NetworkedConstants&lt;/code&gt; and instead should reside inside the class that represents the &lt;code&gt;NetworkedInventory&lt;/code&gt; which needs this data. In fact, if this is the only class that needs this data, you can even make it private although it's perfectly fine to make it public as well if there's a need for it in other scripts as long as you are not just creating dependencies just for accessing the constant field.&lt;/p&gt;

&lt;p&gt;Constants are also tied to the type they are defined in. So to keep things neat, you should only define constants where appropriate. Meaning if you have a constant for the max networked inventory slot capacity, this shouldn't be defined in a class called &lt;code&gt;NetworkedConstants&lt;/code&gt; and instead should reside inside the class that represents the &lt;code&gt;NetworkedInventory&lt;/code&gt; and needs this data. In fact, if this is the only class that needs this data, you can even make it private although it's perfectly fine to make it public as well if there's a need for it in other scripts.&lt;/p&gt;

&lt;p&gt;Magic strings are an architectural vulnerability. If someone changes the setting name from &lt;code&gt;AudioSettings.SFXVolume&lt;/code&gt; to &lt;code&gt;AudioSettings.SFXVolum&lt;/code&gt; (missing the &lt;code&gt;e&lt;/code&gt;), the compiler will not warn you. If the programmer accidentally mistypes the string, the compiler will not warn you. Your code will compile perfectly, but the SetFloat call will silently fail to update the correct data at runtime, breaking your game's logic.&lt;/p&gt;

&lt;p&gt;By transforming our magic strings into named constants we get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No Typos: It prevents bugs caused by misspelling a hardcoded string in multiple files. &lt;/li&gt;
&lt;li&gt;Code Completion: IDE auto-complete works perfectly with named constants.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Singletons &amp;amp; Services
&lt;/h2&gt;

&lt;p&gt;Every Unity developer eventually hits the "Singleton Wall." It usually starts innocently enough, you usually need a single instance of some sort of "manager"(like a GameManager or AudioManager). The general common rules for these managers are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only ONE instance should ever be created at any given time (a single source of truth)&lt;/li&gt;
&lt;li&gt;Accessible from anywhere&lt;/li&gt;
&lt;li&gt;Needs to be initialized as the game starts and when hitting play in the editor before your gameplay scripts run and try to access it&lt;/li&gt;
&lt;li&gt;May or may not need to survive scene loads
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start by addressing the elephant in the room: the Singleton. It has become a bit of a trend in software engineering circles to label the Singleton as an "anti-pattern." It is true that they have downsides. Specifically, they make mocking dependencies much harder, which can be a headache for unit testing.&lt;/p&gt;

&lt;p&gt;But let's be realistic. For many developers, and especially for indie devs, the Singleton is incredibly valuable. It is easy to pick up, exceptionally fast to implement, and cleanly solves the immediate problem of global access. The singleton pattern is very useful and can sometimes also be seen in other patterns like Factory or even the Service Locator itself (part of building a service locator is making a single singleton: the "locator"). It is a widely used pattern for a reason. You should always choose your solutions based on your project's needs and its long term health.   &lt;/p&gt;

&lt;p&gt;In any case, we will go over how to build and implement both solutions, cut down on boiler plate code using generics, discuss how to make use of Unity's SerializeField for dependency injection, and what a Singleton lacks that would make you choose a ServiceLocator instead.&lt;/p&gt;

&lt;p&gt;Now, there is some truth behind the rumors though as the service locator pattern does offer more utility and flexibility. However, some indie developers are content with the good old singleton pattern for their projects. This is totally fine, you should choose your solution based on your project's needs and its long term health. The Singleton pattern also requires less architecture &amp;amp; planning which can be beneficial for small teams, game jam projects, or quick prototypes. In any case, we will cover how to build and implement both solutions, cut down on boiler plate code using generics, and what a Singleton lacks that you'd want a ServiceLocator for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Singleton
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem &amp;amp; Intent
&lt;/h3&gt;

&lt;p&gt;We all know the problem, we want a single global instance (source of truth) for a given type. The singleton pattern is a creational design pattern that ensures a class has only one instance, while also providing a static global access point to that instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;All implementations of the Singleton pattern have these two things in common. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make the default constructor private, to prevent other objects from using the new operator with the Singleton class. This gets a bit more complex when it comes to MonoBehaviours as we will have to check the instance count.&lt;/li&gt;
&lt;li&gt;Create a static creation method that acts as a constructor. Under the hood, this method calls the private constructor to create an object and stores it in a static field. All following calls to this method return the cached object.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What the Singleton Pattern Lacks
&lt;/h3&gt;

&lt;p&gt;The core issue is the lack of inheritence and lose coupling. It is the main reason behind the following downsides of the pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It may be difficult to unit test the client code of the Singleton pattern because many test frameworks rely on inheritance when producing mock objects. Since the constructor of the singleton class is private and more importantly you access the instance statically by type name, you will need to think of a creative way to mock the singleton. If you need robust unit testing and the ability to mock instances, you should opt for the Service Locator pattern. &lt;/li&gt;
&lt;li&gt;One other thing to note is the tight coupling to the Singleton's concrete type. In a large scale application this becomes much much more important. If you really need the lose coupling, you should opt for the Service Locator pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Misuse
&lt;/h3&gt;

&lt;p&gt;Developers frequently use singletons to bypass passing parameters, turning them into a global access point for arbitrary data and unnecessarily polluting the global scope. My main advice with Singletons is just try not to overuse them and first see if you can architect a solution by creating the least possible amount of Singletons. Luckily, I feel like most experienced devs will quickly learn this and consider this when working with Singletons ... I hope :).  &lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Let's start with the simplest implementation. We will create a singleton out of a plain old C# object (POCO).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameManager&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;_mutex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="n"&gt;GameManager&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ensure we are the only ones creating a new object&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;GameManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;// initialize the GameManager here...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Static global point of access&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;GameManager&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetOrCreate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;GameManager&lt;/span&gt; &lt;span class="nf"&gt;GetOrCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// double-checked locking pattern (thread lock)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;lock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_mutex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GameManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// other non static methods containing business logic here...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A client can call business logic from anywhere using the Singleton's type...&lt;/span&gt;
&lt;span class="n"&gt;GameManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty simple right? Sure, but rewriting this logic for every Singleton is annoying, let's try to mitigate the writing of boiler plate code by making our solution generic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;_mutex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ConstructorInfo&lt;/span&gt; &lt;span class="n"&gt;_cachedConstructor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// The static constructor runs exactly once per unique 'T'&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cachedConstructor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;GetConstructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BindingFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmptyTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="k"&gt;null&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_cachedConstructor&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;$"To enforce singleton behavior, &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; must have a private or protected parameterless constructor."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Static global point of access&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetOrCreate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;GetOrCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// double-checked locking pattern (thread lock)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;lock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_mutex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;_cachedConstructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Reduced boilerplate...&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GameManager&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Private constructor prevents client from calling 'new GameManager()'&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;GameManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// other non static methods containing business logic here...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Client usage...&lt;/span&gt;
&lt;span class="n"&gt;GameManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've completed the POCO singleton, let's take a look at a MonoBehaviour singleton implementation. Why? Dependency injection using serialized fields, that's why.&lt;/p&gt;

&lt;p&gt;This is the typical MonoBehaviour singleton you'd find by doing a quick google search.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;GameManager&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Awake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Destroy duplicate instances&lt;/span&gt;
            &lt;span class="nf"&gt;Destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Optional: Keep this object alive across scene changes&lt;/span&gt;
        &lt;span class="nf"&gt;DontDestroyOnLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this works for quick &amp;amp; simple prototypes and throw away code, it quickly falls apart in production. Putting this into a real-world project is like driving a car without insurance, it works fine until you hit an engine edge case.  &lt;/p&gt;

&lt;p&gt;Here are some of the problems you can encounter with this naive approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Execution Order Race Condition&lt;/strong&gt; : Unity runs script &lt;code&gt;Awake()&lt;/code&gt; cycles in an unpredictable, random order. If a normal gameplay script runs its &lt;code&gt;Awake()&lt;/code&gt; before your GameManager executes its own, calling &lt;code&gt;GameManager.Instance&lt;/code&gt; will return &lt;code&gt;null&lt;/code&gt; and could crash your game on frame one.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt; : By explicitly locking the base class into &lt;code&gt;[DefaultExecutionOrder(-50)]&lt;/code&gt;, we guarantee that every singleton in the project initializes before any standard script (which defaults to &lt;code&gt;0&lt;/code&gt;) even wakes up. In an edge case, you can always set a lower value for DefaultExecutionOrder on concrete classes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessing the Instance After its Been Cleaned Up&lt;/strong&gt; : When you exit a Unity game, the engine destroys GameObjects in a random order. If a regular script tries to log data or clean up references inside its &lt;code&gt;OnDestroy()&lt;/code&gt; or &lt;code&gt;OnDisable()&lt;/code&gt; methods, it might call the Singleton instance again after Unity has already deleted it. If you are supporting lazy instantiation, this triggers a ghost lazy-reinstantiation loop, spawning leaky objects in a dying scene and causing potential errors or crashes in the application cleanup step.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt; : We manage a static &lt;code&gt;HasQuit&lt;/code&gt; flag. In &lt;code&gt;OnApplicationQuit()&lt;/code&gt;, we set this to true. If any script tries to access the singleton during the shutdown sequence, the Instance property returns &lt;code&gt;null&lt;/code&gt; instead of spawning a zombie instance. This can lead to null refs in cleanup methods like &lt;code&gt;OnDisable()&lt;/code&gt; and &lt;code&gt;OnDestroy()&lt;/code&gt; so you should simply check the flag before using the Instance property in these cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editor - Fast Enter Play Mode Initialization Bug&lt;/strong&gt; : To speed up compile times, modern Unity projects heavily rely on disabling Domain Reload under Enter Play Mode Settings. However, skipping the domain reload means static fields are never wiped when you press Stop. The next time you press Play, &lt;code&gt;Instance&lt;/code&gt; still points to the dead object from your previous session, causing your fresh script to immediately destroy itself.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt; : We use a C# static constructor and hook into &lt;code&gt;EditorApplication.playModeStateChanged&lt;/code&gt; callback. This automatically intercepts the in-editor play state switch and resets &lt;code&gt;HasQuit&lt;/code&gt; right before the runtime boots up, ensuring compatibility with Fast Enter Play Mode in Editor.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of Architectural Control - Eager vs. Lazy&lt;/strong&gt; : Our simple, naive &lt;code&gt;GameManager&lt;/code&gt; monobehaviour singleton forces us to use the Eager loading paradigm. Meaning they must be manually dragged into every scene. This is usually fine for most Unity devs. In some rare cases though, you may want Lazy Loading meaning that the singleton's gameobject will be spawned in whenever the Instance is accessed.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt; : We create a custom &lt;code&gt;MBSingletonOptionsAttribute&lt;/code&gt; that allows us to configure which instantiation paradigm a singleton should use. So either lazily spawn an empty container and build the gameobject instance ourselves through code, or let Unity automatically instantiate a fully pre-configured gameobject that exists in the scene and fail (returning null) otherwise. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate Code&lt;/strong&gt; : As usual, there is still the issue of boilerplate code that we don't want to rewrite for each concrete type.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt; : We do the same thing that we did for the POCO singleton and use generics to create a generic base class that handles all the generic singleton logic.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alright let's start implementing a solution that solves our problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// other using statements...&lt;/span&gt;

&lt;span class="cp"&gt;#if UNITY_EDITOR
&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEditor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;
&lt;span class="c1"&gt;// singletons should execute before "normal" scripts which have DefaultExecutionOrder = 0&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DefaultExecutionOrder&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MBSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Tooltip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Destroy this object when a new scene is loaded."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;_destroyOnLoad&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#if UNITY_EDITOR
&lt;/span&gt;    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;MBSingleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playModeStateChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;OnPlayModeStateChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playModeStateChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnPlayModeStateChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnPlayModeStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlayModeStateChange&lt;/span&gt; &lt;span class="n"&gt;playModeState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playModeState&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;PlayModeStateChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitingEditMode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;HasQuit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;MBSingleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsInitialized&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// True if application is quitting. When Unity quits it destroys game objects in a random order and this can create issues when working with singletons. &amp;lt;br /&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// As such, you should check "if (!YourSingleton.HasQuit)" before using your singleton in cleanup methods like OnDisable, OnDestroy, etc...&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;HasQuit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetOrCreate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;GetOrCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameObject&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HasQuit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// no need for double checked locking pattern as MonoBehaviours are only created on the main tread&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameObject&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// we'll implement this soon, keep reading...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Override this method to ensure that you run Initialization logic for your singleton only once (this will not be invoked on accidental duplicates before they are destroyed).&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Awake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// we must check backing field (_instance) here because if null we want to call GetOrCreate() and pass it this gameObject&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;GetOrCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// an instance already exists and this is a duplicate, destroy it&lt;/span&gt;
            &lt;span class="nf"&gt;Destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;DestroyOnLoad&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_destroyOnLoad&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnDestroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnApplicationQuit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// When Unity quits it destroys game objects in a random order and this can create issues for monobehaviour singletons. &lt;/span&gt;
        &lt;span class="c1"&gt;// So we prevent possible lazy re-instatiation and access to the instance when the application quits to prevent problems.&lt;/span&gt;
        &lt;span class="n"&gt;HasQuit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;DestroyOnLoad&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_destroyOnLoad&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;set&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_destroyOnLoad&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_destroyOnLoad&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;Scene&lt;/span&gt; &lt;span class="n"&gt;activeScene&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetActiveScene&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;activeScene&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MoveGameObjectToScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activeScene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nf"&gt;DontDestroyOnLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this gives us is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A MonoBehaviour that we can attach to a GameObject and a variable to control whether or not we want this singleton to live and die with the scene or whether we want it to survive scene load/unloads and exist in the persistent DontDestroyOnLoad scene. We also give the option to switch at runtime.&lt;/li&gt;
&lt;li&gt;Static memory management and cleanup in editor for fast enter play mode.&lt;/li&gt;
&lt;li&gt;A default DefaultExecutionOrder of &lt;code&gt;-50&lt;/code&gt; to allow us to execute before most normal scripts.&lt;/li&gt;
&lt;li&gt;A virtual &lt;code&gt;Initialize()&lt;/code&gt; method that can be used in place of &lt;code&gt;Awake()&lt;/code&gt; for initialization. Why? Because when a new gameobject is spawned the Awake method is called even if it's a duplicate and our code will destroy it. We haven't implement the logic that calls it yet but the idea is to have this method for initialization on the singular "true" instance.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Create(GameObject)&lt;/code&gt; method that we can either pass in the GameObject (for eager loading) or &lt;code&gt;null&lt;/code&gt; (for lazy loading). To be honest, I mostly exclusively use Eager loading when it comes to monobehaviour singletons, mostly for the easy setup and dependency injection (serialized fields). However, it doesn't hurt to support both paradigms here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's implement the heart of our MBSingleton script, the &lt;code&gt;Create(GameObject)&lt;/code&gt; function as well as a &lt;code&gt;LazyFallbackInstantiation(GameObject)&lt;/code&gt; function to allow us to Lazily construct our MBSingleton gameobject and configure any parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameObject&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;MBSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;).&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;existingInstances&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FindObjectsByType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existingInstances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;existingInstances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;existingInstances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;DestroyImmediate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existingInstances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;typeofT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// handle how the user wants this type to be loaded ...&lt;/span&gt;
    &lt;span class="n"&gt;MBSingletonOptionsAttribute&lt;/span&gt; &lt;span class="n"&gt;typeOptionsAttr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCustomAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MBSingletonOptionsAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// eager loading (default) : the gameobject instance will be created when it's gameobject is loaded into memory by Unity&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeOptionsAttr&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;typeOptionsAttr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loadType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EInstanceLoadingType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// lazy loading : create the gameobject instance for the user if it doesn't already exist (instance will always create a new GameObject)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeOptionsAttr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loadType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EInstanceLoadingType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// create a new GameObject that will house the singleton instance (lazy instantiation) ...&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;goNamePostFix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;" (Lazy)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// if it's a prefab reference, then instantiate the prefab&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// need to set instance before Instantiate() call otherwise we'll have an infinitely recursive situation in Awake()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;gameObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;goNamePostFix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;MBSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;).&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// create an empty gameobject and attach this component.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;gameObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;goNamePostFix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;singletonComponent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="n"&gt;_instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;singletonComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;MBSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mbSingleton&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;singletonComponent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;MBSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
            &lt;span class="n"&gt;mbSingleton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LazyFallbackInstantiation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;mbSingleton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Override this method if you need to lazily construct the singleton from scratch.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;param name="gameObjectRoot"&amp;gt;The instantiated root gameobject.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LazyFallbackInstantiation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameObject&lt;/span&gt; &lt;span class="n"&gt;gameObjectRoot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// to be overriden by subclasses for lazy loading...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this we have a very robust and solid MonoBehaviour Singleton solution that can be used in any project. The best place to eagerly load your monobehaviour singletons are in the bootstrap scene or wherever you do your bootstrapping logic. You can also place MBSingletons in specific scenes so that they are tied to that scene, just remember to set the &lt;code&gt;DestroyOnLoad&lt;/code&gt; variable to true so that it will not be persisted in the DontDestroyOnLoad scene.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Service Locator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem &amp;amp; Intent
&lt;/h3&gt;

&lt;p&gt;The problem: We want global access to objects (services) and we want lose coupling by making everyone depend on a single global instance to fetch objects by interface/abstract type insetad of concrete type. The Service Locator pattern is an architectual pattern that solves these problems by providing a global point of access to an object instance (service) without tightly coupling users to the concrete class that implements it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;All implementations of the Service Locator pattern have these things in common. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A static global instance (Singleton) that acts as the middleman and a registry for our services.&lt;/li&gt;
&lt;li&gt;A collection of service instances that is managed by our locator and a method to retrieve an instance of a service.&lt;/li&gt;
&lt;li&gt;An interface that acts as a contract for our services to ensure they all implement some common functionality that our locator expects of them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Difference from Singleton
&lt;/h3&gt;

&lt;p&gt;If we look at the Singleton, a script is permanently glued to (dependant on) a specific concrete class. If your &lt;code&gt;PlayerHealth&lt;/code&gt; script calls &lt;code&gt;AudioManager.Instance.Play("Ouch")&lt;/code&gt;, it is hardcoded to depend on that exact &lt;code&gt;AudioManager&lt;/code&gt; type. This makes unit testing more difficult because you can't easily swap the real audio manager for a dummy one without changing all the calls in the codebase with your new type.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Common Misuse
&lt;/h3&gt;

&lt;p&gt;The Service Locator can index the services by their concrete type, however this is a common misuse of the pattern and will end up degrading your solution into a more complex singleton pattern which defeats our initial intent. &lt;u&gt;DO NOT&lt;/u&gt; use Service Locator if you plan to do this as you are just adding more complexity to your project for no reason (instead just use Singleton). If you are using the Service Locator and creating services then you should also create an interface for each service you create in which you will almost explicitly use to talk to the service. You will be programming to an interface instead of a concrete type which means your dependencies are now losely coupled.   &lt;/p&gt;

&lt;h3&gt;
  
  
  The Downside
&lt;/h3&gt;

&lt;p&gt;While it sounds perfect, the Service Locator has one major downside and ironically it is a result of its greatest strength: hidden dependencies. When you look at a script that uses a Singleton, you immediately see the dependency (GameManager.Instance). With a Service Locator, a script might compile perfectly fine but throw a null reference exception at runtime and, at a glance, it may not be obvious which instance of the service is being used making it difficult to trace. However, if Singletons are causing friction in your project then this downside is an acceptable alternative and with the debugging tools Unity and IDEs offer developers it is often very manageable.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Let's start by creating a &lt;code&gt;ServiceLocator&lt;/code&gt; class using our Generic &lt;code&gt;Singleton&amp;lt;T&amp;gt;&lt;/code&gt; implementation from earlier. While some implementations use a static class, we are going to just make it a singleton to save some time. If you do go the static class route, you have to make the class and every member/function static and you would have to make sure to handle cleanup for when the domain is not reloaded in editor and you re-enter play mode (Fast Enter Playmode).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceLocator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServiceLocator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_services&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;typeofT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceLocator&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;] Service of type `&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;` has already been registered!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Unregister&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;typeofT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceLocator&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;] Service of type `&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;typeofT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;` has not been registered!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Called the moment the service is successfully added to the Locator&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnRegister&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Called right before the service is removed from the Locator&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnBeforeUnregister&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Called after the service is removed from the Locator&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnUnregister&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will have to register all our services somewhere in our game's code. Again, the best place for this would be where we do our bootstrapping logic. You can make use of Unity's &lt;code&gt;[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]&lt;/code&gt; attribute for pure C# services. If we want to make use of dependency injection through the use of Unity's &lt;code&gt;SerializeField&lt;/code&gt; attribute then we need to do this in &lt;code&gt;Awake()&lt;/code&gt;. Let's make a script that allows us to register services on startup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// We want services to be registered before the Awake() of normal scripts fire.&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DefaultExecutionOrder&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceBootstrapper&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Services"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;GameManager&lt;/span&gt; &lt;span class="n"&gt;_gameManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;AudioManager&lt;/span&gt; &lt;span class="n"&gt;_audioManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Awake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// register our services here ...&lt;/span&gt;

        &lt;span class="n"&gt;YourService&lt;/span&gt; &lt;span class="n"&gt;yourService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IYourService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;yourService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;ServiceLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IGameService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;_gameManager&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ServiceLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAudioService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;_audioManager&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Now instead of this (tight coupling!):&lt;/span&gt;
&lt;span class="n"&gt;GameManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// We now do this (lose coupling):&lt;/span&gt;
&lt;span class="n"&gt;ServiceLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IGameService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we now have is a proper ServiceLocator implementation with the following traits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A static global instance that will hold a reference to a dictionary of services by type.&lt;/li&gt;
&lt;li&gt;A way to add to our collection of services using our &lt;code&gt;Register&amp;lt;T&amp;gt;(T service)&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;A way to remove from our collection of services using our &lt;code&gt;Unregister&amp;lt;T&amp;gt;()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;A way to fetch from our collection of services using our &lt;code&gt;Get&amp;lt;T&amp;gt;()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;We have a class that handles the bootstrapping process of registering our services and gives us control over the registration order as well.&lt;/li&gt;
&lt;li&gt;A base interface for all our services so that they can all abide to a common contract. Currently, the only methods that we enforce is two callback methods &lt;code&gt;OnRegister()&lt;/code&gt;, &lt;code&gt;OnBeforeUnregister()&lt;/code&gt; and &lt;code&gt;OnUnregister()&lt;/code&gt; to give our services some hooks into the service registration process.&lt;/li&gt;
&lt;li&gt;When creating a service, we can define a unique interface to use as our contract for our service (provided it inherits from the &lt;code&gt;IService&lt;/code&gt; interface) and then we can swap out our services at any time as long as the new instance implements the same interface. This is very useful for unit testing where we'd want to mock a service and alter its functionality for testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's neat about this pattern is that you can easily convert anything into a service, even the Singletons we made earlier. You simply have to make an interface for the singleton to inherit from and then register it with the locator.&lt;/p&gt;

&lt;p&gt;That's pretty much it. There's a bit more setup involved than what we had with singletons (we need to create the interface and the concrete type and then we need to add it to our bootstrapper to register it), but on larger scale projects with bigger teams that require you to do a lot of mocking/unit testing this pattern can save you some headaches down the line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Managing global state doesn't have to mean turning your Unity project into a tangled web of dependencies. The key is choosing the right architectural tool for the specific problem at hand rather than rigidly adhering to a single doctrine.&lt;/p&gt;

&lt;p&gt;When data never changes at runtime, Constants keep your codebase clean, eliminate typo-induced bugs from magic strings, and provide full IDE autocompletion without any instantiation overhead.&lt;/p&gt;

&lt;p&gt;For dynamic global systems, the choice between a Singleton and a Service Locator ultimately comes down to the scale and needs of your game. Despite the criticism it sometimes receives in software engineering circles, the Singleton pattern remains incredibly valuable—especially for indie developers. It is quick to implement, easy to pick up, and solves the immediate need for global access efficiently without over-engineering your prototype.&lt;/p&gt;

&lt;p&gt;However, as a project grows in complexity, the Singleton's limitations become more apparent. If your architecture demands loose coupling, interface-driven design, and robust unit testing with mock objects, the Service Locator provides the flexibility your codebase needs to scale healthily.&lt;/p&gt;

&lt;p&gt;There is no one-size-fits-all answer. The only true mistake is treating global access as a lazy shortcut to avoid passing parameters. By evaluating the long-term health of your project and balancing the need for testability against the realities of rapid development, you can build a foundation that empowers you to focus on what actually matters: shipping your game.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>architecture</category>
      <category>csharp</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Unity Foundational Architecture: Smart Bootstrapping</title>
      <dc:creator>Daniel Rinaldi (TenaciousDan)</dc:creator>
      <pubDate>Tue, 07 Jul 2026 17:34:15 +0000</pubDate>
      <link>https://dev.to/tenaciousdan/unity-foundational-architecture-smart-bootstrapping-52pf</link>
      <guid>https://dev.to/tenaciousdan/unity-foundational-architecture-smart-bootstrapping-52pf</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;Table of Contents&lt;/em&gt;:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Understanding the Unity Initialization Lifecycle&lt;/li&gt;
&lt;li&gt;The RuntimeInitializeOnLoadMethod Attribute (And Its Limitations)&lt;/li&gt;
&lt;li&gt;The Solution: The Bootstrap Scene&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Let’s face it: out-of-the-box Unity is a fantastic engine, and it is built to cater to everyone. However, because of this it has a very basic out of the box workflow (press play to load the current scene). Any kind of logic that is related to bootstrapping (game setup, system initialization, etc.) or any specific control flow for your game is left in the hands of the developer and setting this up for every project you start can be a momentum killer.&lt;/p&gt;

&lt;p&gt;A game's foundation shouldn't rely on you starting from a specific scene in the Editor and clicking through menus or selecting a scene, nor should it rely on you manually dragging a massive GameManager prefab into every single scene in your project and then dealing with timing issues so that your bootstrapping systems initialize first. Today, I am laying down a workflow and talking about the necessary scripts needed so that you can hit play from any scene and have your bootstrapping and game initialization logic execute flawlessly every single time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Unity Initialization Lifecycle
&lt;/h2&gt;

&lt;p&gt;To build our own bootstrapping logic, we first have to understand exactly what happens when you press Play in the editor. Normally, Unity follows a strict execution order that is tied directly to the currently loaded scene(s) in the editor. This is how it goes after you hit the play button: &lt;/p&gt;

&lt;p&gt;The scene(s) are loaded into memory. This means that every GameObject and MonoBehaviour in the currently loaded scenes are created on the heap as well as any assets that are referenced from the scene objects. Awake() is called on every active MonoBehaviour in the scene(s). Then OnEnable() is called. Then Start() is called.&lt;/p&gt;

&lt;p&gt;If any of your gameplay logic relies on a GameManager, AudioManager, etc to be present, and those managers are initializing inside their own Awake() loops, you run into a classic race condition. Which script runs its Awake() first? If a gameplay script wins the race, it will try to access a manager that hasn't finished setting up yet, resulting in possible null reference exceptions or other bugs. &lt;/p&gt;

&lt;h2&gt;
  
  
  The RuntimeInitializeOnLoadMethod Attribute (And Its Limitations)
&lt;/h2&gt;

&lt;p&gt;So, how do we make it so that our bootstrapping logic always execute first before any of our gameplay logic runs? Well, you may have heard of Unity's &lt;code&gt;[RuntimeInitializeOnLoadMethod]&lt;/code&gt; attribute that allows us to "cut in line". Before any scene asset is loaded, before any GameObject initializes, and before any Awake() method is fired anywhere in your project, our bootstrapping code runs. This guarantees our core infrastructure is awake and waiting before the rest of the game even knows it exists. &lt;/p&gt;

&lt;p&gt;However, there are some restrictions to this approach. The target method for this attribute must be static, parameter-less, and inside a non-generic class. This means that if we want to inject some values into these bootstrap classes we are going to have issues. What we really want is the ability to serialize fields and use those values like we do with MonoBehaviours in our scenes but we also want our logic to always run first before any other gameplay logic. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: The Bootstrap Scene
&lt;/h2&gt;

&lt;p&gt;What we really want is the best of both worlds: the ability to use the Unity Inspector to configure our core systems, combined with a guarantee that this logic always runs first.&lt;/p&gt;

&lt;p&gt;The solution is simple: We create a dedicated "Bootstrap" scene that always loads first, which then instantly loads the actual scene we want to play. The only remaining hurdle is making this seamless in the Editor. We don't want to manually open the Bootstrap scene every time we want to test a tweak in Level 3. Luckily for us, Unity allows us to override the functionality of the Editor's play button so that we can force this workflow to behave flawlessly. (As for built games, we don't need to do anything extra other than set our Bootstrap scene as index 0 in the Build Settings).&lt;/p&gt;

&lt;p&gt;Let's take a look at how to set this up...&lt;/p&gt;

&lt;p&gt;First, let's create the Bootstrap scene. We'll create a regular scene asset and call it "Bootstrap". Next we will add this scene to the list of scenes in the build settings and set it to index 0 so that it is the first scene that your game loads when you launch the built executable.&lt;/p&gt;

&lt;p&gt;Ok, now that that's done, we need to override the play button to make the editor workflow smooth. We'll start by creating a static class called &lt;code&gt;EditorBootstrapScene.cs&lt;/code&gt; and placing the script under an &lt;code&gt;Editor&lt;/code&gt; folder (the folder name is important as we don't want to compile this script in a runtime build).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEditor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEditor.SceneManagement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine.SceneManagement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;TenaciousEditor.SceneManagement&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// A Bootstrap scene is a scene that is used for loading/bootstrapping systems on startup. &amp;lt;br /&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// The purpose of this class is to override the functionality of the play button in the editor&lt;/span&gt;
    &lt;span class="c1"&gt;/// to make sure that the Bootstrap scene is additively loaded before running the game in the Editor. &amp;lt;br /&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// This is convenient so that you don't need to always search for and additively load the bootstrap scene&lt;/span&gt;
    &lt;span class="c1"&gt;/// into the hierarchy when you open a new scene in the Editor.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;InitializeOnLoad&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EditorBootstrapScene&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MENU_ITEM_BOOTSTRAP_SCENE_FOLDER&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Tenacious/SceneManagement/Editor Bootstrap Scene/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MENU_ITEM_LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MENU_ITEM_BOOTSTRAP_SCENE_FOLDER&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"Load Bootstrap Scene On Play"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MENU_ITEM_DONT_LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MENU_ITEM_BOOTSTRAP_SCENE_FOLDER&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"Don't Load Bootstrap Scene On Play"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MENU_ITEM_OPEN_BOOTSTRAP_SCENE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MENU_ITEM_BOOTSTRAP_SCENE_FOLDER&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"Open Bootstrap Scene"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;EditorBootstrapScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;RegisterCallbacks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RegisterCallbacks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playModeStateChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;OnPlayModeChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playModeStateChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnPlayModeChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sceneLoaded&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;OnSceneLoaded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sceneLoaded&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnSceneLoaded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UnregisterCallbacks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playModeStateChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;OnPlayModeChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sceneLoaded&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;OnSceneLoaded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MENU_ITEM_LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;EnableLoadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EditorUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenFilePanel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Select Bootstrap Scene"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"unity"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Assets"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;BootstrapScenePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MENU_ITEM_DONT_LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;EnableDontLoadBootstrapSceneOnPlayMenuItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MENU_ITEM_DONT_LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DisableLoadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MENU_ITEM_OPEN_BOOTSTRAP_SCENE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;EnableOpenBootstrapScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MENU_ITEM_OPEN_BOOTSTRAP_SCENE&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OpenBootstrapScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;EditorSceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OpenSceneMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Additive&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EditorBootstrapScenePrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;set&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;EditorBootstrapScenePrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loadBootstrapSceneOnPlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;RegisterCallbacks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="nf"&gt;UnregisterCallbacks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;BootstrapScenePath&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EditorBootstrapScenePrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EditorBootstrapScenePrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bootstrapScenePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnPlayModeChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlayModeStateChange&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// TODO&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnSceneLoaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt; &lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoadSceneMode&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// TODO&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EditorSessionState&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SessionState&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;KEY_PREFIX&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PlayerSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;productGUID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorBootstrapScene&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;KEY_PREVIOUS_ACTIVE_SCENE_PATH&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY_PREFIX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_previousActiveScenePath&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KEY_PREVIOUS_ACTIVE_SCENE_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KEY_PREVIOUS_ACTIVE_SCENE_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EditorBootstrapScenePrefs&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;KEY_PREFIX&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PlayerSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;productGUID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorBootstrapScene&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY_PREFIX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_loadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;BOOTSTRAP_SCENE_PATH&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY_PREFIX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_bootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;_loadBootstrapSceneOnPlay&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EditorPrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EditorPrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOAD_BOOTSTRAP_SCENE_ON_PLAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_bootstrapScenePath&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EditorPrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BOOTSTRAP_SCENE_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EditorPrefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BOOTSTRAP_SCENE_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code should add a menu item in the top toolbar that allows us to select and set a Bootstrap scene via a file menu dialog popup. Once set, we will save the file path to the bootstrap scene to the local EditorPrefs that (by the way) should not get pushed to git. Now let's finish writing the empty callback functions OnPlayModeChanged and OnSceneLoaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnPlayModeChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlayModeStateChange&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;ShouldLoadBootstrapSceneOnPlay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isPlaying&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isPlayingOrWillChangePlaymode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// user pressed the play button...&lt;/span&gt;
        &lt;span class="n"&gt;EditorSessionState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_previousActiveScenePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetActiveScene&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorSceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveCurrentModifiedScenesIfUserWantsTo&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Scene&lt;/span&gt; &lt;span class="n"&gt;bootstrapScene&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EditorSceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OpenSceneMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Additive&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// active scene is loaded first, so we set the active scene to be the bootstrap scene&lt;/span&gt;
                &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActiveScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bootstrapScene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Bootstrap Scene not found: `&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bootstrapScenePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isPlaying&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// user canceled the save operation...&lt;/span&gt;
            &lt;span class="n"&gt;EditorApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isPlaying&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// user pressed stop button or canceled play...&lt;/span&gt;
        &lt;span class="n"&gt;Scene&lt;/span&gt; &lt;span class="n"&gt;previousActiveScene&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSceneByPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorSessionState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_previousActiveScenePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previousActiveScene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActiveScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previousActiveScene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnSceneLoaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt; &lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoadSceneMode&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorSessionState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_previousActiveScenePath&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EditorSessionState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_previousActiveScenePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// at this point the bootstrap scene was already loaded and Awake() methods were invoked, revert to previous active scene&lt;/span&gt;
        &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActiveScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSceneByPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EditorSessionState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_previousActiveScenePath&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this code, we now have a way to set a bootstrap scene and then additively load it when the user presses play. We make sure to set the bootstrap scene as the active scene so that awake methods are prioritized in that scene in the first frame. Afterwards, we set the previously active scene back to being the active scene and then let the scene loading process continue as normal.&lt;/p&gt;

&lt;p&gt;So what now? Well, now we can add Singletons or Services we want to run on game start in order to initialize key systems in this new scene we created. It's important that we tell all the devs on the team to make sure that they set the Bootstrap scene in the editor if they want to test gameplay logic that requires that key game systems have already been setup and initialized. This is as simple as adding a note in the project's README about this.&lt;/p&gt;

&lt;p&gt;There is one last thing that we need to do. In a built game, if we set the bootstrap scene as scene index 0, nothing will happen. This is because we still need a script to load the actual first scene in a built game. To do that let's create a MonoBehaviour script called &lt;code&gt;Bootstrap.cs&lt;/code&gt; and let's make sure this is NOT placed inside an &lt;code&gt;Editor&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine.SceneManagement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Tenacious.SceneManagement&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// This script should be placed on a game object inside a Bootstrap Scene. &lt;/span&gt;
    &lt;span class="c1"&gt;/// In a build, this script will load the next scene (build index + 1) at the end of the first frame.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bootstrap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;StartCoroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;CR_LoadNextScene&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IEnumerator&lt;/span&gt; &lt;span class="nf"&gt;CR_LoadNextScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WaitForEndOfFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nf"&gt;LoadNextScene&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LoadNextScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEditor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sceneCount&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UnloadSceneAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildIndex&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this script created and attached to a GameObject in our Bootstrap scene, we now have it so that during the first frame in a build, assuming the Bootstrap scene is loaded first, we will call &lt;code&gt;Awake()&lt;/code&gt; and &lt;code&gt;Start()&lt;/code&gt; on all MonoBehaviours in the Bootstrap scene and after the first frame has finished we will load the next scene (bootstrap scene index + 1).&lt;/p&gt;

&lt;p&gt;The final application control flow should look like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff5gryz87ici74txt82lp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff5gryz87ici74txt82lp.png" alt="Application Control Flow 1" width="307" height="154"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1b2y2yiaruco5iba5skz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1b2y2yiaruco5iba5skz.png" alt=" " width="767" height="141"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39ljjlig7fqteytuh0wv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39ljjlig7fqteytuh0wv.png" alt=" " width="525" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can add whatever game objects we want to the bootstrap scene and initialize whatever we need in one place. We also have dependency injection via Unity's &lt;code&gt;[SerializeField]&lt;/code&gt; if needed. &lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>architecture</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Unity Foundational Architecture: Project Scaffolding</title>
      <dc:creator>Daniel Rinaldi (TenaciousDan)</dc:creator>
      <pubDate>Tue, 07 Jul 2026 17:34:01 +0000</pubDate>
      <link>https://dev.to/tenaciousdan/unity-foundational-architecture-project-scaffolding-4gni</link>
      <guid>https://dev.to/tenaciousdan/unity-foundational-architecture-project-scaffolding-4gni</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;Table of Contents&lt;/em&gt;:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Scaffolding

&lt;ul&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Project Settings&lt;/li&gt;
&lt;li&gt;Project Folder Structure&lt;/li&gt;
&lt;li&gt;TextMesh Pro&lt;/li&gt;
&lt;li&gt;Scenes&lt;/li&gt;
&lt;li&gt;Code Modules&lt;/li&gt;
&lt;li&gt;README&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Push the Initial Commit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Great architecture in Unity is entirely about drawing the right boundaries. Whether you are building a view-first UI framework or completely decoupling your core logic from your visual representation, the success of those systems is dictated by your initial project setup and that poor scaffolding will fight you evry step of the way. In this article we are going to look at how to properly setup and structure a brand new Unity project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffolding
&lt;/h2&gt;

&lt;p&gt;In this article, we are going to setup some basic scaffolding that most Unity projects will need (one way or another). Scaffolding is about setting up the foundational environment of a project. The goal is to handle all the repetitive, baseline setup so you can immediately start writing actual feature code.&lt;/p&gt;

&lt;p&gt;After creating a new Unity project, there's a few things that Unity devs do but we are going to focus on the shared setup process (we're skipping things that are only local like editor layouts, IDE setup, etc).&lt;/p&gt;

&lt;h3&gt;
  
  
  Git
&lt;/h3&gt;

&lt;p&gt;First we'll create our &lt;code&gt;.gitignore&lt;/code&gt; and &lt;code&gt;.gitattributes&lt;/code&gt; files.&lt;br&gt;&lt;br&gt;
For the &lt;code&gt;.gitignore&lt;/code&gt; we can use the following file which should be placed at the root of your project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;##################################################
# Unity
##################################################
&lt;/span&gt;
&lt;span class="c"&gt;# Unity-generated folders
&lt;/span&gt;.&lt;span class="n"&gt;utmp&lt;/span&gt;/
/[&lt;span class="n"&gt;Ll&lt;/span&gt;]&lt;span class="n"&gt;ibrary&lt;/span&gt;/
/[&lt;span class="n"&gt;Ll&lt;/span&gt;]&lt;span class="n"&gt;ogs&lt;/span&gt;/
/[&lt;span class="n"&gt;Tt&lt;/span&gt;]&lt;span class="n"&gt;emp&lt;/span&gt;/
/[&lt;span class="n"&gt;Oo&lt;/span&gt;]&lt;span class="n"&gt;bj&lt;/span&gt;/
/[&lt;span class="n"&gt;Bb&lt;/span&gt;]&lt;span class="n"&gt;uild&lt;/span&gt;/
/[&lt;span class="n"&gt;Bb&lt;/span&gt;]&lt;span class="n"&gt;uilds&lt;/span&gt;/
/[&lt;span class="n"&gt;Uu&lt;/span&gt;]&lt;span class="n"&gt;ser&lt;/span&gt;[&lt;span class="n"&gt;Ss&lt;/span&gt;]&lt;span class="n"&gt;ettings&lt;/span&gt;/

&lt;span class="c"&gt;# Unity 6 : ignore recovery folder created upon restarting after a crash.
&lt;/span&gt;/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;Assets&lt;/span&gt;/&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;Recovery&lt;/span&gt;/
/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;Assets&lt;/span&gt;/&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;Recovery&lt;/span&gt;.&lt;span class="n"&gt;meta&lt;/span&gt;

&lt;span class="c"&gt;# ignore the asset store tools plugin
&lt;/span&gt;/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;ssets&lt;/span&gt;/&lt;span class="n"&gt;AssetStoreTools&lt;/span&gt;*

&lt;span class="c"&gt;# Explicitly track Unity's package manifest and packages-lock json files
&lt;/span&gt;!**/[&lt;span class="n"&gt;Pp&lt;/span&gt;]&lt;span class="n"&gt;ackages&lt;/span&gt;/&lt;span class="n"&gt;manifest&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt;
!**/[&lt;span class="n"&gt;Pp&lt;/span&gt;]&lt;span class="n"&gt;ackages&lt;/span&gt;/&lt;span class="n"&gt;packages&lt;/span&gt;-&lt;span class="n"&gt;lock&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="c"&gt;# Autogenerated VS/MD/Consulo solution and project files
&lt;/span&gt;&lt;span class="n"&gt;ExportedObj&lt;/span&gt;/
.&lt;span class="n"&gt;consulo&lt;/span&gt;/
*.&lt;span class="n"&gt;csproj&lt;/span&gt;
*.&lt;span class="n"&gt;unityproj&lt;/span&gt;
*.&lt;span class="n"&gt;sln&lt;/span&gt;
*.&lt;span class="n"&gt;suo&lt;/span&gt;
*.&lt;span class="n"&gt;tmp&lt;/span&gt;
*.&lt;span class="n"&gt;user&lt;/span&gt;
*.&lt;span class="n"&gt;userprefs&lt;/span&gt;
*.&lt;span class="n"&gt;pidb&lt;/span&gt;
*.&lt;span class="n"&gt;booproj&lt;/span&gt;
*.&lt;span class="n"&gt;svd&lt;/span&gt;
*.&lt;span class="n"&gt;pdb&lt;/span&gt;
*.&lt;span class="n"&gt;mdb&lt;/span&gt;
*.&lt;span class="n"&gt;opendb&lt;/span&gt;
*.&lt;span class="n"&gt;VC&lt;/span&gt;.&lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="c"&gt;# Unity3D generated meta files
&lt;/span&gt;*.&lt;span class="n"&gt;pidb&lt;/span&gt;.&lt;span class="n"&gt;meta&lt;/span&gt;
*.&lt;span class="n"&gt;pdb&lt;/span&gt;.&lt;span class="n"&gt;meta&lt;/span&gt;

&lt;span class="c"&gt;# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
&lt;/span&gt;/[&lt;span class="n"&gt;Mm&lt;/span&gt;]&lt;span class="n"&gt;emoryCaptures&lt;/span&gt;/

&lt;span class="c"&gt;# Recordings can get excessive in size
&lt;/span&gt;/[&lt;span class="n"&gt;Rr&lt;/span&gt;]&lt;span class="n"&gt;ecordings&lt;/span&gt;/

&lt;span class="c"&gt;# Gradle cache directory
&lt;/span&gt;.&lt;span class="n"&gt;gradle&lt;/span&gt;/

&lt;span class="c"&gt;# Unity3D generated file on crash reports
&lt;/span&gt;&lt;span class="n"&gt;sysinfo&lt;/span&gt;.&lt;span class="n"&gt;txt&lt;/span&gt;

&lt;span class="c"&gt;# Builds artifacts
&lt;/span&gt;*.&lt;span class="n"&gt;apk&lt;/span&gt;
*.&lt;span class="n"&gt;aab&lt;/span&gt;
*.&lt;span class="n"&gt;unitypackage&lt;/span&gt;
*.&lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="c"&gt;# Crashlytics generated file
&lt;/span&gt;&lt;span class="n"&gt;crashlytics&lt;/span&gt;-&lt;span class="n"&gt;build&lt;/span&gt;.&lt;span class="n"&gt;properties&lt;/span&gt;

&lt;span class="c"&gt;# Packed Addressables
&lt;/span&gt;/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;ssets&lt;/span&gt;/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;ddressable&lt;/span&gt;[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;ssets&lt;/span&gt;[&lt;span class="n"&gt;Dd&lt;/span&gt;]&lt;span class="n"&gt;ata&lt;/span&gt;/*/*.&lt;span class="n"&gt;bin&lt;/span&gt;*

&lt;span class="c"&gt;# Temporary auto-generated Android Assets
&lt;/span&gt;/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;ssets&lt;/span&gt;/[&lt;span class="n"&gt;Ss&lt;/span&gt;]&lt;span class="n"&gt;treamingAssets&lt;/span&gt;/&lt;span class="n"&gt;aa&lt;/span&gt;.&lt;span class="n"&gt;meta&lt;/span&gt;
/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;ssets&lt;/span&gt;/[&lt;span class="n"&gt;Ss&lt;/span&gt;]&lt;span class="n"&gt;treamingAssets&lt;/span&gt;/&lt;span class="n"&gt;aa&lt;/span&gt;/*

&lt;span class="c"&gt;##################################################
# IDEs
##################################################
&lt;/span&gt;
&lt;span class="c"&gt;# Rider
##################################################
&lt;/span&gt;/[&lt;span class="n"&gt;Aa&lt;/span&gt;]&lt;span class="n"&gt;ssets&lt;/span&gt;/&lt;span class="n"&gt;Plugins&lt;/span&gt;/&lt;span class="n"&gt;Editor&lt;/span&gt;/&lt;span class="n"&gt;JetBrains&lt;/span&gt;*
.&lt;span class="n"&gt;idea&lt;/span&gt;/

&lt;span class="c"&gt;# VS Code
##################################################
&lt;/span&gt;.&lt;span class="n"&gt;vscode&lt;/span&gt;/

&lt;span class="c"&gt;# Visual Studio
##################################################
&lt;/span&gt;.&lt;span class="n"&gt;vs&lt;/span&gt;/

&lt;span class="c"&gt;##################################################
# Custom
##################################################
&lt;/span&gt;
&lt;span class="c"&gt;# ignore any folder named 'gitignore' and its contents
&lt;/span&gt;&lt;span class="n"&gt;gitignore&lt;/span&gt;/
&lt;span class="n"&gt;gitignore&lt;/span&gt;.&lt;span class="n"&gt;meta&lt;/span&gt;

&lt;span class="c"&gt;# End of Custom ##################################
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;Custom&lt;/code&gt; section at the end. Here is where you can add custom ignore patterns. I added a pattern to ignore any folder called &lt;code&gt;gitignore&lt;/code&gt; in the project as a nice to have. This is so that devs can experiment with things locally without worrying about needing to discard/stash those changes so that they don't show up in another PR.&lt;/p&gt;

&lt;p&gt;Next we'll create the &lt;code&gt;.gitattributes&lt;/code&gt; file which is used to define how Git should track, format, and merge specific file types. But what's more important is its relation to &lt;a href="https://git-lfs.com/" rel="noopener noreferrer"&gt;Git LFS&lt;/a&gt;. Unity projects inherently contain massive binary assets like textures (.png, .psd), 3D models (.fbx, .obj), and audio clips (.wav, .mp3). Normal Git cannot handle these well and they will bloat your repository quickly. The .gitattributes file acts as the explicit instruction manual for &lt;a href="https://git-lfs.com/" rel="noopener noreferrer"&gt;Git LFS&lt;/a&gt;. It intercepts designated file extensions and flags those files to be uploaded to an external storage server (take note of all the lines ending in &lt;code&gt;lfs&lt;/code&gt;), replacing them in your standard repo with a tiny text pointer instead. Make sure that you enable &lt;a href="https://git-lfs.com/" rel="noopener noreferrer"&gt;Git LFS&lt;/a&gt; for your project. Anyways, here's the &lt;code&gt;.gitattributes&lt;/code&gt; file which should be placed at the root of your project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Define macros (only works in top-level gitattributes files)
&lt;/span&gt;[&lt;span class="n"&gt;attr&lt;/span&gt;]&lt;span class="n"&gt;lfs&lt;/span&gt;               &lt;span class="n"&gt;filter&lt;/span&gt;=&lt;span class="n"&gt;lfs&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;=&lt;span class="n"&gt;lfs&lt;/span&gt; &lt;span class="n"&gt;merge&lt;/span&gt;=&lt;span class="n"&gt;lfs&lt;/span&gt; -&lt;span class="n"&gt;text&lt;/span&gt;
[&lt;span class="n"&gt;attr&lt;/span&gt;]&lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;json&lt;/span&gt;        &lt;span class="n"&gt;eol&lt;/span&gt;=&lt;span class="n"&gt;lf&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;json&lt;/span&gt;
[&lt;span class="n"&gt;attr&lt;/span&gt;]&lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;        &lt;span class="n"&gt;merge&lt;/span&gt;=&lt;span class="n"&gt;unityyamlmerge&lt;/span&gt; &lt;span class="n"&gt;eol&lt;/span&gt;=&lt;span class="n"&gt;lf&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;yaml&lt;/span&gt;

&lt;span class="c"&gt;# Ensure that text files that any contributor introduces to the repository have their line endings normalized
&lt;/span&gt;* &lt;span class="n"&gt;text&lt;/span&gt;=&lt;span class="n"&gt;auto&lt;/span&gt;

&lt;span class="c"&gt;# Unity source files
&lt;/span&gt;*.&lt;span class="n"&gt;cginc&lt;/span&gt;                 &lt;span class="n"&gt;text&lt;/span&gt;
*.&lt;span class="n"&gt;compute&lt;/span&gt;               &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;hlsl&lt;/span&gt;
*.&lt;span class="n"&gt;cs&lt;/span&gt;                    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;=&lt;span class="n"&gt;csharp&lt;/span&gt;
*.&lt;span class="n"&gt;hlsl&lt;/span&gt;                  &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;hlsl&lt;/span&gt;
*.&lt;span class="n"&gt;raytrace&lt;/span&gt;              &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;hlsl&lt;/span&gt;
*.&lt;span class="n"&gt;shader&lt;/span&gt;                &lt;span class="n"&gt;text&lt;/span&gt;

&lt;span class="c"&gt;# Unity JSON files
&lt;/span&gt;*.&lt;span class="n"&gt;asmdef&lt;/span&gt;                &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;json&lt;/span&gt;
*.&lt;span class="n"&gt;asmref&lt;/span&gt;                &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;json&lt;/span&gt;
*.&lt;span class="n"&gt;index&lt;/span&gt;                 &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;json&lt;/span&gt;
*.&lt;span class="n"&gt;inputactions&lt;/span&gt;          &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;json&lt;/span&gt;
*.&lt;span class="n"&gt;shadergraph&lt;/span&gt;           &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;json&lt;/span&gt;
*.&lt;span class="n"&gt;shadersubgraph&lt;/span&gt;        &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="c"&gt;# Unity UI Toolkit files
&lt;/span&gt;*.&lt;span class="n"&gt;tss&lt;/span&gt;                   &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;=&lt;span class="n"&gt;css&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;css&lt;/span&gt;
*.&lt;span class="n"&gt;uss&lt;/span&gt;                   &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;=&lt;span class="n"&gt;css&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;css&lt;/span&gt;
*.&lt;span class="n"&gt;uxml&lt;/span&gt;                  &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;language&lt;/span&gt;=&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;linguist&lt;/span&gt;-&lt;span class="n"&gt;detectable&lt;/span&gt;

&lt;span class="c"&gt;# Unity YAML files
&lt;/span&gt;*.&lt;span class="n"&gt;anim&lt;/span&gt;                  &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;asset&lt;/span&gt;                 &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;brush&lt;/span&gt;                 &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;controller&lt;/span&gt;            &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;flare&lt;/span&gt;                 &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;fontsettings&lt;/span&gt;          &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;giparams&lt;/span&gt;              &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;guiskin&lt;/span&gt;               &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;lighting&lt;/span&gt;              &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;mask&lt;/span&gt;                  &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;mat&lt;/span&gt;                   &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;meta&lt;/span&gt;                  &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;mixer&lt;/span&gt;                 &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;overrideController&lt;/span&gt;    &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;playable&lt;/span&gt;              &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;prefab&lt;/span&gt;                &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;preset&lt;/span&gt;                &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;renderTexture&lt;/span&gt;         &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;scenetemplate&lt;/span&gt;         &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;shadervariants&lt;/span&gt;        &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;signal&lt;/span&gt;                &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;spriteatlas&lt;/span&gt;           &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;spriteatlasv2&lt;/span&gt;         &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;terrainlayer&lt;/span&gt;          &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;unity&lt;/span&gt;                 &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;physicMaterial&lt;/span&gt;        &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;
*.&lt;span class="n"&gt;physicsMaterial2D&lt;/span&gt;     &lt;span class="n"&gt;unity&lt;/span&gt;-&lt;span class="n"&gt;yaml&lt;/span&gt;

&lt;span class="c"&gt;# Unity LFS
&lt;/span&gt;*.&lt;span class="n"&gt;cubemap&lt;/span&gt;               &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;unitypackage&lt;/span&gt;          &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## 3D models
&lt;/span&gt;*.&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;dm&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;ds&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;blend&lt;/span&gt;                 &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;blend1&lt;/span&gt;                &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;c4d&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;collada&lt;/span&gt;               &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;dae&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;dxf&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;FBX&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;fbx&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;jas&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;lws&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;lxo&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;ma&lt;/span&gt;                    &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;max&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;mb&lt;/span&gt;                    &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;obj&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;ply&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;skp&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;stl&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;ztl&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Audio
&lt;/span&gt;*.&lt;span class="n"&gt;aif&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;aiff&lt;/span&gt;                  &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;it&lt;/span&gt;                    &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;mod&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;mp3&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;ogg&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;s3m&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;wav&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;xm&lt;/span&gt;                    &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Video
&lt;/span&gt;*.&lt;span class="n"&gt;asf&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;avi&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;flv&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;mov&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;mp4&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;mpeg&lt;/span&gt;                  &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;mpg&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;ogv&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;wmv&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Images
&lt;/span&gt;*.&lt;span class="n"&gt;bmp&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;exr&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;gif&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;hdr&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;iff&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;jpeg&lt;/span&gt;                  &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;jpg&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;pict&lt;/span&gt;                  &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;png&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;psd&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;tga&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;tif&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;tiff&lt;/span&gt;                  &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;webp&lt;/span&gt;                  &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Compressed Archive
&lt;/span&gt;*.&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;                    &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;bz2&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;gz&lt;/span&gt;                    &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;rar&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;tar&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;zip&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Compiled Dynamic Library
&lt;/span&gt;*.&lt;span class="n"&gt;dll&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;pdb&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;so&lt;/span&gt;                    &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Fonts
&lt;/span&gt;*.&lt;span class="n"&gt;otf&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;ttf&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Executable/Installer
&lt;/span&gt;*.&lt;span class="n"&gt;apk&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;
*.&lt;span class="n"&gt;exe&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Documents
&lt;/span&gt;*.&lt;span class="n"&gt;pdf&lt;/span&gt;                   &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## .a files (Static Libraries): These are archive files often used for compiled C++ static libraries, frequently used in Unity projects for iOS native plugins or specialized plug-in libraries.
&lt;/span&gt;*.&lt;span class="n"&gt;a&lt;/span&gt;                     &lt;span class="n"&gt;lfs&lt;/span&gt;

&lt;span class="c"&gt;## Spine export file for Unity
&lt;/span&gt;*.&lt;span class="n"&gt;skel&lt;/span&gt;.&lt;span class="n"&gt;bytes&lt;/span&gt;            &lt;span class="n"&gt;lfs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project Settings
&lt;/h3&gt;

&lt;p&gt;You should configure any initial project settings in the Project Settings window which you can open by selecting &lt;code&gt;Edit &amp;gt; Project Settings...&lt;/code&gt; in the top toolbar. While many of the settings here are project specific there are a few that you should adjust right away.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Player&lt;/code&gt; settings: 

&lt;ul&gt;
&lt;li&gt;You should update the &lt;code&gt;Company Name&lt;/code&gt;, &lt;code&gt;Product Name&lt;/code&gt;, and &lt;code&gt;Version&lt;/code&gt; fields so that they are not set to the default.&lt;/li&gt;
&lt;li&gt;You should also configure the &lt;code&gt;Splash Image&lt;/code&gt;. I usually just set it to this (see image below) and simply append to the list of logos when a logo for the game is ready:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6vj5d5lep8t5bzau7mne.png" alt="Splash Image Configuration" width="748" height="658"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Editor&lt;/code&gt; settings:

&lt;ul&gt;
&lt;li&gt;You should set &lt;code&gt;Asset Serialization &amp;gt; Mode&lt;/code&gt; to &lt;code&gt;Force Text&lt;/code&gt; to force YAML instead of binary for asset files. This makes it easier to work with Git and Unity YAML-Merge.&lt;/li&gt;
&lt;li&gt;You should also set &lt;code&gt;Enter Play Mode Settings &amp;gt; When entering Play Mode&lt;/code&gt; to &lt;code&gt;Reload Scene Only&lt;/code&gt; to enable fast enter play mode. This will be the default setting in Unity 6.5+ and moving forward as they are deprecating and removing the option to Reload Domain when entering Play Mode. This makes it so that Unity does not destroy the entire C# code domain state and does not reload any assemblies which means that static data, variables, events and memory are not reset when entering play mode in editor. The plus side is that this makes you write cleaner code and also makes entering play mode super-super quick!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Folder Structure
&lt;/h3&gt;

&lt;p&gt;Next, let's map out our folder structure. This is the folder hierarchy that I use for my projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[PROJECT_ROOT]/
├── Assets/
│   ├── _Project/
│   │   ├── _Modules/
│   │   │   └── Default/
│   │   │       ├── _Runtime/
│   │   │       └── Editor/
│   │   ├── _Scenes/
│   │   ├── Art/
│   │   ├── Audio/
│   │   │   ├── Music/
│   │   │   └── Sounds/
│   │   ├── Resources/
│   │   ├── Sandbox/
│   │   ├── Settings/
│   │   └── StreamingAssets/
│   │       └── Videos/
│   ├── TextMesh Pro/
│   └── ...
├── Packages/
├── .gitignore
├── .gitattributes
├── README.md
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key take-aways from this folder structure is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://docs.unity3d.com/6000.3/Documentation/Manual/SpecialFolders.html" rel="noopener noreferrer"&gt;Special Folders&lt;/a&gt; that Unity reserves that we have setup prematurely (Editor, Resources, StreamingAssets). Note that you can have multiple of some folders in your project and Unity will consider/group them all together when processing them.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;_&lt;/code&gt; prefix for some of the folder names is just so that Unity orders it first inside its parent folder in the project explorer which uses alphabetical sorting for folders.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;_Project&lt;/code&gt; folder where we have already setup a neat standard moving forward for how we separate and organize our project's code modules (more on that later) from other assets like &lt;code&gt;_Scenes&lt;/code&gt; (level design), &lt;code&gt;Art&lt;/code&gt;, &lt;code&gt;Settings&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;_Scenes&lt;/code&gt; folder, where we keep the scene files as well as any files related to a given scene. Usually I create a folder with the same name as the scene and place stuff like baked navmesh and lighting files there (unity will do this automatically for you as well when you bake navmesh or lighting).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Art&lt;/code&gt; folder. This folder is for artists only (no code). Many artists want to just focus on art and be able to have their own project structure for organization and this folder allows them to do that. Textures, images, 3D models, all go in here.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Audio&lt;/code&gt; folder. Again, this is a folder where audio engineers can have their own file organization. Although I usually impose a &lt;code&gt;Music&lt;/code&gt; and &lt;code&gt;Sounds&lt;/code&gt; subfolder so that my code can easily scan a single folder when generating assets that connect the audio to in-game systems (so I'll leave this up to you).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Sandbox&lt;/code&gt; folder. This is where devs can roughly test things and place files that might not ever be used in production if a feature they are prototyping is rejected. Essentially, this is where someone can prototype a feature. I usually create subfolders with the names of each dev in this folder so each dev has there own working directory.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Settings&lt;/code&gt; folder. This folder is to group all the settings/configuration files for the project. Things like &lt;code&gt;InputSystem&lt;/code&gt; (.inputactions file), &lt;code&gt;Localization&lt;/code&gt; (locales, tables, and settings), &lt;code&gt;URP Settings&lt;/code&gt; (URP Render Pipeline assets, etc),&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;TextMesh Pro&lt;/code&gt; and &lt;code&gt;Assets/...&lt;/code&gt; folders. These folders sit outside the &lt;code&gt;_Project&lt;/code&gt; folder and are mainly for external asset packages like the ones you'd download off the asset store. It may be tempting to group them under a &lt;code&gt;third party&lt;/code&gt; folder but I've seen some assets that assume they are placed at the root of the assets folder and if not placed there don't function properly as a result. While this is a very small minority of asset plugins, it's best to side with caution here. Besides, since we prefixed the project folder with &lt;code&gt;_&lt;/code&gt; these external assets will always end up sorted at the end of the project explorer window.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TextMesh Pro
&lt;/h3&gt;

&lt;p&gt;In the editor toolbar menu at the top, select &lt;code&gt;Window &amp;gt; TextMeshPro &amp;gt; Import TMP Essential Resources&lt;/code&gt;. This will import the TextMesh Pro essential resources. This will also trigger when you add your first TMP Text component in a UI. You might want to configure a default font at some point but I'll leave that up to you. &lt;/p&gt;

&lt;h3&gt;
  
  
  Scenes
&lt;/h3&gt;

&lt;p&gt;There are at least 2-3 scenes that I create at the start of every project.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Bootstrap&lt;/code&gt; scene: This scene is the entry point of the application in a build and should be set as index 0 in the build settings scene list. For more information about bootstrapping see my other &lt;a href="https://dev.to/tenaciousdan/unity-foundational-architecture-smart-bootstrapping-52pf"&gt;article&lt;/a&gt;. If you prefer to use your own bootstrapping logic than you can skip creating this scene however I strongly advise reading the other article and setting up your bootstrapping logic using a scene.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Splash&lt;/code&gt; scene (optional): This scene is only needed if you want to have something more flashy as a splash screen other than the built in logo fades with a plain background (think animations or 3D scene or particle effects). If you implement this scene don't forget to add it as index 1 (after the bootstrap scene) in the build settings scene list.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Welcome&lt;/code&gt; scene: Every game has this, it's the first scene where the player has input agency and is usually reffered to as "the main menu". Here I usually just setup a UI view with the Game's title at the top and a basic vertical menu with some basic buttons which have some simple functionality. The 2 buttons I implement are &lt;code&gt;Play&lt;/code&gt; (brings you to another scene) and &lt;code&gt;Quit&lt;/code&gt; which calls &lt;code&gt;Application.Quit()&lt;/code&gt;. This scene should be set as index 2 (after the splash scene) in the build settings scene list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When creating scenes I also like to impose a structure to the scene hierarchy. To make it easier to keep the same initial hierarchy when creating scenes, I create a scene template via &lt;code&gt;RightClick in Project Window &amp;gt; Create &amp;gt; Scene &amp;gt; Scene Template&lt;/code&gt;. This will create a scenetemplate asset that you can point to a scene to use as the template. So create a regular scene and place it adjacent to the scenetemplate asset and then simply drag the scene into the &lt;code&gt;Template Scene&lt;/code&gt; field of the scenetemplate asset's inspector. Also make sure to check &lt;code&gt;Pin in New Scene Dialog&lt;/code&gt; so that our template stands out.  &lt;/p&gt;

&lt;p&gt;My basic scene template is as follows:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foduspezn1nok1e49aq9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foduspezn1nok1e49aq9s.png" alt="Basic Scene Template" width="294" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything in &lt;code&gt;[]&lt;/code&gt; is at position &lt;code&gt;(0,0,0)&lt;/code&gt; and has zero rotation and a scale of &lt;code&gt;(1,1,1)&lt;/code&gt;. The &lt;code&gt;[Context]&lt;/code&gt; object and everything under it are mostly for contextual things like navmesh, players spawned at runtime, level specific managers, camera management, etc. The &lt;code&gt;[Environment]&lt;/code&gt; object is for the level designer and is meant to contain all the 3D models and gameobjects that make up the 3D world as well as any lights, post processing volumes, etc.&lt;br&gt;&lt;br&gt;
To use this Scene Template that we created, we just have to press &lt;code&gt;Ctrl+N&lt;/code&gt; in the Editor and we should see our scene template in the New Scene Dialog list.&lt;/p&gt;
&lt;h3&gt;
  
  
  Code Modules
&lt;/h3&gt;

&lt;p&gt;Every project can, at some point, have code that can be grouped into its own module and even if you don't have any initially it's good to set this structure up now. The &lt;code&gt;_Modules&lt;/code&gt; folder is where all your codebase will live. Each code module should be separated within its own folder and have assembly definitions to enforce separation of concerns and to ensure a clean dependency between modules and assemblies. I will not go over assembly definitions in this article but I will explain the one or two that every Unity dev uses whether they know it or not which is the "Default" hidden &lt;code&gt;Assembly-CSharp&lt;/code&gt; and &lt;code&gt;Assembly-CSharp-Editor&lt;/code&gt; assemblies.  &lt;/p&gt;

&lt;p&gt;The default &lt;code&gt;Assembly-CSharp&lt;/code&gt; assembly (which you may have already seen in your IDE) is the default assembly that any code under the &lt;code&gt;Assets/&lt;/code&gt; lives in. Unity sets this assembly up by default and automatically makes it depend on every single other assembly in the project. This assembly is also a "Runtime" assembly which is a term I use to mean that the code in this assembly is compiled and packaged in our built game. Basically any code we add under &lt;code&gt;_Modules/Default/Runtime/&lt;/code&gt; will be part of this default &lt;code&gt;Assembly-CSharp&lt;/code&gt; assembly, which is why the folders are structured this way.  &lt;/p&gt;

&lt;p&gt;You may have noticed that there is also an &lt;code&gt;Editor&lt;/code&gt; folder under the &lt;code&gt;_Modules/Default/&lt;/code&gt; folder. This is because if we write Editor code we do not want this code to be compiled and packaged with our built game, in fact Unity will throw errors when building if this is the case. By adding Editor specific scripts under this folder, Unity will automatically add them into a default &lt;code&gt;Assembly-CSharp-Editor&lt;/code&gt; assembly (which you also may have seen in your IDE before). This entire assembly is automatically excluded from all builds except for editor which means that the code here is not packaged with your game builds and Unity will not throw any errors. So you should use this folder for any Editor specific code.&lt;/p&gt;

&lt;p&gt;You can make other modules like a "Core" module which contains interfaces and common logic if you want but you'll have to create your own assembly definitions for that and that is a whole other topic. If you do create another module, you should follow the same folder structure where you create the module folder "Core" and under it create the "_Runtime" and "Editor" folders which will house their respective assembly definition files.&lt;/p&gt;
&lt;h3&gt;
  
  
  README
&lt;/h3&gt;

&lt;p&gt;Finally, you should create a README.md with instructions on how to clone the project and install it on your machine.&lt;br&gt;&lt;br&gt;
Here's a good starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# PROJECT_NAME&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;#project_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;  -&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Installation&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;#installation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;    -&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Clone Project&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;#clone-project-using-git&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;  -&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;#configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;    -&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Setup Custom Mergetool (optional)&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;#setup-custom-mergetool-optional&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;  -&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Getting Started&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;#getting-started&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="gu"&gt;## Installation&lt;/span&gt;

&lt;span class="gu"&gt;### Clone Project Using Git&lt;/span&gt;

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;cd [CHOOSE_A_DIRECTORY_TO_STORE_THE_REPO]
git clone [INSERT_URL_TO_GIT_REPO_HERE]
&lt;span class="p"&gt;```&lt;/span&gt;

Next, Continue to the &lt;span class="sb"&gt;`Git Configuration`&lt;/span&gt; section to configure git or just go straight to &lt;span class="sb"&gt;`Getting Started`&lt;/span&gt; section below.  

&lt;span class="gu"&gt;## Configuration&lt;/span&gt;

&lt;span class="gu"&gt;### Setup Custom Mergetool (optional)&lt;/span&gt;

UnityYAMLMerge.exe is a merge tool that aids with the merging of scene, prefab and asset files (and any other YAML files). It comes packaged with each installation of the Unity Editor, therefore if you upgrade the project version you should also update your custom merge tool to use the newer UnityYAMLMerge.exe for your version of Unity.  

You must tell Git to use UnityYAMLMerge.exe as a custom merge tool and also as a custom diff tool. It is located in the &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;UnityEditor installation folder&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.unity3d.com/2021.3/Documentation/Manual/SmartMerge.html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; under &lt;span class="sb"&gt;`./Editor/Data/Tools/UnityYAMLMerge.exe`&lt;/span&gt;.  
On Windows this is located at &lt;span class="sb"&gt;`C:/Program Files/Unity/Hub/Editor/{EDITOR_VERSION}/Editor/Data/Tools/UnityYAMLMerge.exe`&lt;/span&gt;.  

Once you have setup Git to use UnityYAMLMerge, you must then tell UnityYAMLMerge what diff tool to use as a fallback. To do this, you simply need to edit &lt;span class="sb"&gt;`mergespecfile.txt`&lt;/span&gt; located adjacent (within the same folder) to the UnityYAMLMerge.exe file.  
Follow the instructions in this txt file to setup a fallback merge/diff tool.  

For more info on UnityYAML merge (Smart Merge) and setup with git or git GUIs see : https://docs.unity3d.com/2021.3/Documentation/Manual/SmartMerge.html  

&lt;span class="gu"&gt;## Getting Started  &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how I included a paragraph in the README about Unity YAML-Merge? That's because this is not something that you can do for the whole project. Each developer should be setting this up locally for every project that they are collaborating on. If you are working on a project and are arguing 90% of the time about who is currently modifying a scene or prefab file then you are using Unity wrong in my opinion. Unity YAML-Merge allows you to merge scene, prefab, and asset files without breaking or corrupting anything. There are some edge cases but from my experience they are far and in between and are generally not an issue in practical use when work is separated cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push the Initial Commit
&lt;/h2&gt;

&lt;p&gt;With all of the above done, you can stage all your changes and push the initial commit to your main branch.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>architecture</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
