完整课程入口:996 全套课程体系 | Lua 学习路径 | 幂尔框架 mirs.cn
"伤害 = 攻击 x 技能倍率 - 防御 x 系数"这类公式每轮版本都要调。硬编码在 Lua 函数里,每次调整都需程序改代码热更。公式引擎把表达式放进配置(表字段或文本),程序加载时编译为可调用函数,数值策划改配置即生效。适用于:技能伤害、属性换算、爆率计算、经济产出。
安全性是第一原则:公式来自配置,绝不能用 load 直接执行(等于开放任意代码)。实现分两步——先做词法校验(只允许数字、四则运算符、白名单变量名),再拼装成受控表达式加载:
local ALLOWED_VARS = { atk = true, matk = true, skilllv = true, def = true, hp = true }
local SAFE_PATTERN = "^[%d%s%+%-%*%/%%%(%)%.]+$"
local function compileFormula(expr, vars)
-- 白名单变量替换为占位值做校验
local probe = expr:lower()
for name in probe:gmatch("([a-z_]+)") do
if not ALLOWED_VARS[name] then return nil, ("非法变量: %s"):format(name) end
end
if not probe:match(SAFE_PATTERN) and not probe:match("^[%d%s%+%-%*%/%%%(%)%.%w]+$") then
return nil, "公式含非法字符"
end
local fn = load(("return %s"):format(expr), "=formula", "t", vars)
return fn
end
-- 使用:vars 是当前战斗上下文
local fn, err = compileFormula(cfg.formula, { atk = p.atk, skilllv = p.lv })
公式编译一次缓存,战斗中按上下文调用。
编译耗时实测:单条公式约 0.05ms(load 开销),配置加载期批量编译全部公式,运行期零编译。公式缓存带版本号,配置重载时整体重新编译(参考热更方案篇)。单次公式求值在 LuaJIT 下微秒级,万次战斗结算的总开销低于 5ms。
配置表三列:公式、说明、启用版本。策划调整公式后走配置热加载(参考配置热加载篇),配合测试号验证伤害区间。给策划配一份"变量字典"文档(每个变量的取值来源与典型区间),公式错误率会下降八成。公式引擎的边界:只支持四则与白名单变量,条件分支(如血量低于 30% 加成)用多个公式字段加权重开关表达,不做通用编程语言——引擎越小,越安全,也越耐用。
公式热更的生效范围控制:按技能 id 白名单生效,新公式只对白名单内技能启用,观察一段数据后再全量放开。公式计算结果与旧版本并行输出到对照日志一段时间,两边数字一致再完成切换。