Init(args) - A next-gen DI framework

Init(args)

Init(args) is a straightforward and type-safe solution for providing your components with all the objects that they depend on.

Asset Store | Documentation | Scripting Reference | Video Tutorial

Here are some reasons why Init(args) could be the ideal choice to power the architecture of your next game…

It’s Simple

Init(args)’s Inspector and attribute-based workflow is incredibly intuitive – the basics can literally be learned in a matter of minutes.

Registering a global service:

[Service(FindFromScene = true)]
class Player : MonoBehaviour
{
   ...

Receiving the service:

class Enemy : MonoBehaviour<Player>
{
   protected override void Init(Player player)
   {
      ...

All developers in your team can use Init(args) to connect objects together – no software engineering degree required.

It’s Inspector-Integrated

Services that clients will receive are visualized in the Inspector and support click-to-ping – just like you’re used to with serialized fields!

It’s Dependable

Clients always receive the services they depend on before their Awake event – regardless of whether they’re loaded as part of a scene, instantiated from a prefab, or created using AddComponent.

It’s Debuggable

Forgot to register a service that is required by a client?

Init(args) will display a warning in the Inspector and log a warning in Edit Mode, and throw an exception and visualize unserialized fields in the Inspector in Play Mode.

Edit Mode Warning

Play Mode Inspector

It’s Highly Flexible

Global and local services, full interface support, cross-scene references, value providers… don’t let Init(args)’s ease-of-use mislead you into thinking it’s lacking in the power department!

It’s Fast

The simple and type-safe injection pipelines at the heart of Init(args) don’t depend on reflection, IL post-processing or source generators, while delivering class-leading performance.

Tested Average (μs) Details
Init(args) 3.053 via MonoBehaviour<T…>
VContainer 3.343 via InjectGameObject, Source Generator: On
Reflex 3.757 via GameObjectSelfInjector
[SerializeReference] 8.312
Extenject 8.718 via ZenAutoInjecter, Reflection Baking: Off

Main Features

  1. Global Services – simply add the [Service] attribute to create a global service.
  2. Local Services – simply use the context menu in the Inspector Window to create a local service.
  3. Full Interface Support – assign components, scriptable objects and plain C# objects into the same interface fields.
  4. Cross-Scene References – just drag-and-drop an Object reference from one scene to another.
  5. Prefab Instance References – just drag-and-drop a prefab asset and pick ‘Prefab Instance’.
  6. Value Providers – localized strings, randomized values, addressable assets…
  7. Wrappers – attach plain old C# objects to GameObjects and make unit testing a breeze.
  8. Instantiate with arguments – pass arguments when instantiating an Object.
  9. Add Component with arguments – pass arguments when attaching a component to a GameObject.
  10. Create ScriptableObject with arguments – pass arguments when creating a ScriptableObject.
  11. new GameObject with arguments – create a new GameObject and initialize its components.
  12. Type-safe and reflection-free injection
  13. Asynchronous Initialization Support – Want to load services from Addressables to avoid stuttering? Init(args) has got you covered.
  14. Deferred Initialization Support – A service is not ready yet when a client is being loaded? Init(args) can automatically delay its initialization!

Let Init(args) take care of all the dirty plumbing work, and allow yourself to fully focus on just creating a great game!

Links

1 Like

Version 1.0.1 of Init(args) has been submitted to the Asset Store (should be live within a couple of days).

New features in the update:

ConstructorBehaviour<T…>

A new base class that can be used instead of MonoBehaviour<T…> with support for assigning received arguments into read-only fields and get-only properties without the need for any reflection to be used!

To do this derive from a ConstructorBehaviour<T…> base class and define a parameterless constructor which receives the initialization arguments from the constructor in the base class via its out parameters.

using UnityEngine;
using Sisus.Init;

public class Player : ConstructorBehaviour<IInputManager, Camera>
{
    private readonly IInputManager inputManager;

    public Camera Camera { get; }

    public Player() : base(out IInputManager inputManager, out Camera camera)
    {
        this.inputManager = inputManager;
        Camera = camera;
    }
}

While with MonoBehaviour<T…> providing arguments during initialization is optional, with ConstructorBehaviour<T…> this is mandatory; an exception will be thrown if an instance is instantiated without the arguments it requires. The idea behind this is to mimic the way constructors work with plain old C# objects, allowing you to restrict object creation to only be allowed when all the required dependencies are provided.

// This raises an exception:
var player = gameObject.AddComponent<Player>();
// This works:
var inputManager = Service<IInputManager>.Instance;
var camera = Camera.main;
var player = gameObject.AddComponent<Player, IInputManager, Camera>(inputManager, camera);

This new base class is mainly intended to be used for objects that are dynamically instantiated at runtime using AddComponent(args) or Instantiate(args), in which case the created instance is able to receive the arguments passed to the method in its constructor.

It is also possible to add ConstructorBehaviours directly into a scene in the Editor and provide arguments for them using Initializer components, just like you can with MonoBehaviour<T…>. However, in this situation the arguments can not be assigned through the constructor and have to instead be passed in through the Init method and assigned to the target fields using reflection. This means that performance isn’t as optimal as it would be when no reflection is involved. However this whole process happens automatically behind the scenes, and unless you have a very large number of ConstructorBehaviours in your scenes this probably isn’t a big deal.

Another thing to be aware of with ConstructorBehaviours is that the constructor gets called before Unity’s deserialization process takes place. This means that if you instantiate a ConstructorBehaviour from a prefab and assign any arguments into serialized fields, those will get overridden when the deserialization takes place. Because of this it is recommended to only assign arguments into non-serialized fields or properties in the constructor.
That notwithstanding, assigning arguments into serialized fields in ConstructorBehaviours does still work; the ConstructorBehaviour base class will detect when this happens and automatically handle re-injecting the arguments into the fields once the deserialization process has finished. However this injection also utilizes reflection, so it’s still better to avoid it when you can.

Null / NullOrInactive

Init(args) has been designed with inversion of control in mind and aims to make it as easy as possible to work with interfaces instead of specific classes in Unity.

One pain point when using interfaces in Unity is that checking them for null can be problematic. This is because in addition to being actually null, Objects in Unity can also be destroyed. You usually don’t even notice the difference when working with specific classes directly, because the Object class has a custom == operator which internally also checks whether the Object has been destroyed. However when an Object has been assigned to an interface type variable and this is compared against null, the overloaded == operator does not get used. This can result in null reference exceptions when trying to access the members of a destroyed Object.

Player player = FindObjectOfType<Player>();
IPlayer iplayer = player as IPlayer;

Destroy(player);

Debug.Log(player == null);  // Prints true
Debug.Log(iplayer == null); // Prints false

To help with this problem in version 1.0.1 a new property has been added into every base class in Init(args): Null. When an interface type variable is compared against this Null property it functions just like the overloaded == operator in the Object class and returns true when the instance has been destroyed.

Player player = FindObjectOfType<Player>();
IPlayer iplayer = player as IPlayer;

Destroy(player);

Debug.Log(player == null);  // Prints true
Debug.Log(iplayer == null); // Prints false
Debug.Log(iplayer == Null); // Prints true

It is also possible to utilize this safe null checking inside classes that don’t derive from any of the base classes in Init(args) by importing the static members of the NullExtensions class.

using UnityEngine;
using static Sisus.NullExtensions;

public static class PlainOldClass
{
    public static void Example()
    {
        Player player = Object.FindObjectOfType<Player>();
        IPlayer iplayer = player as IPlayer;

        Object.Destroy(player);

        Debug.Log(player == null);  // Prints true
        Debug.Log(iplayer == null); // Prints false
        Debug.Log(iplayer == Null); // Prints true
    }
}

In addition to the Null property there’s also a new NullOrInactive property. This functions identically to the Null property but also checks if the Object is a component on an inactive GameObject.

Player player = FindObjectOfType<Player>();
IPlayer iplayer = player as IPlayer;

player.gameObject.SetActive(false);

Debug.Log(player.gameObject.activeInHierarchy); // Prints true
Debug.Log(iplayer == NullOrInactive); // Prints true

This functionality can be particularly useful when utilizing Object Pooling and an inactive GameObject is meant to represent an object which doesn’t exist.

Hi Timo,

Hope you’re well. I’ve sent this question on the support form, but I’ll sent here as I can show more precisely the problem with prints.

I’m also using Corgi Game engine(and other plugins), and Corgi relies a lot on singleton MonoBehaviors to add to hierarchy. I’ve been having problems adding the arguments to the classes, as it appears the following warnings**(I send in the image)**. I’ve used the demo scene as reference, but I can’t add the dependencies the way they’re added in the scene idk why

But my main question is, besides adding a singleton dependency, how can I add them to a script from hierarchy? Because I need the settings that are set up on hierarchy and I would like to drag them to the dependencies that way.

Thank you in advance!

Eduardo Bagarrão

Hey @BagarraoEduardo_1

The warning message seems to have happened from trying to drag the Script asset of the LevelManager to the Initializer field, when it is expecting a Component instance from the Hierarchy instead.

If the LevelManager component is only created at runtime you can create an IValueProvider component for the Object and drag that to the Initializer field instance:

using Sisus.Init;
using UnityEngine;
using MoreMountains.CorgiEngine;

public class LevelManagerWeakReference : MonoBehaviour, IValueProvider<LevelManager>
{
    public LevelManager Value => LevelManager.Instance;
}

You could even combine multiple IValueProvider implementations in just one component if you’d prefer that:

using Sisus.Init;
using UnityEngine;
using MoreMountains.CorgiEngine;
public class CorgiServiceProvider : MonoBehaviour, IValueProvider<GameManager>, IValueProvider<LevelManager>, IValueProvider<GUIManager>, IValueProvider<InputManager>
{
    GameManager IValueProvider<GameManager>.Value => GameManager.Instance;
    LevelManager IValueProvider<LevelManager>.Value => LevelManager.Instance;
    GUIManager IValueProvider<GUIManager>.Value => GUIManager.Instance;
    InputManager IValueProvider<InputManager>.Value => InputManager.Instance;
}

If the components already exist in the same scene in edit mode you should be able to just drag-and-drop them directly to the Object field.

UPDATE: I tested it, and it seems like dragging a component into an Initializers from another GameObject doesn’t currently work as expected. It tries to assign the dragged GameObject itself to the field instead of its components, and because of this thinks it’s of incompatible type. This is probably what is causing your issues. I will improve this in the next update.

Until the next update the only way to drag a Component from another GameObject is by opening two inspector windows , locking one of them to display the Initializer and the other one to display the Level Manager and then dragging the Level Manager component from one Inspector to the other.

1 Like

Init(args) version 1.0.2 is now live in the Asset Store.

Bug fixes in the update:

  • Fixed issue with Initializers / Any not being assigned a component value from a GameObject which was drag-and-dropped to them using the Inspector, even if the GameObject contained a component that was of an assignable type.

  • Fixed issue with Initializers / Any not accepting UnityEngine.Object references that implement IValueProvider when they were drag-and-dropped to them using the Inspector.

New features in the update:

Inspector Runtime State

All base classes in Init(args), such as MonoBehaviour<T…>, now have a Custom Editor that visualizes all non-serialized fields of the Object in the Inspector in Play Mode.

This makes it possible to see the state of read-only fields and properties assigned to in the constructor of a ConstructorBehaviour<T…>.

It also makes it possible to see the fields of a plain old class object that is wrapped by a Wrapper component even in cases where the wrapped object doesn’t have the Serializable attribute.

Confirmed that the update solved the issue that I was having. Thank you for the help! :wink:

1 Like

Bit of impulse buy here maybe more examples tutorials to get into this method of doing things

anyway Windows Build errors…

Assets\Sisus\Init(args)\Scripts\Initializer\InitializerT1.cs(6,15): error CS0246: The type or namespace name 'EditorOnly' could not be found (are you missing a using directive or an assembly reference?)

Assets\Sisus\Init(args)\Scripts\Initializer\InitializerUtility.cs(17,15): error CS0246: The type or namespace name 'EditorOnly' could not be found (are you missing a using directive or an assembly reference?)

Assets\Sisus\Init(args)\Scripts\Initializer\WrapperInitializerT1.cs(8,15): error CS0246: The type or namespace name 'EditorOnly' could not be found (are you missing a using directive or an assembly reference?)

Assets\Sisus\Init(args)\Scripts\Utility\InitArgs.cs(11,15): error CS0246: The type or namespace name 'EditorOnly' could not be found (are you missing a using directive or an assembly reference?)
UnityEditor.BuildPlayerWindow+BuildMethodException: 13 errors
  at UnityEditor.BuildPlayerWindow+DefaultBuildMethods.BuildPlayer (UnityEditor.BuildPlayerOptions options) [0x002ca] in <2cd7ebc9c2ef4276a8edbc7de85c89ce>:0 

  at UnityEditor.BuildPlayerWindow.CallBuildMethods (System.Boolean askForBuildLocation, UnityEditor.BuildOptions defaultBuildOptions) [0x00080] in <2cd7ebc9c2ef4276a8edbc7de85c89ce>:0
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

@Slashbot64 Thank you for letting me know about the build errors, I’ll fix these and submit an update asap!

I recommend taking a look at the included Demo scene to get a better feel about how one can use Init(args).
But adding some more written examples is also a good idea :slight_smile:

An update that fixes the build errors has been submitted to the asset store for review.

You can contact me if you need to get the fixed package before it has gone through the asset store review process (this typically takes a day or a few).

UPDATE: The update is now live.

Version 1.0.3 of Init(args) is now out. Improvements in this update include:

  • Added new Services component. These can be attached to any Scene object or prefab and used to define a collection of services straight through the Inspector. Clients that can use the services can be filtered based on where they are in the scene hierarchy relative to the Services component. This component can be useful for defining localized services (e.g. only available inside a certain scene) as well as dynamic services (e.g. only available when a menu is open).
  • Services are now automatically initialized in such and order that services that are needed by other services get initialized before the services that need them! This can help avoid execution order related NullReferenceExceptions when trying to access services inside other services.
  • Added ability to control Initializer null argument validation in a more granular way, and improved edit mode validation in general to get rid of potentially false warnings.
  • Improved Initializer editor Type popup fields. They now have a search box for filtering the listed types and can display any number of types, which means things like System.Object fields are much better supported.
  • Added ability to automatically generate Initializers and Wrappers for classes from the MonoScript context menu.

Let me give a practical example of how the new Services component can be used to provide Init arguments to your components and help completely decouple all your components from each other.

In this example we will create a system consisting of the following components:

  • UIManager component which can be asked to show a Dialog to the user.

  • Dialog component which handles passing the dialog text to the UI and invoking a callback if its Confirm method gets called.

  • DialogButton component which handles calling Confirm on the Dialog if the user clicks a button.

To start lets define the interfaces which our components can use to communicate with each other without knowing anything about each others’ concrete classes:

using System;

public interface IUIManager
{
    bool IsDialogOpen { get; }
    void ShowDialog(string text, Action onConfirmed);
}

public interface IDialog
{
    bool IsOpen { get; }

    void Show(string text, Action onConfirmed);
    void Confirm();
    void Cancel();
}

Then let’s write the actual code for the components.

We will make use of the MonoBehaviour<T…> base class in Init(args) to allow them to receive arguments during initialization.

using Sisus.Init;
using System;

public class UIManager : MonoBehaviour<IDialog>, IUIManager
{
    private IDialog dialog;

    public bool IsDialogOpen => dialog != Null && dialog.IsOpen;

    protected override void Init(IDialog dialog) => this.dialog = dialog;

    protected override void OnAwake() => CloseDialog();

    public void ShowDialog(string text, Action onConfirmed) => dialog.Show(text, onConfirmed);

    public bool CloseDialog()
    {
        if(IsDialogOpen)
        {
            dialog.Cancel();
            return true;
        }

        return false;
    }
}
using Sisus.Init;
using System;

public class Dialog : MonoBehaviour<IText>, IDialog
{
    private IText text;
    private Action onConfirmed;

    public bool IsOpen => gameObject.activeInHierarchy;

    protected override void Init(IText text) => this.text = text;

    public void Show(string text, Action onConfirmed)
    {
        this.onConfirmed = onConfirmed;
        this.text.Content = text;
        gameObject.SetActive(true);
    }

    public void Confirm()
    {
        onConfirmed?.Invoke();
        gameObject.SetActive(false);
    }

    public void Cancel() => gameObject.SetActive(false);
}
using UnityEngine.UI;
using Sisus.Init;
using UnityEngine;

public class DialogButton : MonoBehaviour<IDialog, Button, DialogButtonType>
{
    private IDialog dialog;
    private Button button;
    private DialogButtonType type;

    protected override void Init(IDialog dialog, Button button, DialogButtonType type)
    {
        this.type = type;
        this.dialog = dialog;
        this.button = button;
    }

    private void OnEnable() => button.onClick.AddListener(OnClicked);
    private void OnDisable() => button.onClick.RemoveListener(OnClicked);

    private void OnClicked()
    {
        switch(type)
        {
            case DialogButtonType.Cancel:
                dialog.Cancel();
                return;
            case DialogButtonType.Confirm:
                dialog.Confirm();
                return;
            default:
                Debug.LogError($"Unrecognized ButtonType: {type}.", this);
                return;
        }
    }
}

We will also need to create an Initializer to pass all Init arguments for the DialogButton. It will not receive all its arguments automatically without an Initializer because it needs two arguments which we will not be setting up as services: Button and DialogButtonType.

using Sisus.Init;
using UnityEngine.UI;

public sealed class DialogButtonInitializer : Initializer<DialogButton, IDialog, Button, DialogButtonType> { }

Now we can build a menu system out of these components.

We will use the Services component to turn the UIManager component into a service of type IUIManager.
We will configure the Services component to allow all GameObjects nested under the UIManager to access the service.

7813233--988230--services-uimanager.png

On the GameObject containing the Dialog component will we add two Services components.

The first one we use turn the Dialog into a service which all GameObjects nested underneath the root UI GameObject can access via the IDialog interface.

The second one we only use to provide the Init argument for the Dialog component: the IText component. This is just a simple wrapper for a component responsible for showing the text on the UI, such as UnityEngine.Text or TextMeshProUGUI.
We configure For Clients to In Game Object so that the IText service is only access components in this same GameObject.
(Note that it would probably actually make more sense to just use an Initializer to provide the IText argument to the Dialog component, but I wanted to demonstrate how the Services component can be used to provide services confined within a single GameObject)

7813233--988236--services-dialog.png

And finally we will add the DialogButton as a child of the Dialog and use the Initializer we created earlier to specify the Button and DialogButtonType Init arguments that will be used to initialize the component.
The Initializer automatically detects that IDialog is a service we can use and lets us know we don’t need to specify it manually by just displaying Service in the Inspector next to that parameter’s name.

7813233--988233--services-button.png

Now we have a fully functioning UI system in place that can be used to display dialogs containing any text to the user and performing any actions when the user gives their confirmation!

Here is a simple example component that uses this system to display a dialog to the user and invoke an UnityEvent if the user clicks the Confirm button:

using Sisus.Init;
using UnityEngine;
using UnityEngine.Events;

public class ShowDialog : MonoBehaviour<IUIManager>
{
    [SerializeField]
    private string text = "";

    [SerializeField]
    private UnityEvent onConfirmed = new UnityEvent();

    private IUIManager uiManager;

    protected override void Init(IUIManager uiManager) => this.uiManager = uiManager;

    public void Show() => uiManager.ShowDialog(text, onConfirmed.Invoke);
}

Here’s our menu system in use:
7813233--988254--services-result.gif

Version 1.0.4 of Init(args) is out now.

New features added:

Lazy Initialization
Support was added for lazy initialization of classes decorated with the ServiceAttribute.

If you set LazyInit = true then an instance of the service will not be created when the game starts like usually, but this will be delayed to only take place when the first client requests an instance.

This makes it possible to use the ServiceAttribute with FindFromScene load method, even in cases where the service component doesn’t exist in the first scene of the game when the game is loaded.

In games with lots of services, lazy initialization can also be used to reduce the initial loading time of the game by spreading loading to happen over a longer period of time.

Lazy initialization was implemented using the static constructor feature in C# which means it is guaranteed to only happen once per service type and to be fully thread safe. It also means that subsequently clients can retrieve instances of a service through a simple auto-implemented property without any additional overhead being introduced with reference fetching always happening through something like Lazy.Value.

Wrapped Services
Additionally it is now possible to add the ServiceAttribute to a Wrapper component or asset, and set the service type to the type of the wrapped object (or any interface it implements), instead of the wrapper itself.

When using ServiceAttribute with load methods such as FindFromScene or ResourcePath the service initialization system is now smart enough to recognize this pattern and can retrieve the service instance through the Wrapper.WrappedObject property of the wrapper.

This makes it possible to have plain old class object services while still allowing designers to easily configure their values through the Inspector.

It also makes it possible to register plain old class objects as services while keeping them decoupled from the ServiceAttribute.

Initializer Services
Similarly it is now also possible to add the ServiceAttribute to an Initializer, and set the service type to the type of the initialized Object (or any interface it implements), instead of the Initializer itself.

When using ServiceAttribute with the load method FindFromScene, the service initialization system is now smart enough to handle this scenario. It can find the Initializer for the service from the scene, trigger the Initializer to immediately initialize the object, and then register the initialized Object as a service.

This method or registering Services can be used to decouple your MonoBehaviour services from the ServiceAttribute.

Hey Sisus, hope you’re doing fine!

I’m using this plugin on a project that is running on Unity 2020.3.26f1, but it gives the following error when building the game:

However, when I remove the plugin the project builds without problem. Do you know what it can be? I’m using Mac OS and it’s set to IL2CPP build with .NET Standard 2.0.

Thank you so much in advance!

Best Regards,
Eduardo Bagarrão

Hey @BagarraoEduardo_1 , thank you for letting me know about this!

It looks like some changes I added in the last update are probably missing a vital using directive outside of the editor platform.

I’ll investigate and get this fixed today.

Hello @SisusCo ,

Thank you for the fast answer! Looking forward for the upcoming fix :wink:

Best regards,
Eduardo Bagarrão

@BagarraoEduardo_1 Builds errors are fixed now; I sent you a PM with the updated package.

I’ll also submit the fix to asset store for review during the weekend, so it should become available for download from there some time next week.

Hello Sisus,

It’s working perfectly now! Thank you so much once again!

Best Regards,
Eduardo Bagarrão

1 Like

Version 1.0.5 is now out.

The main improvement in this version is a major overhaul of the inspector UX of Initializers.

Initializers can be used to specify the arguments that are passed to the Init function of a component.
Using Initializers to set up components instead of just exposing serialized fields can offer a bunch of benefits:

  • Initializers can expose and serialize interface arguments (even if the implementing class derives from UnityEngine.Object).
  • Initializers can warn you about missing references at runtime.
  • Initializers can warn you about missing references in edit mode.
  • Initializers allow you to use properties and read-only fields on the client component since Unity doesn’t need to be able to serialize them.
  • Initializers retrieve all Service dependencies for you automatically, so you don’t need to waste time dragging in the same references or worry about missing references if you make changes to your manager components.
  • You can add the InitOnReset attribute on the Initializer to automate the component setup process even further without having to clutter the client component with these implementation details.
  • You can see all dependencies of a component just by looking at its Init function (both on the code side and the Inspector side), instead of having to locate all its serialized fields.

With the latest update working with Initializers is now a much smoother experience.

7886779--1003558--generate-initializer.gif

You will now see a collapsible Init section at the top of any components that accept initialization arguments.

You no longer have to write any code to create an initializer for your components; simply press the + button inside the Init section and select “Generate Initializer”, and the class will be generated for you. This means you can now get all the benefits of using Initializers with basically zero extra work.

If an Initializer class already exists for the component, then the + button can be used to add an Initializer for the component instead.

Once you have added an Initializer for a component you will see a new Null Argument Guard icon on the right side of the Init section. You can customize which null guards are active for the component by clicking the Null Argument Guard icon. By default you will get warnings in your Console if any null arguments are detected on your components in edit mode and at runtime at the moment the initialization takes place.

The Null Argument Guard can be in four different states that are communicated through its icon:

  • Grey icon with a line crossed over it means that null guards are not active.

  • Yellow icon with an exclamation mark means that the component contains null arguments.

  • Blue icon with a check mark means the component contains no null arguments and only some null guards are enabled (for example, edit mode warnings are enabled but runtime exceptions are disabled).

  • Green icon with a check mark means the component contains no null arguments and all null guards are enabled.

Hey Timo!

Hope you’re doing well. I’ve updated to the v1.0.5 and now there are appearing some errors on the project:

Do you know that it can be?

  • MacOS 12.2
  • Unity 2020.3.26f1
  • Metal Graphics API

Thank you in advance!

Best Regards,
Eduardo Bagarrão

Hey @BagarraoEduardo_1

Reinstalling the demo using the ‘Demo Installer’ package should fix the issue.

I had to make a few small changes to the IInitializer interface in order to be able to pull off all improvements I wanted to add in version 1.0.5 and I forgot that the demo scene had a class that manually implemented the interface instead of deriving from one of the base classes.

Sorry for the inconvenience!

1 Like