教程

本页面为自动翻译,可能包含错误。如果发现问题,请通过“编辑此页面”链接帮助修正。

本教程将引导您打开示例项目、编写脚本组件并构建您的第一款游戏。


1. 启动编辑器

zig build run

Turian Studio 将打开一个默认场景(地面、摄像机、方向光)。


2. 打开示例项目

  1. 选择 文件 → 打开项目…
  2. 导航到 examples/basic-project/ 并确认。
  3. 资源浏览器显示项目的 assets/ 文件夹。
  4. 双击 scene-01.prefab 加载场景。

3. 编辑器布局

┌──────────────┬──────────────────────┬────────────────┐
│ 场景         │    场景视图           │   检查器       │
│ 层级         │   (3D 视口)          │                │
├──────────────┴──────────────────────┴────────────────┤
│                  资源浏览器                           │
└───────────────────────────────────────────────────────┘
  • 场景层级 — 每个游戏对象;单击选择,拖拽重新排序。
  • 场景视图 — 实时 3D 预览(SDL3 GPU — Vulkan/Metal/D3D12)。
  • 检查器 — 编辑选中对象的 Transform 和组件字段。
  • 资源浏览器 — 浏览 assets/;双击 .prefab 场景文件打开。

4. 添加游戏对象

  1. 场景 → 添加空对象(或点击菜单项)。
  2. 在层级中选中它。
  3. 在检查器中,展开 Transform 并将位置设置为 (0, 1, 0)
  4. 点击 添加组件 ▾MeshRenderer

5. 编写脚本组件

在项目文件夹内创建 assets/spinner.zig

const engine = @import("engine");

pub const Spinner = struct {
    /// Marks this struct as a component the editor should discover.
    pub const is_component = true;

    /// Rotation speed in degrees per second (editable in Inspector).
    speed: f32 = 90.0,

    pub fn awake(self: *Spinner) void {
        _ = self;
        @import("std").debug.print("[Spinner] awake\n", .{});
    }

    pub fn update(self: *Spinner, time: engine.Time) void {
        _ = self.speed * time.delta; // apply rotation once scene API lands
    }
};

关键规则:

  • 在结构体内部添加 pub const is_component = true; 标记以便被发现。编辑器解析 Zig 源码(非正则表达式),因此无论格式、注释或条件编译如何,标记都有效。设置为 false 可临时将结构体排除在外。
  • 结构体必须是 pub 的,且名称以大写字母开头。
  • 每个组件类型名称在项目中必须是唯一的——重复的名称会被报告,第二个将被忽略。
  • 支持的字段类型:f32i32boolengine.Vec3engine.GameObjectRefengine.ComponentRefengine.AssetRef

点击资源浏览器标题栏中的刷新以加载新脚本,然后通过添加组件 ▾ 添加 Spinner


6. 脚本生命周期钩子

所有钩子都是可选的——只需实现您需要的:

钩子 调用时机
awake(self) 对象首次加载时
enable(self) 对象变为活跃时
start(self) 场景开始运行时
update(self, frame: engine.Frame) 每帧 (推荐)
update(self, time: engine.Time) 每帧 (旧版)
disable(self) 对象变为非活跃时
destroy(self) 对象被销毁时

所有钩子也接受 frame: engine.Frame 作为第二参数——使用 update(self, frame: engine.Frame) 来访问输入、服务和完整的场景上下文。详见输入系统指南。

engine.Time 字段(也可通过 frame.time 访问):

  • .delta — 自上一帧以来的秒数(f32
  • .elapsed — 自场景启动以来的总秒数(f32
  • .frame — 帧计数器(u64

FPS 计数器示例

const std    = @import("std");
const engine = @import("engine");

pub const FpsDisplay = struct {
    pub const is_component = true;

    _timer: f32 = 0,

    pub fn update(self: *FpsDisplay, time: engine.Time) void {
        self._timer += time.delta;
        if (self._timer >= 1.0) {
            self._timer -= 1.0;
            const fps = if (time.delta > 0) 1.0 / time.delta else 0.0;
            std.debug.print("FPS: {d:.0}\n", .{fps});
        }
    }
};

对象引用示例

const engine = @import("engine");

pub const Follower = struct {
    pub const is_component = true;

    /// Drag a game object from the Hierarchy onto this field.
    target: engine.GameObjectRef = .{},

    pub fn start(self: *Follower) void {
        const name = self.target.slice();
        if (name.len > 0) {
            @import("std").debug.print("[Follower] following '{s}'\n", .{name});
        }
    }
};

7. 保存场景

文件 → 保存场景 将写入 assets/scene-01.prefab——一个人类可读的 JSON 文件,您可以进行差异对比并提交到 Git。


8. 构建游戏

通过编辑器

构建 → 构建游戏 将独立可执行文件编译到项目文件夹内的 .cache/zig-out/bin/game

通过 CLI

zig build cli -- build path/to/my-project

或使用安装好的二进制文件:

turian-cli build path/to/my-project
turian-cli new-project ../my-new-game "My Game"   # 创建项目
turian-cli info        path/to/my-project         # 打印元数据

9. 使用数学库

const math = @import("engine").math;

const pos = math.Vector3{ .x = 0, .y = 1, .z = 0 };
const rot = math.Quaternion.fromAxisAngle(math.Vector3.up(), 45.0);
const m   = rot.toMatrix4();
const col = math.Vector3i{ .x = 255, .y = 128, .z = 0 };  // integer vector

可用类型:Vector2Vector3Vector4Vector2iVector3iVector4iMatrix4Quaternion


下一步

  • 阅读安装 / 从源码构建以了解完整的开发者设置。
  • 浏览[示例文件夹]({{ site.Params.Repo }}/-/tree/main/examples)获取更多完整的项目模板。
  • 查看输入系统指南以添加动作映射、游戏手柄支持和运行时重绑定。
  • 在检查器中查看组件字段参考——将鼠标悬停在任何字段标签上可查看其类型信息。

← 所有文档 编辑此页面