Kinematic character controller (#11)
This commit is contained in:
@@ -2,18 +2,21 @@ use crate::{
|
||||
alien::{ALIEN_ASSET_PATH, Animations},
|
||||
camera::{CameraArmRotation, CameraTarget},
|
||||
cash::{Cash, CashCollectEvent},
|
||||
controller::{CharacterControllerBundle, PlayerMovement},
|
||||
controls::Controls,
|
||||
heads_ui::HeadChanged,
|
||||
physics_layers::GameLayer,
|
||||
tb_entities::SpawnPoint,
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use avian3d::{
|
||||
math::{Scalar, Vector},
|
||||
prelude::*,
|
||||
};
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
window::{CursorGrabMode, PrimaryWindow},
|
||||
};
|
||||
use bevy_tnua::{TnuaUserControlsSystemSet, prelude::*};
|
||||
use bevy_tnua_avian3d::TnuaAvian3dSensorShape;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
@@ -33,14 +36,8 @@ struct PlayerSpawned {
|
||||
spawned: bool,
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct PlayerMovement {
|
||||
pub any_direction: bool,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.init_resource::<PlayerSpawned>();
|
||||
app.init_resource::<PlayerMovement>();
|
||||
app.add_systems(Startup, (initial_grab_cursor, cursor_recenter));
|
||||
app.add_systems(
|
||||
Update,
|
||||
@@ -51,10 +48,6 @@ pub fn plugin(app: &mut App) {
|
||||
setup_animations_marker_for_player,
|
||||
),
|
||||
);
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
apply_controls.in_set(TnuaUserControlsSystemSet),
|
||||
);
|
||||
|
||||
app.add_systems(Update, (rotate_view_keyboard, rotate_view_gamepad));
|
||||
|
||||
@@ -80,21 +73,28 @@ fn spawn(
|
||||
let mesh = asset_server
|
||||
.load(GltfAssetLabel::Scene(0).from_asset("models/heads/angry demonstrator.glb"));
|
||||
|
||||
let gravity = Vector::NEG_Y * 40.0;
|
||||
let collider = Collider::capsule(0.9, 1.2);
|
||||
let acceleration = 30.0;
|
||||
let damping = 0.95;
|
||||
let jump_impulse = 18.0;
|
||||
let max_slope_angle = (60.0 as Scalar).to_radians();
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
Name::from("player"),
|
||||
Player,
|
||||
CameraTarget,
|
||||
transform,
|
||||
TransformInterpolation,
|
||||
TransformExtrapolation,
|
||||
Visibility::default(),
|
||||
RigidBody::Dynamic,
|
||||
Collider::capsule(1.2, 1.5),
|
||||
// LockedAxes::ROTATION_LOCKED, todo
|
||||
CollisionLayers::new(LayerMask(GameLayer::Player.to_bits()), LayerMask::ALL),
|
||||
LockedAxes::ROTATION_LOCKED,
|
||||
TnuaController::default(),
|
||||
TnuaAvian3dSensorShape(Collider::cylinder(0.8, 0.0)),
|
||||
CharacterControllerBundle::new(collider, gravity).with_movement(
|
||||
acceleration,
|
||||
damping,
|
||||
jump_impulse,
|
||||
max_slope_angle,
|
||||
),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent
|
||||
@@ -102,7 +102,7 @@ fn spawn(
|
||||
Name::from("body rig"),
|
||||
PlayerRig,
|
||||
CameraArmRotation,
|
||||
Transform::from_translation(Vec3::new(0., -3., 0.))
|
||||
Transform::from_translation(Vec3::new(0., -1.45, 0.))
|
||||
.with_rotation(Quat::from_rotation_y(std::f32::consts::PI))
|
||||
.with_scale(Vec3::splat(1.4)),
|
||||
SceneRoot(
|
||||
@@ -187,57 +187,6 @@ fn initial_grab_cursor(mut primary_window: Query<&mut Window, With<PrimaryWindow
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_controls(
|
||||
controls: Res<Controls>,
|
||||
mut query: Query<&mut TnuaController>,
|
||||
player: Query<&Transform, With<PlayerRig>>,
|
||||
mut movement: ResMut<PlayerMovement>,
|
||||
) {
|
||||
let Ok(mut controller) = query.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(player) = player.get_single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut direction = player.forward().as_vec3() * -controls.keyboard_state.move_dir.y;
|
||||
direction += player.right().as_vec3() * -controls.keyboard_state.move_dir.x;
|
||||
|
||||
if let Some(gamepad) = controls.gamepad_state {
|
||||
direction += player.forward().as_vec3() * -gamepad.move_dir.y;
|
||||
direction += player.right().as_vec3() * -gamepad.move_dir.x;
|
||||
}
|
||||
|
||||
if movement.any_direction != (direction != Vec3::ZERO) {
|
||||
movement.any_direction = direction != Vec3::ZERO;
|
||||
}
|
||||
|
||||
controller.basis(TnuaBuiltinWalk {
|
||||
// The `desired_velocity` determines how the character will move.
|
||||
desired_velocity: direction.normalize_or_zero() * 15.0,
|
||||
spring_strengh: 1000.,
|
||||
spring_dampening: 0.5,
|
||||
// The `float_height` must be greater (even if by little) from the distance between the
|
||||
// character's center and the lowest point of its collider.
|
||||
float_height: 3.0,
|
||||
// `TnuaBuiltinWalk` has many other fields for customizing the movement - but they have
|
||||
// sensible defaults. Refer to the `TnuaBuiltinWalk`'s documentation to learn what they do.
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Feed the jump action every frame as long as the player holds the jump button. If the player
|
||||
// stops holding the jump button, simply stop feeding the action.
|
||||
if controls.keyboard_state.jump || controls.gamepad_state.map(|gp| gp.jump).unwrap_or(false) {
|
||||
controller.action(TnuaBuiltinJump {
|
||||
// The height is the only mandatory field of the jump button.
|
||||
height: 4.0,
|
||||
// `TnuaBuiltinJump` also has customization fields with sensible defaults.
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_cash(
|
||||
mut commands: Commands,
|
||||
mut collision_event_reader: EventReader<CollisionStarted>,
|
||||
@@ -264,7 +213,7 @@ fn setup_animations_marker_for_player(
|
||||
mut commands: Commands,
|
||||
animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
|
||||
parent_query: Query<&Parent>,
|
||||
player: Query<&Player>,
|
||||
player: Query<&PlayerRig>,
|
||||
) {
|
||||
for entity in animation_handles.iter() {
|
||||
for ancestor in parent_query.iter_ancestors(entity) {
|
||||
|
||||
Reference in New Issue
Block a user