bevy cursor to world

发布时间 2023-08-02 16:32:51作者: 明天有风吹


代码

//! bevy version: 0.11
//! Spawn a ball on the plane when you click on it.

use bevy::prelude::*;
use bevy::input::common_conditions::input_just_pressed;
use bevy::window::{PrimaryWindow, close_on_esc};

const PLANE_SIZE: f32 = 5.0;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, close_on_esc)
        .add_systems(
            Update,
            cursor_to_world
                .run_if(input_just_pressed(MouseButton::Left)),
        )
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // plane
    commands
        .spawn(PbrBundle {
            mesh: meshes.add(shape::Plane::from_size(PLANE_SIZE).into()),
            material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
            ..default()
        });

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

    // camera
    commands.spawn(Camera3dBundle {
        transform: Transform::from_translation(Vec3::new(2., 2., 4.))
            .looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}

fn cursor_to_world(
    window: Query<&Window, With<PrimaryWindow>>,
    camera: Query<(&Camera, &GlobalTransform)>,

    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
) {
    let window = window.single();
    let Some(cursor_position) = window.cursor_position() else { return; };

    let (camera, camera_transform) = camera.single();

    let Some(ray) = camera.viewport_to_world(camera_transform, cursor_position) else { return; };

    let Some(distance) = ray.intersect_plane(Vec3::ZERO, Vec3::Y) else { return; };
    let world_position = ray.get_point(distance);

    if world_position.x.abs() > PLANE_SIZE/2. || world_position.z.abs() > PLANE_SIZE/2. { return; }

    let radius = 0.1;

    commands.spawn(PbrBundle {
        mesh: meshes.add(shape::UVSphere { radius, ..default() }.into()),
        transform: Transform::from_translation(world_position + Vec3::new(0., radius, 0.)),
        ..default()
    });
}