mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-26 19:47:44 +00:00
Use camera-relative offboard input and capture airborne bail diagnostics
This commit is contained in:
1 parent
375192e795
commit
0a3904cd00
8 files changed
+94
-3
No files matched your search
@@ -87,6 +87,14 @@ pub fn produce_analog(
|
||||
) -> [OffboardIntent; 4] {
|
||||
let words = controller.words();
|
||||
let stick = [f32::from_bits(words[7]), 0.0, f32::from_bits(words[8]), 0.0];
|
||||
produce_analog_world(stick, observation)
|
||||
}
|
||||
|
||||
/// Host camera conversion precedes the native world-space Biped attributes.
|
||||
pub fn produce_analog_world(
|
||||
stick: [f32; 4],
|
||||
observation: AnalogObservation,
|
||||
) -> [OffboardIntent; 4] {
|
||||
let mut horizontal = observation.effective_skeleton_z;
|
||||
horizontal[1] = 0.0;
|
||||
let forward = safe_unit(horizontal);
|
||||
|
||||
@@ -268,7 +268,7 @@ pub(crate) fn advance(
|
||||
//Native Listener::Fill825999F0, after derived controller sampling and before AG.
|
||||
let mut action_intents = controls.action_intents.clone();
|
||||
use skate_core::input::offboard_intentions::{
|
||||
AnalogObservation, produce_analog, produce_discrete,
|
||||
AnalogObservation, produce_analog_world, produce_discrete,
|
||||
};
|
||||
let biped = &skater.offboard.controller.state.contact;
|
||||
for intent in produce_discrete(
|
||||
@@ -277,8 +277,11 @@ pub(crate) fn advance(
|
||||
physical.air.use_air_reckoning_452 != 0,
|
||||
)
|
||||
.into_iter()
|
||||
.chain(produce_analog(
|
||||
&controls.controller,
|
||||
.chain(produce_analog_world(
|
||||
controls.offboard_direction.unwrap_or_else(|| {
|
||||
let words = controls.controller.words();
|
||||
[f32::from_bits(words[7]), 0., f32::from_bits(words[8]), 0.]
|
||||
}),
|
||||
AnalogObservation {
|
||||
effective_skeleton_z: p.effective_anim_transform_192[2].map(f32::from_bits),
|
||||
biped_correction: biped.active.then_some(biped.direction),
|
||||
|
||||
@@ -12,6 +12,7 @@ use skate_core::input::{
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct PlayerControls {
|
||||
pub controller: DerivedControllerInput,
|
||||
pub offboard_direction: Option<[f32; 4]>,
|
||||
pub intents: Vec<RidingIntent>,
|
||||
pub action_intents: IntentMap,
|
||||
pub ticks: u64,
|
||||
@@ -27,6 +28,7 @@ impl Default for PlayerControls {
|
||||
controller.initialize();
|
||||
Self {
|
||||
controller,
|
||||
offboard_direction: None,
|
||||
intents: Vec::new(),
|
||||
action_intents: IntentMap::new(),
|
||||
ticks: 0,
|
||||
@@ -43,6 +45,7 @@ pub(super) fn sample(
|
||||
input: Res<ControllerInput>,
|
||||
physics: Res<GamePhysics>,
|
||||
skater: Res<SkaterRuntime>,
|
||||
camera: Res<crate::camera::CameraRuntime>,
|
||||
mut player: ResMut<PlayerControls>,
|
||||
) {
|
||||
let mut map = input.player_actions();
|
||||
@@ -52,6 +55,18 @@ pub(super) fn sample(
|
||||
physics.settings.input_magnitude_threshold,
|
||||
skater.player_input.physical.scoring.capabilities_204,
|
||||
);
|
||||
if let Some(frame) = camera.frame {
|
||||
let at = Vec3::from_array(frame.basis.columns[2]);
|
||||
let forward = Vec3::new(at.x, 0., at.z).normalize_or_zero();
|
||||
if forward != Vec3::ZERO {
|
||||
// Match camera::present: native camera Right becomes Bevy -Right.
|
||||
let right = forward.cross(Vec3::Y);
|
||||
let words = player.controller.words();
|
||||
let direction = right * f32::from_bits(words[7])
|
||||
+ forward * f32::from_bits(words[8]);
|
||||
player.offboard_direction = Some([direction.x, 0., direction.z, 0.]);
|
||||
}
|
||||
}
|
||||
player.publish_gestures(physics.animation_profile.physics_mode, skater.player_input.physical.state.state_16);
|
||||
}
|
||||
|
||||
@@ -69,6 +84,7 @@ impl PlayerControls {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, map: &mut impl ActionMap, dt: f32, magnitude_threshold: f32, physical_capabilities: u32) {
|
||||
self.offboard_direction = None;
|
||||
self.controller.update(
|
||||
map,
|
||||
dt,
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
//! Bounded numeric history for user-reproduced airborne bails. No per-frame IO.
|
||||
use crate::physics::SkaterRuntime;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)] // Fields are serialized together only when a bail is requested.
|
||||
struct Sample {
|
||||
tick: i32,
|
||||
flags: [u32; 3],
|
||||
packet: skate_core::player::offboard::air_prediction::Packet,
|
||||
root: [[f32; 4]; 4],
|
||||
root_velocity: [f32; 4],
|
||||
body: [f32; 4],
|
||||
lift: f32,
|
||||
extra: [[f32; 4]; 2],
|
||||
maximum_error: Option<f32>,
|
||||
reasons: [bool; 34],
|
||||
}
|
||||
|
||||
pub(crate) struct History {
|
||||
samples: VecDeque<Sample>,
|
||||
reports: usize,
|
||||
}
|
||||
impl Default for History {
|
||||
fn default() -> Self {
|
||||
Self { samples: VecDeque::with_capacity(32), reports: 0 }
|
||||
}
|
||||
}
|
||||
impl History {
|
||||
pub(crate) fn clear(&mut self) { self.samples.clear(); }
|
||||
}
|
||||
|
||||
pub(crate) fn record(skater: &mut SkaterRuntime) {
|
||||
let history = &mut skater.offboard.air_diagnostics;
|
||||
if history.reports >= 3 { return; }
|
||||
let state = &skater.offboard.air_state;
|
||||
let p = &skater.player_input.processed;
|
||||
if history.samples.len() == 32 { history.samples.pop_front(); }
|
||||
history.samples.push_back(Sample {
|
||||
tick: state.tick,
|
||||
flags: [p.flags_2476, p.flags_2480, p.flags_2484],
|
||||
packet: state.packet,
|
||||
root: skater.animated_skeleton.roots.animation_to_world,
|
||||
root_velocity: skater.skeleton_input.root_velocity,
|
||||
body: state.body_position,
|
||||
lift: state.lift,
|
||||
extra: skater.collision_extra_displacements,
|
||||
maximum_error: skater.collision_maximum_error,
|
||||
reasons: skater.wipeout.state.reasons,
|
||||
});
|
||||
if skater.wipeout.state.reasons.iter().any(|&requested| requested) {
|
||||
// Format in memory, then one write; never send Debug's individual
|
||||
// formatting fragments to the Windows redirected stderr handle.
|
||||
let report = format!("SKATE_BIPED_AIR_BAIL history={:#?}", history.samples);
|
||||
eprintln!("{report}");
|
||||
history.samples.clear();
|
||||
history.reports += 1;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ fn effective(skater: &SkaterRuntime) -> Frame {
|
||||
frame
|
||||
}
|
||||
pub(crate) fn enter(physics: &mut GamePhysics, skater: &mut SkaterRuntime) -> Result<(), String> {
|
||||
skater.offboard.air_diagnostics.clear();
|
||||
let frame = effective(skater);
|
||||
let p = &skater.player_input.processed;
|
||||
let body = skater.animated_skeleton.board_frames.com_frame[3];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Off-board game adapters over the shared physical and scene owners.
|
||||
pub(crate) mod air_state;
|
||||
pub(crate) mod air_diagnostics;
|
||||
pub(crate) mod air_trajectory;
|
||||
pub(crate) mod board_effects;
|
||||
pub(crate) mod contact_queries;
|
||||
|
||||
@@ -10,6 +10,7 @@ use skate_core::point_graph::PointGraph;
|
||||
use skate_data::{animation_metadata::AnimationMetadata, collections::Collections};
|
||||
|
||||
pub(crate) struct Runtime {
|
||||
pub air_diagnostics: super::air_diagnostics::History,
|
||||
pub air_state: skate_core::player::offboard::air_state::State,
|
||||
pub air_prediction: Option<skate_core::player::offboard::air_prediction::Prediction>,
|
||||
pub air_blend_curve: PointGraph<8>,
|
||||
@@ -148,6 +149,7 @@ impl Runtime {
|
||||
pub(crate) fn load(data: &Collections, metadata: &AnimationMetadata) -> Result<Self, String> {
|
||||
let settings = super::settings::Settings::load(data, metadata)?;
|
||||
Ok(Self {
|
||||
air_diagnostics: Default::default(),
|
||||
air_state: Default::default(),
|
||||
air_prediction: None,
|
||||
air_blend_curve: settings.air_blend_curve,
|
||||
|
||||
@@ -117,6 +117,7 @@ pub(super) fn check_after_physics(
|
||||
],
|
||||
},
|
||||
);
|
||||
super::offboard::air_diagnostics::record(skater);
|
||||
Ok(())
|
||||
}
|
||||
PhysicalStateId::PhysicsGround | PhysicalStateId::SlideGround => {
|
||||
|
||||
Reference in new issue
Block a user