ET框架实战教程:三步搭建低耦合Buff系统
2026/9/17 5:47:08 网站建设 项目流程

ET框架实战教程:三步搭建低耦合Buff系统

【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET

以ET框架(Unity3D客户端 + C#服务端框架)为例,本文讲清如何基于事件机制与键值对数值组件,搭出一套职责分明、不互相牵制的Buff/DeBuff技能系统:数值怎么变、数值存哪里、Buff谁来管,最后用一个完整的中毒Debuff实战把三层拼起来,并覆盖层数叠加的处理方式。

![ET框架Buff系统对应的战斗场景:暗红色技能状态UI背景](https://raw.gitcode.com/GitHub_Trending/et/ET/raw/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.lockstep/Assets/GameRes/Loading/Sprites/Warrior_Background2 1.png?utm_source=gitcode_repo_files)

从一条策划需求讲起

"给怪物加个中毒:每2秒扣10血,持续10秒,掉血要有飘字。"

这条需求看着短,拆开却是三个独立问题:扣血之后谁负责飘字、特效、声音?属性的加减存在哪、怎么算最终值?这个Buff什么时候生效、什么时候自己消失?ET框架的对应答案依次是:事件机制、键值对数值组件、组件生命周期。下面按这个顺序一层层搭,每层解决一件事。

第一层:数值怎么变——改完别直接喊UI

先看下常见的紧耦合写法,扣血逻辑里顺手把UI和特效都调了:

// 战斗逻辑里直接驱动UI和特效 target.Hp -= damage; HpBarUI.Refresh(target); // 战斗代码开始关心UI PoisonFx.Play(target); // 还关心特效

问题在于:以后每加一种"对掉血的反应"(伤害统计、仇恨结算、成就判定),都要回头改扣血函数。ET的做法是把这两半拆开——数值组件在值发生变化时,自动向事件系统发布一条NumbericChange事件,发布方不关心谁在听(源码见NumericComponentSystem.Insert):

// NumericComponentSystem.Insert 节选:值变了就发事件 self.NumericDic[numericType] = value; EventSystem.Instance.Publish(scene, new NumbericChange { Unit = unit, Old = oldValue, New = value, NumericType = numericType });

UI这边只写一个订阅者,谁改的血它都管:

[Event(typeof(NumbericChange))] public class HpChange_DamageNumber: AEvent<NumbericChange> { public override void Run(NumbericChange ev) { if (ev.New < ev.Old) DamageNumber.Show(ev.Unit, (int)(ev.Old - ev.New)); // 只认数字变化 } }

订阅、发布互不认识,这就是事件驱动。机制细节可看官方文档:3.4事件机制EventSystem。

数值会变了,接下来要回答它存到哪、怎么算。

第二层:数值怎么存——键值对与多级公式

传统OOP里每加一个属性就加一个字段,Buff想改属性就得改结构体。ET的NumericComponent用一个字典搞定一切属性:

// 一个字典装下所有属性,键是NumericType枚举 public Dictionary<NumericType, long> NumericDic = new();

关键在于NumericType枚举的划分方式:同一属性占一组编号,后五位分别是基础值、增加值、百分比、最终加值、最终百分比:

public enum NumericType { Speed = 1000, SpeedBase = 1001, // 基础值 SpeedAdd = 1002, // 固定增加 SpeedPct = 1003, // 百分比 SpeedFinalAdd = 1004, // 最终加值 SpeedFinalPct = 1005 // 最终百分比 }

这五个槽按固定公式合成最终值(节选自NumericComponentSystem.Update):

// 最终值 = 五级因子按序合成的结果 final = (((base + add) * (100 + pct) / 100) + finalAdd) * (100 + finalPct) / 100;

于是"加速度"有三种标准姿势:SpeedAdd += 10是固定加10点,SpeedPct += 20是乘区加20%,SpeedFinalPct += 50是结果出来后再加50%。Buff只需要往对应槽位写值,合成公式和变化事件都由组件代劳,规则详见 5.6数值组件设计。

知道数怎么变、存哪了,最后看Buff本身谁来管。

第三层:Buff怎么管——让生命周期替你做苦力

在ET里Buff本身就是一个挂在BuffComponent下的子实体,身上只放数据,行为全部交给生命周期方法:

[ChildOf(typeof(BuffComponent))] public class Buff: Entity, IAwake<int>, IDestroy { public int ConfigId; // 配置表ID,效果全靠它查表 public int TickTime; // 跳动间隔(ms) public long ExpireTime; // 到期时间戳 public int Stack; // 当前层数 }

创建时算好到期时间(CreateBuff节选),周期跳动用重复定时器驱动效果树,销毁时自动清理:

// 创建即算到期时间,负值表示永久 buff.ExpireTime = now + buffConfig.Duration; // Tick用重复定时器:每 tickTime 走一次效果逻辑 self.TimerId = timer.NewRepeatedTimer(tickTime, TimerInvokeType.BuffTickTimer, self);

三个时机各干各的事:Awake注册定时器并应用进场效果;Tick周期执行伤害、回血等持续效果;到期或手动移除时RemoveChild,子实体销毁的Destroy里停止定时器、回滚数值改动。开发者不用写一行"检查是否过期"的轮询代码。

三层都齐了,现在拼一个能跑的中毒Debuff。

🧪 实战组装:中毒Debuff的完整实现

配置表里定义:TickTime = 2000Duration = 10000,效果节点选"周期掉血10点"。运行时的核心逻辑只有几行:

// 每次Tick:读当前HP,扣10点,Set会自发NumbericChange事件 Unit unit = buff.Parent.GetParent<Unit>(); int hp = unit.NumericComponent.Get(NumericType.HP); unit.NumericComponent.Set(NumericType.HP, Math.Max(0, hp - 10));

掉血事件发出后,前面写好的HpChange_DamageNumber订阅者自动弹出飘字;客户端收到同一条数值变化消息还能顺手播绿雾特效。你注意一下整条链路:Buff只写了"扣10点",飘字、特效、死亡判定(HP归零后另有订阅者处理)全部是各自订阅事件完成的,中毒代码里找不到任何一个UI或特效的引用。

进阶:层数叠加与上限处理

"灼烧"这类可叠加Debuff是常见需求。Buff上的Stack字段就是为此准备的——再次命中时不新建实体,而是找到旧实例层数加一,并把差值补进数值槽:

// 命中时先找同名Buff,有则叠层,无则新建 Buff old = self.GetOneByConfigId(buffConfigId); if (old != null) { int newStack = Math.Min(old.Stack + 1, buffConfig.StackMax); // 封顶 ApplyDelta(old, newStack - old.Stack); // 只补新层的差值 old.Stack = newStack; return; }

配合第二层的多级公式,"每层+5%灼烧伤害"就是往Pct槽按层数写值,层数清零时把同样的值减掉即可,效果重算天然正确。

这套设计好在哪,还能往哪走

  • 解耦:Buff只管写数值,飘字、特效、仇恨各收各的事件
  • 灵活:新属性加个枚举组即可,不动任何类结构
  • 省心:到期、叠层、清理全靠生命周期与定时器,无轮询代码
  • 一致:同一套组件跑在服务端与客户端,同步的是数值变化本身

往下扩展的方向:Buff间互斥与驱散规则(一个中毒驱散另一个中毒)、按配置表可视化编排效果节点、跨服场景下Buff状态的序列化同步。

想动手的话,从ET仓库拉一份代码,照着 README.md 跑起来,然后给自己写一个"减速20%、持续8秒"的Buff——把本文三层的空各填一遍,你就有了一套能继续往上堆的技能系统底座。

  • 事件机制:3.4事件机制EventSystem
  • 数值组件:5.6数值组件设计
  • 技能包源码参考:Packages/cn.etetet.spell/Scripts/Hotfix/Share/

【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询