Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject with Unity3D

Unity3D uses GameObjects. You add components to these gameobjects, where a component is a script (in c# or js) that inherits a base class. Unity itself is written in native code. Components can't have a constructor, and instead use reflection to find if you have certain named methods (OnStart, Update, etc.).

Instead of making my eyes bleed with lack of constructors and other really annoying things, I figured I could do the following:

public class SomeGameBehaviour
{
    public SomeGameBehaviour(IGameObject gameObject) { }
}

(Monobehaviour is the base class)

public class ComponentWrapper : MonoBehaviour, IGameObject { }

..then I could grab gameObject.Transform or what have you from SomeGameBehaviour, while decoupling it from the Unity enforced retardary.

The Problem: I couldn't use the default injection behaviour because Components/MonoBehaviours don't and can't have constructors - it throws errors at you if you try, so I rolled my own Provider.

public class UnityProvider : IProvider
{
    public object Create(IContext context)
    {
        var go = new GameObject(context.Request.Target.Name, typeof(ComponentWrapper));
        var c = go.GetComponent<ComponentWrapper>();

        return c;
    }

    public Type Type { get; private set; }
}

I can see in the Unity editor that the gameobject gets created, and ComponentWrapper gets attached, however Ninject throws a null ref error at me which I can't figure out. It seems to be doing further stuff to either IGameObject or the Target that's upsetting the process.

NullReferenceException: Object reference not set to an instance of an object
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.GetParentDefinition (System.Reflection.MethodInfo method, BindingFlags flags)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.GetParentDefinition (System.Reflection.PropertyInfo property)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.IsDefined (System.Reflection.PropertyInfo element, System.Type attributeType, Boolean inherit)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.HasAttribute (System.Reflection.MemberInfo member, System.Type type)
Ninject.Selection.Heuristics.StandardInjectionHeuristic.ShouldInject (System.Reflection.MemberInfo member)
Ninject.Selection.Selector+<>c__DisplayClass3.<SelectPropertiesForInjection>b__2 (IInjectionHeuristic h)
System.Linq.Enumerable.Any[IInjectionHeuristic] (IEnumerable`1 source, System.Func`2 predicate)
Ninject.Selection.Selector.<SelectPropertiesForInjection>b__1 (System.Reflection.PropertyInfo p)
System.Linq.Enumerable+<CreateWhereIterator>c__Iterator1D`1[System.Reflection.PropertyInfo].MoveNext ()
System.Collections.Generic.List`1[System.Reflection.PropertyInfo].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[System.Reflection.PropertyInfo].AddRange (IEnumerable`1 collection)
Ninject.Selection.Selector.SelectPropertiesForInjection (System.Type type)
Ninject.Planning.Strategies.PropertyReflectionStrategy.Execute (IPlan plan)
Ninject.Planning.Planner+<>c__DisplayClass2.<GetPlan>b__0 (IPlanningStrategy s)
Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map[IPlanningStrategy] (IEnumerable`1 series, System.Action`1 action)
Ninject.Planning.Planner.GetPlan (System.Type type)
Ninject.Activation.Context.Resolve ()
Ninject.KernelBase.<Resolve>b__4 (IContext context)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Activation.IContext,System.Object].MoveNext ()
System.Linq.Enumerable.Single[Object] (IEnumerable`1 source, System.Func`2 predicate, Fallback fallback)
System.Linq.Enumerable.SingleOrDefault[Object] (IEnumerable`1 source)
Ninject.Planning.Targets.Target`1[T].GetValue (System.Type service, IContext parent)
Ninject.Planning.Targets.Target`1[T].ResolveWithin (IContext parent)
Ninject.Activation.Providers.StandardProvider.GetValue (IContext context, ITarget target)
Ninject.Activation.Providers.StandardProvider+<>c__DisplayClass2.<Create>b__1 (ITarget target)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Planning.Targets.ITarget,System.Object].MoveNext ()
System.Collections.Generic.List`1[System.Object].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[System.Object]..ctor (IEnumerable`1 collection)
System.Linq.Enumerable.ToArray[Object] (IEnumerable`1 source)
Ninject.Activation.Providers.StandardProvider.Create (IContext context)
Ninject.Activation.Context.Resolve ()
Ninject.KernelBase.<Resolve>b__4 (IContext context)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Activation.IContext,System.Object].MoveNext ()
System.Linq.Enumerable+<CreateCastIterator>c__Iterator0`1[SomeGameBehaviour].MoveNext ()
System.Linq.Enumerable.Single[SomeGameBehaviour] (IEnumerable`1 source, System.Func`2 predicate, Fallback fallback)
System.Linq.Enumerable.Single[SomeGameBehaviour] (IEnumerable`1 source)
Ninject.ResolutionExtensions.Get[SomeGameBehaviour] (IResolutionRoot root, Ninject.Parameters.IParameter[] parameters)
ObjectFactory.GetInstance[SomeGameBehaviour] () (at Assets/Scripts/Core/ObjectFactory.cs:31)
Grid.Start () (at Assets/Scripts/World/Grid.cs:27)
like image 589
George R Avatar asked Mar 15 '11 09:03

George R


2 Answers

The author of this article http://outlinegames.com/2012/08/29/on-testability/ managed to port Ninject to Unity and he called it Uniject: https://github.com/banderous/Uniject

However I want to point out my (simple) solution as well:

http://blog.sebaslab.com/ioc-container-for-unity3d-part-1/
http://blog.sebaslab.com/ioc-container-for-unity3d-part-2/

I still need to thank Remo Gloor, because thanks to him I understood better the concept of IoC container.

like image 34
sebas Avatar answered Oct 16 '22 00:10

sebas


Ninject executes the activtion actions after the object is created. In this case it is the property injection activation action. It seems that there is a problem in the reflection part that tries to get the the information if there is an inject attribute on a property of your object. Probably that's a bug in Ninject. But for further investigation I need some information:

  1. Are you on the latest version of Ninject (2.2.1.0)?
  2. Which version do you use exactly? (e.g. .NET 4.0 NoWeb)
  3. Can you debug the StandardInjectionHeuristic.ShouldInject of Ninject and investigate which property is causing the problem? And see what's special about this property? (e.g. is it a virtual property that is overridden, are there other properties with the same name, is it an indexer, ...)
like image 73
Remo Gloor Avatar answered Oct 16 '22 00:10

Remo Gloor