rapier
This commit is contained in:
@@ -30,8 +30,8 @@ pub(crate) struct AudioAssets {}
|
||||
|
||||
#[derive(AssetCollection, Resource, Clone)]
|
||||
pub(crate) struct GltfAssets {
|
||||
#[asset(key = "wall")]
|
||||
pub(crate) wall: Handle<Gltf>,
|
||||
// #[asset(key = "wall")]
|
||||
// pub(crate) wall: Handle<Gltf>,
|
||||
}
|
||||
|
||||
#[derive(AssetCollection, Resource, Clone)]
|
||||
|
||||
@@ -11,7 +11,7 @@ fn spawn_level(
|
||||
models: Res<Assets<Gltf>>,
|
||||
gltf_assets: Res<GltfAssets>
|
||||
) {
|
||||
let gltf = models.get(&gltf_assets.wall).unwrap();
|
||||
// let gltf = models.get(&gltf_assets.wall).unwrap();
|
||||
|
||||
commands.spawn(SceneRoot(gltf.scenes[0].clone()));
|
||||
// commands.spawn(SceneRoot(gltf.scenes[0].clone()));
|
||||
}
|
||||
|
||||
11
src/main.rs
11
src/main.rs
@@ -1,11 +1,11 @@
|
||||
use asset_loading::ImageAssets;
|
||||
use bevy::prelude::*;
|
||||
use bevy_rapier3d::prelude::*;
|
||||
|
||||
mod asset_loading;
|
||||
mod bevy_plugin;
|
||||
mod level_instantiation;
|
||||
mod main_menu;
|
||||
mod physics;
|
||||
mod player;
|
||||
|
||||
fn main() {
|
||||
@@ -13,10 +13,11 @@ fn main() {
|
||||
.add_plugins((
|
||||
bevy_plugin::plugin,
|
||||
asset_loading::plugin,
|
||||
physics::plugin,
|
||||
level_instantiation::map_plugin,
|
||||
player::plugin,
|
||||
main_menu::plugin,
|
||||
RapierPhysicsPlugin::<NoUserData>::default(),
|
||||
RapierDebugRenderPlugin::default(),
|
||||
))
|
||||
.init_state::<GameState>()
|
||||
.add_systems(OnEnter(GameState::Playing), setup)
|
||||
@@ -49,11 +50,17 @@ fn setup(
|
||||
MeshMaterial3d(materials.add(Color::WHITE)),
|
||||
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
|
||||
));
|
||||
commands.spawn((
|
||||
RigidBody::Fixed,
|
||||
Collider::cylinder(0.1, 4.0)
|
||||
));
|
||||
// cube
|
||||
commands.spawn((
|
||||
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
|
||||
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
|
||||
Transform::from_xyz(3.0, 0.5, 0.0),
|
||||
RigidBody::Fixed,
|
||||
Collider::cuboid(0.5, 0.5, 0.5),
|
||||
));
|
||||
// light
|
||||
commands.spawn((
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut)]
|
||||
pub struct Velocity(pub Vec3);
|
||||
|
||||
/// The actual position of the player in the physics simulation.
|
||||
/// This is separate from the `Transform`, which is merely a visual representation.
|
||||
///
|
||||
/// If you want to make sure that this component is always initialized
|
||||
/// with the same value as the `Transform`'s translation, you can
|
||||
/// use a [component lifecycle hook](https://docs.rs/bevy/0.14.0/bevy/ecs/component/struct.ComponentHooks.html)
|
||||
#[derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut)]
|
||||
pub struct PhysicalTranslation(pub Vec3);
|
||||
|
||||
/// The value [`PhysicalTranslation`] had in the last fixed timestep.
|
||||
/// Used for interpolation in the `interpolate_rendered_transform` system.
|
||||
#[derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut)]
|
||||
pub struct PreviousPhysicalTranslation(pub Vec3);
|
||||
|
||||
#[derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut)]
|
||||
pub struct AccumulatedInput(pub Vec3);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
RunFixedMainLoop,
|
||||
interpolate_rendered_transform.in_set(RunFixedMainLoopSystem::AfterFixedMainLoop),
|
||||
)
|
||||
.add_systems(FixedUpdate, step);
|
||||
}
|
||||
|
||||
pub fn interpolate_rendered_transform(
|
||||
fixed_time: Res<Time<Fixed>>,
|
||||
mut query: Query<(
|
||||
&mut Transform,
|
||||
&PhysicalTranslation,
|
||||
&PreviousPhysicalTranslation,
|
||||
)>,
|
||||
) {
|
||||
for (mut transform, current_physical_translation, previous_physical_translation) in
|
||||
query.iter_mut()
|
||||
{
|
||||
let previous = previous_physical_translation.0;
|
||||
let current = current_physical_translation.0;
|
||||
// The overstep fraction is a value between 0 and 1 that tells us how far we are between two fixed timesteps.
|
||||
let alpha = fixed_time.overstep_fraction();
|
||||
|
||||
let rendered_translation = previous.lerp(current, alpha);
|
||||
transform.translation = rendered_translation;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(
|
||||
fixed_time: Res<Time<Fixed>>,
|
||||
mut query: Query<(
|
||||
&mut PhysicalTranslation,
|
||||
&mut PreviousPhysicalTranslation,
|
||||
&mut AccumulatedInput,
|
||||
&Velocity,
|
||||
)>,
|
||||
) {
|
||||
for (
|
||||
mut current_physical_translation,
|
||||
mut previous_physical_translation,
|
||||
mut input,
|
||||
velocity,
|
||||
) in query.iter_mut()
|
||||
{
|
||||
previous_physical_translation.0 = current_physical_translation.0;
|
||||
current_physical_translation.0 += velocity.0 * fixed_time.delta_secs();
|
||||
input.0 = Vec3::ZERO;
|
||||
}
|
||||
}
|
||||
124
src/player.rs
124
src/player.rs
@@ -1,11 +1,10 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
physics::{AccumulatedInput, PhysicalTranslation, PreviousPhysicalTranslation, Velocity},
|
||||
};
|
||||
use bevy::{
|
||||
input::mouse::AccumulatedMouseMotion, pbr::NotShadowCaster, prelude::*,
|
||||
render::view::RenderLayers, window::PrimaryWindow,
|
||||
};
|
||||
use bevy_rapier3d::prelude::*;
|
||||
|
||||
use crate::GameState;
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Player;
|
||||
@@ -21,24 +20,24 @@ impl Default for CameraSensitivity {
|
||||
#[derive(Debug, Component)]
|
||||
struct WorldModelCamera;
|
||||
|
||||
#[derive(Debug, Component, Default)]
|
||||
pub struct PlayerInput {
|
||||
movement_direction: Vec3,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(OnEnter(GameState::Playing), (init_player, hide_cursor))
|
||||
.add_systems(Update, move_camera.run_if(in_state(GameState::Playing)))
|
||||
.add_systems(
|
||||
RunFixedMainLoop,
|
||||
handle_input
|
||||
.in_set(RunFixedMainLoopSystem::BeforeFixedMainLoop)
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
Update,
|
||||
(move_camera, handle_input).run_if(in_state(GameState::Playing)),
|
||||
)
|
||||
.add_systems(
|
||||
FixedUpdate,
|
||||
apply_player_movement.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
|
||||
/// Used implicitly by all entities without a `RenderLayers` component.
|
||||
/// Our world model camera and all objects other than the player are on this layer.
|
||||
/// The light source belongs to both layers.
|
||||
// const DEFAULT_RENDER_LAYER: usize = 0;
|
||||
|
||||
/// Used by the view model camera and the player's arm.
|
||||
/// The light source belongs to both layers.
|
||||
/// used by the view model camera and the player's arm.
|
||||
const STATIC_LAYER: usize = 1;
|
||||
|
||||
pub fn init_player(
|
||||
@@ -53,12 +52,23 @@ pub fn init_player(
|
||||
.spawn((
|
||||
Player,
|
||||
CameraSensitivity::default(),
|
||||
PlayerInput::default(),
|
||||
|
||||
// rapier
|
||||
RigidBody::Dynamic,
|
||||
Collider::capsule(Vec3::new(0.0, -0.5, 0.0), Vec3::new(0.0, 0.5, 0.0), 0.5),
|
||||
Velocity::zero(),
|
||||
LockedAxes::ROTATION_LOCKED,
|
||||
Damping {
|
||||
linear_damping: 6.0,
|
||||
angular_damping: 1.0,
|
||||
},
|
||||
|
||||
Transform::from_xyz(0.0, 1.0, 0.0),
|
||||
GlobalTransform::default(),
|
||||
Visibility::default(),
|
||||
AccumulatedInput::default(),
|
||||
Velocity::default(),
|
||||
PhysicalTranslation(Vec3::new(0.0, 1.0, 0.0)),
|
||||
PreviousPhysicalTranslation(Vec3::new(0.0, 1.0, 0.0)),
|
||||
InheritedVisibility::default(),
|
||||
ViewVisibility::default(),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
@@ -68,11 +78,14 @@ pub fn init_player(
|
||||
fov: 90.0_f32.to_radians(),
|
||||
..default()
|
||||
}),
|
||||
Transform::default(),
|
||||
GlobalTransform::default(),
|
||||
Visibility::default(),
|
||||
InheritedVisibility::default(),
|
||||
ViewVisibility::default(),
|
||||
));
|
||||
|
||||
// we use a second layer ("framebuffer") to draw the player's arm on
|
||||
// there also a second camera that only views this buffer
|
||||
// this makes it easy because the second camera doesn't move with the player
|
||||
// camera voor pitslampke
|
||||
parent.spawn((
|
||||
Camera3d::default(),
|
||||
Camera {
|
||||
@@ -84,19 +97,29 @@ pub fn init_player(
|
||||
..default()
|
||||
}),
|
||||
RenderLayers::layer(STATIC_LAYER),
|
||||
Transform::default(),
|
||||
GlobalTransform::default(),
|
||||
Visibility::default(),
|
||||
InheritedVisibility::default(),
|
||||
ViewVisibility::default(),
|
||||
));
|
||||
|
||||
// pitslampke
|
||||
parent.spawn((
|
||||
Mesh3d(arm),
|
||||
MeshMaterial3d(arm_material),
|
||||
Transform::from_xyz(0.2, -0.1, -0.25),
|
||||
GlobalTransform::default(),
|
||||
RenderLayers::layer(STATIC_LAYER),
|
||||
NotShadowCaster,
|
||||
Visibility::default(),
|
||||
InheritedVisibility::default(),
|
||||
ViewVisibility::default(),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
fn hide_cursor(mut windows: Query<&mut Window, With<PrimaryWindow>>) {
|
||||
// Query returns one window typically.
|
||||
for mut window in windows.iter_mut() {
|
||||
window.cursor_options.visible = false;
|
||||
}
|
||||
@@ -126,35 +149,56 @@ pub fn move_camera(
|
||||
|
||||
pub fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut query: Query<(&Transform, &mut AccumulatedInput, &mut Velocity), With<Player>>,
|
||||
mut query: Query<(&Transform, &mut PlayerInput), With<Player>>,
|
||||
) {
|
||||
const SPEED: f32 = 2.0;
|
||||
for (transform, mut input, mut velocity) in query.iter_mut() {
|
||||
let forward = transform.forward(); // Forward direction (z axis)
|
||||
let right = transform.right(); // Right direction (x axis)
|
||||
for (transform, mut input) in query.iter_mut() {
|
||||
let forward = transform.forward();
|
||||
let right = transform.right();
|
||||
let mut movement_direction = Vec3::ZERO;
|
||||
|
||||
if keyboard_input.pressed(KeyCode::KeyW) {
|
||||
input.x += forward.x;
|
||||
input.z += forward.z;
|
||||
movement_direction += Vec3::new(forward.x, 0.0, forward.z).normalize_or_zero();
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyS) {
|
||||
input.x -= forward.x;
|
||||
input.z -= forward.z;
|
||||
movement_direction -= Vec3::new(forward.x, 0.0, forward.z).normalize_or_zero();
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyA) {
|
||||
input.x -= right.x;
|
||||
input.z -= right.z;
|
||||
movement_direction -= Vec3::new(right.x, 0.0, right.z).normalize_or_zero();
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyD) {
|
||||
input.x += right.x;
|
||||
input.z += right.z;
|
||||
movement_direction += Vec3::new(right.x, 0.0, right.z).normalize_or_zero();
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::Space) {
|
||||
input.y += 1.0;
|
||||
movement_direction += Vec3::Y;
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::ShiftLeft) {
|
||||
input.y -= 1.0;
|
||||
movement_direction -= Vec3::Y;
|
||||
}
|
||||
velocity.0 = input.normalize_or_zero() * SPEED;
|
||||
|
||||
input.movement_direction = movement_direction.normalize_or_zero();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_player_movement(
|
||||
mut player_query: Query<(&PlayerInput, &mut Velocity), With<Player>>,
|
||||
) {
|
||||
const SPEED: f32 = 3.0;
|
||||
const JUMP_FORCE: f32 = 4.0;
|
||||
|
||||
for (input, mut velocity) in player_query.iter_mut() {
|
||||
let horizontal_movement = Vec3::new(
|
||||
input.movement_direction.x * SPEED,
|
||||
0.0,
|
||||
input.movement_direction.z * SPEED,
|
||||
);
|
||||
|
||||
velocity.linvel.x = horizontal_movement.x;
|
||||
velocity.linvel.z = horizontal_movement.z;
|
||||
|
||||
if input.movement_direction.y > 0.0 {
|
||||
velocity.linvel.y = JUMP_FORCE;
|
||||
} else if input.movement_direction.y < 0.0 {
|
||||
velocity.linvel.y = -SPEED;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user