引子
了解设计模式的人应该都多少听说过MVC模式。
严格意义上来说,“MVC模式”是一个伪概念,因为MVC并不属于设计模式,至少不属于GoF的23种设计模式之一,而更像是一个设计模式的结合体:V和C之间会实现观察者模式,M内部会实现单例模式,C在派发任务时会实现Command模式。
不得不说,MVC模式对软件的高可扩展性和高可维护性做出了巨大的贡献,这也使得MVC模式成为很多中等规模甚至大规模软件的常用框架,且经历了20余年仍旧在软件开发领域流行并通用,足可见MVC模式的经典。
但是传统MVC模式真的那么完美吗?
传统MVC的痛点
让我们一个个来说。
Controller:控制器,包含了项目的业务逻辑。但是也是被大家吐槽最多的一个,原因就是很多人,或者说大多数人,习惯于什么都往Controller里写,最后一个Controller超过1000行代码是司空见惯的事。所以关于传统MVC的第一个痛点就是,Controller过于臃肿。
Model:模型,包含了项目的数据模型。MVC定义之初,Model是核心,旨在使得同一个Model可以被复用到多个项目或者被复用到同一个项目的不同模块之中。但是在实际项目中,Model还承载着纯Model层内部的运算的工作,但是运算部分会项目的不同而有所区别,因此与项目的适配反而成为了Model可复用的枷锁。所以关于传统MVC的第二个痛点就是,Model变得不可复用。
View:视图,包含了项目所有的UI组件。视图本身没有什么好被大家诟病的,但是由于MVC中对于View和Controller界限的模糊界定造成了使用者在写代码的时候会觉得这部分代码放在View或者Controller里都可以的情况。例如事件的处理,组件的组合等。所以关于传统MVC的第三个痛点就是,View概念的模糊。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MVC是UI专用的模式,适用于横向铺量的项目。游戏(非UI部分)显然不是这样的项目。
抛弃MVC迷信是每个游戏程序员的必经之路。
通常来讲,游戏项目最合理的方式是由一个主程根据项目需求制定一套新的结构和各部分的依赖关系,事先想好各部分的扩展和通信方式,上面那位说的就是一个例子。这是必须的步骤,但既然有了这个步骤,再硬套一个通用的MVC框架就是多此一举了。
也就是说,游戏正是因为结构不通用,所以才不能用MVC框架。或者说,由于游戏结构复杂度远高于MVC框架,你仅仅使用MVC框架是不能解决问题的。MVC框架里的概念当然是有用的,但只有它们是远远不够的。游戏需要用到的架构知识远比MVC这种基础玩意高得多得多。
而游戏之所以不可能设计通用框架是因为一个有用的通用框架的复杂度和使用难度会超出所有人的忍耐极限。某种类型是可以有分别的框架的,比如上帝视角ARPG,比如GAL,比如格斗,比如跑酷,比如卡牌,比如打飞机游戏。而他们使用的游戏架构自然也是不同的。
讨论这个会没完没了。
我看题主的困惑在于MonoBehavior。这个东西是挺讨厌。这样,你可以把它当作Button一类的东西处理,它只做和视觉有关的特定功能,比如震动,比如变色,比如控制动画,赋点值然后放它一边玩去,控制好它们的依赖,让它们拥有通用性。然后游戏主体是一个空GameObject上的脚本,它负责管理所有,分发事件。其他地方就不要出现MonoBehavior了。
这样的理由在于生命周期。MonoBehavior必须依附与GameObject,也就是必须要能看到,而逻辑往往会脱离于显示部分,GameObject未创建往往就需要有逻辑了。
也就是说你创建一个人物,应该先创建一个非MonoBehavior类,然后在这个类的初始化代码里创建一个GameObject然后再套上各式MonoBehavior,然后依然在这个非MonoBehavior类处理定时逻辑和其他。需要时再控制下其他MonoBehavior播播动画闪一闪之类。
这样和非Unity游戏也就没啥区别了。想干什么就干什么很自由,也不存在隐患。
---
另外我并没有针对的意思啊,比如说伍一峰将Character Manager继承于MonoBehavior其实是有问题的。它是MonoBehavior就必须依附于GameObject,那通常只能是人物资源的那个GameObject。那么我有个需求是换状态的时候切换为完整的另一个资源,那么要不是Compent复制要不是创建子级GameObject,无论哪个都不太好吧。
或者,我想隐藏这个人物,包括里面的动画啊和纯显示用逻辑,但是基本逻辑又想留着(否则什么时候才能恢复显示?),本来直接将GameObject禁用就好了,现在这样就不行了。
或者说,它虽然有状态,但并没有显示,而是一个虚拟对象。
但如果它不是MonoBehavior就没有任何问题了。
这就是我所说的使用MonoBehavior的隐患,其实很多人心中都默默的有这样的感觉吧?这个隐患也是确实存在的。当然我也不是说MonoBehavior就完全不能用,比如受创变色(切换以及根据color对所有Renderer赋值以及恢复为原材质)肯定是可以的,还有什么动画顺序播放,部件绑定。但是涉及到逻辑的部分,真的建议排除掉MonoBehavior。它确实是毒瘤。
此外有些人不喜欢MonoBehavior是因为start发生的不确定性,还有初始化没有构造函数不便。那你需要端正下思路。你看unity默认的那些组件,你会在乎它们哪个先初始化吗?你会在乎它有没有构造函数参数吗?你不会。因为它们设计成了你不需要考虑这些东西。所以如果你写MonoBehavior,就应该设法按他们的模式走,让初始化无需顾忌顺序参数直接赋值就能解决,而不是抛弃start awake自己搞个create。而MonoBehavior的主要优点就在于可以独立使用,不依赖于环境。你建个预置拖上去设设参数就能看效果。这是它最大也是唯一的优点,它编码上的不便也都是为了让这一点成为可能。如果你的MonoBehavior做不到这一点,或者你认为不需要,那费老大劲搞成MonoBehavior又没用,何苦。
链接:https://www.zhihu.com/question/23946609/answer/51551778
链接:http://www.jianshu.com/p/47deaced9eb3