From 5b77492bc1385efdae4795f8e89f2fb48798ae37 Mon Sep 17 00:00:00 2001 From: chazmm Date: Tue, 8 Sep 2026 17:48:29 +0100 Subject: [PATCH] Add right-stick rotation to the character customiser --- crates/skate-game/src/customiser.rs | 32 +++++++++++++++++++++++------ docs/character-customisation.md | 13 +++++++++--- tools/launch_customiser_test.bat | 13 ++++++------ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/crates/skate-game/src/customiser.rs b/crates/skate-game/src/customiser.rs index 1cef1272..24d70cbf 100644 --- a/crates/skate-game/src/customiser.rs +++ b/crates/skate-game/src/customiser.rs @@ -24,6 +24,7 @@ struct Entry { #[derive(Resource, Default)] pub(crate) struct Navigation { pub pressed: u16, + preview_turn: f32, previous: u16, held_for: f32, repeat_at: f32, @@ -33,6 +34,7 @@ pub(crate) struct Customiser { pub open: bool, pub enabled: bool, just_opened: bool, + preview_yaw: f32, index: Entry, path: Vec, selected: usize, @@ -48,6 +50,7 @@ impl Customiser { self.open = true; self.enabled = true; self.just_opened = true; + self.preview_yaw = 0.; self.path.clear(); self.search.clear(); self.selected = 0; @@ -80,6 +83,10 @@ impl Customiser { .collect() } pub fn preview_camera(&self) -> (f32, f32, f32) { + let (height, distance, yaw) = self.preview_framing(); + (height, distance, yaw + self.preview_yaw) + } + fn preview_framing(&self) -> (f32, f32, f32) { let mut page = &self.index; let mut labels = vec![]; for &i in &self.path { @@ -223,7 +230,7 @@ impl Plugin for CustomiserPlugin { .add_systems(PostStartup, setup) .add_systems( Update, - (interact, crate::customiser_parts::update, preferences, draw) + (interact, rotate_preview, crate::customiser_parts::update, preferences, draw) .chain() .after(crate::graphics_menu::interact) .before(crate::app::FrameSet::Animation), @@ -231,9 +238,11 @@ impl Plugin for CustomiserPlugin { } } fn navigation(mut nav: ResMut, time: Res>, keys: Res>) { - let mut current = (0..4) - .find_map(|i| crate::input::platform::poll(i).ok()) - .map_or(0, |p| { + let pad = (0..4).find_map(|i| crate::input::platform::poll(i).ok()); + // Remap outside the dead zone so a resting stick cannot drift the preview. + let axis = pad.as_ref().map_or(0., |p| (p.state.right[0] as f32 / 32767.).clamp(-1., 1.)); + nav.preview_turn = axis.signum() * ((axis.abs() - 0.24) / 0.76).max(0.); + let mut current = pad.map_or(0, |p| { p.state.buttons | if p.state.left[1] > 16000 { 1 @@ -273,6 +282,14 @@ fn navigation(mut nav: ResMut, time: Res>, keys: Res, nav: Res, time: Res>) { + if state.open && nav.preview_turn != 0. { + // Real time keeps inspection responsive while gameplay is paused. + state.preview_yaw = (state.preview_yaw + nav.preview_turn * 2.0 * time.delta_secs().min(0.1)) + .rem_euclid(std::f32::consts::TAU); + } +} + fn page(label: impl Into, children: Vec) -> Entry { Entry { label: label.into(), @@ -670,6 +687,7 @@ fn setup(mut commands: Commands, config: Res, parts: Res< open: false, enabled, just_opened: false, + preview_yaw: 0., index: menu(&parts.library, extras), path: vec![], selected: 0, @@ -1256,9 +1274,10 @@ fn draw( }); p.spawn(( Text::new(if state.path.is_empty() { - "Done saves your changes and returns to skating." + "Right stick: Rotate • Done saves and resumes." } else { - "↑↓ Browse ←→ Change Type to search" + "↑↓ Browse ←→ Change Right stick: Rotate +Type to search" }), TextFont { font_size: 12., @@ -1340,6 +1359,7 @@ mod tests { open: true, enabled: true, just_opened: false, + preview_yaw: 0., index: menu(&parts.library, extras.clone()), path: vec![], selected: 0, diff --git a/docs/character-customisation.md b/docs/character-customisation.md index 9cb63ef6..8aa1681b 100644 --- a/docs/character-customisation.md +++ b/docs/character-customisation.md @@ -1,7 +1,7 @@ -# Character customiser V3 +# Character customiser V4 -The dedicated local test build uses `Play Character Customiser V3.bat` and -`skate3rust-customiser-v3.exe`. Open Escape/Start, then Character customiser. +The dedicated local test build uses `Play Character Customiser V4.bat` and +`skate3rust-customiser-v4.exe`. Open Escape/Start, then Character customiser. The game and launcher have not been executed for validation; the build is ready for user playtesting. No existing game executable is replaced. @@ -51,6 +51,13 @@ fix still needs the user's visual playtest; no game process was launched here. ## Menu and controls +Right stick left/right rotates the character preview through 360 degrees. A +24% dead zone prevents stick drift; rotation accelerates with stick deflection +up to 2 radians/second. It uses real frame time while gameplay is paused and +keeps the existing automatic close-up framing. The viewing angle resets when +opening the customiser and is not saved into the character profile. V4 retains +the V3 hair and gameplay changes and uses the same prepared library. + Four main sections: **Body, Clothes, Board, Style**. Item lists are alphabetical, fit the available panel height, and can be filtered by typing. Backspace edits a search; Escape/B clears it before going back. Arrows/D-pad/left stick browse, diff --git a/tools/launch_customiser_test.bat b/tools/launch_customiser_test.bat index bb876996..bd3a6972 100644 --- a/tools/launch_customiser_test.bat +++ b/tools/launch_customiser_test.bat @@ -1,8 +1,8 @@ @echo off setlocal cd /d "%~dp0" -if not exist "%~dp0skate3rust-customiser-v3.exe" ( - echo The V3 customiser executable is missing. +if not exist "%~dp0skate3rust-customiser-v4.exe" ( + echo The V4 customiser executable is missing. pause exit /b 1 ) @@ -11,16 +11,17 @@ if not exist "%~dp0assets\private\customisation\library-v3.json" ( pause exit /b 1 ) -echo Skate 3 Character Customiser V3 +echo Skate 3 Character Customiser V4 echo Open Escape / Start, then Character customiser. echo Browse with arrows / D-pad. Change with left-right or the minus-plus buttons. +echo Right stick rotates your character. echo Type to search an item list. Escape / B goes back. Done saves and resumes. -echo Diagnostic output: customiser-v3-session.log -"%~dp0skate3rust-customiser-v3.exe" --assets "%~dp0assets" --map "%~dp0maps\SkateSchool.skate" > "%~dp0customiser-v3-session.log" 2>&1 +echo Diagnostic output: customiser-v4-session.log +"%~dp0skate3rust-customiser-v4.exe" --assets "%~dp0assets" --map "%~dp0maps\SkateSchool.skate" > "%~dp0customiser-v4-session.log" 2>&1 set "skate_customiser_exit=%errorlevel%" if not "%skate_customiser_exit%"=="0" ( echo The test exited with code %skate_customiser_exit%. - echo See %~dp0customiser-v3-session.log + echo See %~dp0customiser-v4-session.log pause ) exit /b %skate_customiser_exit%