Add block placement and breaking

main
copygirl 1 week ago
parent b01f37fea4
commit 957d3e962d
  1. 39
      src/block.rs
  2. 5
      src/free_camera.rs
  3. 55
      src/main.rs
  4. 35
      src/placement.rs

@ -0,0 +1,39 @@
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
#[derive(Component)]
pub struct Block;
#[derive(SystemParam)]
pub struct Blocks<'w, 's> {
commands: Commands<'w, 's>,
block_resources: Res<'w, BlockResources>,
}
impl Blocks<'_, '_> {
pub fn spawn(&mut self, pos: IVec3) {
self.commands.spawn((
Block,
Mesh3d(self.block_resources.mesh.clone()),
MeshMaterial3d(self.block_resources.material.clone()),
Transform::from_translation(pos.as_vec3() + Vec3::ONE / 2.),
));
}
}
#[derive(Resource)]
pub struct BlockResources {
mesh: Handle<Mesh>,
material: Handle<StandardMaterial>,
}
pub fn setup_blocks(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.insert_resource(BlockResources {
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
material: materials.add(Color::srgb_u8(124, 144, 255)),
});
}

@ -34,7 +34,7 @@ impl Default for CameraFreeLook {
pub fn camera_free_look(
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
mouse_button_input: Res<ButtonInput<MouseButton>>,
mut mouse_button_input: ResMut<ButtonInput<MouseButton>>,
key_input: Res<ButtonInput<KeyCode>>,
window: Single<(&Window, &mut CursorOptions)>,
camera: Single<(&mut Transform, &mut CameraFreeLook)>,
@ -53,6 +53,9 @@ pub fn camera_free_look(
*crosshair_visibility = Visibility::Inherited;
cursor.grab_mode = CursorGrabMode::Locked;
cursor.visible = false;
// To prevent other systems (such as placement) from seeing
// the mouse buttons being pressed, clear the state here.
mouse_button_input.clear();
}
if cursor.grab_mode == CursorGrabMode::Locked {

@ -1,43 +1,40 @@
use bevy::prelude::*;
mod block;
mod free_camera;
use free_camera::*;
mod placement;
use block::*;
use free_camera::*;
use placement::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, (setup_scene, setup_crosshair))
.add_systems(
Startup,
(setup_blocks, setup_crosshair, setup_scene).chain(),
)
.add_systems(Update, (camera_free_look, noclip_controller).chain())
// This system requires `GlobalTransform`, so for a most
// up-to-date value, run it right after it's been updated.
.add_systems(
PostUpdate,
debug_ray_cast.after(TransformSystems::Propagate),
// This system requires the camera's `GlobalTransform`, so for
// a most up-to-date value, run it right after it's been updated.
place_break_blocks.after(TransformSystems::Propagate),
)
.run();
}
fn setup_scene(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// circular base
commands.spawn((
Mesh3d(meshes.add(Circle::new(4.0))),
MeshMaterial3d(materials.add(Color::WHITE)),
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
));
// cube
fn setup_scene(mut commands: Commands, mut blocks: Blocks) {
// light
commands.spawn((
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
Transform::from_xyz(0.0, 0.5, 0.0),
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
CameraFreeLook::default(),
CameraNoClip::default(),
));
// light
// camera
commands.spawn((
PointLight {
shadows_enabled: true,
@ -45,11 +42,11 @@ fn setup_scene(
},
Transform::from_xyz(4.0, 8.0, 4.0),
));
// camera
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
CameraFreeLook::default(),
CameraNoClip::default(),
));
// blocks
for x in -8..8 {
for z in -8..8 {
blocks.spawn(IVec3::new(x, 0, z));
}
}
}

@ -1,27 +1,48 @@
use bevy::prelude::*;
use bevy::window::{CursorGrabMode, CursorOptions};
pub fn debug_ray_cast(
mut gizmos: Gizmos,
use crate::block::*;
pub fn place_break_blocks(
mut commands: Commands,
mut blocks: Blocks,
mut ray_cast: MeshRayCast,
mouse_button_input: Res<ButtonInput<MouseButton>>,
window: Single<(&Window, &CursorOptions)>,
camera: Single<(&GlobalTransform, &Camera)>,
block_lookup: Query<&Transform, With<Block>>,
) {
if !mouse_button_input.any_just_pressed([MouseButton::Left, MouseButton::Right]) {
return; // only run this system when left or right mouse button is pressed
}
let (window, cursor) = window.into_inner();
let (transform, camera) = camera.into_inner();
let (cam_transform, camera) = camera.into_inner();
let ray = if cursor.grab_mode == CursorGrabMode::Locked {
Ray3d::new(transform.translation(), transform.forward())
Ray3d::new(cam_transform.translation(), cam_transform.forward())
} else if let Some(cursor_pos) = window.cursor_position() {
camera.viewport_to_world(transform, cursor_pos).unwrap()
camera.viewport_to_world(cam_transform, cursor_pos).unwrap()
} else {
return; // cursor outside window area
};
let settings = &MeshRayCastSettings::default();
let Some((_entity, hit)) = ray_cast.cast_ray(ray, settings).first() else {
let Some((block, hit)) = ray_cast.cast_ray(ray, settings).first() else {
return; // ray didn't hit anything
};
let Ok(block_transform) = block_lookup.get(*block) else {
return; // entity hit is not a block
};
gizmos.arrow(hit.point, hit.point + hit.normal / 2., Color::WHITE);
if mouse_button_input.just_pressed(MouseButton::Left) {
// Destroy the block clicked.
commands.entity(*block).despawn();
} else if mouse_button_input.just_pressed(MouseButton::Right) {
// Create a new block next to the one that was just clicked.
let pos = block_transform.translation.floor().as_ivec3();
// FIXME: This only works for axis-aligned normals.
let offset = hit.normal.normalize().round().as_ivec3();
blocks.spawn(pos + offset);
}
}

Loading…
Cancel
Save