北京网站开发/最全bt搜索引擎入口
文章目录
- 先来看看最终效果
- 前言
- 开始
- 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;//查找已有buffif (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){//如果是独立存在,那也直接挂buffcase 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);}}#endregionprivate 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;// 最大等级设置为5BuffType = 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。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
相关文章:

【unity实战】实现类似英雄联盟的buff系统
文章目录 先来看看最终效果前言开始BUFF系统加几个BUFF测试1. 逐层消失,升级不重置剩余时间的BUFF2. 一次性全部消失,升级重置剩余时间的BUFF3. 永久BUFF,类似被动BUFF4. 负面BUFF,根据当前BUFF等级计算每秒收到伤害值,…...

【C语言基础教程】函数指针与指针大小
文章目录 前言一、函数指针1.1 函数指针的概念1.2 三个示例代码示例1: 使用函数指针调用不同的函数示例 2: 使用函数指针实现回调函数示例 3: 使用函数指针数组 二、指针的大小2.1 前述2.2 指针大小如何决定?两方面理解 总结 前言 在C语言中,指针是一项…...

Web前端—网页制作(以“学成在线”为例)
版本说明 当前版本号[20231105]。 版本修改说明20231105初版 目录 文章目录 版本说明目录day07-学成在线01-项目目录02-版心居中03-布局思路04-header区域-整体布局HTML结构CSS样式 05-header区域-logo06-header区域-导航HTML结构CSS样式 07-header区域-搜索布局HTML结构CSS…...

Hive【Hive(八)自定义函数】
自定义函数用的最多的是单行函数,所以这里只介绍自定义单行函数。 Coding 导入依赖 <dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>3.1.3</version></dependency>…...

linux远程桌面管理工具xrdp
一、概述 我们知道,我们日常通过vnc来远程管理linux图形界面,今天分享一工具Xrdp,它是一个开源工具,允许用户通过Windows RDP访问Linux远程桌面。 除了Windows RDP之外,xrdp工具还接受来自其他RDP客户端的连接…...

100天精通Python(可视化篇)——第106天:Pyecharts绘制多种炫酷桑基图参数说明+代码实战
文章目录 专栏导读一、桑基图介绍1. 桑基图是什么?2. 桑基图应用场景?二、桑基图配置选项1. 导包2. add函数3. 分层设置三、桑基图基础1. 普通桑基图2. 修改标签位置3. 修改节点布局方向4、月度开支桑基图书籍推荐专栏导读 🔥🔥本文已收录于《100天精通Python从入门到就…...

什么是OTP认证?OTP认证服务器有哪些应用场景?
OTP是一次性密码,即只能使用一次的密码。它基于专门的算法,每隔60秒生成一个不可预测的随机数字组合。这种密码的有效期仅在一次会话或交易过程中,因此不容易受到重放攻击。在计算器系统或其他数字设备上,OTP是一种只能使用一次的…...

shell_73.Linux使用新 shell 启动脚本
每次启动新 shell,bash shell 都会运行.bashrc 文件。①对此进行验证,可以使用这种方法:在 主目录下的.bashrc 文件中加入一条简单的 echo 语句,然后启动一个新 shell。 $ cat $HOME/.bashrc # .bashrc # Source global definiti…...

【领域驱动设计】聚合
从战术设计上,DDD 最值得借鉴的就是聚合根 什么是聚合 将实体和值对象在一致性边界之内组合聚合 这里的一致性包括 1、业务概念的完整性 2、业务规则的一致性:多个实体需要在一次操作中保持某种一致性(修改 A,同步必须修改 B&a…...

WiFi模块在智能家居中的应用与优化
智能家居技术的迅速发展已经改变了我们对家庭的定义。WiFi模块作为智能设备连接的核心,扮演着连接和控制智能家居生态系统的关键角色。本文将深入研究WiFi模块在智能家居中的应用,同时探讨如何通过优化来提升其性能和用户体验。 1. 智能家居中WiFi模块的…...

LeetCode75——Day27
文章目录 一、题目二、题解 一、题目 933. Number of Recent Calls You have a RecentCounter class which counts the number of recent requests within a certain time frame. Implement the RecentCounter class: RecentCounter() Initializes the counter with zero r…...

P6462补刀
灵光一现,突然就做出来了 正好写一下思路过程 一开始寻思是个数论的问题,貌似需要用到扩展欧几里得,不管那么多,直接写上,接着不断缝缝补补修修改改,此处省略一小时.... 做不出来....好难受 星期天,无聊,做个题.. 突然,不对啊 这个题实际上不就是我当前打还是不打的一个选…...

Python教程---Python交互界面
当我们通过命令行来输入Python,所进入到的界面就是Python的交互界面 结构: 版本和版权声明: Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)] on win32 Type "help", "copyright"…...

基于计算机视觉的身份证识别系统 计算机竞赛
0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于机器视觉的身份证识别系统 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-sen…...

[python] logging输出到控制台(标准输出)
要将logging.info输出到控制台(标准输出),可以使用以下代码: import logging# 创建一个logger对象 logger logging.getLogger(__name__)# 创建一个控制台处理器 console_handler logging.StreamHandler()# 设置控制台处理器的输…...

uniapp 离线打包 google 登录
官方文档: Oauth 模块 | uni小程序SDK 其中有 clientid 和反向url clientid 是 xxxx.apps.googleusercontent.com 反向url 是 com.googleusercontent.apps.xxx...

【实战Flask API项目指南】之一 概述
实战Flask API项目指南之 概述 本系列文章将带你深入探索实战Flask API项目指南,通过跟随小菜的学习之旅,你将逐步掌握Flask在实际项目中的应用。让我们一起踏上这个精彩的学习之旅吧! 前言 小菜是一个Python编程爱好者,他目前…...

AD面试总结
文章目录 CK的面试1.自我介绍2.学习动机3.一天花多久时间4.兴趣爱好5.sql5.1 第二周那道题5.2 对时间盲注和布尔盲注的简单介绍5.3 盲注中可以替代sleep的替代函数 6.反序列化6.1 列举几个函数的触发时机6.2 __wakeup绕过的多种方法6.3 GC垃圾回收机制 7.死亡exit8.mysql8.1.练…...

从今年最硬科幻游戏中的思考
前言 最近有一款“完蛋,我被美女包围了”游戏火爆了,steam上一度达到排行榜第一最低也能到第八(销量据说到了100万份),接下来分享一下自己对于这一款游戏的思考,如果有其他想法,随时可以联系沟…...

Linux多值判断利用case...esac判断
利用这个判断,一定要注意格式的运用,非常容易出错 case $1 in #判断变量的值 "hello") #双引号注意,右括号 echo " afdbab " #语句段,没啥说的 ;; #两个分号结束第一个判断,…...

【教3妹学编程-算法题】重复的DNA序列
3妹:“太阳当空照,花儿对我笑,小鸟说早早早,你为什么背上炸药包” 2哥 :3妹,什么事呀这么开心呀。 3妹:2哥你看今天的天气多好啊,阳光明媚、万里无云、秋高气爽,适合秋游。 2哥&…...

jetsonTX2 nx配置yolov5和D435I相机,完整步骤
转载一篇问题解决博客:问题解决 一、烧录系统 使用SDK烧录 二、安装archiconda3 JETSON TX2 NX的架构是aarch64,与win10,linxu不同,所以不能安装Anaconda,这里安装对应的archiconda。 1. 安装 wget https://github.com/Archiconda/build-tools/rel…...

RflySim | 滤波器设计实验一
滤波器设计实验一 一. 无人机滤波器简介 无人机在飞行时会使用滤波器来处理传感器数据、控制飞行和稳定飞行,以及实现导航和定位等功能。卡尔曼滤波器是无人机领域中常见滤波器类型之一,也称为线性二次型估计,能够从一系列不完全且包含噪声不…...

设计模式——责任链模式(Chain of Responsibility Pattern)+ Spring相关源码
文章目录 一、责任链模式定义二、例子2.1 菜鸟教程2.1.1 定义一个抽象日志类2.1.2 定义日志类的具体实现类ConsoleLogger 、ErrorLogger 、FileLogger2.1.3 将日志类串起来,并使用 2.2 JDK源码——Filter2.3 Spring源码——HandlerInterceptor 三、其他设计模式 一、…...

游戏中的随机抽样算法
相关题目: 382. 链表随机节点 384. 打乱数组 398. 随机数索引 文章详解: 游戏中的随机抽样算法 class ListNode:def __init__(self, val0, nextNone):self.val valself.next nextclass RandListNode:"""382. 链表随机节点https://lee…...

【Qt之QtXlsx模块】安装及使用
1. 安装Perl,编译QtXlsx源码用 可以通过命令行进行查看是否已安装Perl。 下载及安装传送门:链接: https://blog.csdn.net/MrHHHHHH/article/details/134233707?spm1001.2014.3001.5502 1.1 未安装 命令:perl --version 显示以上是未安装…...

如何在 TFRecord 文件上训练 Keras 模型实现黑色素瘤分类器
简介 + 设置 TFRecords 存储一系列二进制记录,线性读取。它们是存储数据的有用格式,因为它们可以有效地读取。在此处了解有关 TFRecords 的更多信息 。 我们将探索如何轻松加载黑色素瘤分类器的 TFRecords。 import tensorflow as tf from functools import partial import…...

C++ 复制控制之复制构造函数
C类用三个特殊的成员函数:复制构造函数、赋值操作符和析构函数 来决定类对象之间的初始化或赋值时发生什么。所谓的“复制控制”即通过这三个成员函数控制对象复制的过程 复制构造函数首先是一个构造函数,它同所有其他的构造函数一样与类同名࿰…...

Windows ObjectType Hook 之 ParseProcedure
1、背景 Object Type Hook 是基于 Object Type的一种深入的 Hook,比起常用的 SSDT Hook 更为深入。 有关 Object Type 的分析见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》。 这里进行的 Hook 为 其中之一的 ParseProcedure。文章实…...

下载树莓派对应的64位Ubuntu系统步骤
说点废话:因为ros2需要安装在64位Ubuntu上面,所以安装64位最合适; 第一步打开https://cn.ubuntu.com/ 网站;选择下载--->iot----> 选择这个镜像文件下载。我觉得镜像文件是img格式的,跟iso文件区别是ÿ…...