GGJ收获
收获
GGJ比赛确实是一次很新奇的体验,时间很赶,还记得第二天早上五点多起来肝,但是收获也很多,第一次在这么短的时间内完整的跑完了整个流程。
关于单例与跨场景销毁
单例 = 唯一性,保留旧实例,销毁新实例 DontDestroyOnLoad = 持久化
如何解决可单场景调试 + 正常跨场景运行: 单例 + DontDestroyOnLoad + 各场景都放一个角色,缺点:重绑定,把相机、出生点、UI 和场景引用的重绑定问题也一起处理好。 更好的解决方法:Bootstrap 场景,单独做一个常驻启动场景,只放 GameRoot、玩家、音频、UI、输入系统。真正的关卡场景只放地形、机关、出生点、相机锚点,不放玩家本体。运行时永远先进入 Bootstrap,再加载 A/B 关卡。
关于自动创建,自动查找
在没有rule说明的情况下,AI很倾向于不依赖unity中的组件,而是完全通过代码实现,例如摄像机跟随,AI会优先考虑直接代码实现,而不是下载unity中的虚拟相机扩展,导致大量重复的,无意义的,浪费token的,效果差的造轮子工作。
此外AI会优先考虑自动创建、自动查找,而不是告诉我需要一个怎样的组件或物体来实现功能,此问题已通过调整提示词缓解。提示词如下:
核心原则
- Editor First: 优先利用 Unity Editor 的功能(Inspector 配置、Prefab 变体、子物体组合)解决问题,而不是编写复杂的构建代码。
- Component Based: 逻辑应封装在组件中,通过
GetComponent或[SerializeField]获取引用,严禁使用new GameObject()来创建本应是 Prefab 的东西。
编码规范
- 引用优于查找:
- ✅ GOOD:
[SerializeField] private GameObject highlightObject;(让用户在面板拖拽) - ❌ BAD:
GameObject.Find("Highlight")或new GameObject("Highlight") - ❌ BAD: 在代码中动态生成 Texture2D、Mesh 或 Material,除非是程序化生成内容的专门需求。
- ✅ GOOD:
关于耦合
耦合一直是做游戏遇到的一个大问题,从《龙舟竞渡:茶峒惊桨》制作的时候就有发现。很多时候不是功能做不出来,而是做到后面发现耦合太严重,改一块会引发连锁反应。在《龙舟竞渡:茶峒惊桨》制作过程中,我完全没有解耦合意识,所有角色相关的模块全都堆到一个脚本里。
在此次GGJ比赛中我对此有进行一定的改进,有尝试按照系统去做拆分,比如拆成对话系统,表里世界切换系统,死亡与复活系统等等,但是做到后面还是会出现隐式耦合的情况,例如死亡与复活不仅要处理玩家状态,还要同时考虑世界切换、摄像机、背景和输入门禁。
后续改进的方向我认为应该是强化订阅与事件的学习,做职责拆分 + 事件化,依赖关系就从“脚本内部偷偷改别人”变成“别人明确订阅这个事件”。
关于事件订阅
一个对象负责“广播发生了什么”,另一些对象负责“监听这个消息并作出反应” 事件(Event):世界切换了 订阅(Subscribe):相机、背景、音效都表示:一旦世界切换,请通知我
事件订阅是unity中比较重要的一个功能,用于解耦非常好用,但是我往往不习惯用这个,这就导致一个系统必须知道所有其它系统,非常容易越写越乱。