给游戏加调试层
Gizmos 画的是"你想看到的线";线框渲染(wireframe)画的是"模型本身的三角面结构"。两者互补:Gizmos 可视化逻辑(碰撞体、路径、朝向),线框可视化拓扑(面数、布线、法线方向)。
线框渲染:WireframePlugin
Bevy 的线框渲染由 WireframePlugin 提供。它需要 GPU 支持 POLYGON_MODE_LINE 特性——这意味着它只在原生平台(DX12、Vulkan、Metal)上工作,WebGL/WebGPU 不支持。
WireframePlugin::default(),配置全局线框通过 WireframeConfig 资源:
.insert_resource(WireframeConfig {
global: true,
default_color: WHITE.into(),
..default()
})global: true 让所有 Mesh 都显示线框。default_color 设定全局线框颜色。
逐实体控制
线框可以在实体级别精细控制:
| 组件 | 效果 |
|---|---|
Wireframe | 强制显示线框,无视全局设置 |
NoWireframe | 强制不显示线框,无视全局设置 |
WireframeColor { color } | 覆盖该实体的线框颜色 |
// 这个方块永远没有线框
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
NoWireframe,
));
// 这个方块永远有线框,颜色是 AQUA
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
Wireframe,
WireframeColor { color: AQUA.into() },
));运行时也可以切换全局线框:
fn toggle_wireframe(
keyboard: Res<ButtonInput<KeyCode>>,
mut config: ResMut<WireframeConfig>,
) {
if keyboard.just_pressed(KeyCode::KeyG) {
config.global = !config.global;
}
}完整示例
use bevy::color::palettes::css::*;
use bevy::pbr::wireframe::{NoWireframe, Wireframe, WireframeColor, WireframeConfig, WireframePlugin};
use bevy::prelude::*;
use bevy::render::render_resource::WgpuFeatures;
use bevy::render::settings::{RenderCreation, WgpuSettings};
use bevy::render::RenderPlugin;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(RenderPlugin {
render_creation: RenderCreation::Automatic(Box::new(WgpuSettings {
features: WgpuFeatures::POLYGON_MODE_LINE,
..default()
})),
..default()
}),
// ANCHOR: wireframe_plugin
WireframePlugin::default(),
// ANCHOR_END: wireframe_plugin
))
// ANCHOR: wireframe_config
.insert_resource(WireframeConfig {
global: true,
default_color: WHITE.into(),
..default()
})
// ANCHOR_END: wireframe_config
.add_systems(Startup, setup)
.add_systems(Update, toggle_wireframe)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// 红色方块:NoWireframe,永远不显示线框
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
MeshMaterial3d(materials.add(Color::from(RED))),
Transform::from_xyz(-1.5, 0.5, 0.),
NoWireframe,
));
// 绿色方块:跟随全局设置
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
MeshMaterial3d(materials.add(Color::from(LIME))),
Transform::from_xyz(0., 0.5, 0.),
));
// 蓝色方块:始终线框,自定义颜色
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
MeshMaterial3d(materials.add(Color::from(BLUE))),
Transform::from_xyz(1.5, 0.5, 0.),
Wireframe,
WireframeColor { color: AQUA.into() },
));
// 地面
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(6., 6.))),
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
));
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2., 2.5, 5.).looking_at(Vec3::ZERO, Vec3::Y),
));
commands.spawn((PointLight::default(), Transform::from_xyz(2., 4., 2.)));
commands.spawn((
Text::new("按 G 切换全局线框"),
Node {
position_type: PositionType::Absolute,
top: px(12.),
left: px(12.),
..default()
},
));
}
// ANCHOR: toggle
fn toggle_wireframe(
keyboard: Res<ButtonInput<KeyCode>>,
mut config: ResMut<WireframeConfig>,
) {
if keyboard.just_pressed(KeyCode::KeyG) {
config.global = !config.global;
}
}
// ANCHOR_END: toggleListing 27-5:线框渲染——全局开关与逐实体控制
运行:
cargo run -p ch27-devtools --example listing-27-05三个方块排成一排:红色的永远无线框(NoWireframe),绿色的跟随全局设置,蓝色的永远有线框且颜色是青色(WireframeColor)。按 G 切换全局线框——绿色方块的线框会出现/消失,红蓝两块不受影响。
综合调试层
实际项目中,你会同时用到 Gizmos、FPS Overlay 和线框。Listing 27-6 展示一个"调试层"系统:用 Gizmos 为每个物体画包围盒和朝向,叠加 FPS 监控。
fn debug_overlay(mut gizmos: Gizmos, query: Query<&Transform>, time: Res<Time>) {
let t = time.elapsed_secs();
for transform in &query {
let pos = transform.translation;
if pos.y > 0. {
gizmos.cube(
Transform::from_translation(pos).with_scale(Vec3::splat(0.82)),
Color::srgb(0.9, 0.9, 0.1),
);
let forward = transform.forward().as_vec3();
gizmos.ray(pos + Vec3::Y * 0.5, forward * 0.6, Color::srgb(0.2, 0.8, 0.2));
}
}
let scan_x = ops::sin(t) * 5.;
gizmos.line(
Vec3::new(scan_x, 0.01, -6.),
Vec3::new(scan_x, 0.01, 6.),
Color::srgb(0.1, 0.8, 0.8),
);
gizmos.grid(
Quat::from_rotation_x(PI / 2.),
UVec2::splat(12),
Vec2::splat(1.),
Color::srgb(0.15, 0.15, 0.15),
);
}这个系统做了三件事:
- 遍历所有带
Transform的实体,为 Y > 0 的物体画黄色包围盒和绿色朝向射线 - 画一条水平扫描线随时间左右移动
- 铺一层淡灰色网格作为地面参考
这些 Gizmos 只在你有 Gizmos 系统参数的系统里才画——发布时只要把系统从 Schedule 里移除(或者用 #[cfg(debug_assertions)] 条件编译),就一行痕迹都不留。
完整示例
use bevy::dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin};
use bevy::prelude::*;
use std::f32::consts::PI;
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
FpsOverlayPlugin {
config: FpsOverlayConfig {
text_config: TextFont {
font_size: FontSize::Px(28.0),
..default()
},
text_color: Color::srgb(0.0, 1.0, 0.0),
..default()
},
},
))
.add_systems(Startup, setup)
.add_systems(Update, debug_overlay)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Camera3d::default(),
Transform::from_xyz(0., 3., 8.).looking_at(Vec3::ZERO, Vec3::Y),
));
commands.spawn((PointLight::default(), Transform::from_xyz(4., 8., 4.)));
for i in -2..=2 {
commands.spawn((
Mesh3d(meshes.add(Cuboid::new(0.8, 0.8, 0.8))),
MeshMaterial3d(materials.add(Color::srgb(0.6, 0.6, 0.7))),
Transform::from_xyz(i as f32 * 2., 0.4, 0.),
));
}
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(12., 12.))),
MeshMaterial3d(materials.add(Color::srgb(0.25, 0.35, 0.25))),
));
}
// ANCHOR: debug_system
fn debug_overlay(mut gizmos: Gizmos, query: Query<&Transform>, time: Res<Time>) {
let t = time.elapsed_secs();
for transform in &query {
let pos = transform.translation;
if pos.y > 0. {
gizmos.cube(
Transform::from_translation(pos).with_scale(Vec3::splat(0.82)),
Color::srgb(0.9, 0.9, 0.1),
);
let forward = transform.forward().as_vec3();
gizmos.ray(pos + Vec3::Y * 0.5, forward * 0.6, Color::srgb(0.2, 0.8, 0.2));
}
}
let scan_x = ops::sin(t) * 5.;
gizmos.line(
Vec3::new(scan_x, 0.01, -6.),
Vec3::new(scan_x, 0.01, 6.),
Color::srgb(0.1, 0.8, 0.8),
);
gizmos.grid(
Quat::from_rotation_x(PI / 2.),
UVec2::splat(12),
Vec2::splat(1.),
Color::srgb(0.15, 0.15, 0.15),
);
}
// ANCHOR_END: debug_systemListing 27-6:综合调试层——Gizmos + FPS Overlay
运行:
cargo run -p ch27-devtools一排灰色立方体铺在绿色地面上,每个都被黄色包围盒框住、头部伸出绿色朝向箭头,一条青色扫描线来回扫过,左上角是绿色 FPS 数字。这就是一个最小的"开发模式"画面。