TXT 与 Lua 脚本全面对比

← 返回课程体系

在传奇 / 传世类引擎中,TXT 与 Lua 并不是「旧方案被新方案取代」的关系,而是「入口层」与「实现层」的分工:TXT 用引擎预置的命令表把能力直连成段落标签,零基础即可写出 NPC 对话与事件触发;Lua 则是嵌入引擎的通用编程语言,用 table、函数与模块承载复杂业务逻辑。实务中最稳妥的用法是让 TXT 负责触发与界面入口、Lua 负责算法与系统,二者通过 callscript 互调,并共享同一套全局变量。速度与效率上也各有胜场:TXT 入门速度极快、门槛极低,记住一张命令表就能动手改脚本,新手与策划都能直接用;Lua 则赢在复杂系统上的开发效率与执行效率,代价是先跨过编程门槛。

结论速览

把一套版本比作一栋楼,TXT 脚本相当于标准化的水电接口面板:规格统一、即插即用、谁都能看懂说明书;Lua 脚本相当于可以自行设计管线的施工能力:自由度极高,但对施工者的要求也高一档。二者并不是竞争关系,因为它们解决的问题不一样。

真正需要记住的判据只有一条:凡是「引擎已经提供现成命令」的事情,TXT 写起来更快;凡是「引擎没有现成命令、需要自己算、自己存、自己封装」的事情,只有 Lua 能写。引擎社区里被反复引用的一句话正是这个意思:TXT 做不到的,Lua 可以做出来;官方一旦开源,剩下的就是自己改、自己封装,而这些都离不开 Lua。

表 1 TXT 脚本与 Lua 脚本的十二个维度对照
对比维度 TXT 脚本 Lua 脚本 对开发的实际影响
语言性质 引擎内置的配置型脚本,只能用引擎预置的指令名 嵌入引擎的标准 Lua 通用编程语言 决定整个系统的能力上限
运行方式 文本逐行解释,读到标签即执行 先编译为字节码,再由 Lua 虚拟机执行 决定执行效率与优化空间
语法风格 段落标签加条件段、执行段、对话段 结构化语句与函数 决定代码可读性与协作效率
流程控制 只有 GOTO 与 BREAK,没有 while 与 for if、while、for、repeat、函数一应俱全 决定复杂逻辑能否写得出来
数据结构 只有整数变量与字符串变量 table 可表达数组、字典、对象 决定能否表达复杂业务数据
复用方式 用 CALL 引用其他 txt 文件的段落 函数、模块、面向对象 决定改版速度与维护成本
变量规模 逻辑变量 1 至 1024、个人变量 499 个,属稀缺资源 局部变量无数量限制 决定大型系统的数据存放方式
触发入口 QFunction-0.txt、QManage.txt 等 QFunction-0.lua 等平行文件 两套入口平行存在,可混用
调试手段 基本只能靠 SENDMSG 打日志 日志更完整,报错含文件名与行号 决定排错效率
学习曲线 低,记住命令即可上手 中,需要编程思维与数据结构基础 决定团队的人手门槛
热更新 改文本即可生效,粒度到文件 改脚本即可生效,粒度到模块 两者都支持,Lua 粒度更细
安全风险 link、goto、call、messagebox 等字段可被伪造调用 客户端同样可被修改 两者都必须在服务端做指纹判断

本质差异:指令表与编程语言

TXT 严格来说不是一门编程语言,而是引擎为策划准备的一套「对话式指令表」。引擎解析 txt 时按行读取:遇到方括号包的标签就登记一个入口,遇到条件段就开始收集判定条件,遇到执行段就按顺序调用引擎在底层注册好的指令。脚本里出现的每一条命令都必须由引擎预先注册,脚本无法自己创造新命令,这正是「TXT 做不到的 Lua 可以做出来」这句话的技术含义。

Lua 则是一门真正的语言。引擎把 Lua 虚拟机嵌进服务端,同时把游戏对象(人物、怪物、物品、地图)通过一套 API 暴露给脚本。脚本因此可以定义函数、引入自己的模块、构造任意结构的数据,甚至把一组常用逻辑封装成「自定义命令」给 TXT 调用。由于 Lua 本身语法简洁、执行效率高、嵌入友好,它长期以来就是游戏引擎最主流的脚本方案。

关键认知

读 TXT 脚本时,不要把它当成代码,要把它当成配置文件:它的每一行都是一条「填好参数的指令」。而读 Lua 脚本时,它是代码:有变量作用域、有返回值、有控制流。这个心态差异决定了排查问题的入手方式完全不同。

图 1 两类脚本在引擎中的执行路径

flowchart TD A["玩家操作或引擎事件"] --> B["引擎事件分发"] B --> C["TXT 入口
QFunction-0.txt 等"] B --> D["Lua 入口
QFunction-0.lua 等"] C --> E["TXT 行解释器
逐行解析条件段与执行段"] E --> F["引擎预置命令表
CHECKITEM、GAMEGOLD、SENDMSG"] D --> G["Lua 虚拟机
编译为字节码后执行"] G --> H["引擎注册的 player 等 API"] F --> I["游戏世界状态变更"] H --> I

说明:TXT 走「解释一行、调用一条预置命令」的路径;Lua 走「编译字节码、调用暴露 API」的路径。

语法结构与可读性

TXT 的组织单位是「段落」。段落用方括号包住的名字声明,既可以被对话选项点击进入,也可以被 GOTO @段落名 直接跳转。每个段落内部再用三个关键字切分为条件区、执行区与对话区:条件区判定是否成立,执行区做实际动作,对话区把文字显示给玩家。行首的分号是注释符,该行既不显示也不参与解析。

下面用同一个需求做对照:玩家等级不低于 50 级、元宝不少于 1000 时,扣除 1000 元宝并奖励 25 万经验,否则提示条件不足。

TXT 写法

[@main]
#IF
CHECKLEVELEX > 49
CHECKGAMEGOLD > 999
#ACT
GAMEGOLD - 1000
CHANGEEXP + 250000
SENDMSG 6 兑换成功,获得 25 万经验!
#SAY
兑换成功,已扣除 1000 元宝。\
<返回/@main>\
#ELSESAY
条件不足:需要 50 级且 1000 元宝以上。\
<离开/@exit>\

Lua 写法

-- 996 引擎 Lua 版:等级与元宝双条件兑换
function main(actor)          -- 入口参数就是玩家对象
    local player = actor      -- 别名成 player,之后统一调用
    local lv   = player:getLevel()
    local gold = player:getGameGold()
    if lv >= 50 and gold >= 1000 then
        player:addGameGold(-1000)
        player:addExp(250000)
        player:send(ConstCfg.notice.own,
            '{"Msg":"兑换成功,获得 25 万经验!","Type":9}')
        return true
    end
    player:send(ConstCfg.notice.own,
        '{"Msg":"条件不足:需 50 级且 1000 元宝以上","Type":9}')
    return false
end

两段代码的信息量差距一眼可见。TXT 版把「条件」「动作」「文案」硬编码在三个区段里,条件之间是隐式的「与」关系(多行并列即为同时成立);Lua 版把条件写成表达式,把提示文案与判定逻辑放在同一个分支里,分支结构从缩进就能读出来。发送消息的写法也体现了这个差异:TXT 用一行 SENDMSG 6 文本 调用预置命令,Lua 则把玩家对象当成消息的主体,用 player:send(类型, JSON) 发送带 JSON 结构体的消息,可以携带颜色与消息类型。这里还有一条写法约定值得记住:入口函数拿到的第一个参数就是玩家对象,脚本里习惯先写 local player = actor 换个名字,之后取等级、扣元宝、发消息全部走 player: 的方法形式,代码读起来主体始终是「谁在做事」,而不是一串散落的对象标识。

表 2 常用语法逐项对照
语法点 TXT 写法 Lua 写法
单条件判断 CHECKLEVELEX > 49 if lv > 49 then
多条件「与」 条件区内连续多行并列,全部成立才执行 if a and b then
多条件「或」 无原生写法,需拆成多个段落用 GOTO 汇合 if a or b then
否则分支 #ELSEACT / #ELSESAY else / elseif
循环 只能用 GOTO 回跳到本段落 while / for / repeat
变量赋值 MOV 变量 数值 x = 1
自增 INC 变量 数值 x = x + n
求和 SUM 变量A 变量B x = a + b
数值比较 SMALL / LARGE / EQUAL < / > / ==
注释 行首分号 --
调用子过程 CALL [\路径\文件.txt] @标签GOTO @标签 func()require("模块")
输出提示 SENDMSG 6 文本 player:send(类型, JSON)
字符串拼接 靠引擎内置的变量占位符拼接,能力有限 a .. b
集合数据 没有数组与字典,只能用编号变量硬凑 local t = {1, 2, name = "x"}
定义可复用逻辑 没有函数概念,靠 CALL 复用整个段落 local function f() end
作用域 全部全局,没有局部变量 local 局部与全局并存
错误处理 没有,写错即中断或产生意外跳转 pcall / assert

社区里的总结很直白:if 套 if 比 goto 好用。这恰好点中了 TXT 的结构性瓶颈。当分支超过三层,TXT 只能靠 GOTO 在段落之间来回跳,一份脚本从「从上往下读」退化成「在文件里反复横跳」;而 Lua 的缩进天然表达了层级,嵌套再深也还能读。

流程控制能力

TXT 的 GOTO 是转向指令,把执行权交给指定段落。它是 TXT 唯一的流程控制手段:要做循环,就让 GOTO 跳回自己;要做分支,就让不同条件跳向不同段落。这带来一个直接后果,循环次数不可预测,条件一写错就形成死循环

引擎为此保留了保护机制:在服务端的 !setup.txt 中查找 ScriptGotoCountLimit,把等号后面的数值调到 10000 至 50000 之间,可以缓解脚本死循环导致的卡服。但调高上限只是让它更难触发,并不能修复逻辑错误。通行的建议是:写脚本时尽量少用 goto @XXX 这类跳转命令。

TXT 用 GOTO 模拟循环

[@main]
#IF
CHECKITEM 金创药 10
#ACT
TAKE 金创药 10
GAMEGOLD + 100
GOTO @main          ; 回收后跳回本段,形成循环,条件写错即死循环

Lua 用循环语句表达同一件事

-- 回收背包内所有满 10 个的指定物品,回收次数由数据决定
-- 前提:player 即当前玩家对象,也就是入口参数 actor 的别名
local count = 0
while player:getItemCount("金创药") >= 10 do
    player:takeItem("金创药", 10)
    player:addGameGold(100)
    count = count + 1
    if count >= 100 then break end   -- 显式设置上限,天然安全
end

对比之下可以看出,Lua 的循环有明确的开始与结束边界,还能用 break 主动设上限;TXT 的循环完全依赖条件在下一轮跳转前失效,一旦条件永远为真,就只能等引擎的跳数上限兜底。

变量体系与数据承载

TXT 的变量是额度制资源,这是它与 Lua 最容易被低估的差距。逻辑变量 [1][1024] 属于单个人物的临时开关,用来记录对话分支走到哪一步这类会话内的状态;真正需要跨登录保留的数值,则要交给个人变量/个人标识 [001][499]——它随角色创建自动生成、初始值为 0,且不会因为人物下线或服务器重启而重置,适合记录会员积分、任务进度这类需要长期保留的数据。

G 变量是全服唯一的共享变量,它不属于任何人物而属于服务器,任何人的运算都会产生影响,数据存放在 Mir200\GlobalVal.ini,取值范围与 P 变量相同。此外还有一类只读的系统变量,例如 <$USERNAME><$GUILDNAME><$LEVEL><$HP><$DC> 等,用来在对话文本里直接显示人物信息。如果预置变量不够用,还可以用 SAVEVAR HUMAN 变量名 ..\QuestDiary\路径\文件.txt 保存自定义变量、用 LOADVAR HUMAN 读回。

表 3 变量类型对照
变量类型 写法示例 作用域 是否持久化 典型用途
TXT 逻辑变量 [1][1024] 单个人物 一次性开关、对话分支标记
TXT 个人变量 [001][499] 单个人物 会员积分、任务进度
TXT N / P / D 变量 N0N9P0P9D0D9 单个人物 临时计算,数量极少
TXT 字符串变量 S0S9 单个人物 存放文本内容
TXT 全局变量 G0G999 全服共享 是,存 GlobalVal.ini 全服活动进度、排行榜基数
TXT 自定义人物变量 SAVEVAR HUMAN 名称 文件路径 单个人物 是,存自定义文件 版本自建属性
TXT 系统变量 <$USERNAME><$LEVEL> 当前人物 只读 对话文本中显示人物信息
Lua 局部变量 local x = 1 函数内 任意计算,无数量上限
Lua 表 local t = {a = 1, "x"} 任意 数组、字典、对象式结构
Lua 共享变量 s.share.LoadGlobalVar(类型, 名, 文件, 模式) 跨脚本 取决于参数 模块间共享配置与数据
为什么变量配额是分水岭

一个稍复杂的玩法,往往要同时跟踪十几个状态:今天是否已领取、累计充值档位、上一次挑战的层数、队伍成员编号、冷却剩余时间……在 TXT 里这些都要挤进编号变量,而且因为变量是全局的,两个功能抢用 [001] 就会互相覆盖,产生极难复现的偶发 Bug。

Lua 用 local 把状态关在函数里,用 table 把它们组织成一个整体,作用域天然隔离。这就是为什么「系统做到一定规模就必须换 Lua」不是风格偏好,而是变量容量与作用域机制决定的硬约束。

文件组织与触发体系

两套脚本共用同一个 Mir200\Envir 目录树,但走的文件不同。TXT 侧以 Market_def\QFunction-0.txt 作为物品使用、技能释放这类事件的主触发文件,以 MapQuest_Def\QManage.txt 承担登录、计时器这类管理型触发,MapQuest_Def\MapQuest.txt 则按「地图编号 + 怪物名」的格式登记杀怪条件,在指定怪物死亡时触发对应脚本段;每个 NPC 的对话脚本各自放在 Market_Def 下。

Lua 侧的文件是与之平行的一套:触发文件同样叫 QFunction-0.lua,NPC 逻辑放进 Market_Def\npc 目录,例如 sabukNpc.lua 负责沙巴克相关 NPC 逻辑、sabukWarSayStr.lua 存放对应文案,召唤这类 NPC 时直接指定 Envir\Market_Def\npc\sabukNpc.lua 即可。所以从目录结构看,Lua 并不是 TXT 的替代品,而是与它并排的另一个入口,这也解释了两者为什么能混写。

TXT 侧目录结构

Mir200\Envir\
├─ Market_def\
│  ├─ QFunction-0.txt      主触发:物品、技能
│  └─ <NPC 名>.txt          各 NPC 对话脚本
└─ MapQuest_Def\
   ├─ QManage.txt           登录、计时器触发
   └─ MapQuest.txt          杀怪触发(按地图与怪物名登记)

Lua 侧目录结构

Mir200\Envir\
├─ Market_Def\
│  ├─ QFunction-0.lua      与 TXT 平行的主触发
│  └─ npc\
│     ├─ sabukNpc.lua      NPC 逻辑模块
│     └─ sabukWarSayStr.lua 文案与配置
└─ 功能模块\                自定义模块,可被 callscript 调用

图 2 TXT 与 Lua 的触发体系对照

flowchart LR subgraph TXTSET["TXT 触发体系"] T1["QFunction-0.txt
物品、技能等主触发"] T2["QManage.txt
登录、计时器管理触发"] T3["MapQuest.txt
杀怪触发(怪物死亡)"] T4["Market_Def 下各 NPC 对话脚本"] end subgraph LUASET["Lua 触发体系"] L1["QFunction-0.lua
与 TXT 平行的主触发"] L2["QManage.lua
登录、计时器触发"] L3["Market_Def 下的 npc 目录
各 NPC 逻辑模块"] L4["自定义功能模块
可被 callscript 调用"] end T1 -.功能对应.-> L1 T2 -.功能对应.-> L2 T4 -.功能对应.-> L3

说明:两侧文件在不同引擎中的命名可能略有差异,但「主触发、管理触发、NPC 脚本」这三层结构是共通的。

表 4 触发入口对照
触发类型 TXT 位置 Lua 位置 触发时机
主触发 Market_def\QFunction-0.txt QFunction-0.lua 物品使用、技能释放等
管理触发 MapQuest_Def\QManage.txt QManage.lua 人物登录、定时器到点
杀怪触发 MapQuest_Def\MapQuest.txt 杀怪事件函数或钩子 指定怪物在地图上被击杀时
NPC 对话 Market_Def\<NPC 名>.txt Market_Def\npc\<NPC 名>.lua 玩家点击 NPC 打开对话
事件段落 如登录段、杀怪段、定时段 同名事件函数或注册的钩子 对应引擎事件被触发时

混写与互调

两套脚本能共存的关键,是引擎提供了一条从 TXT 调用 Lua 的通道。996 引擎给出的用法是 callscript(player, "功能/调用", "@调试功能调用"),并有三条容易踩坑的约定:这行要放在 MarkerDef 下面;调用 txt 文件时不要用大括号把路径括起来;代码里写的路径不带 .txt 后缀。至于数据层面则不需要额外处理,因为全局变量默认就是两边都能用的。

路径可以写成相对形式。例如把主界面基础按钮的功能写成可调用脚本后,在登录时用 callscript(player, "../QuestDiary/主界面基础按钮/主界面基础按钮QM", "@基础按钮QM") 直接触发,就等于在登录流程里执行了那一整段逻辑。

图 3 TXT 与 Lua 的互调关系

flowchart TD TXT["TXT 脚本段落"] -->|callscript 调用| LUA["Lua 功能函数"] TXT -->|读写| GV["引擎全局变量"] LUA -->|读写| GV LUA -->|封装| CMD["自定义命令"] CMD -->|供 TXT 直接调用| TXT

说明:全局变量是两侧共享的数据总线,自定义命令是 Lua 能力向 TXT 的回流通道。

反向的回流同样成立。把 Lua 逻辑封装好之后,它实际上相当于给引擎增加了一条新的命令,TXT 侧可以直接调用,常见的说法是「封装是真好使,相当于自己定义命令」。这就形成了最实用的架构:TXT 保留触发与界面入口,Lua 承担实现,Lua 再把自己暴露成 TXT 能调用的命令。

混写时的安全底线

两套脚本的安全问题是一致的:玩家对功能执行时会试图获取对应的二进制执行功能,因此在对 linkgotocallmessagebox 这类带 @ 字段的调用做接口时,一定要做好指纹判断;用 Lua 做面板时,客户端虽然能做常规检测,但客户端本身就是可修改的,服务端必须同时做判断。

调试与可维护性

TXT 脚本的可观测性很有限。它没有断点,也没有变量查看器,最常用的手段是插入一条 SENDMSG 把中间值发给玩家看,靠肉眼看输出推断执行到了哪一步。由于一段对话会随着条件跳转在多个段落之间流转,出错时往往要在整份文件里追踪跳转链。

Lua 侧的观测手段更接近常规编程:可以用 release_print 把消息打印到控制台,也可以用 player:send 发给玩家,报错信息能带上文件名与行号。更重要的是模块化带来的隔离效果:一个功能出问题,排查范围通常局限在对应模块内,而不是整份触发文件。

表 5 调试与维护能力对照
对比项 TXT 脚本 Lua 脚本
日志输出 主要靠 SENDMSG 发给玩家,服务端侧记录有限 release_print 输出到控制台,player:send 输出给玩家
断点调试 通用引擎一般也无 IDE 断点,但可用日志分层定位
错误定位 按行号与标签推断,信息有限 报错含文件名与行号,定位更直接
死循环保护 依赖 ScriptGotoCountLimit 兜底 循环边界由代码显式控制
故障影响面 CALL 复用导致改动容易波及多处 模块与函数隔离,影响面可控
静态检查 命令与参数靠人工核对,难做语法检查 可做语法检查与代码规范检查
版本管理 纯文本可比较差异,但改动常跨多个段落 纯文本可比较差异,模块粒度清晰

性能特征

脚本语言普遍比宿主程序慢,这是解释执行的固有代价,普遍的经验量级是比同场景的 C# 慢约 5 至 10 倍。但在这个前提下,TXT 与 Lua 的相对优劣取决于场景,而不是绝对速度。

单点判断是 TXT 的舒适区:逐行读到条件就返回,没有虚拟机启动与函数调用开销。一旦进入循环与数据操作,天平就倒向 Lua:在数值计算、循环与函数调用这类场景下,借助即时编译技术,它的速度有时甚至能接近 C 语言编译后的代码。另外在文件读写这类高频操作上,引擎还提供了把数据文件预加载进内存的机制,让脚本读取时不必反复访问硬盘,这对拾取、掉落这类会反复读取配置的触发尤其有用。

表 6 典型场景的性能对照
场景 TXT 表现 Lua 表现 结论
单点条件判定 极快,逐行命中即返回 快,但有函数调用开销 短逻辑 TXT 略优
大量循环遍历 只能靠 GOTO 回跳,代价高且有死循环风险 原生 for 与 while,边界可控 Lua 明显更优
复杂数据结构 没有数组与字典,无法表达 table 支持快速查找与嵌套 只有 Lua 可行
高频触发 开销随脚本长度线性增长 字节码执行,同逻辑代码更短 高频场景 Lua 更省
高频文件读写 需依赖引擎的内存加载命令配合 同样可用预加载机制 取决于是否用上内存缓存
极限优化 没有优化空间 可借助即时编译等技术 Lua 上限更高

因此「TXT 比 Lua 快」这个说法只在很短的单点逻辑上成立。当需求变成遍历、聚合、排序与状态机时,TXT 需要指数级更长的脚本才能勉强表达,而更长的脚本意味着更多的解释开销与更高的死循环概率,实际表现反而更差。

速度、效率与上手门槛

讨论 TXT 与 Lua 谁更快,最容易犯的错是把三种完全不同的「快」混为一谈:执行快指同一段逻辑在运行时耗掉多少 CPU,开发快指把需求变成可用脚本要花多少时间,上手快指一个新人从零到能独立改脚本需要多久。上一节的性能对照回答的是第一种;而实际选型中最容易被忽略的恰恰是后两种,尤其是第三种,它正是 TXT 最大的、也几乎不会被 Lua 取代的优势。

TXT 的上手速度是它最容易被低估、也最难被取代的优势,原因很直接:要学的不是一门编程语言,而是一张命令表。命令与效果一一对应,CHECKITEM 金创药 10 就是「检查背包里有没有 10 个金创药」,GAMEGOLD + 100 就是「元宝加 100」,中间没有变量类型、没有作用域、没有函数、没有返回值,也没有任何需要预先建立的抽象概念。看懂一条就能写一条,记住常用的一批命令,就能独立改出可用的 NPC 对话与触发脚本。

门槛低还体现在工程侧:写 TXT 不需要搭开发环境、不需要编译、不需要理解模块与依赖,一个文本编辑器就是全部工具链,改完保存即生效。语法容错也很宽,写错一行最常见的后果是那一条命令没执行、那一段对话没生效,而不是整个服务端报错,所以新手的试错代价很小,可以边看边改、改完立刻看效果,学习过程本身就是即时反馈的。

这些特性决定了 TXT 最适合的人与场景:没有编程基础的策划、刚接触引擎的新手,以及只需要做 NPC 对话、任务奖励、活动公告、简单检测与数值增减的人。对这些需求来说,学 Lua 的时间成本远远大于收益:同一个「交 10 个金创药换 100 元宝」,TXT 三行就写完,Lua 却要先理解函数、参数、返回值与对象方法。再加上 TXT 脚本在社区里大量流通,新手可以把别人的脚本复制过来改几个数字就能用,这种「抄改」式的路径进一步拉平了入门曲线。

TXT 不可替代的地方

把「新手友好」理解成「技术落后」是常见误判。TXT 的门槛低,不是因为它的机制简单,而是因为它被设计成一张与引擎能力一一对应的命令表:只要引擎提供了对应命令,写起来就是所见即所得,中间没有任何需要额外理解的层次。

这也是为什么大量成熟版本至今仍把 NPC 对话、任务奖励、活动公告这类「入口层」逻辑留在 TXT 里,只把算不出来、存不下的部分交给 Lua。TXT 的定位从来不是入门练习,而是引擎最省事的那一层接口。

Lua 的付出则在跨过门槛之后回本。它第一次实现某个功能确实更慢:要先想清楚数据结构、函数边界与错误处理。但需求一旦开始重复,比如第二个奖励配置、第三套活动规则、第四种掉落判定,Lua 的效率优势就显现出来:写一次的函数可以被反复调用,table 能让配置驱动逻辑,改需求时动的是数据而不是脚本结构。在执行侧同样如此,循环、遍历与数据操作这些 TXT 只能用 GOTO 硬凑的场景,Lua 用原生语句就能完成,代码更短,解释开销反而更小;文件读写这类高频操作还能借助引擎的预加载机制,避开反复访问硬盘。

表 7 执行速度、开发效率与上手门槛的逐项对照
考察角度 TXT 脚本 Lua 脚本 结论
上手速度 记一张命令表即可开写,不需要编程基础 需要变量、作用域、函数与数据结构基础 TXT 明显更快,最适合新手入门
入门门槛 无环境、无编译、无依赖,编辑器即工具链 需理解模块加载、调用约定与调试方式 TXT 门槛最低
试错成本 写错通常只影响那一段,改完即见效果 语法错误会导致整个模块无法加载 TXT 更适合边学边改
简单需求开发速度 引擎有现成命令时,两三行就能写完 要定义函数、组织参数与返回值 需求简单时 TXT 更快
重复需求开发效率 只能 CALL 整段,改一处要核对多处 函数与模块复用,第二个同类需求起大幅提速 需求反复迭代时 Lua 更快
单点执行速度 逐行解析,命中即返回,开销极低 需进入虚拟机并有函数调用开销 短逻辑 TXT 略优
循环与数据执行速度 靠 GOTO 回跳,脚本越长越慢 原生循环与 table,同逻辑代码更短 Lua 明显更优

把三条线合起来看,选型判断就清楚了:如果需求全部落在引擎已有命令的覆盖范围内,TXT 就是最省事的选择,新手也能直接上手,没有任何理由引入 Lua;一旦需求里出现「引擎没有现成命令、需要自己算、自己存、自己封装」的部分,那就是投入学习 Lua 的临界点,此时多付出的上手成本,会在第二个同类功能上由开发效率与执行效率一起赚回来。成熟版本通常不走极端:TXT 守住门槛最低的入口层,Lua 承担实现层,两者各用所长。

← 返回课程体系