云计算百科
云计算领域专业知识百科平台

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

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

羁绊计数

本文是该系统的第(一)部分,本文谈这些:

  • 系统的功能需求
  • 基础系统骨架
  • 系统逻辑流程
  • 处理计数变化,并将变化通知到业务脚本
  • 相关实战代码

文章目录

    • 1. 基础支持:计数的储存和变化检测
    • 2. 计数变化状态的更新,计数效果的开启和关闭
    • 小结

这个系统要实现这些功能:

  • 计数效果的激活、失活
  • 计数文本状态的更新:激活的为亮色,未激活的为暗色
  • 计数效果的阈值节点动态更新
  • 派系图标的显示
  • 实际已获得的计数数量显示
  • 鼠标悬浮Tips文本显示
  • 以上效果对每个玩家各自独立运行
  • 这个系统所处的上下文环境:

  • 多人游戏
  • 单局制
  • 实时战斗
  • 基于这些条件,我们逐步整理思路。


    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相关数据。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 真实项目游戏系统实战记录:羁绊派系计数系统(一)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!