Buff系统
简介
Buff系统提供了一个简单的状态管理功能,用于处理角色身上的基础状态效果。比如:
- 增益效果:攻击力提升、防御力提升等
- 减益效果:中毒、减速等
系统支持:
- 基础的持续时间管理
- 简单的Buff添加和移除
注意
如果达不到需求还需自行修改
核心代码
实体(Entity)定义
BuffInfo.cs
csharp
using System.Collections.Generic;
using cfg;
using Newtonsoft.Json;
namespace MH
{
public class BuffInfo : Entity, IAwake, IAwake<int, Unit, Unit>, IDestroy
{
/// <summary>
/// 计时器ID
/// </summary>
public long TimerId;
/// <summary>
/// 创建时间
/// </summary>
public long CreateTime;
/// <summary>
/// 结束时间
/// </summary>
public long EndTime;
/// <summary>
/// 配置ID
/// </summary>
public int ConfigId;
/// <summary>
/// 层数
/// </summary>
public int Layer;
/// <summary>
/// 施法者
/// </summary>
public Unit Caster;
/// <summary>
/// 目标
/// </summary>
public Unit Target;
/// <summary>
/// 配置
/// </summary>
[JsonIgnore]
public BuffConfig Config => ConfigsSingleton.Instance.Tables.TbBuffConfig.Get(ConfigId);
}
/// <summary>
/// buff添加类型
/// </summary>
public enum BuffAddType
{
ResetTime = 1, //重置Buff时间
MultipleLayer, //增加Buff层数
MultipleLayerAndResetTime, //增加Buff层数且重置Buff时间
}
/// <summary>
/// buff动作类型
/// </summary>
public enum BuffActionType
{
Add = 1,
Sub,
Override,
}
/// <summary>
/// 周期结束类型
/// </summary>
public enum CycleEndType
{
Sub = 1,
Clear,
}
}
BuffInfosComponent.cs
csharp
using System.Collections.Generic;
namespace MH
{
public class BuffInfosComponent: Entity, IAwake, IDestroy
{
/// <summary>
/// buff列表
/// </summary>
public List<BuffInfo> BuffInfos = new List<BuffInfo>();
}
}
系统(System)定义
BuffInfoSystem.cs
csharp
using System.Collections.Generic;
using UnityEngine;
namespace MH
{
[Invoke(TimerInvokeType.BuffActionIntervalTimerInvoke)]
public class Buff_TimerAction : ATimer<BuffInfo>
{
protected override void Run(BuffInfo self)
{
self.TryStartCountDown();
}
}
[EntitySystem]
public class BuffInfoAwakeSystem: AwakeSystem<BuffInfo, int, Unit, Unit>
{
protected override void Awake(BuffInfo self, int configId, Unit caster, Unit target)
{
self.ConfigId = configId;
self.CreateTime = TimeInfo.Instance.ClientNow();
self.EndTime = self.Config.BuffInfoConfig.BuffInfoTimer * 1000 + self.CreateTime;
self.Caster = caster;
self.Target = target;
self.Layer = 1;
BuffActionDispatcher.Instance.OnStart(self);
self.TryStartCountDown();
}
}
[EntitySystem]
public class BuffInfoDestroySystem: DestroySystem<BuffInfo>
{
protected override void Destroy(BuffInfo self)
{
BuffActionDispatcher.Instance.OnEnd(self);
self.EndTime = 0;
self.ConfigId = 0;
self.Target = null;
self.Caster = null;
self.Layer = 0;
self.Root.GetComponent<TimerComponent>().Remove(ref self.TimerId);
}
}
public static class BuffInfoSystem
{
/// <summary>
/// 重复添加buff
/// </summary>
/// <param name="self"></param>
public static void RepeatAddBuffInfo(this BuffInfo self)
{
int oldLayer = self.Layer;
switch (self.Config.BuffInfoConfig.BuffAddType)
{
case (int)BuffAddType.ResetTime:
self.EndTime = self.Config.BuffInfoConfig.BuffInfoTimer * 1000 + TimeInfo.Instance.ClientNow();
break;
case (int)BuffAddType.MultipleLayer:
if (self.Layer < self.Config.BuffInfoConfig.MaxLayer)
self.Layer++;
break;
case (int)BuffAddType.MultipleLayerAndResetTime:
if (self.Layer < self.Config.BuffInfoConfig.MaxLayer)
self.Layer++;
self.EndTime = self.Config.BuffInfoConfig.BuffInfoTimer * 1000 + TimeInfo.Instance.ClientNow();
break;
}
//如果层数发生变化,则调用BuffActionDispatcher的OnLevelChange方法
if (oldLayer != self.Layer)
BuffActionDispatcher.Instance.OnLevelChange(self, oldLayer, self.Layer);
}
/// <summary>
/// 尝试开始计时器
/// </summary>
/// <param name="self"></param>
public static void TryStartCountDown(this BuffInfo self)
{
if (!self.TryCompleted())
{
var timerComponent = self.Root.GetComponent<TimerComponent>();
self.TimerId = timerComponent.NewOnceTimer(TimeInfo.Instance.ClientNow() + (int)(self.Config.BuffInfoConfig.Interval * 1000f),
TimerInvokeType.BuffActionIntervalTimerInvoke, self);
BuffActionDispatcher.Instance.Run(self);
return;
}
//如果buff完成,则移除buff
BuffFactory.RemoveBuff(self);
}
/// <summary>
/// 尝试完成buff
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static bool TryCompleted(this BuffInfo self)
{
int oldLayer = self.Layer;
if (self.EndTime > TimeInfo.Instance.ClientNow())
return false;
//还有层数
if (self.Config.BuffInfoConfig.CycleEndType == (int)CycleEndType.Clear)
self.Layer = 0;
else if (self.Config.BuffInfoConfig.CycleEndType == (int)CycleEndType.Sub)
{
self.Layer -= 1;
self.EndTime = self.Config.BuffInfoConfig.BuffInfoTimer * 1000 + TimeInfo.Instance.ClientNow();
}
//销毁
if (self.Layer > 0)
return false;
//如果层数发生变化,则调用BuffActionDispatcher的OnLevelChange方法
if (oldLayer != self.Layer)
BuffActionDispatcher.Instance.OnLevelChange(self, oldLayer, self.Layer);
return true;
}
}
}
BuffInfosComponentSystem.cs
csharp
namespace MH
{
public static class BuffInfosComponentSystem
{
/// <summary>
/// 添加或更新buff
/// </summary>
/// <param name="self"></param>
/// <param name="buff">buff</param>
public static void AddOrUpdate(this BuffInfosComponent self, BuffInfo buff)
{
BuffInfo old = self.GetByConfigId(buff.ConfigId);
if (old != null)
{
old.RepeatAddBuffInfo();
buff?.Dispose();
return;
}
self.BuffInfos.Add(buff);
}
/// <summary>
/// 根据ID获取buff
/// </summary>
/// <param name="self"></param>
/// <param name="buffInfoId">buffId</param>
/// <returns></returns>
public static BuffInfo GetById(this BuffInfosComponent self, long buffInfoId)
{
foreach (BuffInfo buffInfo in self.BuffInfos)
{
if (buffInfo.Id == buffInfoId)
return buffInfo;
}
return null;
}
/// <summary>
/// 根据配置ID获取buff
/// </summary>
/// <param name="self"></param>
/// <param name="configId">配置表Id</param>
/// <returns></returns>
public static BuffInfo GetByConfigId(this BuffInfosComponent self, int configId)
{
foreach (BuffInfo buffInfo in self.BuffInfos)
{
if (buffInfo.ConfigId == configId)
return buffInfo;
}
return null;
}
/// <summary>
/// 移除buff
/// </summary>
/// <param name="self"></param>
/// <param name="buffInfoId">buffId</param>
public static void Remove(this BuffInfosComponent self, long buffInfoId)
{
BuffInfo buffInfo = self.GetById(buffInfoId);
if (buffInfo != null)
{
self.BuffInfos.Remove(buffInfo);
buffInfo?.Dispose();
}
}
}
}
BuffFactory.cs
csharp
namespace MH
{
public static class BuffFactory
{
/// <summary>
/// 创建buff
/// </summary>
/// <param name="caster"></param>
/// <param name="target"></param>
/// <param name="configId"></param>
public static void CreateBuff(Unit caster, Unit target, int configId)
{
var buffInfosComponent = target.GetComponent<BuffInfosComponent>();
var info = buffInfosComponent.AddChild<BuffInfo, int, Unit, Unit>(configId, caster, target);
buffInfosComponent.AddOrUpdate(info);
}
/// <summary>
/// 移除buff
/// </summary>
/// <param name="buffInfo"></param>
public static void RemoveBuff(BuffInfo buffInfo)
{
buffInfo.GetParent<BuffInfosComponent>().Remove(buffInfo.Id);
}
/// <summary>
/// buff层数变化
/// </summary>
/// <param name="buffInfo"></param>
/// <param name="oldLayer"></param>
/// <param name="newLayer"></param>
public static void OnBuffLayerValueChange(BuffInfo buffInfo, int oldLayer, int newLayer)
{
var attributeModifierComponent = buffInfo.Target.GetComponent<AttributeModifierComponent>();
if (attributeModifierComponent == null)
return;
if (newLayer > oldLayer)
{
for (int i = 0; i < buffInfo.Config.BuffInfoConfig.NumericTypes.Count; i++)
{
var key = buffInfo.Config.BuffInfoConfig.NumericTypes[i];
var value = buffInfo.Config.BuffInfoConfig.NumericValues[i];
attributeModifierComponent.AddModifier(key, buffInfo.Id, value, ModifierType.Add);
}
}
else
{
for (int i = 0; i < buffInfo.Config.BuffInfoConfig.NumericTypes.Count; i++)
{
var key = buffInfo.Config.BuffInfoConfig.NumericTypes[i];
var value = buffInfo.Config.BuffInfoConfig.NumericValues[i];
attributeModifierComponent.AddModifier(key, buffInfo.Id, -value, ModifierType.Add);
}
}
}
/// <summary>
/// buff开始
/// </summary>
/// <param name="buffInfo"></param>
/// <param name="modifierType"></param>
public static void OnBuffStart(BuffInfo buffInfo, ModifierType modifierType)
{
var attributeModifierComponent = buffInfo.Target.GetComponent<AttributeModifierComponent>();
if (attributeModifierComponent == null)
return;
for (int i = 0; i < buffInfo.Config.BuffInfoConfig.NumericTypes.Count; i++)
{
var key = buffInfo.Config.BuffInfoConfig.NumericTypes[i];
var value = buffInfo.Config.BuffInfoConfig.NumericValues[i];
attributeModifierComponent.AddModifier(key, buffInfo.Id, value, modifierType);
}
}
}
}
Buff生命周期的事件分发组件
BuffActionDispatcher.cs
csharp
using System;
using System.Collections.Generic;
namespace MH
{
public class BuffActionDispatcher : LogicSingleton<BuffActionDispatcher>, ISingletonAwake
{
private Dictionary<BuffInfoType, IBuffAction> buffActions = new Dictionary<BuffInfoType, IBuffAction>();
public void Awake()
{
HashSet<Type> types = CodeTypes.Instance.GetTypes(typeof(BuffActionAttribute));
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof(BuffActionAttribute), false);
foreach (object attr in attrs)
{
BuffActionAttribute buffActionAttribute = (BuffActionAttribute)attr;
IBuffAction obj = (IBuffAction)Activator.CreateInstance(type);
if (this.buffActions.ContainsKey(buffActionAttribute.buffInfoType))
throw new Exception($"BuffActionDispatcher中已经存在BuffInfoType为{buffActionAttribute.buffInfoType}的BuffAction");
this.buffActions[buffActionAttribute.buffInfoType] = obj;
}
}
}
public void Run(BuffInfo buffInfo)
{
if (this.buffActions.TryGetValue((BuffInfoType)buffInfo.Config.Id, out IBuffAction buffAction))
{
buffAction.Run(buffInfo);
}
}
public void OnStart(BuffInfo buffInfo)
{
if (this.buffActions.TryGetValue((BuffInfoType)buffInfo.Config.Id, out IBuffAction buffAction))
{
buffAction.OnStart(buffInfo);
}
}
public void OnEnd(BuffInfo buffInfo)
{
if (this.buffActions.TryGetValue((BuffInfoType)buffInfo.Config.Id, out IBuffAction buffAction))
{
buffAction.OnEnd(buffInfo);
}
}
public void OnLevelChange(BuffInfo buffInfo, int oldLayer, int newLayer)
{
if (this.buffActions.TryGetValue((BuffInfoType)buffInfo.Config.Id, out IBuffAction buffAction))
{
buffAction.OnLevelChange(buffInfo, oldLayer, newLayer);
}
}
}
}
这个组件会在Awake的时候收集带有BuffAction
标签的buff处理类缓存起来,方便调用
配置说明
Buff配置表总览

基础字段说明
以下是 Buff 配置的基础字段说明:
字段 | 说明 | 示例 |
---|---|---|
Id | Buff的唯一标识符 | 1 |
Name | Buff的名称 | 生命流逝 |
Desc | Buff的详细描述 | 每秒降低3%的最大生命值,持续3秒,最高叠加1层 |
BuffInfoConfig | 详细配置项 | 见下方详细说明 |
Icon | Buff图标资源名 | UI_Skill_Icon_9 |
BuffInfoConfig 详细配置
BuffInfoConfig 包含了 Buff 的核心配置参数:
配置项 | 说明 | 可选值 |
---|---|---|
数值id | 影响的属性ID | 1001=攻击力 1002=最大生命值 1003=防御力 |
buff添加类型 | Buff重复添加时的处理方式 | 1=重置时间 2=叠加层数 3=叠加层数并重置时间 |
buff数值 | 效果数值(除以10000后得到实际值) | 30000=3% 50000=5% |
持续时间 | Buff持续时间(秒) | 3=3秒 5=5秒 |
buff执行类型 | Buff的执行方式 | |
周期结束执行类型 | Buff结束时的处理方式 | |
间隔 | 周期性执行的时间间隔(秒) | 1=每秒执行一次 |
最大层级 | Buff最大可叠加层数 |
任何数值的改动都搭配着数值组件来做
配置示例
Id: 1
Name: 生命流逝
Desc: 每秒降低3%的最大生命值,持续3秒,最高叠加1层
BuffInfoConfig: {
数值id: 1002, // 影响生命值
buff添加类型: 1, // 重复添加时重置时间
buff数值: 30000, // 3%
持续时间: 3, // 3秒
buff执行类型: 3, // 周期执行
周期结束执行类型: 1, // 时间到直接移除
间隔: 1, // 每秒执行
最大层级: 1 // 不可叠加
}
Icon: UI_Skill_Icon_9
效果说明:
- 该Buff每秒(间隔=1)会降低目标3%的最大生命值
- 效果持续3秒
- 不可叠加(最大层级=1)
- 时间结束后直接移除效果(周期结束执行类型=1)
- 如果重复添加,会刷新持续时间(buff添加类型=1)
使用示例
1. Buff具体的处理类实现
csharp
namespace MH
{
/// <summary>
/// 每秒降低3%的最大生命值,持续3秒,最高叠加1层
/// </summary>
[BuffAction(BuffInfoType.SlowHealth3)]
public class BuffActionDispatcher_SlowHealth3 : IBuffAction
{
public void OnEnd(BuffInfo buffInfo)
{
buffInfo.Target.GetComponent<AttributeModifierComponent>()?.ClearSourceModifiers(buffInfo.Id);
}
public void OnLevelChange(BuffInfo buffInfo, int oldLayer, int newLayer)
{
}
public void OnStart(BuffInfo buffInfo)
{
}
public void Run(BuffInfo buffInfo)
{
var numericComponent = buffInfo.Target.GetComponent<NumericComponent>();
//首先获取目标的数值组件拿到最大生命值
var maxHp = numericComponent[NumericType.MaxHp];
var value = buffInfo.Config.BuffInfoConfig.NumericValues[0];
//这里再处理一下扣血就可以了
EventSystem.Instance.PublishAsync(buffInfo.Root, new AttackUnitStartSpecifyDamage()
{
TargetUnit = buffInfo.Target,
Damage = (long)(maxHp * value / 1000000f),
}).Coroutine();
}
}
}
csharp
namespace MH
{
[Event(SceneType.Main)]
public class AttackUnitStartSpecifyDamage_EventView : AEvent<Scene, AttackUnitStartSpecifyDamage>
{
protected override async ETTask Run(Scene scene, AttackUnitStartSpecifyDamage args)
{
if (!args.TargetUnit.IsAlive())
return;
long hp = args.TargetUnit.GetComponent<NumericComponent>().GetAsInt(NumericType.Hp);
hp -= args.Damage;
if (hp <= 0)
{
args.TargetUnit.GetComponent<NumericComponent>()[NumericType.Hp] = 0;
args.TargetUnit.SetAlive(false);
return;
}
args.TargetUnit.GetComponent<NumericComponent>().Set(NumericType.Hp, hp);
await ETTask.CompletedTask;
}
}
}
技术支持
获取帮助
- 💬 加入QQ群讨论交流
(ET框架群)
: 474643097 - ⭐ 在GitHub上关注项目获取最新更新