层叠、z-index 与 UI 相机
UI 元素和 3D 场景一样,存在"谁在谁上面"的问题。Bevy 的 UI 层叠规则比世界空间简单,但有几个需要注意的细节。
默认层叠规则
同一父节点下的子节点,后 spawn 的在上面。这和 DOM 的 "后来居上" 一致。如果你用 with_children 按顺序添加子节点,最后添加的那个会盖住前面的。
ZIndex 组件
要打破默认顺序,给节点加上 ZIndex 组件:
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:
parent.spawn((
Node { .. },
GlobalZIndex(1), // 始终在所有普通 UI 之上
BackgroundColor(Color::WHITE),
));PositionType:Absolute 定位
默认的 PositionType::Relative 让节点参与 Flexbox/Grid 流式布局。设为 PositionType::Absolute 后,节点脱离文档流,相对于父节点的边界定位(通过 left、right、top、bottom 字段):
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 渲染到哪个摄像机:
let ui_camera = commands.spawn(Camera2d).id();
commands.spawn((
Node { .. },
UiTargetCamera(ui_camera),
));Display::None:隐藏节点
Display::None 让节点完全消失——不渲染、不占空间。和 Visibility::Hidden 不同,Hidden 仍然参与布局(留出空白),None 连布局空间都不占。在条件显示 UI 时常用:
if !show_menu {
node.display = Display::None;
}世界空间中的 UI
默认 UI 在世界空间之上渲染。如果你想在 3D 场景中嵌入 UI(比如游戏中的电脑屏幕),需要使用 Camera 的 RenderTarget 把 UI 渲染到纹理上,然后把纹理贴到 3D 物体——这属于进阶用法,本章不展开。
小结:默认后 spawn 的在上面;ZIndex 控制同级层叠,GlobalZIndex 控制全局层叠;PositionType::Absolute 让节点脱离文档流做绝对定位;Camera2d 是 UI 渲染的必要条件;Display::None 完全隐藏节点。