跳到主要内容

为什么选择 ZTS

Puerts、自管 QuickJS 已经证明「在 Unity 里用 JS/TS」可行。ZTS 要解决的是下一层问题:把 JS↔C# 做成真正现代、完备、且在 Il2Cpp 上足够快、足够省的互操作——而不是再堆一套导出配置、白名单和手写绑定。

设计与 ZLua 同构(门面 / Marshal / 生命周期):会用 ZLua 即可很快上手 ZTS;Lua 与 TS 产品可并存。

详细矩阵见 选型对比;迁移见 migration


七个理由(30 秒)

一句话
更易用设计贴近 C#;零 per-type Wrap 白名单;类型懒绑定
更完备重载、ref/out、struct ByVal/ByObj、Nullable、委托、数组、指针、[TsMarshalAs]
更统一与 ZLua 同一套语义契约;Host / Marshal / Exotic 心智对齐
更高效Player Il2Cpp 热路径为 C++ 桥接;签名复用 stub
更少 GC引用类型与 struct 默认 Registry / ByVal;另有 Opaque 等策略
双运行时Editor Mono + 发布 Il2Cpp Player;JS 可见语义一致
TS 一等公民TsProjectcsharp: 声明、进 Play 闸门;运行时只跑 emit 后的 JS

1. 更易用:现代、简单、零配置

传统方案的心智负担往往是:

  • 维护导出列表 / 生成配置
  • 改 API 就要重新 Generate 海量 Wrap
  • C#→JS 走命令式 DoString / 临时 eval / 路径字符串拼装

ZTS 把互操作做成接近 P/Invoke 的声明式模型:

你要做的事ZTS
C# 调 JSTsAppDomain.GetFunction<T>(module, exportName) 取得 Delegate 后 Invoke
覆盖 Marshal[TsMarshalAs]
JS 访问 C#CSharp 根对象懒加载,或 import { T } from "csharp:…"
// 须在 Initialize 之后(例如 Awake),勿用 static 字段初始化器
var AppAdd = TsAppDomain.GetFunction<Func<int, int, int>>("app", "add");
// AppAdd(10, 20);
import { Demo } from "csharp:Assembly-CSharp";
console.log(Demo.Add(3, 5));
const d = new Demo();
d.Run(10); // 实例方法:obj.Method(args),无 Lua 冒号语法

零配置指:不需要 per-type C# Wrap 白名单与成员级 Wrap 工程。Editor 开箱即用;发 Il2Cpp Player 时执行一次 Generate(生成 C++ stub,不是托管 Wrap 海)。

模块 specifier canonical 不含 .js / .ts(如 "app""game/logic")。

快速开始 · 使用指南


2. 更完备:几乎能调到的 C# 都能调

目标不是「导出几个热路径 API」,而是 标准和完备的 C#↔JS 交互,包括但不限于:

类别能力
类型class / struct / interface / enum / nullable
成员静态与实例:字段、属性、方法
高级泛型类、泛型方法、delegate、数组(含多维)
语言细节方法重载、ref / out / in、Event(add_ / remove_
属性[TsAlias][TsExtension][TsMarshalAs]

语义以 规范 为契约;双端(Mono Editor / Il2Cpp Player)JS 可见行为一致

成员 miss 时 throw Error('zts: member not found: …'),不会静默返回 undefined

兼容性矩阵 · 特性对比


3. 更高效:方法论对齐 ZLua,公开数字待补齐

Il2Cpp 上的性能目标与 ZLua 相同:去掉托管 Wrap 折返,在 C++ 里一次完成 marshal 与 methodPointer 调用;相同 ReducedType 签名 复用 stub

公开基准

目前 尚无 面向 ZTS 的公开四方实测数字;请勿把 ZLua 数字直接当作 ZTS 数字。方法论对齐 ZLua(见 ZLua 性能对比);ZTS 公开数据补齐后会更新 选型对比

提示

互调再快,也要先 profiling。若脚本边界只占帧时间 2%,五倍互调也只省约 1.6%。ZTS 适合 战斗公式、UI、每帧大量小调用 这类边界热点。

选型对比 · 性能说明


4. 更少更快的 GC

默认策略面向热路径:

策略含义
引用类型默认走 ObjectRegistry / exotic object,避免无意义装箱与临时 object[]
struct默认可走 ByVal / ByObj 等路径(见规范);热路径面向少分配
OpaqueValue临时句柄:同步调用链内更灵活的低分配策略
enum默认 number,不强制 boxed 对象

需要写回时用 Opaque / ByVal exotic;裸 number 不回写(与 C# ref 语义对齐,见 ref/out/in)。

少 GC Marshal · 生命周期规范


5. 极小的桥接:签名复用,而非每成员一 Wrap

方案典型体积模型
Puerts / 自管 QuickJS(手写)常随导出成员 / 绑定代码膨胀,或靠生成物维护
ZTS(Il2Cpp)合并同签名 桥接函数,直接生成高效 C++ stub(ReducedType 复用)

因此在「仍能访问几乎全部 C# 类型、字段、属性、方法」的前提下:

  • 桥接代码体积通常远小于「每成员独立 Wrap」模型
  • Editor(Mono)用 Expression Emit,不进 Player 包;Player 体积由 C++ stub 决定

Il2Cpp 实现


6. 支持的 Unity 与 QuickJS

维度ZTS
JS 引擎QuickJS(pin 见包内 ZTS~/
Unity2021.32022.3Unity 6(6000.0 / 6000.3 / 6000.5)
引擎团结引擎
运行时Editor Mono + Player Il2Cpp

完整矩阵见 兼容性

支持的版本与平台


7. 同族产品、维护积极

ZTS 与同族方案由同一产品线演进:

产品引擎 / 宿主状态
ZTS(本站)Unity / 团结 · C#Alpha,本站文档覆盖
ZLuaUnity / 团结 · Lua已发布文档站
zts-ueUnreal Engine · C++开发中;仓库见 GitHub
  • 规范、术语、Marshal / 生命周期心智可在产品间对齐(语法面与宿主不同)
  • Bug 响应与特性迭代可共享方法论
  • 适合把脚本互操作当作 长期基础设施,而不是「停更的第三方插件」

不适合选 ZTS 的情况

诚实边界同样重要:

情况建议
不愿维护 libil2cpp 集成插件形态的 Puerts 或自管 QuickJS 可能更轻
强依赖 Puerts 现有导出管线 / 大量资产先读 迁移12-MIGRATION-ADAPTORS,评估迁移成本
只要极少量手写绑定、无完备互操作需求自管 QuickJS 可能更直接
团队只写 Lua、不需要 JS/TS优先 ZLua
宿主是 Unreal Engine关注 zts-ue(开发中);本站为 Unity 文档

下一步

  1. 5 分钟快速开始
  2. 特性对比 · 摘要
  3. 规范总览
  4. 设计概览

延伸阅读

文档内容
设计概览GetFunction 与双向桥接
双运行时Mono / Il2Cpp 分工
术语表Opaque / ByVal / stub 等
Il2Cpp 实现Player 模块图