尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Buff 系统设计
上一篇讲行为组件的时候减速组件干了件甩锅的事——它自己不管减速持续多久、什么时候消失而是给目标挂了个 buff然后就撒手不管了。ctx.target.addBuff(slow);// 挂上去剩下的交给buff系统那接锅的 buff 系统到底怎么运作这一篇就把这口锅接住。你会发现中毒、灼烧、加攻、护盾、眩晕、狂暴……游戏里几乎所有持续一段时间的状态全靠这一套系统撑着。先想清楚buff 和普通效果有啥不一样上一篇的伤害、治疗都是一锤子买卖——打完这一下事就结束了组件执行完就退场。但 buff 不一样它的核心特征是**“持续”**中毒接下来 5 秒每秒掉 10 血加攻接下来 10 秒攻击力 20%眩晕接下来 2 秒不能动这就带来一堆一锤子买卖不需要操心的问题它得记住自己还剩多久时间到了要自己消失它得在持续期间反复干活比如中毒每秒都要掉血它上身和消失时可能要改变角色的属性加攻上身时攻击力涨消失时得还原同一个 buff 反复叠加怎么办中毒叠三层是什么效果所以 buff 系统的本质是一套管理持续状态的机制。它要管这些状态的生老病死。一个 buff 长什么样先把 buff 这个数据结构定出来。它比普通效果多了时间和生命周期的概念classBuff{Stringid;// 唯一标识比如 poisonStringname;// 显示名中毒Entitycaster;// 谁给我挂的结算伤害归属要用Entityowner;// 我挂在谁身上floatduration;// 总共持续多久floatremaining;// 还剩多久每帧递减intstack;// 当前叠了几层intmaxStack;// 最多能叠几层floattickInterval;// 每隔多久触发一次中毒每1秒floattickTimer;// 距离下次触发还有多久}对比一下上一篇的EffectConfig多出来的全是跟时间和叠加有关的字段。这就是 buff 的特殊之处。buff 也用组件化三个生命周期钩子还记得上一篇的思路吗——每种效果做成一个统一接口的组件。buff 也照搬这套只不过 buff 的接口要复杂一点因为它有生命周期。一个 buff 在它的一生中有三个关键时刻上身 → 持续期间反复触发 → 消失 onApply onTick onRemove所以 buff 组件的接口设计成三个方法interfaceBuffBehavior{voidonApply(Buffbuff);// 刚挂上时执行一次voidonTick(Buffbuff);// 持续期间每隔 tickInterval 执行一次voidonRemove(Buffbuff);// 消失时执行一次}不是每个 buff 三个方法都要用。看具体是哪种 buff中毒——只关心 tick每秒掉血上身和消失时啥都不干classPoisonBuffimplementsBuffBehavior{publicvoidonApply(Buffbuff){}// 不用管publicvoidonTick(Buffbuff){floatdamage10*buff.stack;// 每层10点叠加变强buff.owner.hp-damage;showDamageNumber(buff.owner,damage);}publicvoidonRemove(Buffbuff){}// 不用管}加攻——只关心上身和消失上身加攻击力消失还原中间不用反复干活classAttackUpBuffimplementsBuffBehavior{publicvoidonApply(Buffbuff){buff.owner.attack*1.2f;// 上身攻击力涨20%}publicvoidonTick(Buffbuff){}// 不用管publicvoidonRemove(Buffbuff){buff.owner.attack/1.2f;// 消失还原}}看出规律了一锤子的属性变化用 onApply/onRemove 成对处理周期性的效果用 onTick 处理。这两对钩子基本能覆盖所有 buff。核心让 buff 自己活起来buff 挂上去只是开始得有个东西驱动它——每一帧去更新所有 buff 的时间该触发触发该消失消失。这个活儿交给挂在每个角色身上的buff 容器来干。classBuffContainer{privateListBuffbuffsnewArrayList();privateBuffRegistryregistry;// buff类型 → 组件 的对照表// 每一帧都调用deltaTime 是这一帧过了多少秒voidupdate(floatdeltaTime){IteratorBuffitbuffs.iterator();while(it.hasNext()){Buffbuffit.next();BuffBehaviorbehaviorregistry.get(buff.id);// 1. 处理周期性触发buff.tickTimer-deltaTime;if(buff.tickTimer0){behavior.onTick(buff);// 时间到触发一次buff.tickTimerbuff.tickInterval;// 重置计时}// 2. 处理总时长倒计时buff.remaining-deltaTime;if(buff.remaining0){behavior.onRemove(buff);// 时间耗尽执行消失逻辑it.remove();// 从容器移除}}}}这段是整个系统的发动机。它每帧做两件事给周期计时器倒数到点就 tick给总时长倒数耗尽就 remove。中毒的每秒掉血持续5秒、加攻的持续10秒后还原全靠这几行自动跑起来。老大难问题buff 叠加怎么处理同一个 buff 再次挂上来该怎么办这是 buff 系统最容易出乱子的地方。现实里有好几种不同的规则得分清楚规则一叠层数。中毒再中一次叠成两层掉血翻倍。规则二刷新时间。已经中毒了再中一次不叠层但持续时间重新计满俗称续毒。规则三又叠层又刷新。最常见叠一层同时把时间刷新。规则四独立共存。两个中毒互不干扰各自倒计时少见但有些游戏这么设计。处理叠加的逻辑放在添加 buff的入口统一判断voidaddBuff(BuffnewBuff){BuffexistingfindBuff(newBuff.id);// 身上已经有同款吗if(existingnull){// 没有直接挂上buffs.add(newBuff);registry.get(newBuff.id).onApply(newBuff);return;}// 已经有了按叠加规则处理switch(newBuff.stackRule){caseSTACK:// 只叠层existing.stackMath.min(existing.stack1,existing.maxStack);break;caseREFRESH:// 只刷新时间existing.remainingexisting.duration;break;caseSTACK_REFRESH://又叠层又刷新 existing.stackMath.min(existing.stack1,existing.maxStack);existing.remainingexisting.duration;break;caseINDEPENDENT:// 独立共存buffs.add(newBuff);registry.get(newBuff.id).onApply(newBuff);break;}}叠加规则一定要写进配置让策划自己定。这个 buff 到底是叠层还是刷新是策划的设计决策不该由程序员拍脑袋。一个隐藏的大坑叠层时属性怎么算上面加攻的例子我写的是onApply里attack * 1.2。但如果这个 buff 能叠 3 层问题就来了——叠层的时候只是stack并没有再次调用onApply那多出来的两层攻击力加成怎么生效这是 buff 系统最阴险的坑属性类 buff 遇到叠层简单的上身加、消失减就不够用了。有个更干净的做法buff 不直接改属性而是角色属性每次都重新算一遍。// 角色的最终攻击力是实时算出来的不是被buff改来改去的floatgetFinalAttack(){floatattackbaseAttack;// 基础攻击// 遍历所有buff把加成累加上去for(Buffbuff:buffs){if(buff.id.equals(attack_up)){attack*(10.2f*buff.stack);// 按层数算}}returnattack;}这样一来buff 只管记录我有几层攻击力多少永远是现算的。叠层、掉层、消失属性自动就对了根本不用在 onApply/onRemove 里手动加加减减也就不会出现加了没减回去这种经典 bug。代价是每次读属性都要遍历 buff有性能开销。数据不常变、读取频繁的话可以加个缓存buff 变动时才重算。怎么权衡看项目。和技能系统怎么接上绕了一圈回到最初的问题——上一篇的减速组件是怎么把锅甩给 buff 系统的现在能看清全貌了// 技能行为组件里classSlowEffectimplementsSkillEffect{publicvoidapply(EffectContextctx){BuffslownewBuff();slow.idslow;slow.durationctx.config.duration;slow.remainingctx.config.duration;slow.valuectx.config.value;slow.stackRuleStackRule.REFRESH;// 减速一般是刷新不叠加ctx.target.buffContainer.addBuff(slow);// 甩锅给buff系统}}整条链路就串起来了配置里写 { type: slow, duration: 3 } ↓ 技能引擎读到找到 SlowEffect 组件 ↓ SlowEffect 造一个 buff扔进目标的 BuffContainer ↓ BuffContainer 每帧更新到点自动移除 ↓ 角色算移速时实时读取身上的减速buff技能系统负责产生buffbuff 系统负责管理buff各管一段接口清清爽爽。几个还要注意的点免疫和驱散。真实游戏里有净化能移除减益 buff有免疫能挡住某些 buff。所以 buff 最好带上分类标签增益/减益、可驱散/不可驱散、控制类/属性类方便这些机制批量筛选处理。控制类 buff 要能互相打断和抵抗。眩晕、冰冻、击飞这些控制 buff往往有递减抗性连续被控后续控制时间缩短还涉及互相覆盖的优先级。这块规则复杂得单独设计别硬塞进通用逻辑。buff 消失的原因不止时间到。还有被驱散、角色死亡、被更高优先级 buff 顶掉等等。这些情况都要正确触发 onRemove否则属性还原不了角色属性就乱套了比如加攻 buff 没触发 onRemove攻击力永久涨着。别让 buff 数量失控。有些场景下 buff 会疯狂叠加比如每帧都挂一个要设上限否则容器越来越大性能崩了。一句话收尾Buff 系统这一篇的核心buff 就是带时间的持续状态用 onApply / onTick / onRemove 三个钩子描述它的一生用一个每帧更新的容器驱动它自动运转。叠加规则交给配置属性计算尽量用实时重算而不是手动加减从根上避免加了忘减的坑。到这里技能系统的骨架就比较完整了数据逻辑分离打地基数据结构组织数据行为组件执行瞬时效果buff 系统管理持续状态。四块拼起来一套能扛住策划各种花活的技能框架就成型了。再往下就是把它们联动起来的事件系统——“击杀后回血”“受击时反弹”每third次攻击暴击这类触发式设计全靠事件机制串联。那是下一个话题了。
RELATED

相关推荐

从零跑通 Axon Framework 的第一条事件:CQRS 与事件溯源实战入门

从零跑通 Axon Framework 的第一条事件:CQRS 与事件溯源实战入门

从零跑通 Axon Framework 的第一条事件:CQRS 与事件溯源实战入门 【免费下载链接】AxonFramework Framework for Evolutionary Message-Driven Microservices on the JVM 项目地址: https://gitcode.com/gh_mirrors/ax/AxonFramework Axon Framework 是跑在 …

📅 2026/10/16 7:09:45
versionName 的几个作用

versionName 的几个作用

一、开门见山:核心作用总览 versionName 是一个给人看的字符串版本号,它主要有以下几个作用:序号作用面向对象1️⃣向用户展示版本信息普通用户2️⃣应用商店版本标识商店/访客3️⃣团队沟通的依据开发/测试4️⃣客服与问题排查客服/技术支持…

📅 2026/9/30 19:52:03
MedicalGPT项目中的PPO训练全流程解析

MedicalGPT项目中的PPO训练全流程解析

MedicalGPT项目中的PPO训练全流程解析 【免费下载链接】MedicalGPT MedicalGPT: Training Your Own Medical GPT Model with ChatGPT Training Pipeline. 训练医疗大模型,实现了包括增量预训练(PT)、有监督微调(SFT)、RLHF、DPO、ORPO、GRPO。 项目地址: https://…

📅 2026/10/3 12:32:06
MORE NEWS

更多资讯

📰

Spring Boot日志治理:Logback脱敏与截取实战指南

日志里蹦出用户的手机号、身份证号、银行卡号,这种事在真实项目里一点都不稀奇。我见过太多团队,功能上线跑得飞快,等到要查线上问题、把日志文件拉下来一看,满屏的敏感信息,当场头皮发麻。更麻烦的是,很多…

📰

t3code:三层架构代码模板的落地实践与避坑指南

1. 先说清楚t3code是个什么东西看到“t3code”这个代号,我第一反应是:这多半是某个项目组内部对“Three-Tier Code”的简称,直译过来就是“三层架构代码规范/代码模板”。这个名字起得挺讨巧,t3听起来像一个特定版本号&#xff0c…

📰

t3code实践:用TypeScript打通全栈类型安全与前后端对接

做了几年全栈,我越来越觉得,大部分项目的复杂度并不是业务本身带来的,而是那一层又一层的手动对接——后端写好了类型,前端不知道;数据库里存的是啥,API返回的是啥,全靠人肉同步。一旦需求改起来…

📰

Easy Dataset:大模型训练数据制作的智能解决方案

1. Easy Dataset项目概述:降低大模型训练数据制作门槛在2023年大模型技术爆发的背景下,一个名为Easy Dataset的开源项目在GitHub上迅速走红,获得了超过13K的Star关注。这个项目直击AI领域的一个关键痛点:高质量训练数据集的制作难…

📰

Kubernetes可视化工具下载与部署指南:Linux环境实操与权限配置

1. 为什么要在 Kubernetes 里折腾可视化工具刚接触 Kubernetes 那会儿,我每天的工作流基本就是三件套:kubectl get pods、kubectl describe、kubectl logs,来回切终端窗口,眼睛盯着密密麻麻的文本输出找异常。集群规模小的时候还行…

📰

t3code:用T3 Stack还清全栈工程债的类型安全脚手架

1. t3code 项目概述:把类型安全全栈应用的“工程债”一次还清先开门见山,t3code 是我最近在团队内部落地的一个全栈脚手架项目。它不只是一个create-next-app的二次封装,也不是网上那种只丢给你一个 README 的模板仓库,而是一套完…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬