Unity与C# 制作一个响应式的触发对象与触发源
PS:欢迎关注我的blog: https:// liiii00.top/
稍后会有RSS订阅,可以期待一下,不定时更新,想分享一些好东西
随着比赛项目的完工,我终于有时间来写写自己对于游戏编程的想法啦!
如你们所见,这里是Liiii00的第一篇想法,这里我想开一个新坑来聊一聊我在架构游戏的时候的一些感受与踩过的坑,希望有所帮助,希望大佬轻喷....这里大概只会有几篇文章的篇幅,背后是花费了非常多时间来码代码跑测试的血与泪。
如你们所见,这一篇文章我想讲讲最近才加入的统一的触发系统,我们用这个实现了事件触发,使得游戏内的玩法变成了近乎组件化的构建方式,极大地增加了工作效率与减少了修改代码的次数。当然这里还有很多不足,不过对于我的小游戏,已经够用了。
起点
软件工程里有一句话: 对修改关闭,对扩展开放 。这套触发系统就是基于这样的原则进行构建的。对于游戏而言,实现一个feature经常需要几个脚本配合使用,起初我们使用的是原始的all in one架构,一个脚本写完所有东西,结果就是一个三四百行的代码里面有数不清的逻辑关系,数不清的编辑器接口,以及数不清的bug,比如我们所使用的控制人物运动的脚本(我也不会去重构它了,这里面的东西应该是不会再改)
小改进
再后来,我们把一些常用的组件拆开了,比如控制材质渐变的组件,自定义的球形触发器(这里包含了unity物理系统的巨坑,在bug篇中会再说一说,先挖个坑)
比如这一个染色墙,挂载了一个染色的功能,有一个当前颜色的参数,还有一个Shader Progressor组件用以产生反馈。是不是就比之前的简洁多了?
新的问题
但是这里还有一个问题没解决,那就是在发生了对应的事件时,我该用什么触发它呢?如果你写过Unity的脚本,很自然地就会想到GetComponent,然后存起来或者直接调用里面写好的方法。如果脚本的量很少,这样做没问题,但是如果你有着十几个feature可选,有着几十个这样的物体,还有着很多的这类事件,那你该咋办?每增加一个这种脚本,就需要多记忆或查找至少两个API(初始化和触发)和新增至少五行代码,还不能保证不出bug。这时候,如果你了解一点接口与继承,就能自定义一个非常好用的自动的通用触发器,这就是标题中的触发对象与触发源。
大跃进
先看接口:
public class TriggerSource : MonoBehaviour
//一堆待触发的对象
public List<GameObject> triggerObjects = new List<GameObject>();
//触发与撤回接口
public void TriggerAll();
public void StopAll();
public interface ITriggerable
//接口必须实现的两个函数
public void Trigger();
public void Stop();
非常简单
在此,被触发的物体继承
ITriggerable
接口, 触发源继承
TriggerSource
即可。这里一个用类继承一个用接口定义的原因是:接口里只能指定必须实现的函数,不能放变量,所以对于触发源来说,直接用一个类去同时存方法和变量是比较合适的。同时,因为有统一的触发接口的存在,触发源的两个函数实际上都可以根据游戏逻辑在子类直接调用,不需要修改,因此也就不需要再套一层接口。由于C#并不支持
多继承
(可以多接口),所以触发源类直接继承了Unity的
MonoBehaviour
,子类脚本再继承触发源即可。这样做虽然有点暴力,对于小游戏来说也是足够用了。
至此,接口定义就填充完毕了。
再来看看
TriggerAll()
方法的实现:
public void TriggerAll()
foreach (var obj in triggerObjects)
foreach (var component in obj.GetComponents<ITriggerable>())
if (component != null)
try { component.Trigger(); }
catch(Exception e) { Debug.LogError(e); }