CHUAN2 DEV ENGINE
996 正版授权研发中心 · 360 授权合作教学中心 · 抖音传奇直播合作授权 · 快手推广运营商授权
OFFICIAL LICENSED ACADEMY 查验官方授权证书 →
// 威海旷世互娱教学基地 · 技术文章
进阶实战游戏功能996引擎

Lua深度比较:逐字段递归比对两张表

2026-10-02 08:16 作者:996 技术组 996引擎Lua教程传奇脚本进阶实战游戏功能996引擎

【语法算法】

单行代码拆解:先看一行代码:t1 == t2——两张内容一模一样的表,比较结果却是不等。等号比的是引用不是内容,两张表各占各的内存,地址不同就判不等。深度比较就是绕开引用、逐字段比对内容的方案。这篇把深度比较整套写法拆开:三道闸门、短路返回、递归比对、调试验证,一段一段照抄能跑。

一、效果演示

演示场左右两张表的面板。点「改字段A」或「改字段B」随机改动一处字段值,点「比较」:比较器从外层一路挖进嵌套层,走过比较路径的字段逐个亮起,全等亮绿色通过,任何一处不等立即短路返回,退出点标红。

mermaid
flowchart TD
A[传入两张表] --> B{引用相同}
B -- 是 --> C[直接相等]
B -- 否 --> D{都是表}
D -- 否 --> E[值直接比]
D -- 是 --> F{键数相同}
F -- 否 --> G[返回不等]
F -- 是 --> H[逐字段递归]
H --> I{全部相等}
I -- 是 --> J[返回相等]
I -- 否 --> G
demo
 fx-deepcompare

二、底层原理

深度比较的机关是三道闸门依次放行——第一道比引用,同一张表直接判等,省掉全部递归;第二道比键数,键数不同必然不等,一次计数就短路;第三道才逐字段递归比值。闸门的次序就是性能的次序:最便宜的判断放最前面,最贵的递归留在最后。
短路是灵魂——任何一处不等立即层层返回,剩下的字段一眼都不多看。工程上还要防循环引用:表里套着表自己,递归就永不落地,配一个已访问集合,进递归先登记,见过的直接按相等处理。比较的语义也要先想清楚:键集必须完全一致才算等,还是允许一边缺键?演示实现选严格派,缺一个键就判不等。

这套机制的代码量不到五十行,难的不是递归,而是把"什么时候能提前说不等"想全——闸门越靠前省得越多。

键集核对是新手最容易漏的闸门——只遍历一边的键逐个比,另一边多出来的键就成了漏网之鱼,两张键数不同的表也能假相等。正确的次序是先把两边的键各自登记成集合,互相核对缺席情况,键集完全一致才放行进递归。这一步做完,后面递归时只查一边的键也不会漏——另一边已经确认没有多余。

三、核心代码:完整模块(上·骨架)

lua
-- @file DeepCompare.lua
-- 深度比较 —— 逐字段递归比对两张表

local DeepCompare = {}

local function ShowTip(msg)
    if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end

local function IsTable(t) return type(t) == "table" end

function DeepCompare.Compare(a, b)
    if a == b then return true end
    if not IsTable(a) or not IsTable(b) then return false end
    local keysA = {}
    for k in pairs(a) do keysA[k] = true end
    for k in pairs(b) do
        if not keysA[k] then return false end
        keysA[k] = nil
    end
    if next(keysA) then return false end
    for k, v in pairs(a) do
        if not DeepCompare.Compare(v, b[k]) then return false end
    end
    return true
end

四、核心代码:完整模块(下·推进与卸载)

lua
SL:ScheduleOnce(function()
    SL:BindDebugButton("比较", function()
        local t1 = { a = 1, b = { c = 2 } }
        local t2 = { a = 1, b = { c = 3 } }
        local same = DeepCompare.Compare(t1, t2)
        ShowTip(same and "两张表相等" or "两张表不等")
    end)
    ShowTip("技能已加载: 深度比较")
end, 1.0)

function DeepCompare.Unload()
end

return DeepCompare

五、机制问答

问:循环引用会怎么样?
答:递归永不落地——递归前登记已访问的表,再遇到就按引用相等处理,直接掐断。

问:键数相同但键名不同呢?
答:逐个核对键名——键数只是快筛,键集完全一致才算过第二道闸。

问:数组和表混着比行不行?
答:行——数组下标就是键,同一套递归通吃,但数字键和字符串键永远不会相等。

问:浮点字段怎么比?
答:别直接比相等——误差会让该等的判不等,工程上比差值是否小于容限。

问:性能上还能怎么提?
答:高频场景先比一个指纹——把表的长度和几个关键字段拼成摘要,摘要不同直接判不等,相同才走全量递归。

六、调参与实战怎么用

参数层面可调的是容限和深度——浮点容限按业务精度定,深度上限防异常嵌套。实战里这类机制卡在配置热更比对、存档冲突检测、状态同步差异计算三个位置,共同点是表很大而变化很小,闸门前置的收益特别明显。常见坑两个:比较函数写成单向递归只查一边的键;已访问集合没有在比较结束后清空,下一次比较拿到脏集合全程误判。

补充一个设计层面的思考——这类机制的核心难点不在于代码怎么写,而在于"什么时候触发、触发后做什么、做完了怎么收场"这三个问题的答案。答案清楚了,代码只是把答案翻译成脚本的过程;答案含糊,写出来的东西改来改去都在原地打转。动手前先把这三个问题各用一句话答出来,答不出的那个就是设计缺口,先补设计再动手。

补充一个维护层面的思考——所有的状态变量都收在模块的闭包里,不摆到全局表。全局表的变量在热重载时会残留旧值,闭包的变量每次加载都是新的,这层隔离是热重载环境里防事故的第一道墙。卸载入口要把定时器、连接、标记一揽子清干净,清不干净的下场就是旧事故原样重演。这篇的模块照抄能跑,改的就是参数表。

补充一个工程层面的思考——模块的可测试性和耦合度成反比。耦合越低越能单独验证,越高越要整套环境才能跑起来。降耦合的通用手法是依赖注入:框架接口通过参数传进来,而不是在模块里直接调死,验证的时候传一组假接口就能脱离框架独立运行。平时多花一分钟做注入,排查的时候省下的是一整晚。

写完留一句给做比对系的同学:等号只认地址,深度比较才认内容——闸门次序摆对,再大的表也经不起一次短路。

作者履历与出处

本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。

← 返回文章地图返回研学路径

幂尔框架 · 实战干货 · 接口调用

LATEST ARTICLES

全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →

进阶实战游戏功能

走一格为什么是半秒?拆开996引擎走路状态机

接手二开的兄弟几乎都撞过同一堵墙:玩家嫌走路"一格一格像机器人",老板拍板要"丝滑移动",改服务端速度?不对。改客户端动画帧…

2026-10-03 05:31 996 技术组
进阶实战游戏功能

特效总挂在脚后跟上?挂点表选对一行顶十行

【游戏功能】 先看一个熟场面:法师的火球特效挂在怪的脚后跟上,明明技能说明写着"焚烧其面",火星子却在地上打滚。群里有位策划…

2026-10-03 05:30 996 技术组
进阶实战游戏功能

刀刀都判命中,玩家为什么喊挥空?命中表现差的那一拍

【游戏功能】 上周有个开服的老板在群里发了一段录屏:他的战士明明刀刀都砍中了,观众却一片"这刀挥空了吧"。他原话是"判定日志…

2026-10-03 05:30 996 技术组
进阶实战游戏功能

怪的皮一会儿绿一会儿红?状态染色的色号表就三列

【游戏功能】 先说结论放在开头:怪的皮一会儿绿一会儿红,不是美术换了几套贴图,是染色系统在怪身上盖了一层"色镜"。开服群里有…

2026-10-03 05:30 996 技术组
进阶实战游戏功能

996引擎Buff系统解剖:中毒变绿、冰冻发白是怎么挂上去的

前两天群里有个架设的朋友问了个需求:中毒的人要变绿、每两秒掉一次血、毒还能叠五层,问是不是要在服务端把整套状态机重写。我说你…

2026-10-03 05:25 996 技术组
进阶实战游戏功能

编钟纹:五口钟排着敲,一口更比一口疼

【游戏功能】 先抛一个坑:五口钟一模一样大,为什么一口比一口疼?先后次序动不得吗?倒过来敲会怎样?这篇把编钟纹整套写法拆开:…

2026-10-03 05:01 996 技术组