小结与练习
综合示例
Listing 25-7 把本章的所有要素组合在一起:可飞行的相机、可点选的物体、悬停变色、拖拽移动:
rust
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Camera3d::default(),
Transform::from_xyz(0.0, 5.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
FreeCamera {
walk_speed: 5.0,
run_speed: 15.0,
..default()
},
));
commands.spawn((
PointLight {
contact_shadows_enabled: true,
..default()
},
Transform::from_xyz(4.0, 8.0, 4.0),
));
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))),
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.3, 0.3))),
Pickable::IGNORE,
));
let default_matl = materials.add(Color::srgb(0.2, 0.7, 0.9));
let hover_matl = materials.add(Color::srgb(0.9, 0.7, 0.2));
let cube_mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
let sphere_mesh = meshes.add(Sphere::new(0.5));rust
commands
.spawn((
Mesh3d(cube_mesh),
MeshMaterial3d(default_matl.clone()),
Transform::from_xyz(-1.5, 0.5, 0.0),
))
.observe(on_drag_move)
.observe(
move |over: On<Pointer<Over>>, mut q: Query<&mut MeshMaterial3d<StandardMaterial>>| {
if let Ok(mut m) = q.get_mut(over.entity) {
m.0 = hover_matl.clone();
}
},
)
.observe(
move |out: On<Pointer<Out>>, mut q: Query<&mut MeshMaterial3d<StandardMaterial>>| {
if let Ok(mut m) = q.get_mut(out.entity) {
m.0 = default_matl.clone();
}
},
);
commands
.spawn((
Mesh3d(sphere_mesh),
MeshMaterial3d(materials.add(Color::srgb(0.9, 0.3, 0.3))),
Transform::from_xyz(1.5, 0.5, 0.0),
))
.observe(on_drag_move);
}rust
fn on_drag_move(drag: On<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
if let Ok(mut transform) = transforms.get_mut(drag.entity) {
transform.translation.x += drag.delta.x * 0.01;
transform.translation.z += drag.delta.y * 0.01;
}
}Listing 25-7:Picking + FreeCamera 的完整组合(src/main.rs)
console
cargo run -p ch25-picking操作方式:左键点击/拖拽物体,WASD 移动相机,鼠标转向(左键按住捕获)。
本章要点回顾
MeshPickingPlugin不在DefaultPlugins里,3D picking 必须手动加;Pointer<E>是统一的事件容器,E可以是Click、Over、Out、Drag、DragDrop等十多种事件;.observe()在实体上注册事件回调,闭包参数类型决定响应哪种事件;- 事件冒泡自动从子实体传播到父实体,
event.propagate(false)阻止传播; Pickable::IGNORE让实体不参与 picking(地面、辅助线等);- Drag 事件的
delta是屏幕像素增量,不是世界坐标,需要乘以缩放系数; - DragDrop 需要在接收区实体上挂
DragEnter/DragOver/DragDrop/DragLeave四个 Observer; FreeCamera通过 feature gate"free_camera"启用,挂组件 + 加插件即可 WASD 飞行。
练习
多物体拖拽——场景里放三个不同形状的物体,每个都能独立拖拽。提示:每个实体单独挂 Observer,
drag.entity保证只移动被拖的那个。拖放到目标——一个球、三个不同颜色的"洞"(平面),把球拖到某个洞上松手,球的颜色变成洞的颜色。提示:在
DragDropObserver 里查询目标实体的材质颜色。相机切换——两个相机,一个固定俯视,一个 FreeCamera。按 Tab 键切换哪个相机是活跃的。提示:
Camera组件有order字段,或者用is_active控制;FreeCameraState有enabled字段。Click 放置——点击地面时,在点击位置(
HitData::position)生成一个小物体。提示:用Pointer<Click>Observer,在闭包里读click.hit.position,用commands.spawn(...)放置物体。禁止穿透——场景里有一个 UI 面板浮在 3D 物体上方。要求点击 UI 时不触发后面 3D 物体的 Click 事件。提示:UI 默认的
Pickable会should_block_lower,不需要额外代码——验证一下就好。