数值程序化 lua中的load运用

[TOC]

数值条件

以下是游戏中常见条件达到逻辑

  • 收集X个Y道具
  • 角色达到Y级开启
  • 任意一个装备升级到Y品质 …

如何在策划配置表中,定义以上规则?常见的办法就是预先定义事件类型X事件值Y,代码中监听事件X,根据事件的具体逻辑计算相关当前值事件Y比较,以得出是否满足配置项的条件。

目标类型 说明
1 获得道具
2 角色当前等级
3 升级装备
配置数值 说明
{1, X, Y} 收集X个Y道具
{2, Y} 角色达到Y级开启
{3, Y} 任意一个装备升级到Y品质

对于多条件/运算,需要新增字段表达与/或的语义 假设 1表示逻辑, 0表示逻辑 所集X个Y道具,并且角色达到Y级开启的配置则为


{{1, X, Y}, 1, {2, Y}}

数值条件 程序化

按我的工作了解,几乎所有的项目都是按类似的格式来配置条件数值,且工作得很好。但在程序猿的角度,”不爽点”就是条件的表现形式不直观,又或者说当数值策有稍微的代码逻辑能力时,是否可以将数值表达式,更”程序化”呢!

配置数值 说明
“item_count(X) >= Y” 收集X个Y道具
“role_lvl >= Y” 角色达到Y级开启
“equip_quality_count >= Y” 任意一个装备升级到Y品质

在lua中可以利用load来实现数值程序化

  • 支持逻辑运算
  • 支持自定义条件变量
  • 支持自定义条件方法
--- require above lua 5.3

-- file: keyworlds.lua
local keyworlds = {
    func_map = {
        item_count = function (role, item_id)
            local item_map = role.item_map
            local item = item_map[item_id]
            if item == nil then
              return 0
            end

            return item.count or 0
        end,

        equip_quality_count = function(role, quality)
            local equip_map = role.equip_map
            local count = 0
            for _, data in pairs(equip_map) do
                if data.quality == quality then
                    count = count + 1
                end
            end

            return count
        end,
    },

    var_map = {
        role_lvl = function (role)
            return role.level
        end
    }
}

-- file: exp.lua
local exp = {}
function exp.new(s)
    local obj = setmetatable({}, {__index = exp})
    obj:init(s)
    return obj
end

function exp:init(s)
    self._role = nil
    self._env = {
        func_map = keyworlds.func_map,
        var_map = keyworlds.var_map,
    }
    setmetatable(self._env, {
        __index = function (_, k)
            local var = self._env.var_map[k]
            local func = self._env.func_map[k]
    
            if var ~= nil then
                local ok, v = pcall(var, self._role)
                if not ok then error(v) end
                return v
            elseif func ~= nil then
                return function (...)
                    local ok, v = pcall(func, self._role, ...)
                    if not ok then error(v) end
                    return v
                end
            end
    
            return nil
        end
    })

    self.str = s
    local func, err = load("return " .. self.str, "exp", "t", self._env)
    if not func then
        error(string.format("%s %s", self.str, err))
    end
    self.func = func
end

function exp:run(role)
    self._role = role
    local func, result = pcall(self.func)
    if not func then
      error(string.format("%s %s", self.str, result))
    end

    return result
end

测试代码如下:

local role = {
    level = 1,
    item_map = {[1] = {count=100}},
    equip_map = {[11001] = {quality=1}}
}
print(exp.new("item_count(1) >= 0"):run(role)) -- false
print(exp.new("role_lvl >= 1"):run(role)) -- true
print(exp.new("equip_quality_count(1) >= 0"):run(role)) -- true
print(exp.new("role_lvl > 0 and item_count(1) < 0"):run(role)) -- false

总结

  1. 程序化使条件表达式更强大了。但正如上文提到,如果数值策划人员不懂代码,将增大数值配置难度且更易出错。
  2. 从过往的经验来说,大于等于这两个语义逻辑就够用了。基本上满足所有的任务、成就和活动条件判断,做成程序化显然大材小用。
  3. load的代码其它是一段lua代码,前端要考虑会被注入代码的风险。

并不是数值配置程序化,就没意义。是否真的需要将数值程序化,要视具体业务需求而定。如果运用在AI行为树的配置上就会十分高效(各种复杂条件下的逻辑变换),而且能担负起这项工作的策划必需具备代码逻辑运算的思维能力。



原文:
https://lizijie.github.io/2022/06/11/%E6%95%B0%E5%80%BC%E7%A8%8B%E5%BA%8F%E5%8C%96-lua%E4%B8%AD%E7%9A%84load%E8%BF%90%E7%94%A8.html
作者github:
https://github.com/lizijie

PREVIOUS整理常用linux命令
NEXTlua代码规范