Skip to content

层叠、z-index 与 UI 相机

UI 元素和 3D 场景一样,存在"谁在谁上面"的问题。Bevy 的 UI 层叠规则比世界空间简单,但有几个需要注意的细节。

默认层叠规则

同一父节点下的子节点,后 spawn 的在上面。这和 DOM 的 "后来居上" 一致。如果你用 with_children 按顺序添加子节点,最后添加的那个会盖住前面的。

ZIndex 组件

要打破默认顺序,给节点加上 ZIndex 组件:

rust
parent.spawn((
    Node {
        position_type: PositionType::Absolute,
        left: Val::Px(10.0),
        bottom: Val::Px(40.0),
        width: Val::Px(100.0),
        height: Val::Px(50.0),
        ..default()
    },
    ZIndex(2),           // 同级节点中,值大的在上面
    BackgroundColor(Color::srgb(0.9, 0.3, 0.3)),
));

ZIndex局部的——只在同一个父节点的子节点之间比较。不同父节点下的子节点不受影响。

GlobalZIndex:全局层

GlobalZIndex 打破父节点边界——值最大的节点始终在最上面,即使它的父节点 z-index 很低。常用于弹窗、模态框、tooltip:

rust
parent.spawn((
    Node { .. },
    GlobalZIndex(1),  // 始终在所有普通 UI 之上
    BackgroundColor(Color::WHITE),
));

PositionType:Absolute 定位

默认的 PositionType::Relative 让节点参与 Flexbox/Grid 流式布局。设为 PositionType::Absolute 后,节点脱离文档流,相对于父节点的边界定位(通过 leftrighttopbottom 字段):

rust
Node {
    position_type: PositionType::Absolute,
    right: Val::Vw(2.0),
    top: Val::Vw(2.0),
    width: Val::Px(200.0),
    height: Val::Px(100.0),
    ..default()
}

Absolute 定位的节点不影响兄弟节点的布局——它们会被忽略,就像不存在一样。这对 HUD 元素(右上角得分、左下角血条)至关重要。

UI 相机

Bevy 的 UI 渲染必须有一个 2D 相机Camera2d)。没有它,所有 Node 都不会被渲染。默认情况下,UI 附加到主窗口的第一个 Camera2d

如果你有多个窗口或多个摄像机,可以用 UiTargetCamera 组件指定 UI 渲染到哪个摄像机:

rust
let ui_camera = commands.spawn(Camera2d).id();
commands.spawn((
    Node { .. },
    UiTargetCamera(ui_camera),
));

Display::None:隐藏节点

Display::None 让节点完全消失——不渲染、不占空间。和 Visibility::Hidden 不同,Hidden 仍然参与布局(留出空白),None 连布局空间都不占。在条件显示 UI 时常用:

rust
if !show_menu {
    node.display = Display::None;
}

世界空间中的 UI

默认 UI 在世界空间之上渲染。如果你想在 3D 场景中嵌入 UI(比如游戏中的电脑屏幕),需要使用 CameraRenderTarget 把 UI 渲染到纹理上,然后把纹理贴到 3D 物体——这属于进阶用法,本章不展开。

小结:默认后 spawn 的在上面;ZIndex 控制同级层叠,GlobalZIndex 控制全局层叠;PositionType::Absolute 让节点脱离文档流做绝对定位;Camera2d 是 UI 渲染的必要条件;Display::None 完全隐藏节点。