当前位置:   article > 正文

Unity3D 实现简单的Buff系统_unity 持续一段时间的buff

unity 持续一段时间的buff

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

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

根据以上结果,我们需要制作的功能基本上就很明确了:
1.时间控制
2.层级控制
3.执行次数
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 Num;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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

/// <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
{
    None,
    /// <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,
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

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>();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

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

这里写图片描述
这个类的最主要功能:
1.添加buff
2.执行buff
3.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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126

正如上面所说,这个类拥有这几个方法:
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)
    {

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

功能先不实现,现在我们需要着重的来看一下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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

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

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

    /// <summary>
    /// 开始Buff
    /// </summary>
    public void StartBuff()
    {
    }

    /// <summary>
    /// 关闭buff
    /// </summary>
    public void CloseBuff()
    {
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这样,我们开始,执行中,结束方法都有了,感觉主题框架搭完了,我们得开始写实际功能了。
首先,我们需要先实现类的属性,另外,我们还想在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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

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

   /// <summary>
    /// 构造方法
    /// </summary>
    private BuffData() {
        buffDataID = buffIndex++;
        CallFrequency = 1;
        PersistentTime = 0;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

    /// <summary>
    /// 开始Buff
    /// </summary>
    public void StartBuff()
    {
        //ChangeLimit(MaxLimit);
        _isFinsh = false;
        if (OnStart != null)
            OnStart();
    }
首先我们要确保_isFinsh = false,确保我们buff尚未结束,其次,就是发送事件了,通知我们buff要开始运行了。
其次,我们写结束方法:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
/// <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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

同样,发送结束事件,其次,调用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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

加载上面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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

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

    /// <summary>
    /// 获取当前执行时间
    /// </summary>
    public float GetCurTime
    {
        get { return _CurTime; }
    }
    /// <summary>
    /// 是否结束
    /// </summary>
    public bool IsFinsh
    {
        get { return _isFinsh; }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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


    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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

以上只是更据需求添加就行了,不是必要的。
至此,我们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.Num);
                    });

                }
                break;
            case BuffType.AddMaxHp: //增加最大血量
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        actor.ActorAttr.AddMaxHP((int)buff.Num);
                    }, delegate {
                        actor.ActorAttr.AddMaxHP((int)buff.Num);
                    }, delegate {
                        actor.ActorAttr.SubMaxHp((int)buff.Num);
                    });
                }
                break;
            case BuffType.SubHp: //减少血量
                if (!IsAdd(actor, buff))
                { 
                    db = BuffData.Create(buff, delegate
                    {
                        actor.ActorAttr.SubHp((int)buff.Num);
                    });
                }
                break;
            case BuffType.SubMaxHp: //减少最大血量
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        actor.ActorAttr.SubMaxHp((int)buff.Num);
                    }, delegate
                    {
                        actor.ActorAttr.SubMaxHp((int)buff.Num);
                    }, delegate
                    {
                        actor.ActorAttr.AddMaxHP((int)buff.Num);
                    });
                }
                break;
            case BuffType.AddDamageFloated: //浮空
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        if (actor.ActorState != ActorState.DamageRise)
                            actor.ActorAttr.DamageRiseAbility = buff.Num;
                        actor.SetDamageRiseState();
                    });
                }
                break;
            case BuffType.AddFloated:
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        Vector3 moveDir = Vector3.up;
                        moveDir *= buff.Num;
                        actor.CharacterController.Move(moveDir*Time.deltaTime);
                    });
                }
                 break;
            case BuffType.AddSprint:
                if (!IsAdd(actor,buff))
                {
                    db = BuffData.Create(buff, delegate {
                        Vector3 moveDir = actor.transform.forward;
                        moveDir *= buff.Num;
                        moveDir.y += -20;
                        actor.CharacterController.Move(moveDir*Time.deltaTime);
                        //actor.Translate(Vector3.forward * buff.Num * 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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153

注意,后面还跟了几个方法,我们先来介绍一下:
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;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

db = BuffData.Create(buff, delegate
{
      actor.ActorAttr.AddHP((int)buff.Num);
});
  • 1
  • 2
  • 3
  • 4

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

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

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

这样我们一个加血的buff制作完成了。
根据上面,我在研究一个AddMaxHp的Buff,一看就很明白,这是一个临时增加最大HP的buff,同样我们先看配置:
这里写图片描述
与上面AddHP不同的是,这里我们调用次数为一次,叠加方式为层叠加,调用频率为0。
按照上面的流程,判断是否存在,存在,叠加,不存在创建。

 db = BuffData.Create(buff, delegate
 {
        actor.ActorAttr.AddMaxHP((int)buff.Num);
    }, delegate {
        actor.ActorAttr.AddMaxHP((int)buff.Num);
    }, delegate {
        actor.ActorAttr.SubMaxHp((int)buff.Num);
 });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

调用的是Create(BuffBase buffBase, Action onCallBack,Action<BuffData> addLayerAcion,Action<BuffData> subLayerAction) 方法。
相比上面而言,多了addLayerAcionsubLayerAction两个事件,根据BuffData中的代码知道addLayerAcion是在添加层是调用,而subLayerAction是在删除层时调用。
而onCallBack是在第一次运行该buff的时候调用一次。
我们来看一下,我默认的血量
这里写图片描述
然后我开始执行buff,
这里写图片描述
我执行3次,根据配置,知道一次叠加增加100点血量。那三次后应该是1300的血量
这里写图片描述
正确,为了方便查看buff,我把BuffData在检视面板中显示出来了,我们来看看ActorBuff中的内容
这里写图片描述
我们可以看到,当前的MaxLimit 为10,Limit为3 ,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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

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

 case BuffType.AddSprint:
      if (!IsAdd(actor,buff))
       {
           db = BuffData.Create(buff, delegate {
               Vector3 moveDir = actor.transform.forward;
               moveDir *= buff.Num;
               moveDir.y += -20;
               actor.CharacterController.Move(moveDir*Time.deltaTime);
               //actor.Translate(Vector3.forward * buff.Num * Time.deltaTime);
           });
       }
       break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上面这个buff我就进行简单位移操作。
我们看一下配置
这里写图片描述
这个buff中我们需要用到3个属性:执行次数为Loop,持续时间,距离。
注意要将CallFrequency设置为零这样才能保证CallBack每帧都会调用。
然后在回调中实现位移方法就OK了

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

using UnityEngine;
using System.Collections;
using System.Collections.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;
    #endregion

    void Awake()
    {
        _instance = this;

        GUITools.ResterWindowAction(new Rect(300, 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, 30), "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]));
                    else
                        GUI.Label(new Rect(10, 105, 200, 10), "测试Actor 为 null ,请检查Actor Name是否正确!");
                }
                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.Num);
                    });

                }
                break;
            case BuffType.AddMaxHp: //增加最大血量
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        actor.ActorAttr.AddMaxHP((int)buff.Num);
                    }, delegate {
                        actor.ActorAttr.AddMaxHP((int)buff.Num);
                    }, delegate {
                        actor.ActorAttr.SubMaxHp((int)buff.Num);
                    });
                }
                break;
            case BuffType.SubHp: //减少血量
                if (!IsAdd(actor, buff))
                { 
                    db = BuffData.Create(buff, delegate
                    {
                        actor.ActorAttr.SubHp((int)buff.Num);
                    });
                }
                break;
            case BuffType.SubMaxHp: //减少最大血量
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        actor.ActorAttr.SubMaxHp((int)buff.Num);
                    }, delegate
                    {
                        actor.ActorAttr.SubMaxHp((int)buff.Num);
                    }, delegate
                    {
                        actor.ActorAttr.AddMaxHP((int)buff.Num);
                    });
                }
                break;
            case BuffType.AddDamageFloated: //浮空
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        if (actor.ActorState != ActorState.DamageRise)
                            actor.ActorAttr.DamageRiseAbility = buff.Num;
                        actor.SetDamageRiseState();
                    });
                }
                break;
            case BuffType.AddFloated:
                if (!IsAdd(actor, buff))
                {
                    db = BuffData.Create(buff, delegate
                    {
                        Vector3 moveDir = Vector3.up;
                        moveDir *= buff.Num;
                        actor.CharacterController.Move(moveDir*Time.deltaTime);
                    });
                }
                 break;
            case BuffType.AddSprint:
                if (!IsAdd(actor,buff))
                {
                    db = BuffData.Create(buff, delegate {
                        Vector3 moveDir = actor.transform.forward;
                        moveDir *= buff.Num;
                        moveDir.y += -20;
                        actor.CharacterController.Move(moveDir*Time.deltaTime);
                        //actor.Translate(Vector3.forward * buff.Num * 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
{
    None,
    /// <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 Num;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
using UnityEngine;
using System.Collections;
using System.Collections.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);
    }

}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498

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

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

预热技能系统:
这里写图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号