Ninject与Unity3D的配合使用

9
Unity3D使用游戏对象。您可以向这些游戏对象添加组件,其中组件是继承基类的脚本(使用C#或JS编写)。Unity本身是用本地代码编写的。组件不能有构造函数,而是使用反射来查找是否有特定命名的方法(OnStart、Update等)。
为了避免让我眼睛流血,因为缺少构造函数和其他非常烦人的事情,我想我可以采取以下措施:
public class SomeGameBehaviour
{
    public SomeGameBehaviour(IGameObject gameObject) { }
}

Monobehaviour是基类。

public class ComponentWrapper : MonoBehaviour, IGameObject { }

如果我可以从SomeGameBehaviour中获取gameObject.Transform或其他内容,同时将其与Unity强制执行的减速解耦。

问题: 我无法使用默认注入行为,因为组件/MonoBehaviours没有构造函数-如果您尝试,它会向您抛出错误,因此我编写了自己的提供程序。

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; }
}

我在Unity编辑器中看到游戏对象被创建,并且ComponentWrapper被附加,但是Ninject抛出了一个空引用错误,我无法弄清楚。似乎它对IGameObject或目标进行了进一步的操作,这扰乱了整个过程。

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)

当您完成此操作后,应尽快在目标平台上进行测试。各种Unity3d平台并不一定具有与编辑器相同的mono功能访问权限,特别是Web播放器和iOS播放器。 - Calvin
3个回答

6
Ninject在对象创建后执行激活操作。在这种情况下,它是属性注入激活操作。似乎在反射部分中存在问题,尝试获取对象的属性上是否有注入属性的信息。可能这是Ninject中的一个错误。但是为了进一步调查,我需要一些信息:
  1. 您使用的是最新版本的Ninject(2.2.1.0)吗?
  2. 您确切使用的是哪个版本?(例如.NET 4.0 NoWeb)
  3. 您能否调试Ninject的StandardInjectionHeuristic.ShouldInject并调查哪个属性导致问题?以及这个属性有什么特别之处?(例如,它是被覆盖的虚拟属性,是否有其他同名属性,它是一个索引器等)

我昨晚从NuGet下载了它 - 它是2.2.1,.NET 3.5 NoWeb。顺便说一句,Unity使用Mono 2.6.5。在Unity中唯一调试的方法是使用MonoDevelop,并且为了使用外部dll,它需要自己的mdb,而不是pdb。我使用了附带的转换器,但尝试将其制作为Ninject时出现错误,所以我还需要下载pdb2mdb.exe的源代码来查看情况! - George R
请使用在Github上找到的Mono版本。https://github.com/ninject/ninject/downloads。它附带了mdb。NuGet目前不支持Mono。 - Remo Gloor
你有用Mono版本成功调试过吗? - Remo Gloor
抱歉没有回复。我在使用Mono进行调试时没有什么好运,现在我正在重新审视我的DI实现方式。 - George R

5

0

我想在这里补充一些内容(尽管我的问题可能不同),因为我也想在Unity3D项目中使用ninject。

不幸的是,Ninject在Unity 3D 3.5中无法工作,无论是使用mono develop编译版本还是源代码(2.2.1)。

然而,使用源代码后,我明白了原因:首先,我必须手动禁用所有与NOWEB定义相关的代码(不幸的是,如果我没有记错,Unity3D不支持编译级别的定义),但真正的崩溃发生在NO_ASSEMBLY_SCANNING相关的代码上。确切地说,在Kernel.cs的这些行:

#if !NO_ASSEMBLY_SCANNING
            if (this.Settings.LoadExtensions)
            {
                this.Load(new[] { this.Settings.ExtensionSearchPattern });
            }
#endif

我尝试禁用这个定义,然后Unity3D就不再抱怨了。虽然在我这样做之后,注入似乎停止工作了:

StandardKernel kernel = new StandardKernel();   

kernel.Bind<IKernel>().ToMethod(context => kernel);

kernel.Inject(ObjectWhichNeedsKernel)

它没有起作用,ObjectWhichNeedsKernel没有注入IKernel,但可能只是我的代码有问题。

所以我认为Ninject对于Unity3D来说不够轻量级 :/


我自己从未使用过Unity3D。我的猜测是更接近WP7和SL构建,因为Unity3D似乎有一些限制。你能否尝试移除代码生成部分以切换到反射?有两种方法可以做到这一点。一种是通过删除NO_LCG部分来完全删除代码。或者将ninject设置中的UseReflectionBasedInjection属性设置为true。 - Remo Gloor
另外,不要使用Inject,而是尝试使用kernel.Get<ObjectWhichNeedsKernel>(),让Ninject创建实例。并且删除内核绑定。这已经由内核添加了。 - Remo Gloor
最终我决定自己制作注入器。它不错,简单且面向Unity。我有意创建一个开源项目,但我认为这需要更多的时间。 - sebas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接