Fix floating when hitting the ceiling and slope slowdown

This commit is contained in:
Jens Pitkänen 2026-07-26 20:09:51 +03:00
parent 5e35fff70c
commit 0c87c2fe19

View File

@ -1,6 +1,10 @@
use std::{collections::HashSet, f32::consts::PI, ops::Neg}; use std::{collections::HashSet, f32::consts::PI, ops::Neg};
use godot::{classes::ICharacterBody3D, obj::WithBaseField, prelude::*}; use godot::{
classes::{ICharacterBody3D, KinematicCollision3D},
obj::WithBaseField,
prelude::*,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -70,25 +74,56 @@ pub trait PlayerCommon {
impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerCommon for T { impl<T: IPlayer + ICharacterBody3D + WithBaseField> PlayerCommon for T {
fn player_update(&mut self, delta: f64) { fn player_update(&mut self, delta: f64) {
// Gravity
self.set_y_speed(self.get_y_speed() - 9.8 * delta as f32); self.set_y_speed(self.get_y_speed() - 9.8 * delta as f32);
if self.base().is_on_floor() && self.get_y_speed() < 0. { if self.base().is_on_floor() && self.get_y_speed() < 0. {
self.set_y_speed(0.); self.set_y_speed(0.);
} }
let vertical_velocity = Vector3::UP * self.get_y_speed();
let speed = Vector3::UP * self.get_y_speed() // Figure out the ground plane normal (to move on the right plane)
+ self let mut up_vector = Vector3::UP;
.get_movement_dir() let mut most_upward_coll: Option<Gd<KinematicCollision3D>> = None;
.try_normalized() for coll in (0..self.base().get_slide_collision_count())
.unwrap_or_default() .flat_map(|i| self.base().get_slide_collision(i))
.rotated(Vector3::UP, self.base().get_rotation().y) {
* self.get_move_speed(); let coll_norm_y = coll.get_normal().y;
if let Some(prev_most_upward) = &most_upward_coll {
if coll_norm_y > prev_most_upward.get_normal().y {
most_upward_coll = Some(coll);
}
} else if coll_norm_y > 0.5 {
// sin(30 deg) == 0.5, so this only accepts 30 deg or shallower slopes
most_upward_coll = Some(coll);
}
}
if let Some(coll) = most_upward_coll {
up_vector = coll.get_normal();
}
// Ground movement (along the plane the player is currently on)
let input_move_on_xz = self.get_movement_dir().try_normalized().unwrap_or_default();
let ground_plane_basis = Basis::from_axis_angle(up_vector, self.base().get_rotation().y);
let input_move_on_ground_plane = ground_plane_basis * input_move_on_xz;
let move_speed = self.get_move_speed();
let ground_plane_velocity = input_move_on_ground_plane * move_speed;
// Commit
if self.is_dead() { if self.is_dead() {
self.base_mut().set_velocity(Vector3::ZERO); self.base_mut().set_velocity(Vector3::ZERO);
} else { } else {
self.base_mut().set_velocity(speed); self.base_mut()
.set_velocity(vertical_velocity + ground_plane_velocity);
self.base_mut().move_and_slide(); self.base_mut().move_and_slide();
} }
// Check if hitting a ceiling; if so, stop upwards velocity (done here instead of
// at the start of the next frame to get the right-after-move_and_slide state
// rather than the simulated one).
if self.base().get_velocity().y < 0.001 && self.get_y_speed() > 0.001 {
self.set_y_speed(0.0);
}
} }
fn try_jump(&mut self) -> bool { fn try_jump(&mut self) -> bool {