找回密码
 立即注册
查看: 1660|回复: 20

[笔记] Unity3D如何有效地组织代码?

[复制链接]
发表于 2020-12-15 09:27 | 显示全部楼层 |阅读模式
Unity3D如何有效地组织代码?
发表于 2020-12-15 09:33 | 显示全部楼层
准确地说,代码作为Unity项目里的一种资源,此问题应该扩展到如何组织Unity资源。简单说说我们的经验:
- 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,顶多再简单设置几下。如果你设计的资源不能做到这些,那么得好好重新想想。

写了这些,感觉写不下去了。
想吃透Unity,起码得真做出几款产品放上线才行。真正做产品的过程中会碰到各种各样意想不到的问题,代码不断地被重构和妥协,不存在什么最佳的方案。
暂时就写这些吧,希望能抛砖引玉。
发表于 2020-12-15 09:38 | 显示全部楼层
http://mp.weixin.qq.com/s?__biz=MjM5NjE1MTkwMg==&mid=401571840&idx=1&sn=64128b22a3fd092ed2a3d5f5be25b094&scene=0#wechat_redirect

今天看到unity官微发送2015技术讲座,第一个就是代码架构,看完之后很有启发。
发表于 2020-12-15 09:38 | 显示全部楼层
github: liangxiegame/QFramework
Unity 游戏框架搭建 2017
Unity 游戏框架搭建 2018
Unity 游戏框架搭建 2019
Unity 游戏框架搭建 2020 & 跟着案例学 Shader
凉鞋的笔记
发表于 2020-12-15 09:44 | 显示全部楼层
我的代码大概遵循这么几条原则吧(想到哪儿写哪儿不分先后):
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%都在编辑器上,如果你用过相信你懂得。
发表于 2020-12-15 09:45 | 显示全部楼层
Unity除了挂在Game Object上的代码比较难找,这一点跟其他工具做项目不同外,其他就是每个公司自己的管理风格了。在实际操作中,无非就是文件夹/文件夹/.../文件,名字要起的好。这样就在源代码级别管理好了自己的代码。

至于楼主所提的Component-based architecture,个人在管理过程中会习惯去适应unity的脚本概念,虽然与C++等代码做的项目一样,一样有源代码级的很多manager,但是这些manager的启动不再需要自己做一个类似main一样的入口手动启动,只要挂在某个Game Object上,在Start()中启动就可以。配合Unity的script execution order,慢慢脚本的概念就会扎入你的心里。用Unity,因为逃不开在Game Object上挂脚本,所以,个人觉得还是习惯这种方式,的确在概念上也很简单。唯一要注意的是,一定要对Game Object的名字有一定的约束,养成很好的命名习惯。然后自己再写一些查找工具用来查找用到某个名字的脚本的Game Object,配合起来就会理解Unity这样工具的设计理念:基于脚本的对象化。

举个例子,单机版的一些存储,如果配合Unity的playerprefs和Unity的回调机制(OnApplicationPause(),OnApplicationQuit()),你会发现你不用为save/load去设计存储格式,去设计一个基于虚函数的OnSave()、OnLoad(),更不用需要去考虑各个模块的存储先后问题。Unity都给你做好了。

我不知道有哪些模式,只知道也曾经用C++等熟悉的语言来组织自己的逻辑(因为当初刚开始untiy),但是当我做项目多了,发现其实unity的方式挺简单,也很好用。如果所谓的脚本能做好一件事情,你会发现小即是美。
发表于 2020-12-15 09:45 | 显示全部楼层
有句话,我总会仔细的跟很多朋友讲到
做游戏,牛逼的不是实现某一个功能,而是把成百上千的功能放到一起,却不乱。

而我认为,这条路,必须自己不断总结反思。别人给的经验,往往并不一定合适具体的项目。
不管别的,有几个标准可以检验你的代码是否合理

1:美术资源 提交后,策划配表提交后,不需要程序变换,即可立马看到结果。  比如,一个图标变了,美术可以打包图集,提交。 策划变了一个配表,他可以直接提交配表。 一个新的怪物出现了,让美术自己提交怪物资源.....  最后他们自己跑游戏里一看,发现东西是OK 的。
    不管你程序如何设计,这事是最重要的一条。  简单来说就是 让美术和策划能够提交后直接看到结果。

2:游戏暂停时,能否直接通过 视图窗口,看到尽量多的游戏当前状态。
以我做的ARPG为例,
再某一下,我一刀挥出去,打到一个怪物了。  再怪物受击的那一瞬间,我点击暂停。
这个时候我点击怪物这个GameObject,可以看到怪物当前正在播放那个动画,重力是多少,重力加速度是多少,受击的效果有哪几个,数值分别是多少...  可以通过视图窗口看到当前怪物移动组件是否使用中,AI 组件是不是被停滞掉了
.........
这个点是存在争议的,诸多从C++过来的朋友,cocos写得多的朋友。并不习惯组件化这样的东西

3:是否一个类只干一件事
这个同样很重要
甚至可以说是需要被确保的。组件化或许这点有些爽。就是基本确保了你写的某个组件只干一件事。

4:,,,,,
发表于 2020-12-15 09:53 | 显示全部楼层
我的建议是:不要在开头去想如何组织代码,而是先让代码跑起来。

不过既然lz问了,还是大概说下吧。。。
与unity相比,cocos2d顶多只能算是一个跨平台图形库,他基本没有游戏引擎相关的代码,而unity里component-based design就是为了能更灵活地去处理游戏内的逻辑。梁伟国  说的controller,inputManager的概念(其实这些在游戏编程里很普遍)都狠详细,但这些东西在你没有实际详尽的认识之前,其实并不好理解。我建议lz去大致系统的看一下unity方面的书籍,然后实际照着去写,把设计先放在后头,等做出形了再重构。
无止境的重构是做不出东西的。
发表于 2020-12-15 09:59 | 显示全部楼层
首先是几点要在项目初期考虑清楚的东西,会影响到代码组织:
    是否要做热更新(代码,资源),怎么做?
    项目是什么类型,有些什么特点,不同类型组织方式不同
    是否要套用框架(个人不赞成)

这几点会影响:
    如果你要热更新,就不能在prefab上直接挂脚本(prefab上的脚本不能热更新),无论是dll方式,还是lua。
    如果是lua热更新,要考虑清楚哪些部分用lua,哪些部分用c#,中间如何衔接,c#部分是否通过dll热更?
    不同的项目,特点很不一样,除掉一些框架性的部分(网络,资源管理等),游戏的框架要因项目而异。大而全的框架,维护和调优都是困难的,且会增大持续开发难度,毕竟手游是个快速产品
    是否要用框架?(一些mvc框架),个人不赞成游戏套用MVC,MVC更适合软件开发模式,不适合游戏,如果使用,反而会大大臃肿游戏框架。定一个针对项目类型的清晰直接的更好,手游项目,更看重快速迭代,重构,而不是写好一个,通用之。
参考文章
Why are MVC & TDD not employed more in game architecture?
Unity: Now You're Thinking With Components
以上....一点点小总结
发表于 2020-12-15 10:03 | 显示全部楼层
转答案:那些事情是用Unity开发项目应该一开始规划好的?如何避免后期酿成巨坑? - fonzieyang 的回答
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-12-23 12:39 , Processed in 0.107658 second(s), 28 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表