预制体

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

预制体(prefab) 是一个场景对象及其子对象的可重复使用模板——相当于 Turian 中的 Unity 预制体或 Godot 场景实例。你可以一次创作一个对象,将其保存为 .prefab 资源,然后在场景中任意实例化。实例与源保持链接:对预制体的编辑会传播到每个实例,除非实例有意覆盖了某些字段。

术语说明。 你在场景层级中编辑的内容是场景——活动的、打开的节点树。当你将其序列化到磁盘时,得到的是预制体:一个带有自己稳定 GUID/.meta.prefab 资源。它们共享完全相同的磁盘结构;预制体就是一个保存为可重复使用、可实例化模板的场景。因此预制体像任何场景一样在场景层级中打开进行编辑,任何场景也可以实例化到另一个场景中。它的第一个对象是根节点;子节点嵌套在其下。


创建预制体

两种方式,都在编辑器中完成:

  • 场景层级中,右键单击一个对象并选择创建预制体
  • 场景层级拖动一个对象并将其放到资源浏览器上。

无论哪种方式,编辑器都会在资源浏览器当前显示的文件夹中写入一个 <Name>.prefab 资源,并将所选对象(及其子对象)转换为该新预制体的第一个实例。检查器随后会显示一个预制体实例横幅。

编辑预制体

在资源浏览器中双击 .prefab 以在场景层级中打开它,像编辑任何场景一样进行编辑。保存预制体后,下次打开包含其实例的场景时,实例会更新(覆盖内容会被保留)。

实例化

通过以下方式将新的、链接的副本放入活动场景:

  • 从资源浏览器拖动 .prefab(或任何场景)到场景视口中,或
  • 右键单击它并选择实例化到场景

每个实例都有自己的对象 GUID,但会记住它来自哪个预制体。

识别实例

在场景层级中,预制体实例显示为蓝色着色,其根节点使用方框图标,因此链接的实例一目了然。在实例中选择任意节点,即可在检查器中看到预制体实例横幅。

覆盖

实例化后,你可以在检查器中自由调整实例。你更改的任何内容——名称活动状态、变换组件——都会被记录为覆盖,并列出在预制体实例横幅中。覆盖使每个实例与众不同,并且在源预制体更改时会被保留。

还原和应用

预制体实例横幅提供两个操作:

  • 还原 —— 丢弃实例的覆盖,将每个字段恢复到源预制体的值。
  • 应用 —— 将此实例的当前值向上推送到源预制体,然后将更改传播到场景中的每个其他实例(每个实例保留其自己的覆盖)。

这是核心的预制体循环:编辑一个实例,应用以更新模板,所有兄弟实例继承更改,除非它们覆盖了该更改。

运行时生成

预制体也是运行时的构建块——相当于 Unity 的 Instantiate / Destroy。游戏脚本通过 Frame 上下文生成和销毁:

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

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

    /// 在检查器中拖入一个 `.prefab`。
    prefab: engine.TypedAssetRef(.scene) = .{},

    pub fn update(self: *@This(), frame: engine.Frame) void {
        // Instantiate(prefab, position, rotation) — position/rotation 可选。
        if (frame.input.wasPressed("fire")) {
            frame.instantiate(self.prefab.slice(), .{ .x = 0, .y = 1, .z = 0 }, null);
        }

        // Destroy(node) — 移除节点及其子节点。
        for (frame.objects) |*obj| {
            if (std.mem.eql(u8, obj.nameSlice(), "Doomed")) frame.destroy(obj);
        }
    }
};
  • frame.instantiate(prefab_guid, position, rotation) —— 将预制体的新链接实例加入队列(通过其 GUID 标识,例如 TypedAssetRef(.scene) 字段的 .slice())。positionrotation 是对预制体根节点变换的可选覆盖。
  • frame.destroy(node) —— 将移除节点及其后代加入队列。

两者都是延迟的:它们在当前更新结束时应用,绝不会在迭代过程中应用,因此脚本可以在遍历 frame.objects 时安全地生成/销毁。生成的实例与编辑器放置的实例一样,与其预制体保持链接。参见 examples/basic-project/assets/PrefabSpawner.zig 获取可运行的演示(记录每次生成和销毁)。

嵌套预制体

一个预制体可以包含其他预制体的实例,因此你可以从小模板组合成大模板。构建方法:

  1. 将预制体 A 实例化到你的场景中(拖入,或右键单击 → 实例化)。
  2. 添加一个父对象,将那个实例放在它下面(再加上你想要的任何其他内容)。
  3. 选择父对象并创建预制体 → 新的预制体 B 包含 A 的一个链接实例。

实例化 B 会带上它嵌套的 A,每个实例仍然独立跟踪自己的源——编辑 A 并应用会传播到每个 B 内部嵌套的 A。

变体(尚未实现)

预制体变体——从一个预制体继承并在其上叠加自己的永久覆盖的预制体——目前还不是一个独立的功能。目前最接近的方式:实例化一个预制体,调整它,然后从该实例创建预制体以获得一个新的独立预制体。它不会作为真正的变体保持与原始预制体的链接;那个继承链是计划中的未来工作。


← 所有文档 编辑此页面