Unity 从0开始编写一个技能编辑器_02_Buff系统的Handler

BuffHandler可以是用于处理角色身上buff的Mono类,任何具备跟Buff交互的角色,都要携带这个BuffHandler脚本。如果你的Buff有额外的处理机制,比如互斥Buff(如:免疫负面效果的霸体),需要在AddBuff方法中额外处理。当然 也可以不作为mono转为BaseFightObj的一个引用的实例,在BaseFightObj这一类中的生命周期进行调用

csharp 复制代码
    /// <summary>
    /// Buff处理器的接口
    /// </summary>
    public interface BuffSystem_IBuffHandler
    {
        /// <summary>
        /// 添加Buff 
        /// </summary>
        /// <param name="buffId">施加Buff的ID</param>
        /// <param name="caster">施加Buff者</param>
        public void AddBuff(int buffId, GameObject caster);
        /// <summary>
        /// 移除Buff
        /// </summary>
        /// <param name="buffId">要移除的Buff id</param>
        /// <param name="removeAll">如果对象同时存在多个同id的buff,是否将所有一并移除</param>
        public void RemoveBuff(int buffId, bool removeAll = true);
        /// <summary>
        /// 移除Buff(不执行OnBuffRemove)
        /// </summary>
        /// <param name="buffId">要移除的Buff id</param>
        /// <param name="removeAll">如果对象同时存在多个同id的buff,是否将所有一并移除</param>
        public void InterruptBuff(int buffId, bool interuptAll = true);
        /// <summary>
        /// 注册事件:添加Buff时
        /// </summary>
        /// <param name="act">注册的行为</param>
        public void RegisterOnAddBuff(Action act);
        /// <summary>
        /// 删除事件:添加Buff时
        /// </summary>
        /// <param name="act">注册的行为</param>
        public void RemoveOnAddBuff(Action act);
        /// <summary>
        /// 注册事件:删除Buff时
        /// </summary>
        /// <param name="act">注册的行为</param>
        public void RegisterOnRemoveBuff(Action act);
        /// <summary>
        /// 删除事件:删除Buff时
        /// </summary>
        /// <param name="act">注册的行为</param>
        public void RemoveOnRemoveBuff(Action act);
    }

接口实现如上所示,可以看到实现了一些 "trigger" 可以在特定需求下减轻一些主线程的压力。

依照接口,我们可以实现具体的BuffHandler类

1.AddBuff 方法如下 可以看到 对上文的 重复添加同一种Buff时的行为 做了处理。这是按照策划的需求给出的,不必要求在初次编写时考虑所有情况 便于扩展即可

AddBuff的多种情况
csharp 复制代码
public enum BuffMutilAddType
{
    resetTime,                     //重置Buff时间
    multipleLayer,                 //增加Buff层数
    multipleLayerAndResetTime,     //增加Buff层数且重置Buff时间
    multipleCount                  //同种Buff同时存在多个,互不影响
}
AddBuff方法的具体实现
csharp 复制代码
private void AddBuff(IBuff buff, GameObject caster)
        {
            if (!updated) Update();
            Buff bf = (Buff)buff;
            if (bf.IsEmpty())
            {
                Debug.LogError("尝试加入空Buff");
                return;
            }
            //无论是否可以添加都执行初始化和BuffAwake
            bf.Initialize(this, caster);
            bf.OnBuffAwake();

            //确定能添加Buff时
            onAddBuff?.Invoke();
            //检查是否已有同样的Buff
            Buff previous = buffs.Find(p => p.Equals(bf));
            //没有则直接添加
            if (previous == null)
            {
                //结算Tag效果
                if (bf.BuffTag != BuffTag.none)
                {
                    //首先:如果有已有buff能抵消新buff,则直接抵消
                    if (buffs.Any(b => BuffManager.GetInstance().TagManager.IsTagCanAddWhenHaveOther(bf.BuffTag, b.BuffTag)))
                    {
                        bf.SetEffective(false);
                        bf.OnBuffDestroy();
                        return;
                    }
                    for (int i = buffs.Count - 1; i >= 0; i--)
                    {
                        //之后:如果新buff没有被抵消,则新buff抵消已有的buff
                        //Debug.Log("Running:" + bf.BuffTag + ":" + buffs[i].BuffTag);
                        if (BuffManager.GetInstance().TagManager.IsTagRemoveOther(bf.BuffTag, buffs[i].BuffTag))
                        {
                            RemoveBuff(buffs[i]);
                        }
                    }
                }
                buffs.Add(bf);
                forOnBuffStart += bf.OnBuffStart;
                return;
            }
            //有则根据重复添加的类型处理。
            //一个Buff对象的Start不会重复执行
            //只有mutilCount类型会同时存在多个同id Buff
            switch (previous.MutilAddType)
            {
                case BuffMutilAddType.resetTime:
                    previous.ResetTimer();
                    //forOnBuffStart += previous.OnBuffStart;
                    break;
                case BuffMutilAddType.multipleLayer:
                    previous.ModifyLayer(1);
                    //forOnBuffStart += previous.OnBuffStart;
                    break;
                case BuffMutilAddType.multipleLayerAndResetTime:
                    previous.ResetTimer();
                    previous.ModifyLayer(1);
                    //forOnBuffStart += previous.OnBuffStart;
                    break;
                case BuffMutilAddType.multipleCount:
                    buffs.Add(bf);
                    forOnBuffStart += bf.OnBuffStart;
                    break;
                default:
                    break;
            }
        }

然后是RemoveBuff的两种情况

csharp 复制代码
 /// <summary>
        /// 移除一个Buff,移除后执行OnBuffRemove
        /// </summary>
        /// <param name="buff">要移除的Buff</param>
        private void RemoveBuff(IBuff buff)
        {
            Buff bf = (Buff)buff;
            bf.SetEffective(false);
        }
        /// <summary>
        /// 移除一个Buff,移除后不执行OnBuffRemove
        /// </summary>
        /// <param name="buff">要移除的Buff</param>
        private void InteruptBuff(IBuff buff)
        {
            Buff bf = (Buff)buff;
            bf.SetEffective(false);
            buffs.Remove(bf);
            forOnBuffDestroy += ((Buff)buff).OnBuffDestroy;
        }

之所以这么设计是因为 有些负面Buff效果在onRemoveBuff执行 然而 互斥buff或者说实际的净化buff 不希望onRemoveBuff执行,这会导致一些预期之外的后果 可能不便于实现一些需求,所以编写这两种本质上均为RemoveBuff的方法,为了方便理解 命名为InteruptBuff 打断buff

上述的私有方法AddBuff(IBuff buff, GameObject caster),RemoveBuff(IBuff buff),和InteruptBuff(IBuff buff)封装了实际的Buff处理逻辑。这些方法实现了添加、移除和打断Buff的具体细节。

将复杂的逻辑封装在私有方法中,可以更容易地进行维护和修改。任何更改都只需要在私有方法内部进行,而不影响公有接口

csharp 复制代码
  public void AddBuff(int buffId, GameObject caster)
        {
            var b = BuffManager.GetInstance().GetBuff(buffId);
            AddBuff(b, caster);
        }

        public void RemoveBuff(int buffId, bool removeAll = true)
        {
            var b = buffs.FirstOrDefault(b => b.ID == buffId);
            if (b == null)
            {
                Debug.Log("尝试从" + this.name + "移除没有添加的Buff。 id:" + buffId);
                return;
            }
            else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll)
            {
                var bs = buffs.Where(b => b.ID == buffId);
                foreach (var bf in bs)
                {
                    RemoveBuff(bf);
                }
            }
            else RemoveBuff(b);
        }

        public void InterruptBuff(int buffId, bool removeAll = true)
        {
            var b = buffs.FirstOrDefault(b => b.ID == buffId);
            if (b == null)
            {
                Debug.Log("尝试从" + this.name + "打断没有添加的Buff。 id:" + buffId);
                return;
            }
            else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll)
            {
                var bs = buffs.Where(b => b.ID == buffId);
                foreach (var bf in bs)
                {
                    InteruptBuff(bf);
                }
            }
            else InteruptBuff(b);
        }

然后我们用公有方法将内部复杂的逻辑封装起来,外部只需简单调用。例如,外部调用者只需提供Buff ID和施法者(caster),而不需要关心具体的Buff对象和其处理细节。

完整代码如下

csharp 复制代码
public class BuffHandler : MonoBehaviour, BuffSystem_IBuffHandler
    {
        private List<Buff> buffs = new List<Buff>();
        private Action onAddBuff;
        private Action onRemoveBuff;


        public List<Buff> GetBuffs => new List<Buff>(buffs);
        public void RegisterOnAddBuff(Action act) { onAddBuff += act; }
        public void RemoveOnAddBuff(Action act) { onRemoveBuff -= act; }
        public void RegisterOnRemoveBuff(Action act) { onRemoveBuff += act; }
        public void RemoveOnRemoveBuff(Action act) { onRemoveBuff -= act; }
        #region 私有方法
        private void AddBuff(IBuff buff, GameObject caster)
        {
            if (!updated) Update();
            Buff bf = (Buff)buff;
            if (bf.IsEmpty())
            {
                Debug.LogError("尝试加入空Buff");
                return;
            }
            //无论是否可以添加都执行初始化和BuffAwake
            bf.Initialize(this, caster);
            bf.OnBuffAwake();

            //确定能添加Buff时
            onAddBuff?.Invoke();
            //检查是否已有同样的Buff
            Buff previous = buffs.Find(p => p.Equals(bf));
            //没有则直接添加
            if (previous == null)
            {
                //结算Tag效果
                if (bf.BuffTag != BuffTag.none)
                {
                    //首先:如果有已有buff能抵消新buff,则直接抵消
                    if (buffs.Any(b => BuffManager.GetInstance().TagManager.IsTagCanAddWhenHaveOther(bf.BuffTag, b.BuffTag)))
                    {
                        bf.SetEffective(false);
                        bf.OnBuffDestroy();
                        return;
                    }
                    for (int i = buffs.Count - 1; i >= 0; i--)
                    {
                        //之后:如果新buff没有被抵消,则新buff抵消已有的buff
                        //Debug.Log("Running:" + bf.BuffTag + ":" + buffs[i].BuffTag);
                        if (BuffManager.GetInstance().TagManager.IsTagRemoveOther(bf.BuffTag, buffs[i].BuffTag))
                        {
                            RemoveBuff(buffs[i]);
                        }
                    }
                }
                buffs.Add(bf);
                forOnBuffStart += bf.OnBuffStart;
                return;
            }
            //有则根据重复添加的类型处理。
            //一个Buff对象的Start不会重复执行
            //只有mutilCount类型会同时存在多个同id Buff
            switch (previous.MutilAddType)
            {
                case BuffMutilAddType.resetTime:
                    previous.ResetTimer();
                    //forOnBuffStart += previous.OnBuffStart;
                    break;
                case BuffMutilAddType.multipleLayer:
                    previous.ModifyLayer(1);
                    //forOnBuffStart += previous.OnBuffStart;
                    break;
                case BuffMutilAddType.multipleLayerAndResetTime:
                    previous.ResetTimer();
                    previous.ModifyLayer(1);
                    //forOnBuffStart += previous.OnBuffStart;
                    break;
                case BuffMutilAddType.multipleCount:
                    buffs.Add(bf);
                    forOnBuffStart += bf.OnBuffStart;
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 移除一个Buff,移除后执行OnBuffRemove
        /// </summary>
        /// <param name="buff">要移除的Buff</param>
        private void RemoveBuff(IBuff buff)
        {
            Buff bf = (Buff)buff;
            bf.SetEffective(false);
        }
        /// <summary>
        /// 移除一个Buff,移除后不执行OnBuffRemove
        /// </summary>
        /// <param name="buff">要移除的Buff</param>
        private void InteruptBuff(IBuff buff)
        {
            Buff bf = (Buff)buff;
            bf.SetEffective(false);
            buffs.Remove(bf);
            forOnBuffDestroy += ((Buff)buff).OnBuffDestroy;
        }
        #endregion
        public void AddBuff(int buffId, GameObject caster)
        {
            var b = BuffManager.GetInstance().GetBuff(buffId);
            AddBuff(b, caster);
        }

        public void RemoveBuff(int buffId, bool removeAll = true)
        {
            var b = buffs.FirstOrDefault(b => b.ID == buffId);
            if (b == null)
            {
                Debug.Log("尝试从" + this.name + "移除没有添加的Buff。 id:" + buffId);
                return;
            }
            else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll)
            {
                var bs = buffs.Where(b => b.ID == buffId);
                foreach (var bf in bs)
                {
                    RemoveBuff(bf);
                }
            }
            else RemoveBuff(b);
        }

        public void InterruptBuff(int buffId, bool removeAll = true)
        {
            var b = buffs.FirstOrDefault(b => b.ID == buffId);
            if (b == null)
            {
                Debug.Log("尝试从" + this.name + "打断没有添加的Buff。 id:" + buffId);
                return;
            }
            else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll)
            {
                var bs = buffs.Where(b => b.ID == buffId);
                foreach (var bf in bs)
                {
                    InteruptBuff(bf);
                }
            }
            else InteruptBuff(b);
        }

        private bool updated = false;
        private Action forOnBuffDestroy;    //用于在下一帧执行onBuffDestory
        private Action forOnBuffStart;
        private void Update()
        {
            if (updated) return;
            updated = true;
            forOnBuffDestroy?.Invoke();
            forOnBuffStart?.Invoke();
            forOnBuffDestroy = null;
            forOnBuffStart = null;
        }
        private void LateUpdate()
        {
            updated = false;
            Buff bf;
            bool buffRemoved = false;
            for (int i = buffs.Count - 1; i >= 0; i--)
            {
                bf = buffs[i];
                //Debug.Log(bf);
                bf.OnBuffUpdate();
                if (!bf.IsEffective)
                {
                    bf.OnBuffRemove();
                    buffRemoved = true;
                    buffs.Remove(bf);
                    forOnBuffDestroy += bf.OnBuffDestroy;
                }
            }
            if (buffRemoved) onRemoveBuff?.Invoke();
        }
    }
}

Update 每帧更新Buff状态,执行所有记录的销毁和开始方法。

LateUpdate检查并更新每个Buff的状态,如果Buff无效,则移除并记录其销毁方法。

BuffHandler类通过管理游戏对象上的所有Buff,实现了Buff的添加、移除、打断和更新等功能。其核心逻辑包括Buff的初始化、处理相同Buff的多种添加类型、管理Buff的生命周期以及调用相关回调方法。

相关推荐
fkdw1 小时前
C# Newtonsoft.Json 反序列化派生类数据丢失问题
c#·json
一棵开花的树,枝芽无限靠近你2 小时前
【PPTist】组件结构设计、主题切换
前端·笔记·学习·编辑器
0xdadream2 小时前
typora数学符号
编辑器
浅尝辄止;4 小时前
C# 异步编程
c#
m0_748251725 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
benben0445 小时前
Unity3D仿星露谷物语开发7之事件创建动画
unity·游戏引擎
带电的小王5 小时前
VSCode:VSCode安装 -- 最简洁的VSCode安装教程
ide·vscode·编辑器
ou.cs7 小时前
c# 实现一个简单的异常日志记录(异常迭代+分片+定时清理)+AOP Rougamo全局注入
c#