fix: bullet ghost hits and refactor

This commit is contained in:
2025-01-06 15:34:47 +00:00
parent eb95345745
commit d1230fdcad
18 changed files with 339 additions and 332 deletions

50
src/ui/camera.rs Normal file
View File

@@ -0,0 +1,50 @@
use bevy::prelude::*;
use bevy::input::mouse::{AccumulatedMouseMotion, MouseMotion};
use bevy::ecs::query::QuerySingleError;
use super::cursor::CursorGrabState;
pub struct CameraPlugin;
impl Plugin for CameraPlugin
{
fn build(&self, app: &mut App)
{
app.add_systems(PreUpdate, do_camera_rotation
.run_if(in_state(CursorGrabState(true)))
.run_if(on_event::<MouseMotion>)
);
}
}
#[derive(Component)]
pub struct CameraController
{
pub sensitivity: Vec2,
}
fn do_camera_rotation(
mut query: Query<(&CameraController, &mut Transform)>,
mouse_motion: Res<AccumulatedMouseMotion>,
) {
let (controller, mut transform) = match query.get_single_mut() {
Ok((controller, transform)) => (controller, transform),
Err(QuerySingleError::NoEntities(_)) => return,
Err(QuerySingleError::MultipleEntities(_)) => panic!("Multiple camera controllers found!"),
};
if mouse_motion.delta == Vec2::ZERO {
return;
}
let (yaw, pitch, roll) = transform.rotation.to_euler(EulerRot::YXZ);
let yaw = yaw - mouse_motion.delta.x * controller.sensitivity.x;
let pitch = pitch - mouse_motion.delta.y * controller.sensitivity.y;
const PITCH_LIMIT: f32 = std::f32::consts::FRAC_PI_2 - 0.0001;
let pitch = pitch.clamp(-PITCH_LIMIT, PITCH_LIMIT);
transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
}

View File

@@ -1,6 +1,10 @@
use bevy::prelude::*;
// TODO: add a crosshair component which projects a crosshair to infinity.
// For now, we'll just draw a crosshair in the center of the screen.
// This causes a lot of warnings without a camera!
pub struct CrosshairPlugin;
impl Plugin for CrosshairPlugin

54
src/ui/cursor.rs Normal file
View File

@@ -0,0 +1,54 @@
use bevy::prelude::*;
use bevy::input::{ButtonState, keyboard::KeyboardInput};
use bevy::window::{CursorGrabMode, PrimaryWindow};
const CURSOR_TOGGLE_KEY: KeyCode = KeyCode::Escape;
const CURSOR_TOGGLE_STATE: ButtonState = ButtonState::Released;
pub struct CursorPlugin;
impl Plugin for CursorPlugin
{
fn build(&self, app: &mut App)
{
app.insert_state(CursorGrabState(false));
app.add_systems(PreUpdate, on_cursor_grab_toggled.in_set(CursorSet));
}
}
#[derive(SystemSet, Debug, Copy, Clone, Eq, PartialEq, Hash)]
struct CursorSet;
#[derive(States, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct CursorGrabState(pub bool);
fn on_cursor_grab_toggled(
mut keyboard_events: EventReader<KeyboardInput>,
mut window: Single<&mut Window, With<PrimaryWindow>>,
cursor_state: Res<State<CursorGrabState>>,
mut next_cursor_state: ResMut<NextState<CursorGrabState>>,
) {
let cursor_toggle_count = keyboard_events.read()
.filter(|event| event.key_code == CURSOR_TOGGLE_KEY)
.filter(|event| event.state == CURSOR_TOGGLE_STATE)
.count();
if cursor_toggle_count % 2 == 0 {
return;
}
match cursor_state.get()
{
CursorGrabState(true) => {
window.cursor_options.visible = false;
window.cursor_options.grab_mode = CursorGrabMode::Locked;
next_cursor_state.set(CursorGrabState(false));
}
CursorGrabState(false) => {
window.cursor_options.visible = true;
window.cursor_options.grab_mode = CursorGrabMode::None;
next_cursor_state.set(CursorGrabState(true));
}
}
}

View File

@@ -1,14 +1,22 @@
use bevy::prelude::*;
use bevy::app::PluginGroupBuilder;
mod crosshair;
pub mod window;
pub mod cursor;
pub mod camera;
pub mod crosshair;
pub struct UiPlugin;
pub struct UiPlugins;
impl Plugin for UiPlugin
impl PluginGroup for UiPlugins
{
fn build(&self, app: &mut App)
fn build(self) -> PluginGroupBuilder
{
app.add_plugins(crosshair::CrosshairPlugin);
PluginGroupBuilder::start::<Self>()
.add(window::WindowPlugin)
.add(cursor::CursorPlugin)
.add(camera::CameraPlugin)
.add(crosshair::CrosshairPlugin)
}
}

45
src/ui/window.rs Normal file
View File

@@ -0,0 +1,45 @@
use bevy::prelude::*;
use bevy::window::{CursorGrabMode, PrimaryWindow, WindowResolution};
use super::cursor::CursorGrabState;
pub struct WindowPlugin;
impl Plugin for WindowPlugin
{
fn build(&self, app: &mut App)
{
app.add_systems(PreStartup, setup_window);
app.add_systems(OnEnter(CursorGrabState(true)), on_cursor_grab_toggled);
app.add_systems(OnEnter(CursorGrabState(false)), on_cursor_grab_toggled);
}
}
fn setup_window(
mut window: Query<&mut Window, With<PrimaryWindow>>,
) {
let mut window = window.single_mut();
window.title = "Bevy FPS Game".to_string();
window.resolution = WindowResolution::new(1920.0, 1080.0);
window.position = WindowPosition::Centered(MonitorSelection::Primary);
}
fn on_cursor_grab_toggled(
mut window: Query<&mut Window, With<PrimaryWindow>>,
cursor_state: Res<State<CursorGrabState>>,
) {
let mut window = window.single_mut();
match cursor_state.get()
{
CursorGrabState(true) => {
window.cursor_options.visible = false;
window.cursor_options.grab_mode = CursorGrabMode::Locked;
}
CursorGrabState(false) => {
window.cursor_options.visible = true;
window.cursor_options.grab_mode = CursorGrabMode::None;
}
}
}