feat: First iteration of character controller overhaul
parent
50367637b1
commit
50544b06bf
|
|
@ -70,13 +70,13 @@ pub struct KinematicProperties {
|
||||||
impl Default for KinematicProperties {
|
impl Default for KinematicProperties {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ground_speed: 100.0,
|
ground_speed: 75.0,
|
||||||
ground_acceleration: 20.0,
|
ground_acceleration: 20.0,
|
||||||
ground_friction: 30.0,
|
ground_friction: 30.0,
|
||||||
air_speed: 100.0,
|
air_speed: 75.0,
|
||||||
air_acceleration: 10.0,
|
air_acceleration: 10.0,
|
||||||
air_friction: 10.0,
|
air_friction: 10.0,
|
||||||
jump_height: 150.0,
|
jump_height: 100.0,
|
||||||
gravity: Some(1.0),
|
gravity: Some(1.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,6 @@ pub struct KinematicInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kinematic_movement(
|
fn kinematic_movement(
|
||||||
time: Res<Time>,
|
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
&mut KinematicState,
|
&mut KinematicState,
|
||||||
|
|
@ -98,14 +97,22 @@ fn kinematic_movement(
|
||||||
&KinematicProperties,
|
&KinematicProperties,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
Option<&KinematicInput>,
|
Option<&KinematicInput>,
|
||||||
|
Option<&CollisionGroups>,
|
||||||
)>,
|
)>,
|
||||||
shape_query: Query<&Collider, Without<Sensor>>,
|
shape_query: Query<&Collider, Without<Sensor>>,
|
||||||
child_query: Query<&Children>,
|
child_query: Query<&Children>,
|
||||||
mut rapier_context: ResMut<RapierContext>,
|
mut rapier_context: ResMut<RapierContext>,
|
||||||
) {
|
) {
|
||||||
let dt = rapier_context.integration_parameters.dt;
|
let dt = rapier_context.integration_parameters.dt;
|
||||||
for (entity, mut kinematic_state, mut transform, props, global_transform, input) in
|
for (
|
||||||
query.iter_mut()
|
entity,
|
||||||
|
mut kinematic_state,
|
||||||
|
mut transform,
|
||||||
|
props,
|
||||||
|
global_transform,
|
||||||
|
input,
|
||||||
|
collision_groups,
|
||||||
|
) in query.iter_mut()
|
||||||
{
|
{
|
||||||
let default = &KinematicInput::default();
|
let default = &KinematicInput::default();
|
||||||
let input = input.unwrap_or(default);
|
let input = input.unwrap_or(default);
|
||||||
|
|
@ -125,13 +132,21 @@ fn kinematic_movement(
|
||||||
};
|
};
|
||||||
|
|
||||||
const GRAVITY_DIR: Vec2 = Vec2::NEG_Y;
|
const GRAVITY_DIR: Vec2 = Vec2::NEG_Y;
|
||||||
|
const GRAVITY_COEFFICIENT: f32 = 2.0;
|
||||||
|
|
||||||
let current_velocity = kinematic_state
|
let current_velocity = kinematic_state
|
||||||
.last_move
|
.last_move
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(Vec2::ZERO, |last| last.effective_translation) / dt;
|
.map_or(Vec2::ZERO, |last| {
|
||||||
let target_velocity =
|
if last.grounded {
|
||||||
input.movement * speed;
|
last.effective_translation
|
||||||
|
.reject_from_normalized(GRAVITY_DIR)
|
||||||
|
} else {
|
||||||
|
last.effective_translation
|
||||||
|
}
|
||||||
|
})
|
||||||
|
/ dt;
|
||||||
|
let target_velocity = input.movement * speed;
|
||||||
|
|
||||||
let angle_lerp = if current_velocity.length_squared() > 0.01 {
|
let angle_lerp = if current_velocity.length_squared() > 0.01 {
|
||||||
let result = inverse_lerp(
|
let result = inverse_lerp(
|
||||||
|
|
@ -153,11 +168,13 @@ fn kinematic_movement(
|
||||||
let velocity_change_speed = lerp(acceleration, friction, delta_interpolation) * speed;
|
let velocity_change_speed = lerp(acceleration, friction, delta_interpolation) * speed;
|
||||||
|
|
||||||
let mut velocity = if let Some(gravity) = props.gravity {
|
let mut velocity = if let Some(gravity) = props.gravity {
|
||||||
|
// Also apply gravity
|
||||||
move_towards_vec2(
|
move_towards_vec2(
|
||||||
current_velocity,
|
current_velocity,
|
||||||
target_velocity.reject_from_normalized(GRAVITY_DIR) + current_velocity.project_onto_normalized(GRAVITY_DIR),
|
target_velocity.reject_from_normalized(GRAVITY_DIR)
|
||||||
|
+ current_velocity.project_onto_normalized(GRAVITY_DIR),
|
||||||
velocity_change_speed * dt,
|
velocity_change_speed * dt,
|
||||||
) + GRAVITY_DIR * gravity * dt
|
) + GRAVITY_DIR * GRAVITY_COEFFICIENT * gravity
|
||||||
} else {
|
} else {
|
||||||
move_towards_vec2(
|
move_towards_vec2(
|
||||||
current_velocity,
|
current_velocity,
|
||||||
|
|
@ -166,10 +183,6 @@ fn kinematic_movement(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if velocity.y > 0.01 || velocity.y < -0.01 {
|
|
||||||
println!("vertical velocity: {velocity:?}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.want_jump && kinematic_state.can_jump() {
|
if input.want_jump && kinematic_state.can_jump() {
|
||||||
velocity = Vec2 {
|
velocity = Vec2 {
|
||||||
y: props.jump_height,
|
y: props.jump_height,
|
||||||
|
|
@ -188,11 +201,6 @@ fn kinematic_movement(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// gravity
|
|
||||||
if let Some(gravity) = props.gravity {
|
|
||||||
velocity.y += -9.81 * gravity * dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// move
|
// move
|
||||||
kinematic_state.last_move = if let Some(shape) = shape {
|
kinematic_state.last_move = if let Some(shape) = shape {
|
||||||
let (_scale, rotation, translation) = global_transform.to_scale_rotation_translation();
|
let (_scale, rotation, translation) = global_transform.to_scale_rotation_translation();
|
||||||
|
|
@ -208,24 +216,43 @@ fn kinematic_movement(
|
||||||
max_slope_climb_angle: (50.0_f32).to_radians(),
|
max_slope_climb_angle: (50.0_f32).to_radians(),
|
||||||
min_slope_slide_angle: (50.0_f32).to_radians(),
|
min_slope_slide_angle: (50.0_f32).to_radians(),
|
||||||
snap_to_ground: Some(CharacterLength::Absolute(5.0)),
|
snap_to_ground: Some(CharacterLength::Absolute(5.0)),
|
||||||
|
// snap_to_ground: props.gravity.map_or(None, |_| {
|
||||||
|
// if velocity.y <= 0.0 {
|
||||||
|
// Some(CharacterLength::Absolute(5.0))
|
||||||
|
// } else {
|
||||||
|
// None
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
offset: CharacterLength::Absolute(0.01),
|
offset: CharacterLength::Absolute(0.01),
|
||||||
..MoveShapeOptions::default()
|
..MoveShapeOptions::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut filter = QueryFilter::new();
|
||||||
|
let predicate = |coll_entity| coll_entity != entity;
|
||||||
|
filter.predicate = Some(&predicate);
|
||||||
|
|
||||||
|
if let Some(collision_groups) = collision_groups {
|
||||||
|
filter.groups(InteractionGroups::new(
|
||||||
|
bevy_rapier2d::rapier::geometry::Group::from_bits_truncate(
|
||||||
|
collision_groups.memberships.bits(),
|
||||||
|
),
|
||||||
|
bevy_rapier2d::rapier::geometry::Group::from_bits_truncate(
|
||||||
|
collision_groups.filters.bits(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let last_move: MoveShapeOutput = rapier_context.move_shape(
|
let last_move: MoveShapeOutput = rapier_context.move_shape(
|
||||||
velocity * dt,
|
velocity * dt,
|
||||||
// Vec2::X * dt,
|
|
||||||
shape,
|
shape,
|
||||||
translation.truncate(),
|
translation.truncate(),
|
||||||
rotation.to_euler(EulerRot::ZYX).0,
|
rotation.to_euler(EulerRot::ZYX).0,
|
||||||
shape.raw.0.mass_properties(1.0).mass(),
|
shape.raw.0.mass_properties(1.0).mass(),
|
||||||
move_options,
|
move_options,
|
||||||
QueryFilter::new(),
|
filter,
|
||||||
|_coll: CharacterCollision| (),
|
|_coll: CharacterCollision| (),
|
||||||
);
|
);
|
||||||
|
|
||||||
// println!("moved: {moved:?}, diff: {diff:?}", moved=last_move.effective_translation, diff=(velocity * dt - last_move.effective_translation));
|
|
||||||
|
|
||||||
// Apply movement
|
// Apply movement
|
||||||
transform.translation += last_move.effective_translation.extend(0.0);
|
transform.translation += last_move.effective_translation.extend(0.0);
|
||||||
|
|
||||||
|
|
@ -239,24 +266,6 @@ fn kinematic_movement(
|
||||||
if velocity.y <= 0.0 {
|
if velocity.y <= 0.0 {
|
||||||
kinematic_state.did_jump = false;
|
kinematic_state.did_jump = false;
|
||||||
}
|
}
|
||||||
// if let Some(collider) = collider {
|
|
||||||
// let (_, rot, pos) = global_transform.to_scale_rotation_translation();
|
|
||||||
// let angle = rot.to_euler(EulerRot::YXZ).2;
|
|
||||||
// let mut shape = collider.clone();
|
|
||||||
// shape.set_scale(Vec2::ONE * 0.9, 1);
|
|
||||||
// if let Some((_coll_entity, _hit)) = rapier_context.cast_shape(
|
|
||||||
// Vec2::new(pos.x, pos.y),
|
|
||||||
// angle,
|
|
||||||
// Vec2::NEG_Y,
|
|
||||||
// &shape,
|
|
||||||
// 2.0,
|
|
||||||
// QueryFilter::new().exclude_collider(entity),
|
|
||||||
// ) {
|
|
||||||
// kinematic_state.on_ground = true;
|
|
||||||
// } else {
|
|
||||||
// kinematic_state.on_ground = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,6 @@ pub fn player_spawn(mut commands: Commands) {
|
||||||
transform: TransformBundle::from_transform(Transform::from_translation(Vec3::new(
|
transform: TransformBundle::from_transform(Transform::from_translation(Vec3::new(
|
||||||
256.0, 128.0, 0.0,
|
256.0, 128.0, 0.0,
|
||||||
))),
|
))),
|
||||||
properties: KinematicProperties {
|
|
||||||
gravity: None,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
..default()
|
..default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use super::{
|
||||||
local_to_texel_index, texel_index_to_local, Terrain2D, TerrainEvent2D, Texel2D, TexelID,
|
local_to_texel_index, texel_index_to_local, Terrain2D, TerrainEvent2D, Texel2D, TexelID,
|
||||||
NEIGHBOUR_INDEX_MAP,
|
NEIGHBOUR_INDEX_MAP,
|
||||||
};
|
};
|
||||||
use crate::util::{Segment2I, Vector2I};
|
use crate::util::{CollisionLayers, Segment2I, Vector2I};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::{render_resource::Extent3d, texture::ImageSampler},
|
render::{render_resource::Extent3d, texture::ImageSampler},
|
||||||
|
|
@ -574,7 +574,9 @@ pub fn chunk_collision_sync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kinda messy, partly due do how entity creatin is queued
|
// let layer_membership = CollisionLayers::WORLD;
|
||||||
|
|
||||||
|
// REM: Kinda messy, partly due do how entity creation is timed
|
||||||
for (entity, chunk_component) in updated_chunks.iter() {
|
for (entity, chunk_component) in updated_chunks.iter() {
|
||||||
let chunk = terrain.index_to_chunk(&chunk_component.index).unwrap();
|
let chunk = terrain.index_to_chunk(&chunk_component.index).unwrap();
|
||||||
let new_islands = chunk.create_collision_data();
|
let new_islands = chunk.create_collision_data();
|
||||||
|
|
@ -594,6 +596,7 @@ pub fn chunk_collision_sync(
|
||||||
builder
|
builder
|
||||||
.spawn(Collider::polyline(island.clone(), None))
|
.spawn(Collider::polyline(island.clone(), None))
|
||||||
.insert(TransformBundle::default())
|
.insert(TransformBundle::default())
|
||||||
|
.insert(CollisionGroups::new(CollisionLayers::WORLD, Group::ALL))
|
||||||
.insert(Name::new(format!("Island #{}", index)));
|
.insert(Name::new(format!("Island #{}", index)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -605,6 +608,7 @@ pub fn chunk_collision_sync(
|
||||||
builder
|
builder
|
||||||
.spawn(Collider::polyline(island.clone(), None))
|
.spawn(Collider::polyline(island.clone(), None))
|
||||||
.insert(TransformBundle::default())
|
.insert(TransformBundle::default())
|
||||||
|
.insert(CollisionGroups::new(CollisionLayers::WORLD, Group::ALL))
|
||||||
.insert(Name::new(format!("Island #{}", index)));
|
.insert(Name::new(format!("Island #{}", index)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
mod collision_layers;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
mod segment2_i32;
|
mod segment2_i32;
|
||||||
mod vector2;
|
mod vector2;
|
||||||
mod vector2_i32;
|
mod vector2_i32;
|
||||||
|
|
||||||
|
pub use collision_layers::*;
|
||||||
pub use segment2_i32::*;
|
pub use segment2_i32::*;
|
||||||
pub use vector2::*;
|
pub use vector2::*;
|
||||||
pub use vector2_i32::*;
|
pub use vector2_i32::*;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
use bevy_rapier2d::prelude::*;
|
||||||
|
|
||||||
|
pub struct CollisionLayers;
|
||||||
|
|
||||||
|
impl CollisionLayers {
|
||||||
|
pub const WORLD: Group = Group::GROUP_1;
|
||||||
|
pub const PLAYER: Group = Group::GROUP_2;
|
||||||
|
pub const ENEMY: Group = Group::GROUP_3;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue