Rust 3D游戏开发插件bevy_mod_raycast的使用:实现高效射线检测与拾取功能

Rust 3D游戏开发插件bevy_mod_raycast的使用:实现高效射线检测与拾取功能

bevy_mod_raycast是一个用于Bevy游戏引擎的网格射线检测小插件。

reflecting_lasers example

快速开始

使用Raycast系统参数,你甚至不需要添加插件,可以直接在ECS中进行射线检测:

use bevy_mod_raycast::prelude::*;

fn my_raycast_system(mut raycast: Raycast) {
    let ray = Ray3d::new(Vec3::ZERO, Vec3::X);  // 创建一条从原点沿X轴方向的射线
    let hits = raycast.cast_ray(ray, &RaycastSettings::default());  // 执行射线检测
}

完整示例代码

下面是一个完整的Bevy应用示例,展示了如何使用bevy_mod_raycast实现3D场景中的对象拾取功能:

use bevy::prelude::*;
use bevy_mod_raycast::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // 添加射线检测插件
        .add_plugins(DefaultRaycastingPlugin::default())
        // 添加系统
        .add_systems(Startup, setup_scene)
        .add_systems(Update, (handle_raycast, rotate_cube))
        .run();
}

// 场景设置
fn setup_scene(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // 相机
    commands.spawn((
        Camera3dBundle {
            transform: Transform::from_xyz(0.0, 0.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
            ..default()
        },
        RaycastSource::<RaycastSet>::new_transform_empty(), // 使相机成为射线源
    ));
    
    // 立方体
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
            material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
            transform: Transform::from_xyz(0.0, 0.0, 极0.0),
            ..default()
        },
        RaycastMesh::<RaycastSet>::default(), // 使立方体可被射线检测
    ));
    
    // 光源
    commands.spawn(PointLightBundle {
        point_light: PointLight {
            intensity: 1500.0,
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_xyz(4.0, 8.0, 4.0),
        ..default()
    });
}

// 射线检测处理系统
fn handle_raycast(
    mut cursor: EventReader<CursorMoved>,
    mut raycast: Raycast,
    mut materials: ResMut<Assets<StandardMaterial>>,
    cubes: Query<&Handle<StandardMaterial>>,
) {
    // 获取鼠标位置变化事件
    for event in cursor.read() {
        // 执行射线检测
        let hits = raycast.cast_ray(
            Ray3d::from_screenspace(
                event.position,
                Camera::default(),
                &GlobalTransform::default(),
            ),
            &RaycastSettings::default(),
        );
        
        // 改变被选中物体的颜色
        if let Some(hit) = hits.first() {
            if let Ok(material_handle) = cubes.get(hit.entity()) {
                if let Some(material) = materials.get_mut(material_handle) {
                    material.base_color = Color::rgb(0.9, 0.3, 0.3);
                }
            }
        } else {
            for cube in &cubes {
                if let Some(material) = materials.get_mut(cube) {
                    material.base_color = Color::rgb(0.8, 0.7, 0.6);
                }
            }
        }
    }
}

// 立方体旋转动画
fn rotate_c极ube(mut cubes: Query<&mut Transform, With<RaycastMesh<RaycastSet>>>) {
    for mut transform in &mut cubes {
        transform.rotate_y(0.01);
    }
}

// 定义射线检测组
#[derive(Reflect)]
struct RaycastSet;

版本支持

下表显示了bevy_mod_raycast与Bevy版本的对应关系:

bevy bevy_mod_raycast
0.14 0.18
0.13 0.17
0.12 0.16
0.11 0.9 - 0.15
0.10 0.8
0.9 0.7
0.8 0.6
0.7 0.4 - 0.5
0.6 0.3
0.5 0.2
0.4 0.1

安装

在项目目录中运行以下Cargo命令:

cargo add bevy_mod_raycast

或在Cargo.toml中添加:

bevy_mod_raycast = "0.18.0"

1 回复

Rust 3D游戏开发插件bevy_mod_raycast的使用:实现高效射线检测与拾取功能

完整示例代码

下面是一个完整的bevy_mod_raycast使用示例,实现了鼠标拾取3D物体的功能:

use bevy::prelude::*;
use bevy_mod_raycast::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // 添加默认的射线拾取插件
        .add_plugins(DefaultRaycastingPlugin::default())
        .add_systems(Startup, setup_scene)
        .add_systems(Update, handle_picks)
        .run();
}

// 定义自定义的RaycastSet用于分组
#[derive(Reflect)]
struct MyRaycastSet;

fn setup_scene(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // 创建可拾取的立方体
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
            material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
            transform: Transform::from_xyz(0.0, 0.5, 0.0),
            ..default()
        },
        // 使这个立方体可以被MyRaycastSet组的射线检测到
        RaycastMesh::<MyRaycastSet>::default(),
    ));

    // 创建可拾取的球体
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(Mesh::from(shape::UVSphere { 
                radius: 0.5, 
                sectors: 32, 
                stacks: 32 
            })),
            material: materials.add(Color::rgb(0.3, 0.5, 0.9).into()),
            transform: Transform::from_xyz(2.0, 0.5, 0.0),
            ..default()
        },
        RaycastMesh::<MyRaycastSet>::default(),
    ));

    // 创建光源
    commands.spawn(PointLightBundle {
        point_light: PointLight {
            intensity: 1500.0,
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_xyz(4.0, 8.0, 4.0),
        ..default()
    });

    // 创建相机并设置射线源
    commands.spawn((
        Camera3dBundle {
            transform: Transform::from_xyz(0.0, 2.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
            ..default()
        },
        // 添加射线源组件,用于从相机发射射线
        RaycastSource::<MyRaycastSet>::new(),
    ));
}

fn handle_picks(
    mut cursor_events: EventReader<CursorMoved>,
    mut raycast: Raycast,
    mut pick_events: EventReader<RaycastMeshEvent<MyRaycastSet>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    picked_entities: Query<&Handle<StandardMaterial>>,
) {
    // 更新射线位置跟随鼠标移动
    for event in cursor_events.read() {
        raycast.update_raycast(event.position);
    }

    // 处理拾取事件
    for pick_event in pick_events.read() {
        if let Some((entity, _intersection)) = &pick_event.intersection {
            // 改变被拾取物体的颜色
            if let Ok(material_handle) = picked_entities.get(*entity) {
                if let Some(material) = materials.get_mut(material_handle) {
                    material.base_color = Color::rgb(1.0, 0.0, 0.0); // 变为红色
                }
            }
        }
    }
}

代码说明

  1. 自定义RaycastSet

    #[derive(Reflect)]
    struct MyRaycastSet;
    

    定义了一个自定义的射线检测组,用于区分不同类型的射线检测

  2. 可拾取物体设置

    RaycastMesh::<MyRaycastSet>::default()
    

    为物体添加这个组件使其可以被MyRaycastSet组的射线检测到

  3. 射线源设置

    RaycastSource::<MyRaycastSet>::new()
    

    为相机添加这个组件使其成为射线源

  4. 射线更新

    raycast.update_raycast(event.position);
    

    在鼠标移动时更新射线位置

  5. 拾取事件处理

    EventReader<RaycastMeshEvent<MyRaycastSet>>
    

    监听MyRaycastSet组的拾取事件,当射线与物体相交时会触发

  6. 拾取反馈

    material.base_color = Color::rgb(1.0, 0.0, 0.0);
    

    当物体被拾取时改变其颜色作为视觉反馈

这个完整示例展示了如何使用bevy_mod_raycast实现基本的3D物体拾取功能,包括场景设置、射线源管理、拾取检测和交互反馈等完整流程。

回到顶部