[Unity Practical Combat] Implementing a buff system (with project source code)

Refer to the original video link
[Video]:https://www.bilibili.com/video/BV1Xy4y1N7Cb
注意: This article is a record of study notes. It is recommended to support the original author and watch the original video to type the code by yourself for a deeper understanding

Let’s take a look at the final effect first

Insert image description here

Preface

Most games today have some form of Buff system, which can enhance or weaken specific attributes of game characters. In Unity, we can easily create such a Buff system using scripts.

In this tutorial, we'll explore how to implement a basic buff system that involves applying various different types of buffs to players or enemy characters in the game. We'll also learn how to set time limits and stack limits, as well as how to apply and remove buff effects.

With this tutorial, you'll learn how to create a complete buff system in Unity, adding a whole new level of depth and strategy to your game.

start

Add a new script PlayerController to add player health and attack power variables and display them in real time

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}";
    }
}

Effect
Insert image description here
Draw BUFF display interface
Status bar
Insert image description here
Mask
Insert image description here
Final effect
Insert image description here

BUFF system

Define BUFF type enumeration

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

BUFF conflict mode enumeration type

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

Create a new BuffBase, the base class in the Buff system

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;
    }
}

Create a new ShowBuff to control the display of 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);
        }
    }
 }

Mount the script and configure parameters
Insert image description here
Add UI_BuffItem to control Buff information UI display

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);
            }
        }
    }
}

Bind script, configure parameters and add mouse movement in and out events
Insert image description here
Add ShowBuff to control the display of 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);
        }
    }
}

Mounting script, configuration parameters
Insert image description here
Added BuffManager, BUFF management class

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;
            }
        }
    }
}

Add a few BUFF tests

1. Disappears layer by layer, and upgrading does not reset the remaining time 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;
    }
}

Call test

public class Test : MonoBehaviour
{
    
    
    public PlayerController playerController;

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

Effect
Insert image description here

2. All disappear at once, and the upgrade resets the BUFF for the remaining time.

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;
    }
}

transfer

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

Effect
Insert image description here

3. Permanent BUFF, similar to passive 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;
    }
}

transfer

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

Effect
Insert image description here

4. Negative BUFF, the damage received per second is calculated based on the current BUFF level. When two different units apply the same buff to the same unit, the BUFF exists independently.

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;
    }
}

transfer

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);
}

Effect
Insert image description here

5. Stack two layers on the first level, and one layer on each subsequent layer.

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;
    }
}

transfer

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);
}

Effect

Insert image description here

final effect

Insert image description here

reference

【Video】https://www.bilibili.com/video/BV1Xy4y1N7Cb

Source code

https://gitcode.net/unity1/buffsystem
Insert image description here

reference

【Video】https://www.bilibili.com/video/BV1Xy4y1N7Cb

end

Giving roses to others will leave a lingering fragrance in your hands! If the content of the article is helpful to you, please don't be stingy with your 点赞评论和关注 so that I can receive feedback as soon as possible. Every time you 支持 The greatest motivation for creation. Of course, if you find 存在错误 or 更好的解决方法 in the article, you are welcome to comment and send me a private message!

Good, I am向宇,https://xiangyu.blog.csdn.net

A developer who has been working quietly in a small company recently started studying Unity by himself out of interest. If you encounter any problems, you are also welcome to comment and send me a private message. Although I may not necessarily know some of the questions, I will check the information from all parties and try to give the best suggestions. I hope to help more people who want to learn programming. People, encourage each other~
Insert image description here

Guess you like

Origin blog.csdn.net/qq_36303853/article/details/134230371