跳到主要内容

特性对比(ZTS / Puerts / 自管 QuickJS)

备注

本页为选型辅助,不是行为契约。契约以 spec 为准。

ZTS 状态: Alpha;Editor Mono 与 Il2Cpp Player 主路径可用(见 项目状态)。
与 ZLua: 门面 / Marshal / 生命周期 语义同构(JS vs Lua 语法不同);可同工程并存,不是性能对标对象。


1. 总览对照

维度ZTSPuerts(典型)自管 QuickJS
脚本引擎QuickJS(Editor 动态库 / Player 编入 Il2Cpp)V8 / QuickJS 等视发行版你选的 QuickJS 构建
类型入口CSharp[asm][fullName] 懒绑定;推荐 import { T } from "csharp:…"CS.Ns.Type / puerts.loadType手写 binding / 自研注册表
JS→C# 桥懒 Bind + Mono Emit / Il2Cpp C++ MethodBridgeStaticWrap + 反射兜底等(视配置)手写 JS_NewCFunction
C#→脚本TsAppDomain.GetFunction<T>(module, export)JsEnv.Eval / 回调表 / [JSFunction]自研 eval / 函数句柄
白名单 per-type C# Wrap 白名单;public + 懒 Bind;Il2Cpp 靠 Generate stub常需 Binding / StaticWrapper 列表你维护的导出表
与 ZLua 心智同构GetFunction / Marshal / strict miss / Event 形态)不同
TypeScript官方 TsProject + csharp: 声明 + Play 闸门视方案 / 社区模板自建 tsc / 声明
Editor vs Player双轨:Mono Emit vs Il2Cpp native;JS 可见语义须一致通常更接近「同一套插件」取决于你的双端策略
Il2Cpp官方 zts-runtime 嵌入 libil2cpp有成熟路径自建
Event专用对象 → add_ / remove_常有更接近 C# 的订阅糖自定
完备互操作重载 / ref / struct ByVal… 按 spec视版本与配置视实现

2. 类型访问

2.1 语法对照(同一类型 MyGame.Demo

方案典型写法
ZTS(原生)CSharp['Assembly-CSharp']['MyGame.Demo']
ZTS(csharp:import { Demo } from "csharp:Assembly-CSharp/MyGame"
PuertsCS.MyGame.Demopuerts.loadType('MyGame.Demo')
自管 QuickJS你注册的全局 / 模块导出

ZTS 规则要点:

  • 命名空间 须括号键整段 typeFullName,禁止 CSharp.AC.MyGame.Demo 链式穿越 namespace。
  • 嵌套类型用 +CSharp.AC['Outer+Inner']
  • csharp: named export 与 CSharp[asm][fullName] 同一类型对象 identity
  • 迁移期可用 adaptor 继续写 CS.*(见 迁移);新脚本优先 csharp:

详见 JS 调用 C#spec/02-TYPE-SYSTEM

2.2 白名单 vs 懒绑定

方案模型工程影响
Puerts(典型)导出 / StaticWrap 白名单决定可访问类型未导出则不可调;改 API 常要重生 Wrap
自管 QuickJS手写或生成绑定列表完全可控,完备度靠人力
ZTS首次访问类型时 EnsureBinding per-type 托管 Wrap 白名单Editor 开箱;Player 须 ZTS/Generate/All(C++ stub,不是 C# Wrap 海)

敏感 API:应用 非 public 收口,而不是依赖「没进导出列表」。

2.3 并行示例:取类型并调静态方法

// ZTS — csharp:(推荐)
import { Demo } from "csharp:Assembly-CSharp/MyGame";
console.log(Demo.Add(1, 2));

// ZTS — CSharp 根表
const Demo2 = CSharp['Assembly-CSharp']['MyGame.Demo'];
console.log(Demo2.Add(1, 2));

// Puerts 风格(旧工程;ZTS 上需 adaptor 或改写)
// const sum = CS.MyGame.Demo.Add(1, 2);
// ZTS — TypeScript(编辑期;运行时仍是 emit 后的 JS)
import { Demo } from "csharp:Assembly-CSharp/MyGame";

export function addDemo(a: number, b: number): number {
return Demo.Add(a, b);
}

3. 成员调用(脚本 → C#)

3.1 静态 / 实例

方案静态实例
ZTSDemo.Add(1, 2)obj.GetX()点号;无 Lua 冒号)
PuertsCS.Demo.Add(1, 2)obj.GetX()
自管视绑定视绑定

ZTS:静/实例元数据分离;继承成员在 Bind 期扁平化;未知成员 strict missthrow Error('zts: …')静默 undefined

3.2 方法重载

方案策略
PuertsWrap / 运行时分派(视版本)
自管自定
ZTSBind 期注册;默认最佳匹配;[TsAlias] / 显式绑定见 重载

3.3 __index / 属性 miss

方案不存在成员
Puertsundefined 或错误(视路径)
自管视实现
ZTS读 / 写未知键均 throw(与 ZLua 同构的严格策略)

4. C# → 脚本

4.1 入口对照

方案C# 调脚本脚本函数 → C# delegate
ZTSTsAppDomain.GetFunction<T>("app", "add")形参隐式 marshal(Action/Func 等)
PuertsJsEnv.Eval / 模块回调 / 特性JsFunction
自管自研自研

ZTS GetFunction 示例:

// 须在 Initialize 之后(例如 Awake);勿放 static 字段初始化器
var add = TsAppDomain.GetFunction<Func<int, int, int>>("app", "add");
int sum = add(10, 20);

var onTick = TsAppDomain.GetFunction<Action<float>>("game/logic", "OnTick");
onTick(0.016f);
// JsScripts/app.js 或 TsProject emit 产物 — named export
export function add(a, b) {
return a + b;
}
  • Editor / Player:同一 API;热路径请缓存 delegate。
  • jsModulecanonical(不含 .js / .ts);不要csharp: 模块 GetFunction
  • 详见 C# 调用 JS

4.2 模块加载

方案加载
ZTSTsAppDomain.Initialize(moduleLoader) + ES module;csharp: 由运行时拦截
Puerts自有 loader / require 习惯(视版本)
自管自定

Player 读 StreamingAssets(Js/ZTS/),不是工程旁源目录——见 构建


5. Event、ref、struct

5.1 Event

方案订阅方式
Puerts常有接近 C# 的 += / 专用 API(视版本)
ZTS Event 专用对象;obj.add_OnX(fn) / obj.remove_OnX(fn);remove 须 同一 function 引用
ZLua(同构)同样 add_ / remove_
function onChanged(v) {
console.log("hp", v);
}
obj.add_OnHpChanged(onChanged);
obj.remove_OnHpChanged(onChanged); // 必须是同一引用

5.2 ref / out / in

主题ZTS
C#→JS(GetFunctionbyref 默认 OpaqueValue;用 zts.get_opaquevalue / set_opaquevalue
JS→C#不按关键字区分;裸 number 不写回;同型 ByVal exotic / Opaque 可写回
Opaque 生命周期不可跨帧 / 跨异步当长期句柄

与 ZLua 的 Opaque / ByVal 故事同构;语法是 JS API。见 ref/out/in

5.3 struct

主题ZTS 要点
传参 / 返回ByVal exotic 拷贝 或 ByObj boxed(见 值类型
与 Puerts BlittableCopy不对等;用 [TsMarshalAs] / 默认 Marshal,勿假设指针模型
自管 QuickJS常需自写 struct 装箱策略

6. TypeScript 工作流

维度ZTSPuerts(典型)自管
运行时只跑 emit 后的 JS(ESM)视方案自建
官方工程ZTS/Init TypeScript ProjectTsProject/社区 / 自建自建
C# 类型声明ZTS/Generate Typingscsharp: modules视工具链手写 .d.ts
进 Play可选 tsc --noEmit 闸门,失败阻止 Play视项目自定
发布emit(禁止 bundle)→ StreamingAssets视方案自定
// TsProject/src/game/logic.ts
import { Demo } from "csharp:Assembly-CSharp";

export function OnTick(dt: number): void {
const demo = new Demo();
demo.SetX(10);
}

权威:TypeScript 工作流spec/14-TYPESCRIPT


7. Editor / Player 与生成

方案双端发布前「生成」
Puerts通常插件路径较统一StaticWrap / 配置 Generate
自管自定自定
ZTSMono(Editor)vs Il2Cpp(Player)实现不同、语义须一致Il2Cpp:ZTS/Generate/All(C++ stub);C#→JS 无 codegen

测试要求:关键路径在 Editor 与 Il2Cpp Player 各验一次。见 Editor 与 Player社区测试


8. 侵入性与维护

浅 ←────────────────────────────────────────→ 深(Il2Cpp 侵入)

自管 QuickJS(纯插件 + 手写桥)
Puerts(插件 + native / Wrap)
★ ZTS Player(嵌入 libil2cpp + zts-runtime)
层级ZTSPuerts自管
修改 libil2cpp(Player,经 Install)视发行版通常否
独立 nativeEditor:quickjs 动态库;Player:编入常见常见
维护焦点Unity 版本 + Install/Generate + QuickJS pin包版本与导出配置绑定完备度与升级

9. 性能(诚实边界)

状态
公开四方实测表暂无本页不写 ns / 倍数
建议热路径在 Il2Cpp Player 自测;Editor 数字勿当发版依据
参考方法论ZLua PERFORMANCE(Lua 方案,不可直接引用为 ZTS 结果)

完备互操作与懒绑定的取舍,见 为什么选择 ZTS


10. 迁移检查清单(选型用)

从 Puerts / 自管迁到 ZTS 前,逐项确认:

说明
□ 类型访问CS.* / loadTypecsharp:CSharp[…];或装 adaptor(只解决类型路径)
□ C#→JSEval / JsFunction → GetFunction + named export不在 adaptor 范围)
□ Event糖语法 → add_ / remove_
□ 实例调用确认无冒号习惯;提取方法会丢 this
□ ref / struct按 ZTS Opaque / ByVal 改写,勿假设 BlittableCopy
□ 泛型loadGeneric 等 → zts.make_generic_type
□ 模块CommonJS require → ES import + moduleLoader
□ TS启用 TsProject;禁止 bundle;Generate Typings 与 Generate 同源
□ PlayerInstall → Generate → Sync/拷贝 StreamingAssets
□ 严格 miss依赖「读不到成员得 undefined」的代码会炸 → 显式存在性检查 / 改 API

用户向步骤:迁移;契约:12-MIGRATION-ADAPTORS


11. 同一示例三列对照

需求: 调用 MyGame.Demo.Add(1, 2),创建实例并读字段 x

// ——— ZTS(推荐 csharp:)———
import { Demo } from "csharp:Assembly-CSharp/MyGame";
const sum = Demo.Add(1, 2);
const obj = new Demo();
const x = obj.x;

// ——— ZTS(CSharp 根表)———
// const Demo = CSharp['Assembly-CSharp']['MyGame.Demo'];
// const sum = Demo.Add(1, 2);
// const obj = new Demo();
// const x = obj.x;

// ——— Puerts 风格(须 adaptor 或改写后才能在 ZTS 跑)———
// const Demo = CS.MyGame.Demo;
// const sum = Demo.Add(1, 2);
// const obj = new Demo();
// const x = obj.x;
// ——— ZTS TypeScript(emit 后同上)———
import { Demo } from "csharp:Assembly-CSharp/MyGame";

export function sample(): number {
const sum = Demo.Add(1, 2);
const obj = new Demo();
return sum + obj.x;
}

12. 选型摘要

更适合方案
JS/TS + 完备互操作 + Il2Cpp,愿维护 Install/Generate,要与 ZLua 同构心智ZTS
已有大型 Puerts 资产、短期要少改类型路径渐进迁移(adaptor + 人工改 C#→JS / Event / Marshal)
极薄嵌入、API 面极小、团队能自研桥自管 QuickJS
产品语言是 LuaZLua

一句话导航见 选型摘要


相关文档

文档内容
SUMMARY阅读顺序与诚实边界
为什么选择 ZTS产品叙事
迁移用户向迁移
spec/00-OVERVIEW行为契约入口