真实项目游戏系统实战记录:羁绊派系计数系统(一)

📅 发布时间:2026/8/6 5:52:27
真实项目游戏系统实战记录:羁绊派系计数系统(一)
最近正在开发一个PC游戏的羁绊计数系统和《金铲铲之战》的羁绊计数逻辑相似。玩家获得特定派系羁绊达一定数量时对应派系羁绊效果激活反之失效。本文是该系统的第一部分本文谈这些系统的功能需求基础系统骨架系统逻辑流程处理计数变化并将变化通知到业务脚本相关实战代码文章目录1. 基础支持计数的储存和变化检测2. 计数变化状态的更新计数效果的开启和关闭小结这个系统要实现这些功能计数效果的激活、失活计数文本状态的更新激活的为亮色未激活的为暗色计数效果的阈值节点动态更新派系图标的显示实际已获得的计数数量显示鼠标悬浮Tips文本显示以上效果对每个玩家各自独立运行这个系统所处的上下文环境多人游戏单局制实时战斗基于这些条件我们逐步整理思路。1. 基础支持计数的储存和变化检测首先根据已有的需求我们确定这个系统是一个客户端系统只需要全程在客户端进行处理。同时这个系统是每个玩家数据各自独立的因此该系统必须持有玩家状态的引用。计数存到哪里我们要开发一个能对比计数变化并动态反应的系统自然涉及到计数数据的储存。数据变化时要确保系统能识别到变化。我们需要两处数据储存位置玩家状态数据未经过处理的计数状态外部的脚本和游戏内的互动均直接操作玩家状态数据。系统内部状态数据已经经过变化处理的计数状态。计数变化的更新方式我们必须能够在游戏过程中动态检测流派计数的实时状态这天然地有两种方案任意计数变化时立即处理变化周期间隔时间处理变化在我当前负责的这个项目中游戏不需要特别实时的羁绊计数更新频率在这里我可以采用方案2直接给定一个计时器每1秒获取一次羁绊计数对比缓存中的计数后处理变化。综上我们已经确定数据怎样储存怎样检测变化。现在我们可以着手整理系统的基本骨架。骨架流程我们按照系统需求整理出基本的逻辑流程。骨架脚本-- 流派羁绊系统---class FBS : ObjectlocalFBSoop.class()---注册系统functionFBS:__init(plr)self._plrplrend---初始化系统functionFBS:setup()---流派计数缓存---type { [string]: integer }self._bonds{}-- 创建每1秒计时器self._updaterTimer.StartLoop(1,function()self:update()end)end---获取羁绊计数缓存值functionFBS:get_bond(key)returnself._bonds[key]or0end---设置羁绊计数缓存值functionFBS:set_bond(key,value)self._bonds[key]valueend---检测到羁绊计数变化functionFBS:on_bond_change(key,new_val)localold_valself:get_bond(key)localdeltanew_val-old_val--TODO ... 处理变化传播逻辑-- 更新缓存self:set_bond(key,new_val)end---检测一次变化functionFBS:update()localplr_bondsself._plr:get_bonds()-- 假设玩家类存在该方法获取实时计数状态fork,vinpairs(plr_bonds)doifself:get_bond(k)~vthenself:on_bond_change(k,v)endendend---注销系统functionFBS:remove()self._updater:remove()self._updaternilself._bondsnilself._plrnilend2. 计数变化状态的更新计数效果的开启和关闭在上1节中我们已经搭建好了检测变化的系统骨架现在我们的系统已经具备了获取计数变化并更新缓存的能力。为了让系统能真正影响游戏逻辑效果我们还需要这个系统能够和游戏脚本交互。假设我们有这样的游戏脚本逻辑-- 剑修-2每1秒增加1点攻击力GameScript.Bond[剑修-2]{-- 开启效果on_openfunction(plr,ctx)ctx.timerTimer.StartLoop(1,function()plr:setAttr(攻击力,plr:getAttr(攻击力)1)end)end,-- 关闭效果on_closefunction(plr,ctx)ctx.timer:remove()end,}GameScript.Bond[剑修-4]...GameScript.Bond[剑修-6]...GameScript.Bond[剑修-8]...现在我们需要通过我们的羁绊计数系统对其进行管理实现上1节中未完成的部分。我们需要预设各流派的激活节点并在计数变化时进行一次状态更新并调用相关的脚本。FBS.ActivatePoints{[剑修]{2,4,6,8}}functionFBS:setup()-- ... 原有代码---各节点激活状态---type { [string]: { [integer]: boolean? } }self._activate_states{}end---动态为 activate_states 分配必要的子项functionFBS:protect_activate_state(key,point)-- 确保只处理被注册了解锁节点的部分ifnottable.find(FBS.ActivatePoints[key],point)thenreturnfalseendifnotself._activate_states[key]thenself._activate_states[key]{}endreturntrueend---设置羁绊计数节点的解锁状态缓存functionFBS:set_activate_state(key,point,new_state)ifnotself:protect_activate_state(key,point)thenreturnendself._activate_states[key][point]new_stateend---处理羁绊计数节点的解锁状态functionFBS:process_activate_state(key,point,new_state)ifnotself:protect_activate_state(key,point)thenreturnendlocalold_stateself._activate_states[key][point]ifold_statenew_statethenreturnendself._activate_states[key][point]new_state self:call_script(key,point,new_statetrueandon_openoron_close)end---调用羁绊计数效果脚本functionFBS:call_script(key,point,method_name)localscript_keystring.format(%s-%s,key,point)localscript_bodyGameScript.Bond[script_key]ifscript_bodythenlocalmethodscript_body[method_name]ifmethodthen---假设存在一个方法用于通过唯一string KEY获取一个table用于bond脚本储存临时数据---type tablelocalctxself._plr:get_ctx(FBS-..script_key)method(self._plr,ctx)-- 如果调用的是失活逻辑运行完脚本后回收上下文表ifmethod_nameon_closethenself._plr:remove_ctx(FBS-..script_key)endendendend---检测到羁绊计数变化functionFBS:on_bond_change(key,new_val)localold_valself:get_bond(key)localdeltanew_val-old_val-- 处理变化传播逻辑逐节点处理localdire1ifnew_valold_valthendire-1endforiold_val,new_val,diredoself:process_activate_state(key,i,dire1)end-- 更新缓存self:set_bond(key,new_val)end小结数据部分我们是如何处理的我们将玩家的实时数据直接储存到玩家状态本身这个状态属于外部。我们在系统内周期性检测外部状态变化并更新我们系统内部的状态。我们如何处理计数节点状态我们使用self._activate_states以KEY 节点的方式储存每一个流派的每一个计数节点的开启/关闭状态。在发现数据变化时我们通过检查缓存中的节点状态确认某个节点是否产生变化。以此确保我们只响应产生变化的部分无变化的部分则跳过逻辑。我们如何设计业务脚本羁绊效果的业务脚本本质上是一系列开关脚本。我们在对应节点进入开启状态时调用on_open逻辑。而在进入关闭状态时我们调用on_close逻辑。后续谈什么在第二部分中我们会讨论如何让这个系统和UI交互确保能够正确渲染UI文本和Tips相关数据。