您现在的位置是:首页 > 数码 > 

UnityD 实现简单的Buff系统

2025-07-27 01:54:34
UnityD 实现简单的Buff系统 今天来考虑一下,想要实现一个buff系统需要什么功能。 能力不行,写的不好,请大家指正完善,谢谢~~ 在我们接到一个需求的时候,往往需要先分析一下需求,确定我们要实现什么样的功能,大部分的情况下需求功能由策划提供,而你只需要考虑怎么实现就行了

UnityD 实现简单的Buff系统

今天来考虑一下,想要实现一个buff系统需要什么功能。
能力不行,写的不好,请大家指正完善,谢谢~~

在我们接到一个需求的时候,往往需要先分析一下需求,确定我们要实现什么样的功能,大部分的情况下需求功能由策划提供,而你只需要考虑怎么实现就行了。不过今天特殊,没有策划,只能我们自己分析考虑了。
更据以往玩过的游戏,来看看buff系统都有什么功能:
1.计时,一个buff往往都会有存在时间,几秒,几分,甚至几天的存在
2.层级,有的buff只有一层,而有的buff可以拥有多层,效果叠加,有了层数,我们还需要考虑到,buff结束后是一次消除,还是逐层消除
.次数,有的buff只会执行一次,而有的buff可以在一段时间内一直执行
4.间隔调用,有的buff比如加血,往往会1秒或两秒钟才会执行一次,其他时间是不会执行
5.行为操控,眩晕buff,击飞buff 等都是对玩家行为进行一种操控,夺取玩家控制权。

根据以上结果,我们需要制作的功能基本上就很明确了:
1.时间控制
2.层级控制
.执行次数
5.位移控制等操作

下面就开始书写代码了
首先,我们先来实现一个配置类,这个类用来实现加载buff的配置表信息

[System.Serializable]
public class BuffBase
{/// <summary>/// BuffID/// </summary>public int BuffID;/// <summary>/// Buff类型/// </summary>public BuffType BuffType;/// <summary>/// 执行此/// </summary>public BuffCalculateType BuffCalculate = BuffCalculateType.Loop;/// <summary>/// 叠加类型/// </summary>public BuffOverlap BuffOverlap = BuffOverlap.StackedLayer;/// <summary>/// 消除类型/// </summary>public BuffShutDownType BuffShutDownType = BuffShutDownType.All;/// <summary>/// 如果是堆叠层数,表示最大层数,如果是时间,表示最大时间/// </summary>public int MaxLimit = 0;/// <summary>/// 执行时间/// </summary>public float Time = 0;/// <summary>/// 间隔时间/// </summary>public float CallFrequency = 1;/// <summary>/// 执行数值 比如加血就是每次加多少/// </summary>public float um;
}

接下来是上面使用的几个枚举类型

/// <summary>
/// buff类型
/// </summary>
public enum BuffType
{/// <summary>/// 恢复HP/// </summary>AddHp,/// <summary>/// 增加最大血量/// </summary>AddMaxHp,/// <summary>/// 减血/// </summary>SubHp,/// <summary>/// 减最大生命值/// </summary>SubMaxHp,/// <summary>/// 眩晕/// </summary>AddVertigo,/// <summary>/// 被击浮空/// </summary>AddFloated,/// <summary>/// 击退/// </summary>AddRepel,/// <summary>/// 冲刺/// </summary>AddSprint,/// <summary>/// 被击浮空/// </summary>AddDamageFloated,/// <summary>/// 添加忽略重力/// </summary>AddIsIgnoreGravity,}/// <summary>
/// 叠加类型
/// </summary>
public enum BuffOverlap
{one,/// <summary>/// 增加时间/// </summary>StackedTime,/// <summary>/// 堆叠层数/// </summary>StackedLayer,/// <summary>/// 重置时间/// </summary>ResterTime,
}/// <summary>
/// 关闭类型
/// </summary>
public enum BuffShutDownType
{/// <summary>/// 关闭所有/// </summary>All,/// <summary>/// 单层关闭/// </summary>Layer,
}/// <summary>
/// 执行类型
/// </summary>
public enum BuffCalculateType
{/// <summary>/// 一次/// </summary>Once,/// <summary>/// 每次/// </summary>Loop,
}

BuffType枚举,是我们用来控制buff类型的,比如眩晕,比如增加HP,比如减少HP等等…
BuffOverlap枚举,就是叠加类型,上面我们说,有的buff是叠加层数,有的buff是重置时间,或者增加时间。
BuffShutDownType枚举,这个枚举的作用是控制倒计时结束后,应该是减一层,还是直接清空buff

然后我们创建一个BuffManager类,用来配置我们buff属性:


public class BuffManager : MonoBehaviour {private static BuffManager _instance;public static BuffManager Instance{get { return _instance; }}public List<BuffBase> buffBase = new List<BuffBase>();
}

因为我们还没有配置表,所以在检视面板中直接填写数据就行了

buff数据有了,我们还需要提供buff的执行方法吧,就是我使用这个buff后这个buff要做什么,是我们需要知道的。
这就需要我们在写一个类,实现buff 执行中的功能。
buff使用用到角身上的,所以我们写一个脚本ActorBuff 挂载到角身上。


这个类的最主要功能:
1.添加buff
2.执行buff
.buff移除buff
我们先来看看实现:

public class ActorBuff : ActorCompent {[SerializeField]private List<BuffData> buffs = new List<BuffData>();public override void OnInit(){InitBuff();}void InitBuff(){}void Update(){}/// <summary>/// 执行buff/// </summary>void FixedUpdate(){for (int i = buffs.Count - 1; i >= 0; i--){buffs[i].OnTick(Time.deltaTime);if (buffs[i].IsFinsh){buffs[i].CloseBuff();buffs.Remove(buffs[i]);}}}/// <summary>/// 添加buff/// </summary>/// <param name=buffData></param>public void AddBuff(BuffData buffData){if (!buffs.Contains(buffData)){buffs.Add(buffData);buffData.StartBuff();}}/// <summary>/// 移除buff/// </summary>/// <param name=buffDataID></param>public void RemoveBuff(int buffDataID){BuffData bd = GetBuff(buffDataID);if(bd!=null)bd.CloseBuff();}/// <summary>/// 移除buff/// </summary>/// <param name=buffData></param>public void RemoveBuff(BuffData buffData){if (buffs.Contains(buffData)){buffData.CloseBuff();}}/// <summary>/// 获取buff/// </summary>/// <param name=buffDataID></param>/// <returns></returns>public BuffData GetBuff(int buffDataID){for (int i = 0; i < buffs.Count; i){if (buffs[i].buffDataID == buffDataID)return buffs[i];}return null;}/// <summary>/// 获取buff/// </summary>/// <param name=buffBaseID></param>/// <returns></returns>public BuffData GetBuffByBaseID(int buffBaseID){for (int i = 0; i < buffs.Count; i){if (buffs[i].BuffID == buffBaseID)return buffs[i];}return null;}/// <summary>/// 获取buff/// </summary>/// <param name=buffType></param>/// <returns></returns>public BuffData[] GetBuff(BuffType buffType){List<BuffData> buffdatas = new List<BuffData>();for (int i = 0; i < buffs.Count; i){if (buffs[i].BuffType == buffType)buffdatas.Add(buffs[i]);}return buffdatas.ToArray();}/// <summary>/// 执行buff/// </summary>/// <param name=buffID></param>public void DoBuff(int buffID){BuffManager.Instance.DoBuff(Actor,buffID);}
}

正如上面所说,这个类拥有这几个方法:
FixedUpdate()这个方法中用来实现buff的执行,因为我写的buff有牵扯到位移,所以我放到FixedUpdate中了
DoBuff(int buffID) 这个方法是添加buff 的开端,他会调用BuffManager.Instance.DoBuff(Actor,buffID)方法,将自己和要添加buff发送过去,在BuffManager中解析完buff数据后会生成BuffData数据,然后调用 AddBuff(BuffData buffData)方法,将buff添加到buffs列表中
AddBuff(BuffData buffData)正如上面所说 BuffManager解析完buff数据后会生成BuffData 调用进行添加
RemoveBuff buff执行完毕后移除buff
RemoveBuff 同上
GetBuffByBaseID(int buffBaseID)GetBuff(int buffDataID) ,GetBuff(BuffType buffType)三个方法都是获取buffdata,不过所需数据不同,其中GetBuffByBaseID是根据BuffBase中的BuffID 获取配置表的,而GetBuff是根据BuffData类自己生成的ID来获取,GetBuff(BuffType buffType) 就是根据buff类型来获取BuffData

在上面的类型调用了BuffManager的DoBuff(Actor,buffID) 方法,我们没有实现,我们先写个空方法来占个坑。在BuffManager写入两个方法:

    /// <summary>/// 执行buff/// </summary>/// <param name=actor></param>/// <param name=buffID></param>public void DoBuff(Actor actor,int buffID){DoBuff(actor,GetBuffBase(buffID));}/// <summary>/// 执行buff/// </summary>/// <param name=actor></param>/// <param name=buff></param>public void DoBuff(Actor actor, BuffBase buff){}

功能先不实现,现在我们需要着重的来看一下BuffData类的实现,这个类是执行中的buff,主要的buff功能均在此类中实现。

写这个类的时候比较蛋疼的是当初写的不知道怎么想的把BuffBase中的属性在此类中基本上都写了一遍,其实只要把BuffBase引用过来就行了,不过要过年了我先不该了,你们看的时候知道这个问题就行了。

因为这个BuffData的创建频率应该比较高,因为添加buff的时候就需要创建一个,使用完了就销毁,所以为了避免buffdata的重建,我们需要将失效buffdata存起来,后面在需要的时候重置里面的数据,再次使用。

[Serializable]
public class BuffData
{/// <summary>/// 缓存栈/// </summary>private static Stack<BuffData> poolCache = new Stack<BuffData>();/// <summary>/// BuffData下一个ID/// </summary>public static int buffIndex { get; private set; }/// <summary>/// 构造方法/// </summary>private BuffData() {buffDataID = buffIndex;}/// <summary>/// 创建BuffData/// </summary>/// <returns></returns>public static BuffData Create(){if (poolCache.Count < 1)return new BuffData();BuffData buffData = poolCache.Pop();return buffData;}/// <summary>/// 弹出/// </summary>/// <returns></returns>private static BuffData Pop(){if (poolCache.Count < 1){BuffData bd = new BuffData();return bd;}BuffData buffData = poolCache.Pop();return buffData;}/// <summary>/// 压入/// </summary>/// <param name=buffData></param>private static void Push(BuffData buffData){poolCache.Push(buffData);}
}

这样,我们在创建BuffData的时候通过BuffData.Create()方法创建,它会先去poolCache缓存池中去查看有没有空闲的BuffData,如果有,就取出来使用,没有就进行创建。

按照逻辑上来说,万物有始有终,所以我们需要在BuffData中添加两个方法,即开始方法StartBuff()和结束方法CloseBuff()

    /// <summary>/// 开始Buff/// </summary>public void StartBuff(){}/// <summary>/// 关闭buff/// </summary>public void CloseBuff(){}

但是别忘了,我们还需要一个中间不断执行的方法OnTick(float delayTime)

 /// <summary>/// 执行中/// </summary>public void OnTick(float delayTime){}

这样,我们开始,执行中,结束方法都有了,感觉主题框架搭完了,我们得开始写实际功能了。
首先,我们需要先实现类的属性,另外,我们还想在buff执行时候发送一个事件,和结束的时候发送一个事件给我们,所以我们还需要定义几个事件

    /// <summary>/// ID/// </summary>public int buffDataID;/// <summary>/// 配置表ID/// </summary>public int BuffID;/// <summary>/// buff类型/// </summary>public BuffType BuffType;/// <summary>/// 叠加类型/// </summary>public BuffOverlap BuffOverlap = BuffOverlap.StackedLayer;/// <summary>/// 执行次数/// </summary>public BuffCalculateType BuffCalculate = BuffCalculateType.Loop;/// <summary>/// 关闭类型/// </summary>public BuffShutDownType BuffShutDownType = BuffShutDownType.All;/// <summary>/// 最大限制/// </summary>public int MaxLimit;/// <summary>/// 当前数据/// </summary>[SerializeField]private int _Limit;public int GetLimit { get { return _Limit; } }/// <summary>/// 执行时间/// </summary>[SerializeField]private float PersistentTime;public float GetPersistentTime { get { return PersistentTime; } }/// <summary>/// 当前时间/// </summary>[SerializeField]private float _CurTime;/// <summary>/// 调用频率/// </summary>public float CallFrequency { get; set; }/// <summary>/// 当前时间/// </summary>private float _curCallFrequency { get; set; }/// <summary>/// 执行次数/// </summary>[SerializeField]private int index = 0;/// <summary>/// 开始调用/// </summary>public Action OnStart;/// <summary>/// 结束调用/// </summary>public Action OnFinsh;

上面很多属性其实不需要重复的写,注意了直接引用BuffBase的实例即可,但是以下划线开头的属性,是需要在这个类中定义的,他们控制时间,当前数量等数据。
我们构造方法中最好重置一个部分属性的值,看起来向这样:

   /// <summary>/// 构造方法/// </summary>private BuffData() {buffDataID = buffIndex;CallFrequency = 1;PersistentTime = 0;}

然后我们开始写开始方法:

    /// <summary>/// 开始Buff/// </summary>public void StartBuff(){//ChangeLimit(MaxLimit);_isFinsh = false;if (OnStart != null)OnStart();}
首先我们要确保_isFinsh = false,确保我们buff尚未结束,其次,就是发送事件了,通知我们buff要开始运行了。
其次,我们写结束方法:
/// <summary>
/// 关闭buff
/// </summary>
public void CloseBuff()
{if (OnFinsh != null)OnFinsh();Clear();
}
//重置数据
public void Clear()
{_Limit = 0;BuffID = -1;index = 0;PersistentTime = 0;_CurTime = 0;Data = null;CallFrequency = 0;_curCallFrequency = 0;//OnCallBackParam = null;//OnCallBack = null;OnStart = null;OnFinsh = null;_isFinsh = false;Push(this);
}

同样,发送结束事件,其次,调用Clear()方法重置部分属性值。最后别忘了Push(this),将此类插入缓存中。

另外,我们buff叠加方式,有所不同,所以我们想在一些特殊的情况下通知一个事件,所以我在BuffData又添加了以下几个事件:

    /// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次 会传递 Data数据/// </summary>public Action<object> OnCallBackParam;/// <summary>///   /// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次 会传递 Data数据 int 次数/// </summary>/// </summary>public Action<object, int> OnCallBackParamIndex;/// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次/// </summary>public Action OnCallBack;/// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次 int 次数/// </summary>public Action<int> OnCallBackIndex;/// <summary>/// 当改变时间/// </summary>public Action<BuffData> OnChagneTime;/// <summary>/// 当添加层/// </summary>public Action<BuffData> OnAddLayer;/// <summary>/// 当删除层/// </summary>public Action<BuffData> OnSubLayer;

加载上面OnFinsh 事件下就行了。

剩下的就是需要实现OnTick方法了:

    /// <summary>/// 执行中/// </summary>/// <param name=delayTime></param>public void OnTick(float delayTime){_CurTime = delayTime;//判断时间是否结束if (_CurTime >= PersistentTime){///调用事件CallBack();//判断结束类型 为层方式if (BuffShutDownType == BuffShutDownType.Layer){SubLayer();//判读层数小于1 则结束if (_Limit <= 0){_isFinsh = true;return;}//重置时间_curCallFrequency = 0;_CurTime = 0;return;}_isFinsh = true;return;}//如果是按频率调用if (CallFrequency > 0){_curCallFrequency = delayTime;if (_curCallFrequency >= CallFrequency){_curCallFrequency = 0;CallBack();}return;}///调用回调CallBack();}/// <summary>/// 调用回调/// </summary>private void CallBack(){//次数增加index;//判断buff执行次数 if (BuffCalculate == BuffCalculateType.Once){if (index > 1) { index = 2; return; }}if (OnCallBack != null)OnCallBack();if (OnCallBackIndex != null)OnCallBackIndex(index);if (OnCallBackParam != null)OnCallBackParam(Data);if (OnCallBackParamIndex != null)OnCallBackParamIndex(Data, index);}/// <summary>/// 加一层/// </summary>public void AddLayer(){_Limit;_CurTime = 0;if (_Limit > MaxLimit){_Limit = MaxLimit;return;}if (OnAddLayer != null)OnAddLayer(this);}/// <summary>/// 减一层/// </summary>public void SubLayer(){_Limit--;if (OnSubLayer != null)OnSubLayer(this);}/// <summary>/// 重置时间/// </summary>public void ResterTime(){_CurTime = 0;}/// <summary>/// 修改 时间/// </summary>/// <param name=time></param>public void ChangePersistentTime(float time){PersistentTime = time;if (PersistentTime >= MaxLimit)PersistentTime = MaxLimit;if (OnChagneTime != null)OnChagneTime(this);}

主要流程控制是在Tick方法中执行的。而主要的逻辑操作是在提供的几个事件中进行编辑。这个先不急,我们再在BuffData中添加几个方法:

    /// <summary>/// 获取当前执行时间/// </summary>public float GetCurTime{get { return _CurTime; }}/// <summary>/// 是否结束/// </summary>public bool IsFinsh{get { return _isFinsh; }}

根据需求,我们在写几个创建方法,和构造函数:

private BuffData(float persistentTime,Action onCallBack){PersistentTime = persistentTime;OnCallBack = onCallBack;buffDataID = buffIndex;}public static BuffData Create(BuffBase buffBase,Action onCallBack){return Create(buffBase,onCallBack,null,null);}public static BuffData Create(BuffBase buffBase, Action onCallBack,Action<BuffData> addLayerAcion,Action<BuffData> subLayerAction){BuffData db = Pop();db.BuffCalculate = buffBase.BuffCalculate;db.BuffID = buffBase.BuffID;db.CallFrequency = buffBase.CallFrequency;db.PersistentTime = buffBase.Time;db.BuffOverlap = buffBase.BuffOverlap;db.BuffShutDownType = buffBase.BuffShutDownType;db.BuffType = buffBase.BuffType;db.MaxLimit = buffBase.MaxLimit;db.OnCallBack = onCallBack;db.OnAddLayer = addLayerAcion;db.OnSubLayer = subLayerAction;db._Limit = 1;return db;}

以上只是更据需求添加就行了,不是必要的。
至此,我们BuffData列结束了。
接下来,Buff的流程控制有,我们还需要具体的执行方法,还记得上面提供的几个事件吗?在事件通知的时候我们进行逻辑操作就行了。
所以我们需要完善一下BuffManager类。
首先,我们在上面的BuffManager类中添加了一个方法DoBuff(Actor actor, BuffBase buff),如果忘记了可以翻上去看看。然后我们只实现了方法,还没实现功能,接下来我们就需要实现次数功能了。
它看起像这样:

 /// <summary>/// 执行buff/// </summary>/// <param name=actor></param>/// <param name=buff></param>public void DoBuff(Actor actor, BuffBase buff){if (buff == null) return;BuffData db = null;switch (buff.BuffType){case BuffType.AddHp: //增加血量if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{actor.ActorAttr.AddHP((int)buff.um);});}break;case BuffType.AddMaxHp: //增加最大血量if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{actor.ActorAttr.AddMaxHP((int)buff.um);}, delegate {actor.ActorAttr.AddMaxHP((int)buff.um);}, delegate {actor.ActorAttr.SubMaxHp((int)buff.um);});}break;case BuffType.SubHp: //减少血量if (!IsAdd(actor, buff)){ db = BuffData.Create(buff, delegate{actor.ActorAttr.SubHp((int)buff.um);});}break;case BuffType.SubMaxHp: //减少最大血量if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{actor.ActorAttr.SubMaxHp((int)buff.um);}, delegate{actor.ActorAttr.SubMaxHp((int)buff.um);}, delegate{actor.ActorAttr.AddMaxHP((int)buff.um);});}break;case BuffType.AddDamageFloated: //浮空if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{if (actor.ActorState != ActorState.DamageRise)actor.ActorAttr.DamageRiseAbility = buff.um;actor.SetDamageRiseState();});}break;case BuffType.AddFloated:if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{Vector moveDir = Vector.up;moveDir *= buff.um;actor.CharacterController.Move(moveDir*Time.deltaTime);});}break;case BuffType.AddSprint:if (!IsAdd(actor,buff)){db = BuffData.Create(buff, delegate {Vector moveDir = forward;moveDir *= buff.um;moveDir.y = -20;actor.CharacterController.Move(moveDir*Time.deltaTime);//actor.Translate(Vector.forward * buff.um * Time.deltaTime);});}break;case BuffType.AddIsIgnoreGravity:if (!IsAdd(actor, buff)){db = BuffData.Create(buff, null);db.OnStart = delegate { actor.ActorPhysical.IsIgnoreGravity = true; };db.OnFinsh = delegate{actor.ActorPhysical.IsIgnoreGravity = false;};}break;}if (db != null)actor.ActorBuff.AddBuff(db);}/// <summary>/// 玩家是否已经有此buff/// </summary>/// <param name=actor></param>/// <param name=buff></param>/// <returns></returns>private bool IsAdd(Actor actor,BuffBase buff){BuffData oldBuff = actor.ActorBuff.GetBuffByBaseID(buff.BuffID);if (oldBuff != null){switch (buff.BuffOverlap){case BuffOverlap.ResterTime:oldBuff.ResterTime();break;case BuffOverlap.StackedLayer:oldBuff.AddLayer();break;case BuffOverlap.StackedTime:oldBuff.ChangePersistentTime(oldBuff.GetPersistentTime  buff.Time);break;default:break;}return true;}return false;}/// <summary>/// 获取配置数据/// </summary>/// <param name=buffID></param>/// <returns></returns>public BuffBase GetBuffBase(int buffID){for (int i = 0; i < buffBase.Count; i){if (buffBase[i].BuffID == buffID)return buffBase[i];}return null;}

注意,后面还跟了几个方法,我们先来介绍一下:
IsAdd(Actor actor,BuffBase buff)方法,主要是查看玩家身上是否已经曾在相同的buff,并且根据BuffOverlap(叠加类型)进行相对应的buff叠加。还记得在BuffData中添加的那几个方法吗?ResterTime()AddLayer()ChangePersistentTime() 分别对应重置时间,叠加层、修改时间,注意哟我这里叠加层会重置buff时间,当然你也可以不用重置时间。

GetBuffBase(int buffID)这个方法作用,就是获取配置表,当然现在我们没用配置表,所以直接检索列表就行。

最后我们介绍DoBuff(Actor actor, BuffBase buff)方法中内容:
根据上面的代码来看一大串的case,这个显然不好看,但是你们知道的,过年了。不过这个地方还是可以用工厂或者注册类的方式去做,我这里直接用switch和case了。
我们先看BuffType.AddHp的分支:
我们先看一下addhp的配置

id为0,buff类型为Addhp(BuffType) ,添加血量,执行次数(BuffCalculate)为Loop就是不断的执行,叠加方式(BuffOverlap) 时间叠加,清除类型(Buff Shut Down)为层清除,在这里这个清除类型什么样的都可以。根据上下文知道,添加血量按时间计算最大限制就是1000秒(MaxLimit),持续时间为5秒(Time),调用频率为1秒一次(CallFrequency)。每次增加血量为10(num).
那我们在添加 Buff的时候就会调用BuffManager中的DoBuff方法,通过BuffType执行BuffType.AddHpcase分支中的逻辑:
首先,我们需要判断一下玩家身上是否存在该类型buff,没错就是通过IsAdd方法,如果有,则根据叠加方式进行叠加,并返回true,否则返回flase。
其次,如果为flase,那代表我们要创建BuffData类了,这个是什么呢?就是运行时的Buff类。我们调用Create(BuffBase buffBase,Action onCallBack),两个参数,一个是buff数据,一个回调方法,因为AddHP的Call Frequency为1,所以会走频率调用的方,就是BuffData中的Tick方法:

  //如果是按频率调用if (CallFrequency > 0){_curCallFrequency = delayTime;if (_curCallFrequency >= CallFrequency){_curCallFrequency = 0;CallBack();}return;}

这样这里CallBack就会按频率进行调用。

db = BuffData.Create(buff, delegate
{actor.ActorAttr.AddHP((int)buff.um);
});

CallBack里的逻辑,就是你增加血量的逻辑了。如上,这是我的加血方法,你应该提供你自己的方法

最后别忘了把BuffData添加到玩家身上进行执行:

if (db != null)actor.ActorBuff.AddBuff(db);

这样我们一个加血的buff制作完成了。
根据上面,我在研究一个AddMaxHp的Buff,一看就很明白,这是一个临时增加最大HP的buff,同样我们先看配置:

与上面AddHP不同的是,这里我们调用次数为一次,叠加方式为层叠加,调用频率为0。
按照上面的流程,判断是否存在,存在,叠加,不存在创建。

 db = BuffData.Create(buff, delegate{actor.ActorAttr.AddMaxHP((int)buff.um);}, delegate {actor.ActorAttr.AddMaxHP((int)buff.um);}, delegate {actor.ActorAttr.SubMaxHp((int)buff.um);});

调用的是Create(BuffBase buffBase, Action onCallBack,Action<BuffData> addLayerAcion,Action<BuffData> subLayerAction) 方法。
相比上面而言,多了addLayerAcionsubLayerAction两个事件,根据BuffData中的代码知道addLayerAcion是在添加层是调用,而subLayerAction是在删除层时调用。
而onCallBack是在第一次运行该buff的时候调用一次。
我们来看一下,我默认的血量

然后我开始执行buff,

我执行次,根据配置,知道一次叠加增加100点血量。那三次后应该是100的血量

正确,为了方便查看buff,我把BuffData在检视面板中显示出来了,我们来看看ActorBuff中的内容

我们可以看到,当前的MaxLimit 为10,Limit为 ,Persistent Time 为10。在这里MaxLimit表示最大层数,也就说最大血量叠加最多只能叠加10层,如果超过10层则强制为0层,Limit为当前层数,PersistentTime 为每层存在的时间。
通过实例,就可以看到,没过10秒钟,就会减少一层知道所有结束为止,血量也会变回原来的数值。

上面的两个例子都是关于属性的增减,下面看一个关于控制的例子:
每个游戏基本都会有眩晕,但不幸的是,我没写。不过我这里有个关于忽略重力的例子和眩晕差不多:

 case BuffType.AddIsIgnoreGravity:if (!IsAdd(actor, buff)){db = BuffData.Create(buff, null);db.OnStart = delegate { actor.ActorPhysical.IsIgnoreGravity = true; };db.OnFinsh = delegate{actor.ActorPhysical.IsIgnoreGravity = false;};}break;

大概流程都差不多,不同的回调的使用方法。这里同样创建一个BuffData,但是使用的回调是OnStartOnFinsh
在回调用实现了忽略重力的方法。
在这里,配置表中的数据基本上没什么作用,除了一个时间,不过我这里没有用配置表里的时间,我用的动画时间。

最后一个,位移的例子:
比如我有个一个技能,我想让英雄使用该技能时进行冲刺,就是一个直线位移,通过buff怎么实现呢?

 case BuffType.AddSprint:if (!IsAdd(actor,buff)){db = BuffData.Create(buff, delegate {Vector moveDir = forward;moveDir *= buff.um;moveDir.y = -20;actor.CharacterController.Move(moveDir*Time.deltaTime);//actor.Translate(Vector.forward * buff.um * Time.deltaTime);});}break;

上面这个buff我就进行简单位移操作。
我们看一下配置

这个buff中我们需要用到个属性:执行次数为Loop,持续时间,距离。
注意要将CallFrequency设置为零这样才能保证CallBack每帧都会调用。
然后在回调中实现位移方法就OK了

为了防止我书写的漏掉什么代码,下面我把完整的代码贴出来:

using UnityEngine;
using System.Collecti;
using System.Collecti.Generic;public class BuffManager : MonoBehaviour {private static BuffManager _instance;public static BuffManager Instance{get { return _instance; }}public List<BuffBase> buffBase = new List<BuffBase>();#region GUI[SerializeField]private Actor TestActor;#endregionvoid Awake(){_instance = this;GUITools.ResterWindowAction(new Rect(00, 0, 220, 120), delegate(GUIAction action){action.Rect = GUI.Window(action.Id, action.Rect, delegate{action.Param[0] = GUI.TextField(new Rect(10, 20, 200, 20), action.Param[0]);action.Param[1] = GUI.TextField(new Rect(10, 45, 200, 20), action.Param[1]);if (GUI.Button(new Rect(10, 70, 200, 0), AddBuff)){if (TestActor == null || !TestActor.gameObject.Equals(action.Param[1])){GameObject obj = GameObject.Find(action.Param[1]);if (obj != null)TestActor = obj.GetComponent<Actor>();}if (TestActor != null)DoBuff(TestActor, int.Parse(action.Param[0]));elseGUI.Label(new Rect(10, 105, 200, 10), 测试Actor 为 null ,请检查Actor ame是否正确!);}GUI.DragWindow();}, action.Id  - Buff Test|BuffManager);},0,Player);}/// <summary>/// 执行buff/// </summary>/// <param name=actor></param>/// <param name=buffID></param>public void DoBuff(Actor actor,int buffID){DoBuff(actor,GetBuffBase(buffID));}/// <summary>/// 执行buff/// </summary>/// <param name=actor></param>/// <param name=buff></param>public void DoBuff(Actor actor, BuffBase buff){if (buff == null) return;BuffData db = null;switch (buff.BuffType){case BuffType.AddHp: //增加血量if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{actor.ActorAttr.AddHP((int)buff.um);});}break;case BuffType.AddMaxHp: //增加最大血量if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{actor.ActorAttr.AddMaxHP((int)buff.um);}, delegate {actor.ActorAttr.AddMaxHP((int)buff.um);}, delegate {actor.ActorAttr.SubMaxHp((int)buff.um);});}break;case BuffType.SubHp: //减少血量if (!IsAdd(actor, buff)){ db = BuffData.Create(buff, delegate{actor.ActorAttr.SubHp((int)buff.um);});}break;case BuffType.SubMaxHp: //减少最大血量if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{actor.ActorAttr.SubMaxHp((int)buff.um);}, delegate{actor.ActorAttr.SubMaxHp((int)buff.um);}, delegate{actor.ActorAttr.AddMaxHP((int)buff.um);});}break;case BuffType.AddDamageFloated: //浮空if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{if (actor.ActorState != ActorState.DamageRise)actor.ActorAttr.DamageRiseAbility = buff.um;actor.SetDamageRiseState();});}break;case BuffType.AddFloated:if (!IsAdd(actor, buff)){db = BuffData.Create(buff, delegate{Vector moveDir = Vector.up;moveDir *= buff.um;actor.CharacterController.Move(moveDir*Time.deltaTime);});}break;case BuffType.AddSprint:if (!IsAdd(actor,buff)){db = BuffData.Create(buff, delegate {Vector moveDir = forward;moveDir *= buff.um;moveDir.y = -20;actor.CharacterController.Move(moveDir*Time.deltaTime);//actor.Translate(Vector.forward * buff.um * Time.deltaTime);});}break;case BuffType.AddIsIgnoreGravity:if (!IsAdd(actor, buff)){db = BuffData.Create(buff, null);db.OnStart = delegate { actor.ActorPhysical.IsIgnoreGravity = true; };db.OnFinsh = delegate{actor.ActorPhysical.IsIgnoreGravity = false;};}break;}if (db != null)actor.ActorBuff.AddBuff(db);}/// <summary>/// 玩家是否已经有此buff/// </summary>/// <param name=actor></param>/// <param name=buff></param>/// <returns></returns>private bool IsAdd(Actor actor,BuffBase buff){BuffData oldBuff = actor.ActorBuff.GetBuffByBaseID(buff.BuffID);if (oldBuff != null){switch (buff.BuffOverlap){case BuffOverlap.ResterTime:oldBuff.ResterTime();break;case BuffOverlap.StackedLayer:oldBuff.AddLayer();break;case BuffOverlap.StackedTime:oldBuff.ChangePersistentTime(oldBuff.GetPersistentTime  buff.Time);break;default:break;}return true;}return false;}/// <summary>/// 获取配置数据/// </summary>/// <param name=buffID></param>/// <returns></returns>public BuffBase GetBuffBase(int buffID){for (int i = 0; i < buffBase.Count; i){if (buffBase[i].BuffID == buffID)return buffBase[i];}return null;}
}/// <summary>
/// buff类型
/// </summary>
public enum BuffType
{/// <summary>/// 恢复HP/// </summary>AddHp,/// <summary>/// 增加最大血量/// </summary>AddMaxHp,/// <summary>/// 减血/// </summary>SubHp,/// <summary>/// 减最大生命值/// </summary>SubMaxHp,/// <summary>/// 眩晕/// </summary>AddVertigo,/// <summary>/// 被击浮空/// </summary>AddFloated,/// <summary>/// 击退/// </summary>AddRepel,/// <summary>/// 冲刺/// </summary>AddSprint,/// <summary>/// 被击浮空/// </summary>AddDamageFloated,/// <summary>/// 添加忽略重力/// </summary>AddIsIgnoreGravity,}/// <summary>
/// 叠加类型
/// </summary>
public enum BuffOverlap
{one,/// <summary>/// 增加时间/// </summary>StackedTime,/// <summary>/// 堆叠层数/// </summary>StackedLayer,/// <summary>/// 重置时间/// </summary>ResterTime,
}/// <summary>
/// 关闭类型
/// </summary>
public enum BuffShutDownType
{/// <summary>/// 关闭所有/// </summary>All,/// <summary>/// 单层关闭/// </summary>Layer,
}/// <summary>
/// 执行类型
/// </summary>
public enum BuffCalculateType
{/// <summary>/// 一次/// </summary>Once,/// <summary>/// 每次/// </summary>Loop,
}[System.Serializable]
public class BuffBase
{/// <summary>/// BuffID/// </summary>public int BuffID;/// <summary>/// Buff类型/// </summary>public BuffType BuffType;/// <summary>/// 执行此/// </summary>public BuffCalculateType BuffCalculate = BuffCalculateType.Loop;/// <summary>/// 叠加类型/// </summary>public BuffOverlap BuffOverlap = BuffOverlap.StackedLayer;/// <summary>/// 消除类型/// </summary>public BuffShutDownType BuffShutDownType = BuffShutDownType.All;/// <summary>/// 如果是堆叠层数,表示最大层数,如果是时间,表示最大时间/// </summary>public int MaxLimit = 0;/// <summary>/// 执行时间/// </summary>public float Time = 0;/// <summary>/// 间隔时间/// </summary>public float CallFrequency = 1;/// <summary>/// 执行数值 比如加血就是每次加多少/// </summary>public float um;
}
using UnityEngine;
using System.Collecti;
using System.Collecti.Generic;
using System;public class ActorBuff : ActorCompent {[SerializeField]private List<BuffData> buffs = new List<BuffData>();public override void OnInit(){InitBuff();}void InitBuff(){}void Update(){}/// <summary>/// 执行buff/// </summary>void FixedUpdate(){for (int i = buffs.Count - 1; i >= 0; i--){buffs[i].OnTick(Time.deltaTime);if (buffs[i].IsFinsh){buffs[i].CloseBuff();buffs.Remove(buffs[i]);}}}/// <summary>/// 添加buff/// </summary>/// <param name=buffData></param>public void AddBuff(BuffData buffData){if (!buffs.Contains(buffData)){buffs.Add(buffData);buffData.StartBuff();}}/// <summary>/// 移除buff/// </summary>/// <param name=buffDataID></param>public void RemoveBuff(int buffDataID){BuffData bd = GetBuff(buffDataID);if(bd!=null)bd.CloseBuff();}/// <summary>/// 移除buff/// </summary>/// <param name=buffData></param>public void RemoveBuff(BuffData buffData){if (buffs.Contains(buffData)){buffData.CloseBuff();}}/// <summary>/// 获取buff/// </summary>/// <param name=buffDataID></param>/// <returns></returns>public BuffData GetBuff(int buffDataID){for (int i = 0; i < buffs.Count; i){if (buffs[i].buffDataID == buffDataID)return buffs[i];}return null;}/// <summary>/// 获取buff/// </summary>/// <param name=buffBaseID></param>/// <returns></returns>public BuffData GetBuffByBaseID(int buffBaseID){for (int i = 0; i < buffs.Count; i){if (buffs[i].BuffID == buffBaseID)return buffs[i];}return null;}/// <summary>/// 获取buff/// </summary>/// <param name=buffType></param>/// <returns></returns>public BuffData[] GetBuff(BuffType buffType){List<BuffData> buffdatas = new List<BuffData>();for (int i = 0; i < buffs.Count; i){if (buffs[i].BuffType == buffType)buffdatas.Add(buffs[i]);}return buffdatas.ToArray();}/// <summary>/// 执行buff/// </summary>/// <param name=buffID></param>public void DoBuff(int buffID){BuffManager.Instance.DoBuff(Actor,buffID);}}[Serializable]
public class BuffData
{/// <summary>/// 缓存栈/// </summary>private static Stack<BuffData> poolCache = new Stack<BuffData>();/// <summary>/// BuffData下一个ID/// </summary>public static int buffIndex { get; private set; }/// <summary>/// ID/// </summary>public int buffDataID;/// <summary>/// 配置表ID/// </summary>public int BuffID;/// <summary>/// buff类型/// </summary>public BuffType BuffType;/// <summary>/// 叠加类型/// </summary>public BuffOverlap BuffOverlap = BuffOverlap.StackedLayer;/// <summary>/// 执行次数/// </summary>public BuffCalculateType BuffCalculate = BuffCalculateType.Loop;/// <summary>/// 关闭类型/// </summary>public BuffShutDownType BuffShutDownType = BuffShutDownType.All;/// <summary>/// 最大限制/// </summary>public int MaxLimit;/// <summary>/// 当前数据/// </summary>[SerializeField]private int _Limit;public int GetLimit { get { return _Limit; } }/// <summary>/// 执行时间/// </summary>[SerializeField]private float PersistentTime;public float GetPersistentTime { get { return PersistentTime; } }/// <summary>/// 当前时间/// </summary>[SerializeField]private float _CurTime;/// <summary>/// 事件参数/// </summary>public object Data;/// <summary>/// 调用频率/// </summary>public float CallFrequency { get; set; }/// <summary>/// 当前频率/// </summary>private float _curCallFrequency { get; set; }/// <summary>/// 执行次数/// </summary>[SerializeField]private int index = 0;/// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次 会传递 Data数据/// </summary>public Action<object> OnCallBackParam;/// <summary>///   /// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次 会传递 Data数据 int 次数/// </summary>/// </summary>public Action<object, int> OnCallBackParamIndex;/// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次/// </summary>public Action OnCallBack;/// <summary>/// 根据 CallFrequency 间隔 调用 结束时会调用一次 int 次数/// </summary>public Action<int> OnCallBackIndex;/// <summary>/// 当改变时间/// </summary>public Action<BuffData> OnChagneTime;/// <summary>/// 当添加层/// </summary>public Action<BuffData> OnAddLayer;/// <summary>/// 当删除层/// </summary>public Action<BuffData> OnSubLayer;/// <summary>/// 开始调用/// </summary>public Action OnStart;/// <summary>/// 结束调用/// </summary>public Action OnFinsh;[SerializeField]private bool _isFinsh;/// <summary>/// 构造方法/// </summary>private BuffData() {buffDataID = buffIndex;CallFrequency = 1;PersistentTime = 0;}private BuffData(float persistentTime,Action onCallBack){PersistentTime = persistentTime;OnCallBack = onCallBack;buffDataID = buffIndex;}/// <summary>/// 重置时间/// </summary>public void ResterTime(){_CurTime = 0;}/// <summary>/// 修改 时间/// </summary>/// <param name=time></param>public void ChangePersistentTime(float time){PersistentTime = time;if (PersistentTime >= MaxLimit)PersistentTime = MaxLimit;if (OnChagneTime != null)OnChagneTime(this);}/// <summary>/// 加一层/// </summary>public void AddLayer(){_Limit;_CurTime = 0;if (_Limit > MaxLimit){_Limit = MaxLimit;return;}if (OnAddLayer != null)OnAddLayer(this);}/// <summary>/// 减一层/// </summary>public void SubLayer(){_Limit--;if (OnSubLayer != null)OnSubLayer(this);}/// <summary>/// 开始Buff/// </summary>public void StartBuff(){//ChangeLimit(MaxLimit);_isFinsh = false;if (OnStart != null)OnStart();}/// <summary>/// 执行中/// </summary>/// <param name=delayTime></param>public void OnTick(float delayTime){_CurTime = delayTime;//判断时间是否结束if (_CurTime >= PersistentTime){///调用事件CallBack();//判断结束类型 为层方式if (BuffShutDownType == BuffShutDownType.Layer){SubLayer();//判读层数小于1 则结束if (_Limit <= 0){_isFinsh = true;return;}//重置时间_curCallFrequency = 0;_CurTime = 0;return;}_isFinsh = true;return;}//如果是按频率调用if (CallFrequency > 0){_curCallFrequency = delayTime;if (_curCallFrequency >= CallFrequency){_curCallFrequency = 0;CallBack();}return;}///调用回调CallBack();}/// <summary>/// 获取当前执行时间/// </summary>public float GetCurTime{get { return _CurTime; }}/// <summary>/// 是否结束/// </summary>public bool IsFinsh{get { return _isFinsh; }}/// <summary>/// 调用回调/// </summary>private void CallBack(){//次数增加index;//判断buff执行次数 if (BuffCalculate == BuffCalculateType.Once){if (index > 1) { index = 2; return; }}if (OnCallBack != null)OnCallBack();if (OnCallBackIndex != null)OnCallBackIndex(index);if (OnCallBackParam != null)OnCallBackParam(Data);if (OnCallBackParamIndex != null)OnCallBackParamIndex(Data, index);}/// <summary>/// 关闭buff/// </summary>public void CloseBuff(){if (OnFinsh != null)OnFinsh();Clear();}public void Clear(){_Limit = 0;BuffID = -1;index = 0;PersistentTime = 0;_CurTime = 0;Data = null;CallFrequency = 0;_curCallFrequency = 0;OnCallBackParam = null;OnCallBack = null;OnStart = null;OnFinsh = null;_isFinsh = false;Push(this);}/// <summary>/// 创建BuffData/// </summary>/// <returns></returns>public static BuffData Create(){if (poolCache.Count < 1)return new BuffData();BuffData buffData = poolCache.Pop();return buffData;}public static BuffData Create(BuffBase buffBase,Action onCallBack){return Create(buffBase,onCallBack,null,null);}public static BuffData Create(BuffBase buffBase, Action onCallBack,Action<BuffData> addLayerAcion,Action<BuffData> subLayerAction){BuffData db = Pop();db.BuffCalculate = buffBase.BuffCalculate;db.BuffID = buffBase.BuffID;db.CallFrequency = buffBase.CallFrequency;db.PersistentTime = buffBase.Time;db.BuffOverlap = buffBase.BuffOverlap;db.BuffShutDownType = buffBase.BuffShutDownType;db.BuffType = buffBase.BuffType;db.MaxLimit = buffBase.MaxLimit;db.OnCallBack = onCallBack;db.OnAddLayer = addLayerAcion;db.OnSubLayer = subLayerAction;db._Limit = 1;return db;}/// <summary>/// 弹出/// </summary>/// <returns></returns>private static BuffData Pop(){if (poolCache.Count < 1){BuffData bd = new BuffData();return bd;}BuffData buffData = poolCache.Pop();return buffData;}/// <summary>/// 压入/// </summary>/// <param name=buffData></param>private static void Push(BuffData buffData){poolCache.Push(buffData);}}

OK,到此,大部分功能都做完了,当然还有很多需要完善的部分,不过这毕竟是个人作品,没动力啊。buff系统大概流程就是如此了,在上述提供的列子中还有浮空的Buff等等,大家就需要凭才华进行制作了。

这样我们已经完成,UI系统,关卡系统,Buff系统,剧情系统,寻路系统,还有配置表。等明年回来我在完善一下任务系统,指引系统,技能系统,Ai系统和状态机,这样一个游戏大部分功能就都已经实现了。

预热技能系统:

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/shuma/857043.html

相关标签:无
上传时间: 2024-02-10 05:56:10
留言与评论(共有 5 条评论)
本站网友 恶性肿瘤发病率
11分钟前 发表
MonoBehaviour {private static BuffManager _instance;public static BuffManager Instance{get { return _instance; }}public List<BuffBase> buffBase = new List<BuffBase>(); } 因为我们还没有配置表,所以在检视面板中直接填写数据就行了 buff数据有了
本站网友 东新园租房
28分钟前 发表
buff)){db = BuffData.Create(buff
本站网友 西安ktv团购
28分钟前 发表
delegate{if (actor.ActorState != ActorState.DamageRise)actor.ActorAttr.DamageRiseAbility = buff.um;actor.SetDamageRiseState();});}break;case BuffType.AddFloated
本站网友 一泡而红
17分钟前 发表
null