【unity实战】实现类似英雄联盟的buff系统

news2024/11/24 9:55:20

文章目录

  • 先来看看最终效果
  • 前言
  • 开始
  • BUFF系统
  • 加几个BUFF测试
    • 1. 逐层消失,升级不重置剩余时间的BUFF
    • 2. 一次性全部消失,升级重置剩余时间的BUFF
    • 3. 永久BUFF,类似被动BUFF
    • 4. 负面BUFF,根据当前BUFF等级计算每秒收到伤害值,当两个不同单位向同一个单位施加同一个buff时BUFF独立存在
    • 5. 一级叠加两层,后面都叠加一层
  • 最终效果
  • 参考
  • 源码
  • 完结

先来看看最终效果

在这里插入图片描述

前言

当今大多数游戏都拥有一些形式的Buff系统,利用这种系统可以增强或削弱游戏角色的特定属性。在Unity中,我们可以使用脚本轻松地创建这样的Buff系统。

在本教程中,我们将探索如何实现一种基本的Buff系统,其中包括对游戏中的玩家或敌对角色施加各种不同类型的Buff。我们还将学习如何设置时间限制和叠加限制,以及如何实现Buff效果的应用和移除。

通过本教程,您将学习如何在Unity中创建一个完整的Buff系统,为您的游戏增加全新的深度和策略性。

开始

新增脚本PlayerController,添加玩家血量和攻击力变量,并实时显示

public class PlayerController : MonoBehaviour
{
    [Header("生命值")]
    public float HP;
    [Header("攻击力")]
    public float AD;
    public TextMeshProUGUI HPText;
    public TextMeshProUGUI ADText;

    private void Update()
    {
        HPText.text = $"生命值:{HP}";
        ADText.text = $"攻击力:{AD}";
    }
}

效果
在这里插入图片描述
绘制BUFF显示界面
状态栏
在这里插入图片描述
遮罩
在这里插入图片描述
最终效果
在这里插入图片描述

BUFF系统

定义BUFF类型枚举

public enum BuffType
{
    /// <summary>
    /// 正面buff
    /// </summary>
    Buff,
    /// <summary>
    /// 负面buff
    /// </summary>
    Debuff,
    /// <summary>
    /// 没有buff
    /// </summary>
    None,
}

BUFF冲突方式枚举类型

/// <summary>
/// 当两个不同单位向同一个单位施加同一个buff时的冲突处理
/// </summary>
public enum ConflictResolution
{
  /// <summary>
  /// 合并为一个buff,叠层(提高等级)
  /// </summary>
  combine,
  /// <summary>
  /// 独立存在
  /// </summary>
  separate,
  /// <summary>
  /// 覆盖,后者覆盖前者
  /// </summary>
  cover,
}

新建BuffBase,Buff系统中的基类

public class BuffBase
{
    private GameObject m_Owner;
    private string m_Provider = "";
    private float m_MaxDuration = 3;
    private float m_TimeScale = 1;
    private int m_MaxLevel = 1;
    private BuffType m_BuffType = BuffType.None;
    private ConflictResolution m_ConflictResolution = ConflictResolution.cover;
    private bool m_Dispellable = true;
    private string m_Name = "默认名称";
    private string m_Description = "这个Buff没有介绍";
    private int m_Demotion = 1;
    private string m_IconPath = "";


    private int m_CurrentLevel = 0;
    private float m_ResidualDuration = 3;

    private bool m_Initialized = false;

    /// <summary>
    /// 此buff的持有者
    /// </summary>
    public GameObject Owner
    {
        get { return m_Owner; }
        protected set { m_Owner = value; }
    }
    /// <summary>
    /// 此Buff的提供者
    /// </summary>
    public string Provider
    {
        get { return m_Provider; }
        protected set { m_Provider = value; }
    }
    /// <summary>
    /// Buff的初始持续时间
    /// </summary>
    public float MaxDuration
    {
        get { return m_MaxDuration; }
        protected set { m_MaxDuration = Math.Clamp(value, 0, float.MaxValue); }
    }
    /// <summary>
    /// buff的时间流失速度,最小为0,最大为10。
    /// </summary>
    public float TimeScale
    {
        get { return m_TimeScale; }
        set { m_TimeScale = Math.Clamp(value, 0, 10); }
    }
    /// <summary>
    /// buff的最大堆叠层数,最小为1,最大为2147483647
    /// </summary>
    public int MaxLevel
    {
        get { return m_MaxLevel; }
        protected set { m_MaxLevel = Math.Clamp(value, 1, int.MaxValue); }
    }
    /// <summary>
    /// Buff的类型,分为正面、负面、中立三种
    /// </summary>
    public BuffType BuffType
    {
        get { return m_BuffType; }
        protected set { m_BuffType = value; }
    }
    /// <summary>
    /// 当两个不同单位向同一个单位施加同一个buff时的冲突处理
    /// 分为三种:
    /// combine,合并为一个buff,叠层(提高等级)
    ///  separate,独立存在
    ///   cover, 覆盖,后者覆盖前者
    /// </summary>
    public ConflictResolution ConflictResolution
    {
        get { return m_ConflictResolution; }
        protected set { m_ConflictResolution = value; }
    }
    /// <summary>
    /// 可否被驱散
    /// </summary>
    public bool Dispellable
    {
        get { return m_Dispellable; }
        protected set { m_Dispellable = value; }
    }
    /// <summary>
    /// Buff对外显示的名称
    /// </summary>
    public string Name
    {
        get { return m_Name; }
        protected set { m_Name = value; }
    }
    /// <summary>
    /// Buff的介绍文本
    /// </summary>
    public string Description
    {
        get { return m_Description; }
        protected set { m_Description = value; }
    }
    /// <summary>
    /// 图标资源的路径
    /// </summary>
    public string IconPath
    {
        get { return m_IconPath; }
        protected set { m_IconPath = value; }
    }

    /// <summary>
    /// 每次Buff持续时间结束时降低的等级,一般降低1级或者降低为0级。
    /// </summary>
    public int Demotion
    {
        get { return m_Demotion; }
        protected set { m_Demotion = Math.Clamp(value, 0, MaxLevel); }
    }




    /// <summary>
    /// Buff的当前等级
    /// </summary>
    public int CurrentLevel
    {
        get { return m_CurrentLevel; }
        set
        {
            //计算出改变值
            int change = Math.Clamp(value, 0, MaxLevel) - m_CurrentLevel;
            OnLevelChange(change);
            m_CurrentLevel += change;
        }
    }
    /// <summary>
    /// Buff的当前剩余时间
    /// </summary>
    public float ResidualDuration
    {
        get { return m_ResidualDuration; }
        set { m_ResidualDuration = Math.Clamp(value, 0, float.MaxValue); }
    }

    /// <summary>
    /// 当Owner获得此buff时触发
    /// 由BuffManager在合适的时候调用
    /// </summary>
    public virtual void OnGet() { }
    /// <summary>
    /// 当Owner失去此buff时触发
    /// 由BuffManager在合适的时候调用
    /// </summary>
    public virtual void OnLost() { }
    /// <summary>
    /// Update,由BuffManager每物理帧调用
    /// </summary>
    public virtual void FixedUpdate() { }
    /// <summary>
    /// 当等级改变时调用
    /// </summary>
    /// <param name="change">改变了多少级</param>
    protected virtual void OnLevelChange(int change) { }
    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="owner"></param>
    /// <param name="provider"></param>
    /// <exception cref="Exception"></exception>
    public virtual void Initialize(GameObject owner, string provider)
    {
        if (m_Initialized)
        {
            throw new Exception("不能对已经初始化的buff再次初始化");
        }
        if (owner == null || provider == null)
        {
            throw new Exception("初始化值不能为空");
        }
        Owner = owner;
        Provider = provider;
        m_Initialized = true;
    }
}

新建ShowBuff,控制BUFF的显示

public class ShowBuff : MonoBehaviour
{
    [SerializeField, Header("Buff项预制体")]
    private GameObject m_BuffItemTemplate;
    [SerializeField, Header("对象池")]
    private GameObject m_Pool;
    [SerializeField, Header("Buff项父物体")]
    private GameObject m_Buffs;
    [SerializeField, Header("与Buff相关联的游戏对象")]
    private PlayerController m_Hero;

    private ObjectPool<UI_BuffItem> m_BuffItemPool;// Buff项对象池
    // Buff项对象池的创建函数,用于实例化Buff项
    private UI_BuffItem Pool_CreateFunc()
    {
        return Instantiate(m_BuffItemTemplate, this.transform).GetComponent<UI_BuffItem>();
    }
    // Buff项对象池的获取时回调,用于激活对象并设置父物体
    private void Pool_ActionOnGet(UI_BuffItem UI_BuffItem)
    {
        UI_BuffItem.gameObject.SetActive(true);
        UI_BuffItem.transform.SetParent(m_Buffs.transform);
    }
    // Buff项对象池的回收时回调,用于隐藏对象并设置父物体
    private void Pool_ActionOnRelease(UI_BuffItem UI_BuffItem)
    {
        UI_BuffItem.gameObject.SetActive(false);
        UI_BuffItem.transform.SetParent(m_Pool.transform);
    }
    // Buff项对象池的销毁时回调,用于销毁对象
    private void Pool_ActionOnDestroy(UI_BuffItem UI_BuffItem)
    {
        Destroy(UI_BuffItem.gameObject);
    }
    // Buff监听器,当有新的Buff时调用ShowBuffCore方法
    private void BuffListener(BuffBase newBuff)
    {
        ShowBuffCore(newBuff);
    }

    private void ShowBuffCore(BuffBase buff)
    {
        m_BuffItemPool.Get().Initialize(buff, m_BuffItemPool);
    }
    private void Awake()
    {
        m_BuffItemPool = new ObjectPool<UI_BuffItem>(
                    Pool_CreateFunc,
                    Pool_ActionOnGet,
                    Pool_ActionOnRelease,
                    Pool_ActionOnDestroy,
                    true,
                    100,
                    10000
                    );
        // 遍历BuffManager中与m_Hero关联的所有Buff,并调用ShowBuffCore方法显示它们
        foreach (BuffBase item in BuffManager.Instance.StartObserving(m_Hero.gameObject, BuffListener))
        {
            ShowBuffCore(item);
        }
    }
 }

挂载脚本并配置参数
在这里插入图片描述
新增UI_BuffItem,控制Buff信息UI显示

public class UI_BuffItem : MonoBehaviour
{
    [SerializeField, Header("遮罩层")]
    private Image m_Mask_M;
    [SerializeField, Header("等级文本")]
    private TextMeshProUGUI m_Level;
    
    [SerializeField, Header("边框")]
    private Image m_Frame;
    [SerializeField, Header("图标")]
    private Image m_Icon;

    [Space]
    [Header("Buff详情")]
    [SerializeField, Header("详情弹窗")]
    private GameObject m_BuffInfo;
    [SerializeField, Header("Buff名称文本")]
    private TextMeshProUGUI m_BuffName;

    [SerializeField, Header("Buff描述文本")]
    private TextMeshProUGUI m_Description;

    [SerializeField, Header("Buff来源文本")]
    private TextMeshProUGUI m_Provider;


    private ObjectPool<UI_BuffItem> m_RecyclePool;


    private bool m_Initialized = false;// 是否已经初始化
    private bool m_NeedNumber = false;// 是否需要显示等级
    private bool m_NeedLine = false;// 是否需要显示计时工具


    private BuffBase m_TargetBuff;

    public void OnPointerEnter()
    {
        m_BuffInfo.gameObject.SetActive(true);
        ShowInfo(m_TargetBuff);
    }

    // 显示Buff详细信息
    public void ShowInfo(BuffBase buff)
    {
        m_BuffName.text = buff.Name;
        m_Description.text = buff.Description;
        m_Provider.text = "来自:" + buff.Provider;
    }

    public void OnPointerExit()
    {
        m_BuffInfo.gameObject.SetActive(false);
    }
    public void Initialize(BuffBase buff, ObjectPool<UI_BuffItem> recyclePool)
    {
        m_Icon.sprite = Resources.Load<Sprite>(buff.IconPath);
        m_TargetBuff = buff;
        m_RecyclePool = recyclePool;
        if (m_TargetBuff.MaxLevel > 1)
        {
            m_NeedNumber = true;
            m_Level.gameObject.SetActive(true);
        }
        else
        {
            m_NeedNumber = false;
            m_Level.gameObject.SetActive(false);
        }


        if (m_TargetBuff.TimeScale > 0)
        {
            m_NeedLine = true;
            m_Mask_M.gameObject.SetActive(true);
        }
        else
        {
            m_NeedLine = false;
            m_Mask_M.gameObject.SetActive(false);
        }

        switch (buff.BuffType)
        {
            case BuffType.Buff:
                m_Frame.color = Color.green;
                break;
            case BuffType.Debuff:
                m_Frame.color = Color.red;
                break;
            case BuffType.None:
                m_Frame.color = Color.white;
                break;
            default:
                break;
        }
        m_Initialized = true;
    }

    private void Update()
    {
        if (m_Initialized)
        {
            //需要显示计时工具才显示
            if (m_NeedLine)
            {
                m_Mask_M.fillAmount = 1 - (m_TargetBuff.ResidualDuration / m_TargetBuff.MaxDuration);
            }
            //需要显示等级才显示
            if (m_NeedNumber)
            {
                m_Level.text = m_TargetBuff.CurrentLevel.ToString();
            }
            //如果当前等级等于零说明他已经被废弃了,所以就可以回收了
            if (m_TargetBuff.CurrentLevel == 0 )
            {
                m_RecyclePool.Release(this);
            }
        }
    }
}

绑定脚本,配置参数并添加鼠标移入移出事件
在这里插入图片描述
新增ShowBuff,控制BUFFBuff的显示

public class ShowBuff : MonoBehaviour
{
    [SerializeField, Header("Buff项预制体")]
    private GameObject m_BuffItemTemplate;
    [SerializeField, Header("对象池")]
    private GameObject m_Pool;
    [SerializeField, Header("Buff项父物体")]
    private GameObject m_Buffs;
    [SerializeField, Header("与Buff相关联的游戏对象")]
    private PlayerController m_Hero;

    private ObjectPool<UI_BuffItem> m_BuffItemPool;// Buff项对象池
    // Buff项对象池的创建函数,用于实例化Buff项
    private UI_BuffItem Pool_CreateFunc()
    {
        return Instantiate(m_BuffItemTemplate, this.transform).GetComponent<UI_BuffItem>();
    }
    // Buff项对象池的获取时回调,用于激活对象并设置父物体
    private void Pool_ActionOnGet(UI_BuffItem UI_BuffItem)
    {
        UI_BuffItem.gameObject.SetActive(true);
        UI_BuffItem.transform.SetParent(m_Buffs.transform);
    }
    // Buff项对象池的回收时回调,用于隐藏对象并设置父物体
    private void Pool_ActionOnRelease(UI_BuffItem UI_BuffItem)
    {
        UI_BuffItem.gameObject.SetActive(false);
        UI_BuffItem.transform.SetParent(m_Pool.transform);
    }
    // Buff项对象池的销毁时回调,用于销毁对象
    private void Pool_ActionOnDestroy(UI_BuffItem UI_BuffItem)
    {
        Destroy(UI_BuffItem.gameObject);
    }
    // Buff监听器,当有新的Buff时调用ShowBuffCore方法
    private void BuffListener(BuffBase newBuff)
    {
        ShowBuffCore(newBuff);
    }

    private void ShowBuffCore(BuffBase buff)
    {
        m_BuffItemPool.Get().Initialize(buff, m_BuffItemPool);
    }
    private void Awake()
    {
        m_BuffItemPool = new ObjectPool<UI_BuffItem>(
                    Pool_CreateFunc,
                    Pool_ActionOnGet,
                    Pool_ActionOnRelease,
                    Pool_ActionOnDestroy,
                    true,
                    100,
                    10000
                    );
        // 遍历BuffManager中与m_Hero关联的所有Buff,并调用ShowBuffCore方法显示它们
        foreach (BuffBase item in BuffManager.Instance.StartObserving(m_Hero.gameObject, BuffListener))
        {
            ShowBuffCore(item);
        }
    }
}

挂载脚本,配置参数
在这里插入图片描述
新增BuffManager,BUFF管理类

public class BuffManager : MonoBehaviour
{
    /// <summary>
    /// 固定时间更新的更新频率,此值不宜过高,可以过低(会增加性能消耗)。
    /// </summary>
    public const float FixedDeltaTime = 0.1f;


    #region 单例
    private static BuffManager m_Instance;
    public static BuffManager Instance
    {
        get
        {
            if (m_Instance == null)
            {
                GameObject l_GameObject = new GameObject("Buff Manager");
                m_Instance = l_GameObject.AddComponent<BuffManager>();
                DontDestroyOnLoad(l_GameObject);
            }
            return m_Instance;
        }
    }
    #endregion

    /// <summary>
    /// 存储了所有的buff,key为buff持有者,value为他所持有的所有buff。
    /// </summary>
    private Dictionary<GameObject, List<BuffBase>> m_BuffDictionary = new Dictionary<GameObject, List<BuffBase>>(25);
    private Dictionary<GameObject, Action<BuffBase>> m_ObserverDicitinary = new Dictionary<GameObject, Action<BuffBase>>(25);
    #region Public方法
    /// <summary>
    /// 返回要观察的对象现有的buff,并且在对象被添加新buff时通知你
    /// (如果现在对象身上没有buff会返回空列表,不会返回null)
    /// </summary>
    /// <returns></returns>
    public List<BuffBase> StartObserving(GameObject target, Action<BuffBase> listener)
    {
        List<BuffBase> list;
        //添加监听
        if (!m_ObserverDicitinary.ContainsKey(target))
        {
            m_ObserverDicitinary.Add(target, null);
        }
        m_ObserverDicitinary[target] += listener;
        //查找已有buff
        if (m_BuffDictionary.ContainsKey(target))
        {
            list = m_BuffDictionary[target];
        }
        else
        {
            list = new List<BuffBase>();
        }
        //返回
        return list;
    }
    /// <summary>
    /// 停止观察某一对象,请传入与调用开始观察方法时使用的相同参数。
    /// </summary>
    /// <param name="target"></param>
    /// <param name="listener"></param>
    /// <exception cref="Exception"></exception>
    public void StopObsveving(GameObject target, Action<BuffBase> listener)
    {
        if (!m_ObserverDicitinary.ContainsKey(target))
        {
            throw new Exception("要停止观察的对象不存在");
        }
        m_ObserverDicitinary[target] -= listener;
        if (m_ObserverDicitinary[target] == null)
        {
            m_ObserverDicitinary.Remove(target);
        }
    }
    /// <summary>
    /// 在目标身上挂buff
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="target"></param>
    /// <param name="provider"></param>
    /// <param name="level"></param>
    public void AddBuff<T>(GameObject target, string provider, int level = 1) where T : BuffBase, new()
    {
        //如果我们的字典里没有存储这个key,就进行初始化
        if (!m_BuffDictionary.ContainsKey(target))
        {
            m_BuffDictionary.Add(target, new List<BuffBase>(5));
            //目标身上自然没有任何buff,直接挂一个新buff即可
            AddNewBuff<T>(target, provider, level);
            return;
        }

        //如果目标身上没有任何buff,直接挂一个新buff即可
        if (m_BuffDictionary[target].Count == 0)
        {
            AddNewBuff<T>(target, provider, level);
            return;
        }

        //遍历看看目标身上有没有已存在的要挂的buff。
        List<T> temp01 = new List<T>();
        foreach (BuffBase item in m_BuffDictionary[target])
        {
            if (item is T)
            {
                temp01.Add(item as T);
            }
        }
        //如果没有直接挂一个新buff就行了
        //如果有已存在的要挂的buff,就要进行冲突处理了
        if (temp01.Count == 0)
        {
            AddNewBuff<T>(target, provider, level);
        }
        else
        {
            switch (temp01[0].ConflictResolution)
            {
                //如果是独立存在,那也直接挂buff
                case ConflictResolution.separate:
                    bool temp = true;
                    foreach (T item in temp01)
                    {
                        if (item.Provider == provider)
                        {
                            item.CurrentLevel += level;
                            temp = false;
                            continue;
                        }
                    }
                    if (temp)
                    {
                        AddNewBuff<T>(target, provider, level);
                    }
                    break;
                //如果是合并,则跟已有的buff叠层。
                case ConflictResolution.combine:
                    temp01[0].CurrentLevel += level;
                    break;
                //如果是覆盖,则移除旧buff,然后添加这个buff。
                case ConflictResolution.cover:
                    RemoveBuff(target, temp01[0]);
                    AddNewBuff<T>(target, provider, level);
                    break;
            }
        }

    }
    /// <summary>
    /// 获得单位身上指定类型的buff的列表
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="Owner"></param>
    /// <returns></returns>
    public List<T> FindBuff<T>(GameObject Owner) where T : BuffBase, new()
    {
        List<T> result = new List<T>();
        if (m_BuffDictionary.ContainsKey(Owner))
        {
            List<BuffBase> buff = m_BuffDictionary[Owner];
            foreach (BuffBase item in buff)
            {
                if (item is T)
                {
                    result.Add(item as T);
                }
            }
        }
        return result;
    }
    /// <summary>
    /// 获得单位身上所有的buff
    /// 如果单位身上没有任何buff则返回空列表
    /// </summary>
    /// <param name="Owner"></param>
    /// <returns></returns>
    public List<BuffBase> FindAllBuff(GameObject Owner)
    {
        List<BuffBase> result = new List<BuffBase>();
        if (m_BuffDictionary.ContainsKey(Owner))
        {
            result = m_BuffDictionary[Owner];
        }
        return result;
    }
    /// <summary>
    /// 移除单位身上指定的一个buff
    /// </summary>
    /// <param name="owner"></param>
    /// <param name="buff"></param>
    /// <returns>是否成功,如果失败说明目标不存在</returns>
    public bool RemoveBuff(GameObject owner, BuffBase buff)
    {
        if (!m_BuffDictionary.ContainsKey(owner))
        {
            return false;
        }

        bool haveTarget = false;
        foreach (BuffBase item in m_BuffDictionary[owner])
        {
            if (item == buff)
            {
                haveTarget = true;
                item.CurrentLevel -= item.CurrentLevel;
                item.OnLost();
                m_BuffDictionary[owner].Remove(item);
                break;
            }
        }
        if (!haveTarget)
        {
            return false;
        }
        return true;
    }
    #endregion

    #region Private方法
    private void AddNewBuff<T>(GameObject target, string provider, int level) where T : BuffBase, new()
    {
        T buff = new T();
        buff.Initialize(target, provider);
        m_BuffDictionary[target].Add(buff);
        buff.ResidualDuration = buff.MaxDuration;
        buff.CurrentLevel = level;
        buff.OnGet();
        if (m_ObserverDicitinary.ContainsKey(target))
        {
            m_ObserverDicitinary[target]?.Invoke(buff);
        }
    }
    #endregion


    private WaitForSeconds m_WaitForFixedDeltaTimeSeconds = new WaitForSeconds(FixedDeltaTime);
    private IEnumerator ExecuteFixedUpdate()
    {
        while (true)
        {
            yield return m_WaitForFixedDeltaTimeSeconds;
            //执行所有buff的update;
            foreach (KeyValuePair<GameObject, List<BuffBase>> item1 in m_BuffDictionary)
            {
                foreach (BuffBase item2 in item1.Value)
                {
                    if (item2.CurrentLevel > 0 && item2.Owner != null)
                    {
                        item2.FixedUpdate();
                    }
                }
            }
        }
    }
    private WaitForSeconds m_WaitFor10Seconds = new WaitForSeconds(10f);
    private Dictionary<GameObject, List<BuffBase>> m_BuffDictionaryCopy = new Dictionary<GameObject, List<BuffBase>>(25);
    private IEnumerator ExecuteGrabageCollection()
    {
        while (true)
        {
            yield return m_WaitFor10Seconds;
            //复制一份
            m_BuffDictionaryCopy.Clear();
            foreach (KeyValuePair<GameObject, List<BuffBase>> item in m_BuffDictionary)
            {
                m_BuffDictionaryCopy.Add(item.Key, item.Value);
            }
            //清理无用对象
            foreach (KeyValuePair<GameObject, List<BuffBase>> item in m_BuffDictionaryCopy)
            {
                //如果owner被删除,我们这边也跟着删除
                if (item.Key == null)
                {
                    m_BuffDictionary.Remove(item.Key);
                    continue;
                }
                //如果一个owner身上没有任何buff,就没必要留着他了
                if (item.Value.Count == 0)
                {
                    m_BuffDictionary.Remove(item.Key);
                    continue;
                }
            }
        }
    }

    private void Awake()
    {
        StartCoroutine(ExecuteFixedUpdate());
        StartCoroutine(ExecuteGrabageCollection());
    }


    private BuffBase m_Transfer_Buff;
    private void FixedUpdate()
    {
        //清理无用对象
        foreach (KeyValuePair<GameObject, List<BuffBase>> item in m_BuffDictionary)
        {
            //清理无用buff
            //降低持续时间
            for (int i = item.Value.Count - 1; i >= 0; i--)
            {
                m_Transfer_Buff = item.Value[i];
                //如果等级为0,则移除
                if (m_Transfer_Buff.CurrentLevel == 0)
                {
                    RemoveBuff(item.Key, m_Transfer_Buff);
                    continue;
                }
                //如果持续时间为0,则降级,
                //降级后如果等级为0则移除,否则刷新持续时间
                if (m_Transfer_Buff.ResidualDuration == 0)
                {
                    m_Transfer_Buff.CurrentLevel -= m_Transfer_Buff.Demotion;
                    if (m_Transfer_Buff.CurrentLevel == 0)
                    {
                        RemoveBuff(item.Key, m_Transfer_Buff);
                        continue;
                    }
                    else
                    {
                        m_Transfer_Buff.ResidualDuration = m_Transfer_Buff.MaxDuration;
                    }
                }
                //降低持续时间
                m_Transfer_Buff.ResidualDuration -= Time.fixedDeltaTime;
            }
        }
    }
}

加几个BUFF测试

1. 逐层消失,升级不重置剩余时间的BUFF

public class Buff001 : BuffBase
{
    // Buff每秒钟恢复的生命值
    private float m_HealingPerSecond = 20f;

    // 作用目标,即被添加Buff的角色
    private PlayerController playerController;

    // 初始化Buff的属性和状态
    public override void Initialize(GameObject owner, string provider)
    {
        base.Initialize(owner, provider);

        // 获取作用目标的PlayerController组件
        playerController = owner.GetComponent<PlayerController>();

        // 设置Buff的基本属性
        MaxDuration = 15; // 最大持续时间为15秒
        TimeScale = 1f;   // 时间流失速度为正常值
        MaxLevel = 5;     // 最大等级为5级
        BuffType = BuffType.Buff; // Buff类型为增益效果
        ConflictResolution = ConflictResolution.combine; // Buff冲突时采用合并方式
        Dispellable = false; // 不可被驱散
        Name = "生命值";   // Buff的名称
        Description = $"每秒恢复{m_HealingPerSecond}点生命值"; // Buff的描述
        Demotion = 1; // 每次Buff持续时间结束时降低的等级
        IconPath = "Icon/2003"; // Buff的图标路径
    }

    // 在固定时间间隔内更新Buff的效果
    public override void FixedUpdate()
    {
        // 每秒钟恢复指定的生命值
        playerController.HP += m_HealingPerSecond * BuffManager.FixedDeltaTime;
    }
}

调用测试

public class Test : MonoBehaviour
{
    public PlayerController playerController;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
        	//作用目标 来源:自己,每次加1层
            BuffManager.Instance.AddBuff<Buff001>(playerController.gameObject, "自己", 1);
        }
    }
}

效果
在这里插入图片描述

2. 一次性全部消失,升级重置剩余时间的BUFF

public class Buff002 : BuffBase
{
     // 攻击力增加的数值
    private float m_ADUp = 10f;
    private PlayerController playerController;
    public override void Initialize(GameObject owner, string provider)
    {
        base.Initialize(owner, provider);
        MaxDuration = 5f;// 最大持续时间为5秒
        MaxLevel = 10;// 最大等级为10级
        BuffType = BuffType.Buff;// Buff类型为增益效果
        ConflictResolution = ConflictResolution.combine;// Buff冲突时采用合并方式
        Dispellable = false;// 不可被驱散
        Name = "借来的短剑";// Buff的名称
        Description = "每层增加10点攻击力";// Buff的描述
        IconPath = "Icon/1036";// Buff的图标路径
        Demotion = MaxLevel;// 每次Buff持续时间结束时降低的等级
        playerController = Owner.GetComponent<PlayerController>();
    }

    //当等级改变时调用
    protected override void OnLevelChange(int change)
    {
        // 根据变化的等级调整角色的攻击力
        playerController.AD += m_ADUp * change;
        //每次升级,重置Buff的当前剩余时间
        ResidualDuration = MaxDuration;
    }
}

调用

BuffManager.Instance.AddBuff<Buff002>(playerController.gameObject, "自己", 1);

效果
在这里插入图片描述

3. 永久BUFF,类似被动BUFF

public class Buff003 : BuffBase
{
    PlayerController playerController;
    public override void Initialize(GameObject owner, string provider)
    {
        base.Initialize(owner, provider);
        TimeScale = 0f;// 时间缩放为0,暂停游戏中的时间流逝
        MaxLevel = int.MaxValue;// 最大等级设置为int的最大值,表示无限等级
        BuffType = BuffType.Buff;// Buff类型为增益效果
        ConflictResolution = ConflictResolution.separate;// Buff冲突时采用分离方式
        Dispellable = false;// 不可被驱散
        Name = "盛宴";
        Description = "增加生命值";
        IconPath = "Icon/Feast";
        Demotion = 0;// 每次Buff持续时间结束时降低的等级
        playerController = owner.GetComponent<PlayerController>();
    }

     // 当Buff等级发生变化时触发
    protected override void OnLevelChange(int change)
    {
        // 根据变化的等级调整角色的生命值
        playerController.HP += change;
    }
}

调用

BuffManager.Instance.AddBuff<Buff003>(playerController.gameObject, "自己", 80);

效果
在这里插入图片描述

4. 负面BUFF,根据当前BUFF等级计算每秒收到伤害值,当两个不同单位向同一个单位施加同一个buff时BUFF独立存在

public class Buff004 : BuffBase
{
    PlayerController playerController;
    // 每秒受到的伤害值
    float m_DamagePerSeconds = 30;
    public override void Initialize(GameObject owner, string provider)
    {
        base.Initialize(owner, provider);

        playerController = owner.GetComponent<PlayerController>();

        MaxDuration = 5f;// Buff的最大持续时间为5秒
        TimeScale = 1f;// 时间缩放为1,正常流逝时间
        MaxLevel = 5;// 最大等级设置为5
        BuffType = BuffType.Debuff;// Buff类型为减益效果
        ConflictResolution = ConflictResolution.separate;// Buff冲突时采用分离方式
        Dispellable = true;// 可以被驱散
        Name = "流血";
        Description = "每层每秒受到30点伤害";
        IconPath = "Icon/Darius_PassiveBuff";
        Demotion = MaxLevel;// 每次Buff持续时间结束时降低的等级
    }

    // 当Buff等级发生变化时触发
    protected override void OnLevelChange(int change)
    {
        //每次升级,重置Buff的当前剩余时间
        ResidualDuration = MaxDuration;
    }
    public override void FixedUpdate()
    {
        // 根据当前等级、每秒伤害值和固定时间步长来计算角色受到的伤害
        playerController.HP -= m_DamagePerSeconds * CurrentLevel * BuffManager.FixedDeltaTime;
    }
}

调用

if (Input.GetKeyDown(KeyCode.Alpha4))
{
    BuffManager.Instance.AddBuff<Buff004>(playerController.gameObject, "敌人1", 1);
}
if (Input.GetKeyDown(KeyCode.Alpha5))
{
    BuffManager.Instance.AddBuff<Buff004>(playerController.gameObject, "敌人2", 1);
}

效果
在这里插入图片描述

5. 一级叠加两层,后面都叠加一层

public class Buff005 : BuffBase
{
    PlayerController playerController;

    // 每秒受到的伤害值
    float m_DamagePerSeconds = 10;

    public override void Initialize(GameObject owner, string provider)
    {
        base.Initialize(owner, provider);

        playerController = owner.GetComponent<PlayerController>();

        MaxDuration = 1f;// Buff的最大持续时间为1秒
        TimeScale = 1f;// 时间缩放为1,正常流逝时间
        MaxLevel = int.MaxValue;// 最大等级设置为int.MaxValue,即无限大
        BuffType = BuffType.Debuff;// Buff类型为减益效果
        ConflictResolution = ConflictResolution.combine;// Buff冲突时采用合并方式
        Dispellable = true;// 可以被驱散
        Name = "被点燃";
        Description = "每秒受到10点伤害,首次受到该BUFF伤害,一次叠加2层,后续叠加1层";
        IconPath = "Icon/Darius_PassiveBuff";
        Demotion = 1;// 每次Buff持续时间结束时降低的等级
    }

    public override void FixedUpdate()
    {
        // 根据每秒伤害值和固定时间步长来计算角色受到的伤害
        playerController.HP -= m_DamagePerSeconds * BuffManager.FixedDeltaTime;
    }
}

调用

if (Input.GetKeyDown(KeyCode.Alpha6))
{
    int number = 1;
    //获取叠加的BUff层数
    if(BuffManager.Instance.FindBuff<Buff005>(playerController.gameObject).Count == 0  )
    {
        number = 2;
    }
    BuffManager.Instance.AddBuff<Buff005>(playerController.gameObject, "敌人1", number);
}

效果

在这里插入图片描述

最终效果

在这里插入图片描述

参考

【视频】https://www.bilibili.com/video/BV1Xy4y1N7Cb

源码

整理好了,我会放上来

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1174099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker Stack部署应用详解+Tomcat项目部署详细实战

Docker Stack 部署应用 概述 单机模式下&#xff0c;可以使用 Docker Compose 来编排多个服务。Docker Swarm 只能实现对单个服务的简单部署。而Docker Stack 只需对已有的 docker-compose.yml 配置文件稍加改造就可以完成 Docker 集群环境下的多服务编排。 stack是一组共享…

Pyhotn: Mac安装selenium没有chromedriver-114以上及chromedriver无法挪到/usr/bin目录下的问题

1.0 安装selenium 终端输入&#xff1a; pip install selenium 查看版本&#xff1a; pip show selenium2.0 安装chromedriver 查看chrome版本 网上大多数是&#xff0c;基本到114就停了。 https://registry.npmmirror.com/binary.html?pathchromedriver/ 各种搜索&#…

Blender基础操作:面操作细分、整体切分、挤出、内插、尖分、融并、切割、面的法向、填充等

目录 1. 面操作&#xff1a;进入‘面选择’模式&#xff0c;选择一个面 2. 面的挤出 3. 内插面 4. 尖分面 5. 面的切割 6. 面的法向normal 7. 填充面 8. X-Ray透视 1. 面操作&#xff1a; 进入“面选择”模式&#xff0c;选择一个面 4种操作手段&#xff1a; 菜单 工…

四、数据库系统

数据库系统&#xff08;Database System&#xff09;&#xff0c;是由数据库及其管理软件组成的系统。数据库系统是为适应数据处理的需要而发展起来的一种较为理想的数据处理系统&#xff0c;也是一个为实际可运行的存储、维护和应用系统提供数据的软件系统&#xff0c;是存储介…

Redis01-缓存击穿、穿透和雪崩

目录 开场白-追命3连 使用场景 01缓存穿透场景与方案 02布隆过滤器 03缓存击穿场景与方案 04缓存雪崩场景与方案 开场白-追命3连 看你项目中有说用到Redis&#xff0c;都是哪些场景使用了Redis呢&#xff1f; 如果发生了缓存穿透、击穿、雪崩如何应对呢&#xff1f;缓存…

研读论文之Image Quality-aware Diagnosis via Meta-knowledge Co-embedding

研读论文之 Image Quality-aware Diagnosis via Meta-knowledge Co-embedding 前言一、简介二、主要内容2.1. 图像退化2.2. 图像质量感知诊断(IQAD)2.3. 元知识协同嵌入网络(MKCNet) 三、实现过程3.1. IQAD问题3.2. 元知识协同嵌入网络 (MKCNet)3.2.1功能3.2.2优化策略3.2.3 Ta…

轻信息服务展示预约小程序的内容是什么

预约几乎是适应所有经营商家的&#xff0c;可以提升客户服务/产品获取度、锁客及便捷性需求&#xff0c;同时也利于提升商家整体经营效率&#xff0c;无论获客还是留存线索都有很高帮助。 尤其对线下服务实体店来说&#xff0c;需要预约形式将客户引流到店&#xff0c;传统商家…

考研数学(数二)核心要点(极限)

前言 okey&#xff0c;今天难得有时间那就稍微整理一下数学部分的内容。注意&#xff0c;本文仅适用于考研冲刺阶段&#xff0c;对知识点进行复习使用。前提是你已经知道了相关知识点&#xff0c;我这里只是把我认为比较重要的&#xff0c;我自己总结的内容给出来&#xff0c;…

基于机器学习的学生成绩预测

学生成绩预测是一个基于回归问题的流行数据科学案例研究。对于数据科学初学者来说&#xff0c;这是一个很好的回归问题&#xff0c;因为它很容易解决和理解。本文中&#xff0c;将带你通过使用Python的机器学习来完成学生成绩预测的任务。 学生成绩预测&#xff08;案例研究&a…

本地idea远程调试服务器程序

本文主要介绍idea本地调试远程服务器程序的方式。相信很多同行跟我一样&#xff0c;在最初接触公司项目的时候&#xff0c;遇到测试提出的缺陷&#xff0c;往往会在本地进行调试、替换jar包远程调试等方式&#xff0c;本地调试往往会导致数据和环境不一致的问题使得问题无法复现…

机器学习领域经典书籍推荐

机器学习领域经典书籍 1. 数据挖掘概念与技术2. 机器学习3. 统计学习方法4. 深度学习5. 动手学深度学习&#xff08;PyTorch版&#xff09; 1. 数据挖掘概念与技术 原名: Data Mining&#xff1a;Concepts and Techniques&#xff0c;Third Edition 作者: &#xff08;美&#…

Solidity入门第一步之数据类型

各种类型介绍 数值类型(Value Type)&#xff1a;包括布尔型(bool)&#xff0c;整数型(int、uint、uint256)等等&#xff0c;这类变量赋值时候直接传递数值。引用类型(Reference Type)&#xff1a;包括数组和结构体&#xff0c;这类变量占空间大&#xff0c;赋值时候直接传递地…

Leetcode—707.设计链表【中等】

2023每日刷题&#xff08;十七&#xff09; Leetcode—707.设计链表 设计单链表实现代码 typedef struct Node {int val;struct Node* next; } MyLinkedList;MyLinkedList* myLinkedListCreate() {MyLinkedList* mList (MyLinkedList *)malloc(sizeof(MyLinkedList));mList-…

chroot

1.chroot技术 在Linux系统中&#xff0c;系统默认的目录结构都是以/&#xff0c;即根(root)开始的。而在使用chroot之后&#xff0c;进程的系统目录结构将以指定的位置作为根(/)位置。chroot实际作用就是将进程描述符中struct fs_struct中的root的值设置为选定的目录。 在经过…

深度学习之基于Yolov5人体姿态摔倒识别分析报警系统(GUI界面)

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 系统设计概述&#xff1a; 传感器采集&#xff1a;通过在场景中布置摄像头或红外传感器等设备&#xff0c;采集人体…

latex自定义缩写

Latex 写文章可能常用到一些缩写&#xff0c;如&#xff1a; .e.g.i.e.cf.etc.w.r.t.i.i.d.et al. 其中有些要斜体&#xff0c;如果每次都要用 \textit{...}、{\it ...} 弄斜&#xff0c;有点麻烦。CVPR 模板中有定义一些命令&#xff0c;可以更方便地输入这些缩写。这里记录…

openebs

1. 简介 OpenEBS是一款使用Go语言编写的基于容器的块存储开源软件。OpenEBS使得在容器中运行关键性任务和需要数据持久化的负载变得更可靠。 OpenEBS是一组存储引擎&#xff0c;允许您为有状态工作负载(StatefulSet)和Kubernetes平台类型选择正确的存储解决方案。 在高层次上…

CCF-CSP真题《202309-4 阴阳龙》思路+python,c++满分题解

想查看其他题的真题及题解的同学可以前往查看&#xff1a;CCF-CSP真题附题解大全 试题编号&#xff1a;202309-4试题名称&#xff1a;阴阳龙时间限制&#xff1a;2.0s内存限制&#xff1a;1.0GB问题描述&#xff1a; 问题描述 西西艾弗岛的下方是一个庞大的遗迹群&#xff0c;神…

项目实战:优化Servlet,把所有围绕Fruit操作的Servlet封装成一个Servlet

1、FruitServlet 这些Servlet都是围绕着Fruit进行的把所有对水果增删改查的Servlet放到一个Servlet里面&#xff0c;让tomcat实例化一个Servlet对象 package com.csdn.fruit.servlet; import com.csdn.fruit.dto.PageInfo; import com.csdn.fruit.dto.PageQueryParam; import c…

Go语言文本处理:正则表达式与字符串操作

大家好&#xff0c;我是[lincyang]。 今天&#xff0c;我们将一起探讨Go语言在文本处理方面的强大功能&#xff0c;特别是正则表达式和字符串操作。 Go语言以其简洁和高效而闻名&#xff0c;这些特性在文本处理上也得到了很好的体现。 接下来&#xff0c;我将通过丰富的案例…