From 9ac58fe9b99b1c8180b7cdcc2587eb3aabe0a331 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sun, 22 Jun 2025 15:35:23 -0400 Subject: [PATCH] wip character controller --- .zed/debug.json | 17 ++++++++ Cargo.toml | 2 +- src/components/character_controller.rs | 60 ++++++++++++++++++++++++++ src/components/mod.rs | 1 + src/main.rs | 2 +- src/plugins/character_controller.rs | 46 ++++++++++++++++++++ src/plugins/game.rs | 19 ++++++-- src/plugins/mod.rs | 23 +++++++--- src/plugins/player.rs | 9 ++++ src/plugins/ship.rs | 4 +- 10 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 .zed/debug.json create mode 100644 src/components/character_controller.rs create mode 100644 src/plugins/character_controller.rs create mode 100644 src/plugins/player.rs diff --git a/.zed/debug.json b/.zed/debug.json new file mode 100644 index 0000000..c3134e8 --- /dev/null +++ b/.zed/debug.json @@ -0,0 +1,17 @@ +// Project-local debug tasks +// +// For more documentation on how to configure debug tasks, +// see: https://zed.dev/docs/debugger +[ + { + "label": "Debug Run", + "build": { + "command": "cargo", + "args": ["build"] + }, + "program": "$ZED_WORKTREE_ROOT/target/debug/space-game.exe", + "cwd": "$ZED_WORKTREE_ROOT", + "request": "launch", + "adapter": "GDB" + } +] diff --git a/Cargo.toml b/Cargo.toml index 0c5e26f..85900da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ dev = [ "dev-viz" ] dev-viz = [] -dev-phsy = [] +dev-phys = [] [lints.clippy] # Bevy supplies arguments to systems via dependency injection, so it's natural for systems to diff --git a/src/components/character_controller.rs b/src/components/character_controller.rs new file mode 100644 index 0000000..797572f --- /dev/null +++ b/src/components/character_controller.rs @@ -0,0 +1,60 @@ +use avian3d::prelude::*; +use bevy::prelude::*; + +#[derive(Component, Reflect, Clone, Copy)] +#[require( + GravityDirection, + RigidBody, + Transform, + ExternalForce, + GravityScale, + CharacterMotion, + CharacterRotation +)] +pub struct CharacterController { + pub step_height: f32, + pub max_slope: f32, + height: f32, + radius: f32, +} + +impl Default for CharacterController { + fn default() -> Self { + return Self { + height: 1.0, + radius: 0.5, + max_slope: 0.5, + step_height: 0.25, + }; + } +} +impl CharacterController { + #[allow(dead_code)] + pub fn with_size(mut self, height: f32, radius: f32) -> Self { + self.height = height; + self.radius = radius; + return self; + } + + pub fn height(&self) -> f32 { + self.height + } + + pub fn radius(&self) -> f32 { + self.radius + } +} +#[derive(Component, Default, Reflect)] +pub struct CharacterMotion(pub Vec3); + +#[derive(Component, Default, Reflect)] +pub struct CharacterRotation(pub Quat); + +#[derive(Component, Reflect)] +pub struct GravityDirection(pub Dir3); + +impl Default for GravityDirection { + fn default() -> Self { + return GravityDirection(Dir3::NEG_Y); + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 3296553..bf3e8b2 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,2 +1,3 @@ pub mod camera; +pub mod character_controller; pub mod tags; diff --git a/src/main.rs b/src/main.rs index 03235d0..e321fb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ mod plugins; mod utils; use bevy::{prelude::*, window::PresentMode}; use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin}; -use plugins::game::GamePlugin; +use plugins::GamePlugin; mod components; mod resources; diff --git a/src/plugins/character_controller.rs b/src/plugins/character_controller.rs new file mode 100644 index 0000000..308b8af --- /dev/null +++ b/src/plugins/character_controller.rs @@ -0,0 +1,46 @@ +use avian3d::prelude::{Collider, ExternalForce, GravityScale, LockedAxes, Mass}; +use bevy::prelude::*; + +use crate::components::character_controller::{CharacterController, GravityDirection}; + +pub struct CharacterControllerPlugin; + +impl Plugin for CharacterControllerPlugin { + fn build(&self, app: &mut App) { + app.add_systems(PreStartup, setup_hooks); + app.add_systems(Update, apply_gravity); + } +} + +fn setup_hooks(world: &mut World) { + world + .register_component_hooks::() + .on_insert(|mut world, ctx| { + let controller = world.get::(ctx.entity).unwrap(); + let height = controller.height(); + let radius = controller.radius(); + + let mut commands = world.commands(); + let mut entity_commands = commands.entity(ctx.entity); + entity_commands.insert(( + LockedAxes::ROTATION_LOCKED, + Collider::capsule(radius, height), + ExternalForce::ZERO.with_persistence(false), + )); + }); +} + +fn apply_gravity( + controllers: Query< + (&mut ExternalForce, &GravityScale, &GravityDirection, Option<&Mass>), + With, + >, +) { + for (mut force, grav_scale, grav_dir, mass) in controllers { + if grav_scale.0 == 0.0 { + continue; + } + let m = if let Some(mass) = mass { mass.0 } else { 1.0 }; + force.apply_force(grav_dir.0 * grav_scale.0 * 9.3 * m); + } +} diff --git a/src/plugins/game.rs b/src/plugins/game.rs index 9c33c8a..52fe3df 100644 --- a/src/plugins/game.rs +++ b/src/plugins/game.rs @@ -7,9 +7,10 @@ use bevy::{ use crate::{ components::{ camera::{CameraPitch, CameraRoot, FollowCam, MainCamera, Unfocused}, - tags::Ship, + character_controller::CharacterController, + tags::{Player, Ship}, }, - plugins::{follow_cam::FollowCamPlugin, free_cam::FreeCamPlugin, ship::ShipPlugin, types::TypesPlugin}, + plugins::*, }; // use bevy_rapier3d::prelude::*; @@ -18,7 +19,7 @@ pub struct GamePlugin; impl Plugin for GamePlugin { fn build(&self, app: &mut bevy::app::App) { - app.add_plugins((FollowCamPlugin, ShipPlugin, TypesPlugin)); + app.add_plugins((FollowCamPlugin, ShipPlugin, TypesPlugin, CharacterControllerPlugin)); app.add_plugins(( PhysicsPlugins::default(), #[cfg(feature = "dev-phys")] @@ -36,10 +37,22 @@ fn setup_scene( mut meshes: ResMut>, mut materials: ResMut>, mut window: Single<&mut Window, With>, + mut gravity: ResMut, ) { + gravity.0 = Vec3::ZERO; + window.cursor_options.visible = false; window.cursor_options.grab_mode = CursorGrabMode::Locked; + commands.spawn(( + Name::new("Player"), + CharacterController::default(), + Player, + Mesh3d(meshes.add(Capsule3d::new(0.5, 1.0))), + MeshMaterial3d(materials.add(Color::linear_rgb(1.0, 0.0, 0.2))), + Transform::from_translation(Vec3::new(0.0, 10.0, 0.0)), + )); + commands.spawn(( Name::new("Camera Root"), Transform::from_xyz(0.0, 1.3, 0.0), diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 7926daf..8af45fb 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,6 +1,17 @@ -pub mod follow_cam; -pub mod free_cam; -pub mod game; -pub mod ship; -pub mod ship_cam; -pub mod types; +mod character_controller; +mod follow_cam; +mod free_cam; +mod game; +mod player; +mod ship; +mod ship_cam; +mod types; + +pub use character_controller::*; +pub use follow_cam::*; +pub use free_cam::*; +pub use game::*; +pub use player::*; +pub use ship::*; +pub use ship_cam::*; +pub use types::*; diff --git a/src/plugins/player.rs b/src/plugins/player.rs new file mode 100644 index 0000000..70f5b58 --- /dev/null +++ b/src/plugins/player.rs @@ -0,0 +1,9 @@ +use bevy::prelude::*; + +pub struct PlayerPlugin; + +impl Plugin for PlayerPlugin { + fn build(&self, app: &mut App) { + todo!() + } +} diff --git a/src/plugins/ship.rs b/src/plugins/ship.rs index 2f29899..bab6b67 100644 --- a/src/plugins/ship.rs +++ b/src/plugins/ship.rs @@ -7,7 +7,7 @@ pub struct ShipPlugin; impl Plugin for ShipPlugin { fn build(&self, app: &mut App) { - app.add_systems(Update, ship_controls); + // app.add_systems(Update, ship_controls); #[cfg(feature = "dev-viz")] app.add_systems(Update, ship_debug); } @@ -26,7 +26,7 @@ fn ship_controls( >, key: Res>, time: Res