最近正在开发一个PC游戏的羁绊计数系统,和《金铲铲之战》的羁绊计数逻辑相似。 玩家获得特定派系羁绊达一定数量时,对应派系羁绊效果激活,反之失效。

本文是该系统的第(一)部分,本文谈这些:
- 系统的功能需求
- 基础系统骨架
- 系统逻辑流程
- 处理计数变化,并将变化通知到业务脚本
- 相关实战代码
文章目录
-
- 1. 基础支持:计数的储存和变化检测
- 2. 计数变化状态的更新,计数效果的开启和关闭
- 小结
这个系统要实现这些功能:
这个系统所处的上下文环境:
基于这些条件,我们逐步整理思路。
1. 基础支持:计数的储存和变化检测
首先,根据已有的需求,我们确定这个系统是一个客户端系统,只需要全程在客户端进行处理。 同时,这个系统是每个玩家数据各自独立的,因此该系统必须持有玩家状态的引用。
计数存到哪里?
我们要开发一个能对比计数变化并动态反应的系统,自然涉及到计数数据的储存。 数据变化时,要确保系统能识别到变化。
我们需要两处数据储存位置:
- 玩家状态数据:未经过处理的计数状态,外部的脚本和游戏内的互动,均直接操作玩家状态数据。
- 系统内部状态数据:已经经过变化处理的计数状态。
计数变化的更新方式?
我们必须能够在游戏过程中动态检测流派计数的实时状态,这天然地有两种方案:
在我当前负责的这个项目中,游戏不需要特别实时的羁绊计数更新频率,在这里我可以采用方案2,直接给定一个计时器,每1秒获取一次羁绊计数,对比缓存中的计数后处理变化。
综上:
我们已经确定数据怎样储存,怎样检测变化。 现在我们可以着手整理系统的基本骨架。
骨架流程:
我们按照系统需求,整理出基本的逻辑流程。

骨架脚本:
— 流派羁绊系统
—@class FBS : Object
local FBS = oop.class()
—注册系统
function FBS:__init(plr)
self._plr = plr
end
—初始化系统
function FBS:setup()
—流派计数缓存
—@type { [string]: integer }
self._bonds = {}
— 创建每1秒计时器
self._updater = Timer.StartLoop(1, function()
self:update()
end)
end
—获取羁绊计数缓存值
function FBS:get_bond(key)
return self._bonds[key] or 0
end
—设置羁绊计数缓存值
function FBS:set_bond(key, value)
self._bonds[key] = value
end
—检测到羁绊计数变化
function FBS:on_bond_change(key, new_val)
local old_val = self:get_bond(key)
local delta = new_val – old_val
–TODO … 处理变化传播逻辑
— 更新缓存
self:set_bond(key, new_val)
end
—检测一次变化
function FBS:update()
local plr_bonds = self._plr:get_bonds() — 假设玩家类存在该方法获取实时计数状态
for k, v in pairs(plr_bonds) do
if self:get_bond(k) ~= v then
self:on_bond_change(k, v)
end
end
end
—注销系统
function FBS:remove()
self._updater:remove()
self._updater = nil
self._bonds = nil
self._plr = nil
end
2. 计数变化状态的更新,计数效果的开启和关闭
在上1节中我们已经搭建好了检测变化的系统骨架,现在我们的系统已经具备了获取计数变化并更新缓存的能力。 为了让系统能真正影响游戏逻辑效果,我们还需要这个系统能够和游戏脚本交互。
假设我们有这样的游戏脚本逻辑:
— 剑修-2:每1秒增加1点攻击力
GameScript.Bond['剑修-2'] = {
— 开启效果
on_open = function(plr, ctx)
ctx.timer = Timer.StartLoop(1, function()
plr:setAttr('攻击力', plr:getAttr('攻击力') + 1)
end)
end,
— 关闭效果
on_close = function(plr, ctx)
ctx.timer:remove()
end,
}
GameScript.Bond['剑修-4'] = …
GameScript.Bond['剑修-6'] = …
GameScript.Bond['剑修-8'] = …
现在我们需要通过我们的羁绊计数系统对其进行管理,实现上1节中未完成的部分。 我们需要预设各流派的激活节点,并在计数变化时进行一次状态更新,并调用相关的脚本。
FBS.ActivatePoints = {
['剑修'] = { 2, 4, 6, 8 }
}
function FBS:setup()
— … 原有代码
—各节点激活状态
—@type { [string]: { [integer]: boolean? } }
self._activate_states = {}
end
—动态为 activate_states 分配必要的子项
function FBS:protect_activate_state(key, point)
— 确保只处理被注册了解锁节点的部分
if not table.find(FBS.ActivatePoints[key], point) then
return false
end
if not self._activate_states[key] then
self._activate_states[key] = {}
end
return true
end
—设置羁绊计数节点的解锁状态缓存
function FBS:set_activate_state(key, point, new_state)
if not self:protect_activate_state(key, point) then
return
end
self._activate_states[key][point] = new_state
end
—处理羁绊计数节点的解锁状态
function FBS:process_activate_state(key, point, new_state)
if not self:protect_activate_state(key, point) then
return
end
local old_state = self._activate_states[key][point]
if old_state == new_state then
return
end
self._activate_states[key][point] = new_state
self:call_script(key, point, new_state == true and 'on_open' or 'on_close')
end
—调用羁绊计数效果脚本
function FBS:call_script(key, point, method_name)
local script_key = string.format('%s-%s', key, point)
local script_body = GameScript.Bond[script_key]
if script_body then
local method = script_body[method_name]
if method then
—假设存在一个方法,用于通过唯一string KEY获取一个table,用于bond脚本储存临时数据
—@type table
local ctx = self._plr:get_ctx('FBS-' .. script_key)
method(self._plr, ctx)
— 如果调用的是失活逻辑,运行完脚本后回收上下文表
if method_name == 'on_close' then
self._plr:remove_ctx('FBS-' .. script_key)
end
end
end
end
—检测到羁绊计数变化
function FBS:on_bond_change(key, new_val)
local old_val = self:get_bond(key)
local delta = new_val – old_val
— 处理变化传播逻辑(逐节点处理)
local dire = 1
if new_val < old_val then
dire = –1
end
for i = old_val, new_val, dire do
self:process_activate_state(key, i, dire == 1)
end
— 更新缓存
self:set_bond(key, new_val)
end
小结
数据部分我们是如何处理的?
我们将玩家的实时数据直接储存到玩家状态本身,这个状态属于外部。 我们在系统内周期性检测外部状态变化,并更新我们系统内部的状态。
我们如何处理计数节点状态?
我们使用 self._activate_states 以 KEY + 节点 的方式,储存每一个流派的每一个计数节点的 开启/关闭 状态。
在发现数据变化时,我们通过检查缓存中的节点状态,确认某个节点是否产生变化。 以此确保我们只响应产生变化的部分,无变化的部分则跳过逻辑。
我们如何设计业务脚本?
羁绊效果的业务脚本本质上是一系列开关脚本。
我们在对应节点进入开启状态时,调用 on_open 逻辑。 而在进入关闭状态时,我们调用 on_close 逻辑。
后续谈什么?
在第(二)部分中,我们会讨论如何让这个系统和UI交互,确保能够正确渲染UI文本和Tips相关数据。
网硕互联帮助中心








评论前必须登录!
注册