场景

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

Turian 场景可以被实例化为节点树(类似于 Godot 场景或 Unity 预制体)。场景管理器补充了另一半功能——像 Unity 的 SceneManager 一样将场景视为命名的、可寻址的单元,可以整体加载卸载,支持叠加场景、持久对象、异步加载和生命周期事件。

引导场景来自你的项目设置first_scene);从那里开始,你的游戏通过 engine.SceneManager 服务驱动场景切换。


从脚本中访问管理器

管理器作为服务注入,可从任何组件的 Frame 访问:

const engine = @import("engine");

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

    // 在检查器中配置,非硬编码——参见"场景引用作为字段"
    level_a: engine.TypedAssetRef(.scene) = .{},
    level_b: engine.TypedAssetRef(.scene) = .{},

    pub fn start(self: *SceneDirector, frame: engine.Frame) void {
        const mgr = frame.service(engine.SceneManager) orelse return;
        // 保持引导场景(摄像机/导演)在关卡切换时存活
        if (mgr.getActiveScene()) |boot| mgr.setScenePersistent(boot, true);
        mgr.requestLoad(self.level_a.guid(), .additive);
    }

    pub fn update(self: *SceneDirector, frame: engine.Frame) void {
        const mgr = frame.service(engine.SceneManager) orelse return;
        if (frame.input.wasKeyPressed(.num_2)) mgr.requestLoad(self.level_b.guid(), .single);
    }
};

场景引用作为字段

与其在代码中粘贴场景 GUID,不如声明一个 engine.TypedAssetRef(.scene) 字段。检查器会为其渲染一个场景资源选择器(从资源浏览器拖动场景,或使用 ... 按钮从筛选为场景的列表中选择)。所选 GUID 保存到场景文件中,并在运行时还原到字段中,因此脚本使用 field.guid() 读取它:

next_scene: engine.TypedAssetRef(.scene) = .{},

pub fn update(self: *@This(), frame: engine.Frame) void {
    const mgr = frame.service(engine.SceneManager) orelse return;
    if (frame.input.wasKeyPressed(.space))
        mgr.requestLoad(self.next_scene.guid(), .single);
}

这与网格和材质使用的类型化资源引用机制相同,专门针对场景——设计人员无需接触代码即可配置场景切换。

加载模式

模式 行为
.single 卸载所有非持久场景,然后加载新场景——经典的"下一关"切换。
.additive 在已加载的场景旁叠加加载新场景。

持久场景(DontDestroyOnLoad)

mgr.setScenePersistent(handle, true) 将场景标记为在 .single 加载时保持存活。将你的摄像机、HUD 和控制器放在一个持久的引导场景中,它们将在每次关卡切换后继续存在——相当于 Unity 的 DontDestroyOnLoad 功能。(单个节点也可以通过 markDontDestroyOnLoad 移动到专用的持久场景中。)

延迟请求

脚本使用 requestLoad / requestUnload 请求场景切换。这些请求在帧末尾应用,绝不会在更新过程中执行——因此脚本可以安全地卸载它正在运行的场景。(loadScene / unloadScene 也可用于即时的宿主端控制。)

跟踪与查询

const active = mgr.getActiveScene();          // ?SceneHandle
var buf: [engine.SCENE_MANAGER_MAX_SCENES]engine.SceneHandle = undefined;
const loaded = mgr.getLoadedScenes(&buf);     // []SceneHandle
const here = mgr.findById(LEVEL_A);           // ?SceneHandle
const ok = mgr.isLoaded(handle);

生命周期事件

订阅以在场景状态改变时接收通知:

mgr.subscribe(onSceneEvent, ctx); // 触发 loaded / unloaded / activated / deactivated

异步加载

大型场景可以在工作线程上加载,不会阻塞帧:

const h = try mgr.loadSceneAsync(io, id, .single);
// ... 继续渲染加载画面 ...
if (mgr.isReady(h)) _ = mgr.finishAsync(io, h, .single);

错误处理

同步加载返回类型化错误(NoLoaderLoadFailedTooManyScenes);对于异步加载,可通过 mgr.getError(handle) 获取每个场景的原因。

示例

请参阅仓库中的 examples/scene-management/ 目录,获取一个完整可运行的演示,包含引导 + 持久引导 + 叠加加载 + 运行时切换。


← 所有文档 编辑此页面