您的位置:首页 > 移动开发 > Unity3D

(来自知乎)Unity反射机制的理解

2016-07-20 09:19 387 查看


Unity3d是如何调用MonoBehaviour子类中的Start等方法的?

Unity3d开发,需要继承MonoBehaviour,实现Start方法:

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
private GameObject target;
void Start() {
target = GameObject.FindWithTag("Player");
}
}


但是这个Start方法是私有的,而不是override MonoBehaviour中已有的方法。

按我的理解,应该在MonoBehaviour中设计一个abstract或virtual的Start方法,然后让子类去实现它才对啊!

难道Unity3d 引擎部分在调用ExampleClass时用的是反射?他们这样设计是出于什么目的的?有什么好处?

添加评论

分享


查看全部 8 个回答

18赞同
反对,不会显示你的姓名



hillin

18 人赞同

2016/1/21:阅读到这篇文章:(English)
Going deep with IMGUI and Editor Customization,本答案中的部分内容是不正确的,特此更正:

Unity使用类反射而非虚函数的方式来调用Update等函数,主要原因是在于,并非所有的MonoBehaviour都需要Update(或Start,Awake等等,下同)。Unity会维护一个需要Update的Behaviour列表,藉此避免进行空的虚函数调用,提高性能。

但这一点与原回答第5条的建议并不冲突。

以下是原回答。

1. 是使用反射来调用的,这一点@愤怒的泡面 已经说过了。反射可以访问私有方法;

@庞巍伟 指出这是用mono的API来实现的(而不是C#内建的反射机制),我没看过Unity的源码所以不敢做定论,但我的理解是,mono的这套机制也是reflection,只不过不是The Reflection而已,不需要太教条地去理解。Mono的API可能效率更高,但对于单次调用,性能仍然逊于虚方法调用。不过能了解Unity的实际实现方式也是收益良多,感谢。

2. 反射实际上是开销非常大的调用方式,比之虚方法来说要高得多,因此Unity选择使用反射并非是出于性能方面的考虑。实际上,每帧调用的这些事件方法从数量级上来说是很卑微的,反射造成的性能影响亦可忽略不计;

3. Unity使用这种事件机制的根本原因是出于对灵活性的考虑。Unity采用组件式设计,触发一个事件,需要通知到相应gameobject的所有组件。如果使用多态来实现,则必须假设所有组件都派生自包含此事件的基类,或者筛选出派生自此基类的组件逐一通知。这样一来是麻烦,二来则容易带来复杂的继承关系,与组件式这种倡导用聚合代替继承的设计从理念上就是相悖的。

另一方面的原因则是为了跟JS保持一致性。这种事件机制对于JS这种动态类型语言来说是浑然天成的。

4. 这种设计最大的缺陷在于事件名通过字符串耦合,如此一来,完全绕开了编译期静态检查,无法为事件调用的正确性提供保障;在复杂的系统里,也可能因为事件重名而导致bug。

5. 我个人倾向的改良性设计是:提供基础接口IEvent<T>

interface IEvent<T> where T : EventArgs
{
void Raise(object sender, T args);
}


令所有事件都声明一个继承于此接口的接口,如:

interface IReviveEvent : IEvent<EventArg>
{
}


所有包含此事件的类显示实现此接口:

class Mob : MonoBehaviour, IReviveEvent
{
void IReviveEvent.Raise(object sender, EventArgs e)
{
//...
}
}


调用事件可以通过为GameObject类写一个扩展方法来实现,手机党就不挣扎码字了。

不过这只适用于自己的代码,Unity内建的那些事件(Update之类的)只能由它去了。



知乎用户

c++层做反射各大游戏引擎都能看到类似实现。IReviveEvent侵入性太强了,直接导致每个monoBhr都得有全部的方法,引擎也得对所有monoBhr的所有方法做无用功调用,而事实上只有少部分monoBhr需要update。

7 月前

更多回答

20赞同
反对,不会显示你的姓名

知乎用户 ,飘逸的程序员/初级制作人

20 人赞同
unity用的是mono,mono的api就支持基于字符串查找的方法, 然后存下来指针,以后调用:

最后给你贴一段unity源代码吧:

inline void MonoBehaviour::Start ()

{

....

method = m_Methods[MonoScriptCache::kCoroutineStart];

if (method)

InvokeMethodOrCoroutineChecked (method, SCRIPTING_NULL);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: