One Strange Week in Spain

will_aurimas_marc

Let’s start with what this article will not be about. Dependency injection will not be a native feature in Unity. Not, at least, for the foreseeable future.

But for one exciting week, it was.

A question I got more than once when I joined Unity was “will Strange become a part of Unity?” I answered, “No, Unity are hiring me. Not buying Strange.” Nevertheless, the idea of integrating certain features of Strange  directly into Unity has never been far from my mind.

Last week, Unity engineers from all over the world met up in Malaga, Spain for a big hackweek: one hundred and fifty engineers, all brainstorming and blue-skying projects they thought might be fun or useful for the company and/or community.  And a mighty assembly of ideas it was.

Among these brave souls, our friend Will Corwin proposed the integration of dependency injection directly into Unity as a native feature. Obviously, this was something I couldn’t pass up. Together with colleague Aurimas Cernius, we leapt in and made it so.

Our goals:

  • Create a simple, easy-to-use DI system
  • Explain the value of DI to hackweek participants

We were not attempting to make Strange a native feature. The MVC construct on which Strange is based is a useful application paradigm, but it’s certainly not the only one. None of us thought that the larger framework was appropriate as a native feature. What we did (and do) regard as appropriate, was the injection feature itself. To do this we needed the following pieces:

  • The injection system, obviously
  • The reflection system, for efficient caching
  • A context, for writing bindings

We started by copying the appropriate packages from Strange, stripping out anything that wasn’t useful to our case. Mostly this meant simplifying the Context.

One of the cool things about adding injection directly to Unity is the ability to ditch some of the shoehorned functionality Strange copes with on startup. UnityEngine.Injection, as our system came to be, had C++ hooks to kick off certain processes, making those early milliseconds cleaner and more reliable.

It all started with this player setting.

Use_DI

With this Boolean switched on, the DI system went to work, searching for a Context marked with the [BindingContext] attribute. This meant that a ContextView in our system was optional. Sure, you’d want one to mark out sub-contexts, but a viewless Context is probably a better choice for your first Context, which shouldn’t have to be tied to a view in any way, and shouldn’t be destroyed if your scene changes (we’ve even thought of implementing this idea in Strange itself).

Of course we have our familiar [Inject] and [PostConstruct] tags. The only other place where our injection system touches the developer is in MonoBehaviours. Aurimas wrote this native hook:

[InjectDependencies]
public class SomeClass : MonoBehaviour {
    [Inject]
    public ISomeService YouKnowTheDrill { get; set; }
}

Any MonoBehaviour so marked seeks out and finds the correct Context, based on the same set of rules Strange uses. The results of these simple changes were pretty cool.

  • A developer can start playing with DI with as little as a button click and about 5 lines of code (An Inject tag and a simple Context). Compare that with what it currently takes to get into Strange for the first time.
  • MonoBehaviours can request injection directly. They don’t need to extend View.
  • Strange (or other IoC systems) could be layered on top of this system.
  • A colleague pointed out that resolving bindings should really be a compile-time operation, not runtime. We agree. That could potentially be fixed by internalizing DI.

We had most of the functionality working by Wednesday morning, so we had accomplished our first goal. But for the second goal — explaining the value — we didn’t yet feel like we had an ideal presentation. Fortunately, inspiration struck late Wednesday: we quickly re-tooled a one player game to two player and wired it up with a real-time server.

Actually, we used not one real time server but two! We leveraged an experimental server written by another hackweek team, then showed how using DI bindings we could instantly swap between the experimental one and UNet.

The video below (no audio) shows our presentation “slides” along with some side-by-side video of the game running with the experimental server.

[BindingContext]
public class MyGameContext : Context
{
  protected override void mapBindings()
  {
    InjectionBinder.Bind<GameManager>().ToSingleton();
    InjectionBinder.Bind<IPlayer>().To<PlayerModel>().ToSingleton();
    InjectionBinder.Bind<IDispatcher>().To<Dispatcher>().ToSingleton();
    //InjectionBinder.Bind<IRealtimeServer>().To<NullServer>().ToSingleton();
    InjectionBinder.Bind<IRealtimeServer>().To<UnetServer>().ToSingleton();
    //InjectionBinder.Bind<IRealtimeServer>().To<CloudCodeServer>().ToSingleton();
  }

  protected override void postBindings ()
  {
    InjectionBinder.GetInstance<GameManager>();
  }
}

This was a fun project. It led us to consider more deeply than we ever had before what it would mean to integrate DI directly into Unity. As I said at the top, this isn’t something we see being implemented in real product anytime soon, but we made more folks within Unity aware of what we do and why. We met some great people, heard about some amazing ideas, and started some conversations that may someday bear fruit.

Advertisements

8 thoughts on “One Strange Week in Spain

  1. Would love to have this built-in. Do you have a work around for the fact that Instantiate doesn’t allow you to parent before creation or Awake? Or rather, is there a way to find the context for dynamic GameObjects on creation rather than waiting for us to reparent and then wait for Start to get called?

    • Embarrassingly, I’m not entirely sure. As is sometimes the way during a coding jam like this, we cut a lot of corners just to make a working proof-of-concept. I *believe* that Aurimas tested exactly this case to ensure that injections worked right from the earliest possible moment…but I won’t swear to it.

  2. Thanks for sharing. I would surely love to see proper DI in Unity to build more on top of it and it’s great to see that the hack week brings this kind of experiments to life and inspire 🙂

  3. I’d love to see the Injection bits pulled out of Strange, so that they could be used like this. Is there an easy way to extricate that code from the rest of Strange? Any suggestions for someone trying this?

    • In fact, you can, and fairly easily. You simply need to write your own subclass of Context that includes the injectionBinder, but avoids all the MVCS structure. That was, in essence, what our stripped-down version was (plus the tie-ins to Unity, of course).

      I’d actually like to see more people try this out, in a way. I really like the MVCS structure…it suits my way of thinking…but DI and MVCS are obviously *not* the same thing, and exploration of other ways to use DI within Unity would be beneficial to the entire cause, I think.

      • Sorry, the demo…such as it is…is Strange. This was a hack week project that requires the ability to edit and compile the actual Unity engine, so it’s not something I can provide outside the company. 😦

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s