unity 架构设计的学习
对于游戏开发而言,不同种类的游戏其架构方式都不同,并不存在什么完美架构模式某种类型是可以有分别的框架的,比如上帝视角ARPG,比如GAL,比如格斗,比如跑酷,比如卡牌,比如打飞机游戏。而他们使用的游戏架构自然也是不同的。通常来说游戏项目最合理的方式是由一个主程根据项目需求制定一套新的结构和各部分间的依赖关系,事先想好各个部分的拓展和通讯方式。有了这个过程,也就没有硬是生搬硬套的再去套用什么模式(MVC之类).
一、一定程度上的逻辑代码的分离,提高代码的可维护性和 可重用性。
https://www.zhihu.com/question/23946609
我对Unity 代码结构组织的困惑 ,经过各种查阅 ,发现其实主要是对于MonoBehaviour 上。mono必须是可是化的,要绑在GameObject上。 对于mono只做视觉上的效果,比如视觉上的特定效果(摄像机振动,飘血,特效,道具掉落,buff类),振动,变色,控制动画,赋值点(控制行为参数数据),控制好它们的依赖,让他们拥有通用性(比如道具掉落的动画,UI动画等),然后这些就不用再开发逻辑的时候去处理这些东西,可以丢一遍了。 而游戏的主体是一个空GameObject 上的脚本,它负责管理所有,分发事件。 其他的地方就不要出现MonoBehaviour了。
这样处理的理由在于生命周期。 mono必须能看到,而逻辑往往会脱离于显示部分,GameObject 未创建往往就需要有逻辑了。 也就是是说你创建一个人物,因该先创建一个MonoBehaviour类,然后在这个类的初始化代码里创建一个GameObject 然后再套上各式MonoBehaviour,然后依然在这个非MonoBehaviour类处理定时逻辑和其他(定时逻辑,比如技能释放,我释放一个技能,由一个技能发射类管理这个技能特效的时效性)。需要时再控制下其他MonoBehaviour播放动画、控制效果之类。 这就和其他非Unity游戏架构没多大区别了。
对于MonoBehaviour的逻辑分离的一个理由在于,比如现在对角色进行控制,使用Character Manager、Controller (也可是说是FSM 、或者Behaviour Tree),来进行角色状态间的控制,将它放在角色obj上。那通常只能是人物资源的那个GameObject。那么我有个需求是换状态的时候切换为完整的另一个资源,那么要不是Compent复制要不是创建子级GameObject,无论哪个都不太好吧。
或者,我想隐藏这个人物,包括里面的动画啊和纯显示用逻辑,但是基本逻辑又想留着(否则什么时候才能恢复显示?),本来直接将GameObject禁用就好了,现在这样就不行了。或者说,它虽然有状态,但并没有显示,而是一个虚拟对象。但如果它不是MonoBehavior就没有任何问题了。这就是我所说的使用MonoBehavior的隐患,其实很多人心中都默默的有这样的感觉吧?这个隐患也是确实存在的。
当然我也不是说MonoBehavior就完全不能用,比如受创变色(切换以及根据color对所有Renderer赋值以及恢复为原材质)肯定是可以的,还有什么动画顺序播放,部件绑定。但是涉及到逻辑的部分,真的建议排除掉MonoBehavior。它确实是毒瘤。
此外有些人不喜欢MonoBehavior是因为start发生的不确定性,还有初始化没有构造函数不便。那你需要端正下思路。你看unity默认的那些组件,你会在乎它们哪个先初始化吗?你会在乎它有没有构造函数参数吗?你不会。因为它们设计成了你不需要考虑这些东西。所以如果你写MonoBehavior,就应该设法按他们的模式走,让初始化无需顾忌顺序参数直接赋值就能解决,而不是抛弃start awake自己搞个create。
而MonoBehavior的主要优点就在于可以独立使用,不依赖于环境。你建个预置拖上去设设参数就能看效果。这是它最大也是唯一的优点,它编码上的不便也都是为了让这一点成为可能。如果你的MonoBehavior做不到这一点,或者你认为不需要,那费老大劲搞成MonoBehavior又没用,何苦。
二 、依旧是Unity 资源组织的问题
https://www.zhihu.com/question/21070379
- Unity有一些自身的约定,譬如项目里的Editor,Plugins等目录作为编辑器,插件目录等等。知名的插件会自己存放一个目录,譬如NGUI等。
所以我们自己的代码,一般目录名会以下划线开头,譬如 "_Scripts", "_Prefabs"等。
对于场景,文档等目录,用两条下划线,以便他们能排在最顶部。
- 代码用C#,别用JS。必要的话用namespace将自己的代码括起来。我们是用namespace把自己积攒的公用库包住。
- C#的注释要认真写,打///就能帮你补全了,没理由偷懒。
- 每个程序文件开头要用一段注释写修改Log,谁改过什么简单留一条说明。就算用了Unity的版本管理或者Git,那些log终究会丢失,只有认真把log写在代码里,才会有意识去认真优化它。
- Unity的脚本逻辑,就功能而言大体分为两种,一种是比较独立的,譬如爆炸之后1秒钟消失,这种单独写个脚本绑定到目标上即可。
更多的是脚本里与其它的脚本进行交互。Unity里提供了一种万金油的方法是SendMessage, 这种方法性能略差,如果你调用的频率不高,随便用也无妨。另一种方法是直接通过对象的实例去调用。
我们的做法是写几个公用的控制器,让它们各司其职,负责各自的事情:
- 写一个一个GlobalManager.cs来控制游戏的全局变量及全局方法。静态类模式。譬如当前玩到第几大关第几小关,玩家的金币数量等。
- 写一个GameController.cs来控制当前关的游戏进程。单实例模式。游戏的主循环也是用它控制。初始化,胜利、失败判定等等。
- 写一个InputController.cs来控制所有的用户输入。单实例模式。鼠标、键盘、触摸屏,我们做游戏是保证同时支持这三种输入的,因为大部分时间是在PC上测试。
关于GameController与InputController的联系,有点让人纠结。一般来讲是在InputContoller里调用GameController.Instance.Foo()执行方法。或者直接对Input写成Listener的模式,让GameController去监听。
- 其它的类似菜单控制器,声音控制器,成就控制器,IAP虚拟道具控制器等等,也是采用类似的方法管理。
- 关于PlayerPref的操作,统一写成静态类的get/set模式,程序中哪里要用则直接读写。
- 如果你的项目里场景的数量少(<5),那么拖入场景的资源可以很随意。如果场景数量很多(几十个,有的解谜游戏每个关卡就是一个场景),那么拖入场景的prefab数量一定要少。
- 设计你的prefab资源里,你要想像当其他人拿到这些资源,是否直接拖入一个空场景里就能run,顶多再简单设置几下。如果你设计的资源不能做到这些,那么得好好重新想想。
三、代码原则
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:feicheng wu
链接:https://www.zhihu.com/question/21070379/answer/24182335
来源:知乎
1.逻辑脚本基于场景划分
2.抽离静态配置数据、全局管理数据以及对局临时数据的管理
3.使用单例模式创建不依赖于场景的游戏对象及其上的全局管理器
4.避免使用GameObject.Find以及SendMessage,声明对象引用以显示标明脚本之间的依赖性,活用delegate解耦合
5.多用组合少用继承(Component的架构真的是太棒了)
6.数据行为与逻辑表现分离,即V与MC的分离,换句话说多写class少写MonoBehavior。(通常初期在快速开发原型时会把一个功能全部实现写在一个继承于MonoBehavior的脚本中,尽早进行重构,抽离出负责数据管理与控制的类,这对于后期功能的增加与修改时很有必要的)
7.善用Coroutine(Coroutine真是太方便了)
8.尽量能够使用自定义的配置文件辅助Prefab上脚本参数的配置。
总的来说记得知乎上看到谁说过cocos2d是程序员友好的,而Unity3D是设计师友好的,写了这么多年Unity3D代码我真是觉得我的思考方式越来越像策划而不是程序员了,使用Unity3D开发,写代码应该只占了大概50%的工作,另外50%都在编辑器上,如果你用过相信你懂得。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:赵忠健
链接:https://www.zhihu.com/question/21070379/answer/65360313
来源:知乎
unity是基于组件开发 一上来确实不太适应,也跟同事讨论了好久,至今没什么太好的结论。说下我们的方案,
一.启动场景一个 object上挂脚本主入口,他负责驱动游戏内所有manager的创建,更新及销毁。
二.人物,AI等需要继承的通过持有gameObject来是实现。而不是挂载到gameObject上。
三.基础功能通过组件实现。挂载到gameObject上。(逻辑表现分离)
总体上就是最基础层面 负责具体功能的用组件来降低耦合度,这样也清晰。而一些需要始终存在的或者需要继承则通过原来的方式,创建脚本对象,然后持有Object,这是这个object不在负责具体逻辑,只是各个基础功能的总和。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:欧宇龙
链接:https://www.zhihu.com/question/21070379/answer/76962412
来源:知乎
有句话,我总会仔细的跟很多朋友讲到
做游戏,牛逼的不是实现某一个功能,而是把成百上千的功能放到一起,却不乱。
而我认为,这条路,必须自己不断总结反思。别人给的经验,往往并不一定合适具体的项目。
不管别的,有几个标准可以检验你的代码是否合理
1:美术资源 提交后,策划配表提交后,不需要程序变换,即可立马看到结果。 比如,一个图标变了,美术可以打包图集,提交。 策划变了一个配表,他可以直接提交配表。 一个新的怪物出现了,让美术自己提交怪物资源..... 最后他们自己跑游戏里一看,发现东西是OK 的。
不管你程序如何设计,这事是最重要的一条。 简单来说就是 让美术和策划能够提交后直接看到结果。
2:游戏暂停时,能否直接通过 视图窗口,看到尽量多的游戏当前状态。
以我做的ARPG为例,
再某一下,我一刀挥出去,打到一个怪物了。 再怪物受击的那一瞬间,我点击暂停。
这个时候我点击怪物这个GameObject,可以看到怪物当前正在播放那个动画,重力是多少,重力加速度是多少,受击的效果有哪几个,数值分别是多少... 可以通过视图窗口看到当前怪物移动组件是否使用中,AI 组件是不是被停滞掉了
.........
这个点是存在争议的,诸多从C++过来的朋友,cocos写得多的朋友。并不习惯组件化这样的东西
3:是否一个类只干一件事
这个同样很重要
甚至可以说是需要被确保的。组件化或许这点有些爽。就是基本确保了你写的某个组件只干一件事。