使用 Unity3D 进行 Ninject

Posted

技术标签:

【中文标题】使用 Unity3D 进行 Ninject【英文标题】:Ninject with Unity3D 【发布时间】:2011-07-15 15:35:33 【问题描述】:

Unity3D 使用游戏对象。您将组件添加到这些游戏对象,其中组件是继承基类的脚本(在 c# 或 js 中)。 Unity 本身是用本机代码编写的。组件不能有构造函数,而是使用反射来查找是否有某些命名方法(OnStart、Update 等)。

与其因为缺少构造函数和其他真正令人讨厌的事情而让我的眼睛流血,我想我可以做以下事情:

public class SomeGameBehaviour

    public SomeGameBehaviour(IGameObject gameObject)  

(Monobehaviour 是基类)

public class ComponentWrapper : MonoBehaviour, IGameObject  

..然后我可以从 SomeGameBehaviour 中获取 gameObject.Transform 或你有什么,同时将它与 Unity 强制延迟解耦。

问题: 我不能使用默认的注入行为,因为组件/MonoBehaviours 没有也不能有构造函数——如果你尝试它会向你抛出错误,所以我推出了自己的 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; 

我可以在 Unity 编辑器中看到游戏对象被创建,并且 ComponentWrapper 被附加,但是 Ninject 向我抛出了一个我无法弄清楚的空引用错误。它似乎正在对 IGameObject 或 Target 做进一步的事情,这会扰乱这个过程。

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 平台不一定可以访问与编辑器相同的单声道功能,尤其是网络播放器和 ios 播放器。 【参考方案1】:

Ninject 在对象创建后执行激活动作。在这种情况下,它是属性注入激活操作。如果对象的属性上有注入属性,则反射部分似乎存在问题,该问题试图获取信息。这可能是 Ninject 中的一个错误。但为了进一步调查,我需要一些信息:

    您使用的是最新版本的 Ninject (2.2.1.0) 吗? 您具体使用哪个版本? (例如 .NET 4.0 NoWeb) 能否调试 Ninject 的 StandardInjectionHeuristic.ShouldInject 并调查导致问题的属性?看看这家酒店有什么特别之处? (例如,它是否是被覆盖的虚拟属性,是否有其他同名的属性,是否是索引器,...)

【讨论】:

我昨晚从 NuGet 中删除了它 - 它是 2.2.1、.NET 3.5 NoWeb。顺便说一句,Unity 使用 Mono 2.6.5。在 Unity 中进行调试的唯一方法是使用 MonoDevelop,而要使用外部 dll,它需要自己的 mdb,而不是 pdb。我使用了包含的转换器和它的抛出错误,试图让它用于 Ninject,所以我也必须拉下 pdb2mdb.exe 的源代码来看看发生了什么! 请使用 github 上的单声道版本。 github.com/ninject/ninject/downloads。它带有 mdb。 NuGet 目前不支持单声道。 Mono版本调试成功了吗? 抱歉没有回复。我在使用 Mono 调试时没有任何运气,我正在重新审视我是如何进行 DI 的。【参考方案2】:

本文作者http://outlinegames.com/2012/08/29/on-testability/ 成功将Ninject 移植到Unity,并将其命名为Uniject:https://github.com/banderous/Uniject

不过我也想指出我的(简单)解决方案:

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

我还是要感谢 Remo Gloor,因为多亏了他,我才更好地理解了 IoC 容器的概念。

【讨论】:

【参考方案3】:

我想对此添加一些内容(尽管我的问题可能有所不同),因为我也想在 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。 也不要使用 Inject 尝试使用 kernel.Get() 并让 Ninject 创建实例。并删除内核绑定。这已经被内核添加了。 最终我决定制作自己的注射器。它还不错,简单且面向统一。我有创建一个开源项目的打算,但我认为这需要一些时间。

以上是关于使用 Unity3D 进行 Ninject的主要内容,如果未能解决你的问题,请参考以下文章

使用 Unity3d 插件在我自己的智能手机上进行测试

基于Unity3D之TimeLine的讲解(一)

使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试

用unity3d制作游戏的时候用得上Python技术吗?

Unity3D学习:使用JSON进行对象数据的存储读取

unity3d 如何进行场景切换?