最近在考虑怎么写游戏中人物的技能与buff状态,在一些论坛里搜索了一些帖子和资料,但是讲的清楚明白的感觉比较少,这里讲一下如何最简单地实现游戏中人物技能和buff的实现。
在游戏中Buff的作用是使人物产生被动的效果影响。比如提升人物的属性、增加人物判定成功的概率、在动作执行时附加额外动作等。
实现的思路如下:
编写ScriptableObject的子类BuffData,以创建保存Buff的id、图标、介绍等信息的asset文件;创建可持有BuffData对象的父类IBuff,其子类的对象将作为玩家在游戏进行中持有的Buff;在IBuff类中设置三个函数BuffStart、BuffEnd、BuffEffect,分别用于Buff添加、Buff消除、Buff触发。制作管理类BuffManager,声明添加Buff、消除Buff、层数叠加等管理方法。对于设计完备的游戏而言,在某一动作下可能触发的Buff数量可能是非常庞大的。如果给每一种buff都设置一个bool变量,每一次操作都对所有的变量进行判断,无论是代码的冗余量还是程序的运行效率都是非常糟糕的。
因此,可以使用C#中的委托来实现Buff功能的动态添加与消除:
在BuffManager类中声明buff触发的委托buffCall;当Buff被添加时,将IBuff类中的Buff触发函数BuffEffect添加至设置好的委托buffCall中;在需要触发的动作的方法中调用buffCall。具体的Unity委托用法可以参考我的这篇博文 C#的委托类(2分钟了解委托用法)
整个系统的结构如图:
BuffData类:
/// <summary> /// 保存Buff的各项数据,如最高等级、持续时间、效果值等 /// </summary> [CreateAssetMenu(menuName = "BuffData", fileName = "buff")] public class BuffData : ScriptableObject { public int id; public string buffName; public int maxLevel; public float[] buffValue = new float[10]; public float[] lastTime = new float[10]; public float restTime; public Sprite buffIcon; [TextArea(2, 3)] public string description; }IBuff类:
/// <summary> /// Buff类的父类,管理buff的变量和通用方法 /// 包括Buff的初始化、升级等 /// </summary> public class IBuff { public int buffLevel; public BuffData data; //初始化,获取对应BuffData,输入至IBuff子类 public IBuff() { buffLevel = 0; } //触发类buff单次发生 public virtual void BuffEffect() { } //状态类buff开始 public virtual void BuffStart() { } //状态类buff结束 public virtual void BuffEnd() { } public void BuffLevelUp() { //不能超过最大等级 if (buffLevel >= data.maxLevel) { return; } buffLevel++; } //获取buff的data数据 public void SetBuffData(BuffData inputdata) { if (this.data != null || inputdata == null) { return; } this.data = inputdata; } }BuffManager类:
/// <summary> /// 管理角色拥有的buff /// </summary> public class BuffManager : MonoBehaviour { //Player相关Manager类的都做成单例模式 public static BuffManager instance; public IBuff[] buffs; public int buffQuantity; //buff执行效果的委托类 public delegate void BuffCall(); public BuffCall buffcall; void Awake() { if (instance == null) { instance = this; } if (instance != this) { Destroy(this); } DontDestroyOnLoad(this.gameObject); } // Start is called before the first frame update void Start() { buffs = new IBuff[5]; buffcall = delegate() { }; buffQuantity = 0; } //添加Buff public bool AddBuff(IBuff buff) { //寻找数组中的空位,添加buff for (int i = 0; i < buffs.Length; i++) { if (buffs[i] == null) { buffs[i] = buff; buffcall += buffs[i].BuffEffect; //添加响应函数 buffQuantity++; buffs[i].BuffStart(); return true; } //存在相同buff--buff升级 else if (buffs[i].id == buff.id) { buffs[i].BuffEnd(); //以防因为数据变化产生的bug buffs[i].BuffLevelUp(); buffs[i].BuffStart(); return true; } } return false; } //删除指定位置的Buff public bool RemoveBuff(int index) { //如果已经为空则不用进行 if (buffs[index] == null) { return false; } buffs[index].BuffEnd(); buffcall -= buffs[index].BuffEffect; //删除响应函数 buffQuantity--; //如果删除的位置不在末尾,则剩余buff队列前移 while (index < buffs.Length) { buffs[index] = null; buffs[index] = index + 1 >= buffs.Length ? null : buffs[index + 1]; index++; } return true; } }本文分享的内容就到这里,总的来说只是提供了一个最简单的buff实现思路,在这个基础上可以增加的内容包括且不限于:
增加响应函数和buff委托的种类,以实现buff在不同行为时的触发响应;编写协程实现buff的计时功能;在BuffEffect方法中也可以结合工厂类和对象池制作buff触发的特效。本文内容更是我自己在开发过程中的一些学习的过程的思路,如果这篇文章有帮助到你,麻烦顺手在下面点个赞,谢谢!