(来自知乎)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);
}
相关文章推荐
- Unity 3D - MipMap
- Unity和Android相互通信
- Unity3d gameObject
- Unity3d Time
- Unity3d Vector3
- Unity3d transform
- 简单总结Unity使用Resources类资源管理
- 【Unity】Unity下载器下载不下IOS/Android等模块的解决办法
- unity平台相关宏
- [置顶] 【shaderToy】中openGL转到unity中的一些技巧
- Unity UGGUI RawImage 渲染小地图
- unity3d meshBaker 基本的使用
- Unity开发篇(1)
- HoloLens开发手记 - Unity development overview 使用Unity开发概述
- unity游戏在安卓按home或者锁屏键后不能后台运行的结局方案
- unity调android ios 浏览器 uniwebview2.1使用
- 京城游戏人-Day6:Unity 点击坐标与世界坐标
- 【Unity】关于Unity切换手机前后相机
- Unity3d爬坑记—'physicMaterial' is not a member of 'UnityEngine.TerrainData'. 报错!!
- Unity UGUI —— 无限循环List(转载)