working on a kinematic source like character controller based on Project Borealis's open source implementation https://github.com/ProjectBorealis/PBCharacterMovement, also working on re-organizing the src folder
This commit is contained in:
parent
5ebd95ec17
commit
bb8a94f07e
11 changed files with 229 additions and 20 deletions
3
.cargo/config
Normal file
3
.cargo/config
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[target.x86_64-unknown-linux-gnu]
|
||||
linker = "clang"
|
||||
rustflags = ["-Clink-arg=-fuse-ld=mold"]
|
||||
2
.github/workflows/deploy-page.yaml
vendored
2
.github/workflows/deploy-page.yaml
vendored
|
|
@ -18,7 +18,7 @@ jobs:
|
|||
with:
|
||||
toolchain: stable
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev
|
||||
run: apt-get update; apt-get install --no-install-recommends -y pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev
|
||||
- name: Install trunk
|
||||
uses: jetli/trunk-action@v0.4.0
|
||||
with:
|
||||
|
|
|
|||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
|
|
@ -94,7 +94,7 @@ jobs:
|
|||
with:
|
||||
toolchain: stable
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev
|
||||
run: apt-get update; apt-get install -y pkg-config libx11-dev libasound2-dev libudev-dev
|
||||
- name: Build release
|
||||
run: |
|
||||
cargo build --profile dist
|
||||
|
|
@ -180,7 +180,7 @@ jobs:
|
|||
with:
|
||||
toolchain: stable
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev
|
||||
run: apt-get update; apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev
|
||||
- name: Install trunk
|
||||
uses: jetli/trunk-action@v0.4.0
|
||||
with:
|
||||
|
|
|
|||
29
.lapce/run.toml
Normal file
29
.lapce/run.toml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# The run config is used for both run mode and debug mode
|
||||
|
||||
[[configs]]
|
||||
# the name of this task
|
||||
name = "[Dev] Build and run"
|
||||
|
||||
# the type of the debugger. If not set, it can't be debugged but can still be run
|
||||
# type = "lldb"
|
||||
|
||||
# the program to run
|
||||
program = "nix-shell"
|
||||
|
||||
# the program arguments, e.g. args = ["arg1", "arg2"], optional
|
||||
args = ["--run"]
|
||||
|
||||
# current working directory, optional
|
||||
# cwd = "${workspace}"
|
||||
|
||||
# enviroment variables, optional
|
||||
# [configs.env]
|
||||
# VAR1 = "VAL1"
|
||||
# VAR2 = "VAL2"
|
||||
|
||||
# task to run before the run/debug session is started, optional
|
||||
# [configs.prelaunch]
|
||||
# program = "cargo"
|
||||
# args = [
|
||||
# "build",
|
||||
# ]
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.debug.engine": "vadimcn.vscode-lldb",
|
||||
}
|
||||
|
|
@ -23,11 +23,7 @@ use bevy::prelude::*;
|
|||
use bevy_editor_pls::prelude::*;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
|
||||
mod charcacter_controller;
|
||||
mod input_processor;
|
||||
mod player_look;
|
||||
|
||||
use charcacter_controller::*;
|
||||
use character_controller::*;
|
||||
use input_processor::*;
|
||||
use player_look::*;
|
||||
|
||||
|
|
|
|||
3
src/player_utils.rs
Normal file
3
src/player_utils.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
mod character_controller;
|
||||
mod input_processor;
|
||||
mod player_look;
|
||||
|
|
@ -16,15 +16,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html
|
|||
*/
|
||||
|
||||
use crate::{input_processor::*, player_look::*};
|
||||
use bevy::{prelude::*, transform::commands};
|
||||
use bevy_xpbd_3d::{math::*, prelude::*};
|
||||
use bevy::prelude::*;
|
||||
use bevy_xpbd_3d::{math::*, prelude::*, SubstepSchedule, SubstepSet};
|
||||
|
||||
pub struct CharacterControllerPlugin;
|
||||
|
||||
impl Plugin for CharacterControllerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, move_character);
|
||||
app.add_systems(FixedUpdate, (dampen_movement, update_grounded));
|
||||
app.add_systems(FixedUpdate, (apply_gravity, update_grounded, dampen_movement,));
|
||||
app.add_systems(SubstepSchedule, kinematic_controller_collisions.in_set(SubstepSet::SolveUserConstraints));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +39,9 @@ pub struct CharacterController {
|
|||
pub air_control_factor: Scalar,
|
||||
pub movement_dampening_factor: Scalar,
|
||||
pub jump_impulse: Scalar,
|
||||
pub gravity: Vector,
|
||||
pub max_slope_angle: Scalar,
|
||||
pub speed_limit: f32,
|
||||
}
|
||||
|
||||
impl Default for CharacterController {
|
||||
|
|
@ -48,7 +51,9 @@ impl Default for CharacterController {
|
|||
air_control_factor: 0.5,
|
||||
movement_dampening_factor: 0.95,
|
||||
jump_impulse: 4.0,
|
||||
gravity: Vector::NEG_Y * 9.81 * 2.0,
|
||||
max_slope_angle: (45.0 as Scalar).to_radians(),
|
||||
speed_limit: 6668.5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +64,6 @@ pub struct CharacterControllerBundle {
|
|||
rigid_body: RigidBody,
|
||||
collider: Collider,
|
||||
ground_caster: ShapeCaster,
|
||||
locked_axis: LockedAxes,
|
||||
restitution: Restitution,
|
||||
player_look_rotatatable: PlayerLookRotatatable,
|
||||
}
|
||||
|
|
@ -71,7 +75,7 @@ impl CharacterControllerBundle {
|
|||
|
||||
Self {
|
||||
character_controller: controller,
|
||||
rigid_body: RigidBody::Dynamic,
|
||||
rigid_body: RigidBody::Kinematic,
|
||||
collider: controller_collider,
|
||||
ground_caster: ShapeCaster::new(
|
||||
caster_shape,
|
||||
|
|
@ -80,7 +84,6 @@ impl CharacterControllerBundle {
|
|||
Direction3d::NEG_Y,
|
||||
)
|
||||
.with_max_time_of_impact(0.2),
|
||||
locked_axis: LockedAxes::ROTATION_LOCKED,
|
||||
restitution: Restitution::new(0.0).with_combine_rule(CoefficientCombine::Min),
|
||||
player_look_rotatatable: PlayerLookRotatatable,
|
||||
}
|
||||
|
|
@ -97,6 +100,109 @@ impl Default for CharacterControllerBundle {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_lateral_acceleration(
|
||||
controller: &CharacterController,
|
||||
delta_time: f32,
|
||||
velocity: LinearVelocity,
|
||||
) -> Vec3 {
|
||||
// No acceleration in Z
|
||||
let fall_acceleration = Vec3::new(
|
||||
controller.gravity.x,
|
||||
0.0,
|
||||
controller.gravity.y,
|
||||
);
|
||||
|
||||
// Bound acceleration, falling object has minimal ability to impact acceleration
|
||||
let mut acceleration = fall_acceleration;
|
||||
if velocity.length_squared() > 0.0 {
|
||||
acceleration *= controller.air_control_factor;
|
||||
acceleration = acceleration.clamp_length(0.0, controller.movement_acceleration);
|
||||
}
|
||||
|
||||
acceleration
|
||||
}
|
||||
|
||||
|
||||
// function "borrowed" from UE
|
||||
fn point_plane_project(point: Vec3, plane_base: Vec3, plane_normal: Vec3) -> Vec3 {
|
||||
let projection = point - plane_normal * plane_normal.dot(point - plane_base);
|
||||
projection
|
||||
}
|
||||
|
||||
// function "borrowed" from UE
|
||||
fn new_fall_velocity(initial_velocity: LinearVelocity, gravity: Vec3, delta_time: f32, speed_limit: f32)
|
||||
-> Vec3 {
|
||||
|
||||
let mut result: Vec3 = Vec3 { x: initial_velocity.x, y: initial_velocity.y, z: initial_velocity.z };
|
||||
let terminal_velocity: f32 = 50.0;
|
||||
|
||||
if delta_time > 0.0
|
||||
{
|
||||
|
||||
// Apply gravity.
|
||||
result += gravity * delta_time;
|
||||
|
||||
|
||||
// Don't exceed terminal velocity.
|
||||
let terminal_limit: f32 = f32::abs(terminal_velocity); //FMath::Abs(GetPhysicsVolume()->TerminalVelocity);
|
||||
if result.length_squared() > (terminal_limit * terminal_limit)
|
||||
{
|
||||
let gravity_dir: Vec3 = gravity.normalize();
|
||||
|
||||
if result.y > terminal_limit || gravity_dir.y > terminal_limit
|
||||
{
|
||||
result = point_plane_project(result, Vec3::ZERO, gravity_dir) + gravity_dir * terminal_limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.z = f32::clamp(result.z, -speed_limit, speed_limit);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn apply_gravity(
|
||||
time: Res<Time>,
|
||||
mut controllers: Query<(&CharacterController, &mut LinearVelocity)>,
|
||||
) {
|
||||
// Precision is adjusted so that the example works with
|
||||
// both the `f32` and `f64` features. Otherwise you don't need this.
|
||||
let delta_time = time.delta_seconds_f64().adjust_precision();
|
||||
|
||||
for (character_controller, mut linear_velocity) in &mut controllers {
|
||||
|
||||
let fall_acceleration: Vec3 = get_lateral_acceleration(character_controller, delta_time, linear_velocity);
|
||||
fall_acceleration.y = 0.0;
|
||||
let has_limited_air_control: bool = false;
|
||||
|
||||
|
||||
// Ugly fix for mismatch types, find a proper solution later
|
||||
let new_velocity: Vec3 = new_fall_velocity(linear_velocity.clone(), character_controller.gravity, delta_time, character_controller.speed_limit);
|
||||
linear_velocity.x = new_velocity.x;
|
||||
linear_velocity.y = new_velocity.y;
|
||||
linear_velocity.z = new_velocity.z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn calculate_velocity(delta_time: f32, friction: f32, fluid: bool , braking_deceleration: f32, acceleration: Vec3, velocity: LinearVelocity) -> Vec3 {
|
||||
let new_friction: f32 = f32::max(0.0, friction);
|
||||
const MAX_ACCELERATION: f32 = 857.25;
|
||||
let max_speed: f32 = 1714.5;
|
||||
let force_max_acceleration: bool = false;
|
||||
let analog_input_modifier: f32 = 1.0;
|
||||
let analog_speed: f32 = 10.0;
|
||||
|
||||
if(force_max_acceleration){
|
||||
|
||||
acceleration = acceleration.normalize() * MAX_ACCELERATION;
|
||||
|
||||
}
|
||||
|
||||
max_speed = f32::max(max_speed * analog_input_modifier, analog_speed)
|
||||
|
||||
}
|
||||
|
||||
fn update_grounded(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(Entity, &CharacterController, &Rotation, &ShapeHits)>,
|
||||
|
|
@ -113,7 +219,7 @@ fn update_grounded(
|
|||
angle <= character_controller.max_slope_angle
|
||||
});
|
||||
|
||||
println!("is grounded: {}", is_grounded);
|
||||
//println!("is grounded: {}", is_grounded);
|
||||
|
||||
if is_grounded {
|
||||
commands.entity(entity).insert(Grounded);
|
||||
|
|
@ -123,6 +229,78 @@ fn update_grounded(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn kinematic_controller_collisions(
|
||||
collisions: Res<Collisions>,
|
||||
collider_parents: Query<&ColliderParent, Without<Sensor>>,
|
||||
mut character_controllers: Query<
|
||||
(
|
||||
&RigidBody,
|
||||
&mut Position,
|
||||
&Rotation,
|
||||
&mut LinearVelocity,
|
||||
&CharacterController,
|
||||
)
|
||||
>,
|
||||
) {
|
||||
// Iterate through collisions and move the kinematic body to resolve penetration
|
||||
for contacts in collisions.iter() {
|
||||
// If the collision didn't happen during this substep, skip the collision
|
||||
if !contacts.during_current_substep {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the rigid body entities of the colliders (colliders could be children)
|
||||
let Ok([collider_parent1, collider_parent2]) =
|
||||
collider_parents.get_many([contacts.entity1, contacts.entity2])
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Get the body of the character controller and whether it is the first
|
||||
// or second entity in the collision.
|
||||
let is_first: bool;
|
||||
let (rb, mut position, rotation, mut linear_velocity, character_controller) =
|
||||
if let Ok(character) = character_controllers.get_mut(collider_parent1.get()) {
|
||||
is_first = true;
|
||||
character
|
||||
} else if let Ok(character) = character_controllers.get_mut(collider_parent2.get()) {
|
||||
is_first = false;
|
||||
character
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// This system only handles collision response for kinematic character controllers
|
||||
if !rb.is_kinematic() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate through contact manifolds and their contacts.
|
||||
// Each contact in a single manifold shares the same contact normal.
|
||||
for manifold in contacts.manifolds.iter() {
|
||||
let normal = if is_first {
|
||||
-manifold.global_normal1(rotation)
|
||||
} else {
|
||||
-manifold.global_normal2(rotation)
|
||||
};
|
||||
|
||||
// Solve each penetrating contact in the manifold
|
||||
for contact in manifold.contacts.iter().filter(|c| c.penetration > 0.0) {
|
||||
position.0 += normal * contact.penetration;
|
||||
}
|
||||
|
||||
// If the slope isn't too steep to walk on but the character
|
||||
// is falling, reset vertical velocity.
|
||||
if normal.angle_between(Vector::Y).abs() <= character_controller.max_slope_angle
|
||||
&& linear_velocity.y < 0.0
|
||||
{
|
||||
linear_velocity.y = linear_velocity.y.max(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_character(
|
||||
time: Res<Time>,
|
||||
mut movement_event_reader: EventReader<MovementAction>,
|
||||
|
|
@ -15,12 +15,9 @@ You should have received a copy of the GNU General Public License 3.0
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
||||
*/
|
||||
|
||||
use crate::{charcacter_controller, input_processor::*, CharacterController};
|
||||
use crate::input_processor::*;
|
||||
use bevy::{
|
||||
input::{
|
||||
mouse::{MouseButtonInput, MouseMotion, MouseWheel},
|
||||
touchpad::{TouchpadMagnify, TouchpadRotate},
|
||||
},
|
||||
input::mouse::MouseMotion,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
0
src/weapon_framework.rs
Normal file
0
src/weapon_framework.rs
Normal file
Loading…
Reference in a new issue