The Problem

Unity's system for managing different areas of your game as separate scenes is very powerful for things that are naturally part of a scene, but is poorly suited to making systems that are shared by multiple scenes. For example, a Frame Rate Counter is something that should not be tied to any particular scene or camera prefab, it should just show on screen at any point in the application and operate entirely on its own.

There are several traditional methods for setting up such systems, each of which has its own advantages and disadvantages outlined below, followed by Weaver's Asset Injection system which avoids those drawbacks.

Create Itself

The script creates an instance of itself - either on startup using a [RuntimeInitializeOnLoad] method or whenever it first gets accessed. For a MonoBehaviour system this might be as simple as new GameObject().AddComponent<MyScript>();.

Advantages:

  • Very self-contained. The script doesn't depend on any other files to work.
  • Procedural generation can be much more effective than setting things up using the Unity Editor GUI.

Disadvantages:

  • Hard coded so it's less reusable. For the Frame Rate Counter example, this would mean you need to change the script if you want to move it to a different area of the screen to better fit in with your other GUI elements or want to change its appearance.
  • Configuring complex systems is often much harder with code than with the Unity Editor GUI.
  • Can't reference assets such as textures or sounds without using one of the other approaches.
  • Modification is tedious because you have to wait for the code to recompile every time you want to see a change. Most likely this means that you won't be able to make changes while in play mode either.

In Scene

Put the script on an object in every scene and destroy each new one after the first so there is only ever one instance.

Advantages:

  • Easy to setup.
  • Avoids the disadvantages described above.

Disadvantages:

  • Easy to make mistakes during the setup or while refactoring, even if you make it a prefab.
  • Not clear from looking at the scene that this script is a global system that will be shared.
  • Wastes some performance loading and destroying useless objects.

First Scene

Put the script on an object in the start scene only (the first scene in the Build Settings).

Advantages:

  • Efficient in runtime builds.
  • Avoids the disadvantages described above.

Disadvantages:

  • Extremely inconvenient for development as you need to play the game from the start scene every time.

Load Resource

Put the script on a prefab and use Resources.Load to load and instantiate it at runtime (or do something similar with asset bundles).

Advantages:

  • Logical organisation. It's a globally accessible asset, not an object specific to a particular scene.
  • Avoids the disadvantages described above.

Disadvantages:

  • The script is dependent on the prefab being at exactly the specified path, but from looking at that path in the Project window you don't see any indication that it must not be moved. This means that refactoring or reorganising your project involves blindly guessing which parts are safe to move or delete and which are needed by scripts (and where those scripts are).
  • The prefab must be in a Resources folder. This limits your ability to neatly organise your project and will always include the prefab in builds, even if the script that used it is no longer present.

Find Resource

Put the script on a prefab and use Resources.FindObjectsOfTypeAll to find and instantiate it at runtime (or do something similar with asset bundles).

Advantages:

  • Logical organisation. It's a globally accessible system, not an object specific to a particular scene.
  • Avoids the disadvantages described above (except the prefab must still be in a Resources folder).

Disadvantages:

  • If there are multiple prefabs in the project with the correct component type you can't predict which one it will use and it might change without warning between the editor and a runtime build, or between editor sessions, or between builds.
    • This means you can't make a backup copy of the prefab while testing a modified version.
    • You don't even get a clear indication of which one it's using.
    • This approach wouldn't work for the Floating Text example since it needs to reference multiple different prefabs which all have the same FloatingText component (each damage type has a different appearance, status effect messages look different again, etc.).
  • Looking at the prefab in the Project window still gives you no indication of what it is used for (if anything).
  • The prefab must be in a Resources folder. Same as above.
  • Inefficient performance. Though this is generally insignificant and can be done on startup so it won't affect gameplay.

Asset Injection

Place one of Weaver's Asset Injection Attributes on a static field or property then use the Injectors Panel to choose an asset for it. With that done, Weaver will automatically assign that asset to the attributed member when entering play mode as well as on startup in runtime builds. This is very similar to having a regular serialized instance field that shows up in the inspector.

Advantages:

  • Avoids the disadvantages described above.
  • Very efficient in terms of setup time, refactoring flexibility, and runtime performance.
  • Can find the target asset automatically by name and also allows manual assignment.
  • Displays additional icons in the Project window with tooltips to indicate which assets are linked to what code.
  • Logs a warning immediately when entering play mode or compiling a build if something hasn't been assigned.
  • Your script is not at all dependant on the asset being at a specific path or even in a Resources folder so you can freely reorganise your project.
  • Not limited to only one reference of each type. For example, the Project Window icon textures that Weaver uses are all referenced using this feature.
  • Can be used on readonly fields.

Disadvantages:

  • Requires Weaver, which is an extra dependency your code requires to work properly.
  • Pro Only: Weaver Lite allows you to try out this feature in the Unity Editor, but it will not be available in a runtime build unless you purchase Weaver Pro.
  • Everything is locked in at build time.

This system was inspired by the Auto Load feature of the Godot engine.