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

【Lua实战】select('#') 与变参函数的 nil 尾巴

2026-09-21 15:43 作者:996 技术组 Lua实战

实战应用:用在哪里

变参函数(...)在 Lua 里无处不在:日志接口、消息转发、引擎触发器的参数透传都靠它。但变参遇到 nil 就变了味:# 的长度不可信、遍历会提前终止,select('#') 是唯一可靠的参数计数器。

nil 的陷阱现场

lua

---变参里的 nil 截断遍历
local function log(fmt, ...)
    local args = { ... }          -- nil 之后全部丢失
    for i = 1, #args do           -- # 在含 nil 时不可预期
        print(i, args[i])
    end
end
log("test", "a", nil, "c")        -- 只打印 a,#args == 1

---正确写法:select('#') 计数
local function logAll(...)
    local n = select("#", ...)
    for i = 1, n do
        local v = select(i, ...)
        print(i, tostring(v))
    end
end
logAll("test", "a", nil, "c")     -- 完整打印 3 个参数含 nil

{...} 打包变参时,nil 之后的参数全部丢失——表的构造在 nil 处截断。select("#", ...) 返回包括 nil 在内的真实参数个数,select(i, ...) 逐个取值,两者配合让 nil 值参数也能完整遍历。引擎触发器的参数透传正踩在这条线上:killmon 的第二参数在部分场景是 nil,透传链上任何一层用了 # 计数,nil 之后的参数就静默蒸发。

尾巴的处理

lua

---变参转表保留 nil:用 select 手工打包
local function pack(...)
    local t = {}
    local n = select("#", ...)
    for i = 1, n do
        t[i] = select(i, ...)
    end
    t.n = n
    return t
end

---透传时解包:带上 n 保证长度
local function forward(fn, packed)
    local args = {}
    for i = 1, packed.n do
        args[i] = packed[i]
    end
    fn(table.unpack(args, 1, packed.n))
end

变参转表的正确姿势是 select 手工打包并把长度存进表字段,透传时按存的长度解包——table.unpack 的第三参数显式给长度,nil 尾巴不丢。性能的边界:select(i, ...) 在循环里的开销可接受,万级参数遍历毫秒内完成;热路径上的变参函数(每帧调用)改定参版本,变参的灵活性不值那点打包开销。

验证与监控

变参工具函数的单测覆盖 nil 的所有位置:头部 nil、中间 nil、尾部 nil、全 nil、零参数;触发器透传链的参数计数审计,每层的参数个数断言。

踩坑记录

消息转发的中间层曾经用 {...} 打包,消息参数含 nil 时下游收到被截断的参数表,排查三天定位到打包层,select 打包重写。#args 曾经作为参数个数用于循环边界,nil 尾巴让循环少跑一半,n 字段方案替代。table.unpack 在 5.1 的名字是 unpack,跨版本的兼容封装统一入口,改名引发的两套写法收口。

上线要点

变参的规范进编码评审:日志与转发类函数用 select 全参遍历,业务函数优先定参。触发器透传的参数个数写进触发器的接口文档,每个触发器的参数表是契约。变参工具函数进公共库统一维护,各自造轮子的时代结束。

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

作者履历与出处

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

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

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

LATEST ARTICLES

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

玩法策划黑市限时货架

【玩法策划】矿洞黑市:黑市商人限时货架的玩法设计

实战应用:用在哪里 盟重矿洞挖出的黑曜石除了堆仓库没有去向,矿工玩法缺一条消耗链。黑市商人每 4 小时换一批限时货架,6 个…

2026-09-23 00:25 996 技术组
玩法策划护航海盗

【玩法策划】码头护航:商船护航与海盗袭击的玩法设计

实战应用:用在哪里 盟重码头到沙巴克水路要 20 分钟,商船运货收益高但会被半兽海盗打劫,单人押船九死一生。码头护航做成对抗…

2026-09-23 00:25 996 技术组
网络通信合并打包

【网络通信】消息合并:同帧多包的打包下发

实战应用:用在哪里 拍卖行竞拍高峰,一件屠龙刀 1 秒能产生 60 条竞价事件,逐条 sendluamsg 下发,竞拍面板每…

2026-09-23 00:25 996 技术组
Lua实战权重抽奖

【Lua实战】权重轮盘:动态权重的原地缩减抽取

实战应用:用在哪里 金盒转盘 8 格奖池,普通奖越来越疲,大奖既要概率递增又不能失控。动态权重轮盘:每抽一次普通奖权重缩减 …

2026-09-23 00:25 996 技术组
案例复盘周年庆重复领取

【案例复盘】周年庆福利重复领取的事故复盘

实战应用:用在哪里 周年庆礼包开领 30 分钟,3000 名玩家多领了一份礼盒,回收与舆情处理耗掉两天。事故根因简单得刺眼:…

2026-09-23 00:25 996 技术组
数据存储流水强化

【数据存储】道具流水:装备强化全程的流水追踪

实战应用:用在哪里 一次回档让玩家的裁决之杖从强化 7 退回 5,口头承诺补偿却没有账可查。强化流水把每次强化的时间、道具、…

2026-09-23 00:25 996 技术组