Use picking system for block placing / breaking

main
copygirl 3 weeks ago
parent 5467859d6d
commit 72625dd949
  1. 95
      client/src/input/client_inputs.rs
  2. 10
      client/src/main.rs

@ -1,5 +1,5 @@
//! Handles client-side input using [`lightyear`], updating the
//! [`ActionState`] of the affected entities, such as the `Player`.
//! [`ActionState`] of the affected entities, such as the [`Player`].
use bevy::prelude::*;
use common::prelude::*;
@ -9,10 +9,55 @@ use lightyear::prelude::input::native::*;
use bevy::window::{CursorGrabMode, CursorOptions};
pub fn plugin(app: &mut App) {
app.init_resource::<CurrentAction>();
app.add_systems(
FixedPreUpdate,
(buffer_input, buffer_action).in_set(InputSystems::WriteClientInputs),
);
app.add_observer(insert_block_pickable);
app.add_observer(place_or_break_blocks);
}
fn insert_block_pickable(event: On<Add, Block>, mut commands: Commands) {
let mut block = commands.entity(event.entity);
block.insert(Pickable::default());
}
#[derive(Resource, Default)]
struct CurrentAction(Action);
fn place_or_break_blocks(
mut event: On<Pointer<Press>>,
mut current_action: ResMut<CurrentAction>,
cursor: Single<&CursorOptions>,
blocks: Blocks,
) {
let is_place = match event.button {
PointerButton::Primary => false, // left-click
PointerButton::Secondary => true, // right-click
PointerButton::Middle => return, // not handled
};
if cursor.grab_mode != CursorGrabMode::Locked {
return; // Cursor is not grabbed.
}
let Some(block_pos) = blocks.position(event.entity) else {
return; // Clicked entity is not a block.
};
let Some(normal) = event.hit.normal else {
return; // Picking system didn't return a normal.
};
current_action.0 = if is_place {
// FIXME: This only works for axis-aligned normals.
let offset = normal.normalize().round().as_ivec3();
// TODO: Don't hardcode block type.
Action::PlaceBlock(block_pos + offset, Block::DEFAULT)
} else {
Action::BreakBlock(block_pos)
};
// Handle the event.
event.propagate(false);
}
fn buffer_input(
@ -44,50 +89,10 @@ fn buffer_input(
}
}
// TODO: Use picking system instead of manually raycasting.
pub fn buffer_action(
buttons: Res<ButtonInput<MouseButton>>,
fn buffer_action(
mut player: Single<&mut ActionState<Action>, With<InputMarker<Action>>>,
cursor: Single<&CursorOptions>,
window: Single<(&Window, &CursorOptions)>,
camera: Single<(&GlobalTransform, &Camera)>,
mut ray_cast: MeshRayCast,
blocks: Blocks,
mut current_action: ResMut<CurrentAction>,
) {
player.0 = Action::None;
if cursor.grab_mode == CursorGrabMode::None {
return;
}
if !buttons.any_just_pressed([MouseButton::Right, MouseButton::Left]) {
return;
}
let (window, cursor) = window.into_inner();
let (cam_transform, camera) = camera.into_inner();
let ray = if cursor.grab_mode == CursorGrabMode::Locked {
Ray3d::new(cam_transform.translation(), cam_transform.forward())
} else if let Some(cursor_pos) = window.cursor_position() {
camera.viewport_to_world(cam_transform, cursor_pos).unwrap()
} else {
return; // cursor outside window area
};
let settings = MeshRayCastSettings::default();
let Some((block, hit)) = ray_cast.cast_ray(ray, &settings).first() else {
return; // ray didn't hit anything
};
let Some(block_pos) = blocks.position(*block) else {
return; // entity hit is not a block
};
player.0 = if buttons.just_pressed(MouseButton::Right) {
// FIXME: This only works for axis-aligned normals.
let offset = hit.normal.normalize().round().as_ivec3();
// TODO: Don't hardcode block type.
Action::PlaceBlock(block_pos + offset, Block::DEFAULT)
} else {
Action::BreakBlock(block_pos)
};
player.0 = current_action.0.clone();
current_action.0 = Action::None;
}

@ -47,6 +47,7 @@ fn main() -> Result {
meta_check: AssetMetaCheck::Never,
..default()
}),
MeshPickingPlugin, // allow Mesh entities to be "picked" (ray-traced against)
bevy_fix_cursor_unlock_web::FixPointerUnlockPlugin,
));
@ -55,6 +56,10 @@ fn main() -> Result {
app.insert_resource(UiPickingSettings {
require_markers: true,
});
app.insert_resource(MeshPickingSettings {
require_markers: true,
..default()
});
app.add_plugins((
common::assets::plugin,
@ -119,12 +124,13 @@ fn spawn_initial_blocks(_event: On<Add, Started>, mut blocks: Blocks) {
/// When the `Predicted` player we control is being spawned, insert necessary components.
fn handle_predicted_player_spawn(event: On<Add, Predicted>, mut commands: Commands) {
let player = event.entity;
commands.entity(player).insert((
let mut player = commands.entity(event.entity);
player.insert((
// Handle inputs on this entity.
InputMarker::<Inputs>::default(),
InputMarker::<Action>::default(),
// TODO: Attach camera to player head eventually.
Camera3d::default(),
MeshPickingCamera,
));
}

Loading…
Cancel
Save