プレハブ
プレハブは、シーンオブジェクトとその子オブジェクトの再利用可能なテンプレートです——Turian における Unity プレハブや Godot シーンインスタンスに相当します。オブジェクトを一度作成し、.prefab アセットとして保存した後、好きなだけインスタンスをシーンに配置できます。インスタンスはソースにリンクされたままです:プレハブへの編集はすべてのインスタンスに反映されますが、インスタンスが意図的に上書きしたフィールドは除きます。
用語。 シーン階層で編集するものはシーン——ライブで開いているノードツリーです。それをディスクにシリアライズするとプレハブになります:独自の安定した GUID/.meta を持つ .prefab アセットです。両者はまったく同じディスク構造を共有します。プレハブは、再利用可能でインスタンス化可能なテンプレートとして保存されたシーンにすぎません。したがって、プレハブは任意のシーンとまったく同様にシーン階層で編集のために開くことができ、任意のシーンを別のシーンにインスタンス化することもできます。その最初のオブジェクトがルートで、子はその下にネストされます。
プレハブの作成
2つの方法があり、どちらもエディターで行います:
- シーン階層でオブジェクトを右クリックし、プレハブを作成を選択します。
- シーン階層からオブジェクトをドラッグし、アセットブラウザにドロップします。
どちらの方法でも、エディターはアセットブラウザが現在表示しているフォルダに <Name>.prefab アセットを書き込み、選択したオブジェクト(とその子オブジェクト)をその新しいプレハブの最初のインスタンスに変換します。インスペクターにはプレハブインスタンスのバナーが表示されます。
プレハブの編集
アセットブラウザで .prefab をダブルクリックしてシーン階層で開き、任意のシーンと同様に編集します。プレハブを保存すると、それらを含むシーンが次に開かれたときにインスタンスが更新されます(上書きは保持されます)。
インスタンス化
新しいリンクされたコピーをアクティブシーンに配置する方法:
- アセットブラウザから
.prefab(または任意のシーン)をシーンビューポートにドラッグする、または - 右クリックしてシーンにインスタンス化を選択します。
各インスタンスは独自のオブジェクト GUID を持ちますが、どのプレハブから来たかを記憶しています。
インスタンスの識別
シーン階層では、プレハブインスタンスは青色で着色され、そのルートはボックスアイコンを使用するため、リンクされたインスタンスはプレーンなオブジェクトから一目で区別できます。インスタンス内の任意のノードを選択すると、インスペクターにプレハブインスタンスのバナーが表示されます。
上書き
インスタンス化後、インスタンスをインスペクターで自由に調整できます。変更したグループ——名前、アクティブ状態、トランスフォーム、またはコンポーネント——は上書きとして記録され、プレハブインスタンスのバナーにリスト表示されます。上書きが各インスタンスを個別のものにし、ソースプレハブが変更されても保持されます。
元に戻すと適用
プレハブインスタンスのバナーには2つのアクションがあります:
- 元に戻す — インスタンスの上書きを破棄し、すべてのフィールドをソースプレハブの状態に戻します。
- 適用 — このインスタンスの現在の値をソースプレハブに上書きとしてプッシュし、変更をシーン内の他のすべてのインスタンスに反映します(各インスタンスは独自の上書きを保持します)。
これがプレハブの核心的なループです:1つのインスタンスを編集し、適用してテンプレートを更新し、すべての兄弟インスタンスが上書きした箇所以外は変更を継承します。
ランタイムでの生成
プレハブはランタイムの構成要素でもあります——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())で識別)。positionとrotationはプレハブルートのトランスフォームに対するオプションの上書きです。frame.destroy(node)— ノードとその子孫の削除をキューに入れます。
どちらも遅延されます:現在の更新の終了時に適用され、反復の途中で適用されることはないため、スクリプトは frame.objects をループしながら安全に生成/破棄できます。生成されたインスタンスは、エディターで配置されたものと同様にプレハブにリンクされたままです。各生成と破棄をログに記録する実行可能なデモについては、examples/basic-project/assets/PrefabSpawner.zig を参照してください。
ネストされたプレハブ
プレハブは他のプレハブのインスタンスを含むことができるため、小さなテンプレートから大きなテンプレートを構成できます。作成手順:
- プレハブ A をシーンにインスタンス化します(ドラッグインするか、右クリック → インスタンス化)。
- 親オブジェクトを追加し、そのインスタンスをその下に配置します(さらに必要なものを追加)。
- 親を選択してプレハブを作成 → 新しいプレハブ B には A のリンクインスタンスが含まれます。
B をインスタンス化すると、ネストされた A も一緒に含まれ、各インスタンスは独自のソースを独立して追跡します——A を編集して適用すると、すべての B の中にネストされた A に反映されます。
バリアント(未実装)
プレハブバリアント——別のプレハブを継承し、その上に独自の永続的な上書きを重ねるプレハブ——はまだ個別の機能として実装されていません。現在の最も近い方法:プレハブをインスタンス化し、調整し、そのインスタンスからプレハブを作成して新しい独立したプレハブを取得します。真のバリアントとして元のプレハブにリンクされたままにはなりません。その継承チェーンは将来の作業として計画されています。