ZenTS 是一个针对 Il2Cpp 优化的现代、简洁、易用的 Unity TypeScript / JavaScript 脚本方案,由 QuickJS 驱动,设计与 ZLua 对齐。
- 文档:zents.code-philosophy.com
- 对照产品(Lua):ZLua / zlua GitHub
- Unreal 版本(开发中):ZenTS-UE
- English:README_EN.md
相对 Puerts / xLua-JS 类方案,以及「自管 QuickJS + 手写绑定」:
| 更易用 | 设计贴近 C#;零 per-type Wrap 白名单;类型懒绑定 |
| 更完备 | 标准和完备的 C#↔JS 交互:方法重载、ref/out、struct ByVal/ByObj、Nullable、委托、数组、指针、[JsMarshalAs] 等 |
| 更统一 | 与 ZLua 同一套语义契约(门面 / Marshal / 类型系统 / 生命周期),Lua 与 TS/JS 可共用产品心智 |
| 更高效 | Player Il2Cpp 热路径为 C++ 桥接;签名复用 stub;支持少生成甚至 0 桥接函数 仍可跑通主路径 |
| 更少 GC | 引用类型与 struct(含含引用字段的 struct)默认走 Registry / ByVal exotic;另有 OpaqueValue 等策略 |
| 双运行时 | 开发期 Editor Mono + 发布 Il2Cpp Player,兼顾迭代与线上效率 |
| TS 一等公民 | 官方 TypeScript 工作流(TsProject、csharp: 声明、进 Play 闸门);运行时仍只跑 emit 后的 JS |
| ZLua | ZenTS | |
|---|---|---|
| 脚本侧 | Lua(PUC-Rio / LuaJIT) | JavaScript(QuickJS);可选 TypeScript → ES module |
| 宿主门面 | LuaAppDomain |
JsAppDomain |
| 类型入口 | CSharp[...] |
同左;另支持 import { T } from "csharp:…" |
| 标准库 | zlua.* |
zents.* |
| 属性 | [LuaMarshalAs] 等 |
[JsMarshalAs] / [JsAlias] / [JsExtension] |
业务侧 API 形态刻意对齐:会用 ZLua,即可很快上手 ZenTS。
面向 Unreal Engine、对 C++ 优化的现代 TypeScript 方案见 ZenTS-UE。目前仍在开发中;本仓库与文档站仅覆盖 Unity / 团结 上的 ZenTS,UE 用法与进度请跟进该仓库。
- 零配置开箱:无需为每个类型 Generate C# Wrap;
CSharp[assembly][typeFullName]懒绑定 - 完备互操作:字段 / 属性 / 方法 / 重载 / 扩展方法 / 泛型 / 委托 / 数组 / struct / enum / Nullable / ref·out·in / 指针
- 双 Runtime:Editor Mono(Expression Emit)+ Player Il2Cpp(C++
zents-runtime) - TypeScript 工作流:
ZenTS/Init TypeScript Project→TsProject/;tsc --noEmit检查;esbuild 1:1 emit;Player 拷贝到 StreamingAssets - 原生调试路径预留:Editor 侧 Debugger Host 接口(持续完善)
| 类别 | 已支持 |
|---|---|
| 引擎 | Unity 2021.3.x / 2022.3.x / 6000.0.x / 6000.3.x / 6000.5.x;团结引擎 1.x.y |
| 脚本 VM | QuickJS(pin 见包内 ZenTS~/ / 文档) |
| 运行时 | Editor Mono + Player Il2Cpp |
| 平台(Editor) | Windows x64、macOS(Apple Silicon / Intel) |
| 平台(Player) | Il2Cpp 支持的平台(含 Win64、Android、iOS、WebGL、微信小游戏、鸿蒙 / 车机等) |
运行时 只 加载 ES module(QuickJS)。模块名使用 canonical specifier(相对逻辑路径,不含 .js)。
契约细节见文档站与 Docs/spec/01-HOST-API.md、02-TYPE-SYSTEM.md。
using System.IO;
using System.Text;
using UnityEngine;
using ZenTS;
public static class ZentsBootstrap
{
static object LoadJsModule(string module)
{
// module 为 canonical,例如 "app" / "game/logic"(不含 .js)
#if UNITY_EDITOR
var path = Path.Combine(Application.dataPath, "..", "JsScripts", module + ".js");
#else
var path = Path.Combine(Application.streamingAssetsPath, "Js", module + ".js");
#endif
return File.Exists(path) ? File.ReadAllText(path, Encoding.UTF8) : null;
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Init() => JsAppDomain.Initialize(LoadJsModule);
}无需 JsCallCSharp 列表,无需为每个类型 Generate Wrap。
public class GameEntry : MonoBehaviour
{
void Start()
{
var add = JsAppDomain.GetFunction<System.Func<int, int, int>>("app", "add");
Debug.Log(add(10, 20)); // 30
}
}// JsScripts/app.js → GetFunction("app", "add")
export function add(a, b) {
return a + b;
}仅 named export;不要对 csharp: 模块调用 GetFunction。
// 任意 public 类型,无需导出配置
public class Demo
{
public int x;
public static int Add(int a, int b) => a + b;
public void SetX(int v) => x = v;
}const AC = CSharp['Assembly-CSharp'];
const Demo = AC['Demo'];
console.log(Demo.Add(3, 5)); // 静态方法
const d = new Demo(); // 构造
d.x = 10; // 字段
d.SetX(20); // 实例方法
console.log(d.x);
// 标准库
const arr = zents.new_szarray_by_element_type(zents.types.int32, 2);手写回归 / 矩阵测试可继续放在 StreamingAssets/Tests/Js(纯 JS),不强制迁 TypeScript。
ZenTS 已支持官方 TypeScript 工作流:用 TS 写业务、生成 csharp: 声明、进 Play 前类型检查;运行时仍只执行 emit 后的 JS(不读 .ts)。
完整契约:TypeScript 工作流(对应 Docs/spec/14-TYPESCRIPT.md)。
Unity 菜单:
ZenTS/Init TypeScript Project— 将包内脚手架复制到工程根TsProject/- 在
TsProject/执行npm install(devDependencies:typescript、esbuild) ZenTS/Generate Typings— 生成TsProject/generated/csharp/**(与 Il2Cpp Generate 类型集同源)ZenTS/Compile TypeScript—tsc --noEmit+ esbuild/tsc emit
布局摘要:
<UnityProject>/
TsProject/
src/ # 业务 .ts(入库)
generated/ # csharp: 声明(入库)
out/ # emit 的 .js(gitignore)
Assets/StreamingAssets/ZenTS/ # Player 构建拷贝
// TsProject/src/game/logic.ts
import { Demo } from "csharp:Assembly-CSharp/Demo"; // 示例;以生成声明为准
export function OnTick(dt: number): void {
const d = new Demo();
d.SetX(20);
}
export function add(a: number, b: number): number {
return a + b;
}要点:
| 项 | 约定 |
|---|---|
| Canonical | game/logic(无 .ts / .js) |
| C# 类型 | import { T } from "csharp:…"(禁止 import type,类型对象是运行时值) |
| 相对导入 | 可写 ./foo.js(Node16 风格);loader 会规范为 canonical |
| 检查 | tsc --noEmit(进 Play 闸门默认开启,Settings 可关) |
| Emit | esbuild 1:1 ESM、不 bundle(或同 outDir 的 tsc emit) |
// jsModule = canonical,不含后缀
var onTick = JsAppDomain.GetFunction<System.Action<float>>("game/logic", "OnTick");
var add = JsAppDomain.GetFunction<System.Func<int, int, int>>("game/logic", "add");Editor:moduleLoader 读 TsProject/out/{canonical}.js。
Player:构建时拷贝 out/**/*.js → StreamingAssets/ZenTS/,运行时 只 读 StreamingAssets。
进 Play 前默认跑 tsc --noEmit(Settings 可关)。日常也可手动 ZenTS/Compile TypeScript。
| 程序集 | 平台 | 说明 |
|---|---|---|
ZenTS.Common |
全平台 | JsAppDomain 门面、属性、公共类型 |
ZenTS.Mono |
Editor | QuickJS P/Invoke + Mono Callback Gate + Emit |
ZenTS.Il2Cpp |
Player | Il2Cpp 宿主接线(实现于 ZenTS~/zents-runtime) |
ZenTS.Editor |
Editor | Install / Export / TypeScript 工具链 / Settings |
Plugins/quickjs/
win32-x64/quickjs.dll # QuickJS Win64(非 NAN boxing:JSValue = 16 字节)
zents_mono_gate.dll # Editor-only JS→C# callback gate(Windows)
darwin-arm64/quickjs.dylib # QuickJS macOS arm64
libzents_mono_gate.dylib # Editor-only gate(macOS)
构建(在包目录下):
ZenTS~\mono-native\build_quickjs_msvc.batpowershell -NoProfile -ExecutionPolicy Bypass -File ZenTS~\mono-native\build_zents_mono_gate.ps1macOS:ZenTS~/mono-native/build_quickjs_darwin.sh、build_zents_mono_gate_unix.sh。
Il2Cpp:菜单 ZenTS / Install... 将 ZenTS~/zents-runtime 与 QuickJS 源装入 LocalIl2Cpp;Windows 上可用 ZenTS / Export Build-Win64... 导出可 MSBuild 的 Player 工程。iOS 等目标按 Unity 导出 Xcode 后构建(见文档)。
MIT。欢迎自由使用、修改和分发。
- 文档:https://zents.code-philosophy.com/
- 邮件:
[email protected] - 产品站:code-philosophy.com
- QQ 群:
1095435513(ZenTS 交流群) - Discord:https://discord.gg/5bT7w9aRMz
- Unreal(开发中):ZenTS-UE