head bobbing and sprite resizing

This commit is contained in:
Back777space
2025-04-06 12:18:53 +02:00
parent de69318f51
commit b8899ec536
2 changed files with 149 additions and 32 deletions

View File

@@ -1,4 +1,4 @@
use bevy::{gltf::GltfMesh, math::Vec3A, prelude::*, render::mesh::MeshAabb};
use bevy::{prelude::*};
use bevy_rapier3d::prelude::{Collider, RigidBody};
use crate::{GameState, asset_loading::GltfAssets};

View File

@@ -1,14 +1,17 @@
use bevy::{
input::mouse::AccumulatedMouseMotion, prelude::*, render::view::RenderLayers, transform, window::{self, PrimaryWindow}
input::mouse::AccumulatedMouseMotion, prelude::*, render::view::RenderLayers, window::{PrimaryWindow, WindowResized}
};
use bevy_rapier3d::prelude::*;
pub mod toolbar;
use crate::{asset_loading::{FlashlightAssets}, GameState};
use crate::{asset_loading::FlashlightAssets, GameState};
#[derive(Debug, Component)]
pub struct Player;
#[derive(Debug, Component, Default)]
pub struct Player {
pub speed_factor: f32,
}
#[derive(Debug, Component, Deref, DerefMut)]
pub struct CameraSensitivity(Vec2);
@@ -19,19 +22,41 @@ impl Default for CameraSensitivity {
}
#[derive(Debug, Component)]
struct WorldModelCamera;
pub struct WorldModelCamera;
#[derive(Debug, Component, Default)]
pub struct PlayerInput {
movement_direction: Vec3,
}
#[derive(Component, Debug)]
pub struct HeadBob {
pub enabled: bool,
pub intensity: f32,
pub speed: f32,
pub time_offset: f32,
}
impl Default for HeadBob {
fn default() -> Self {
Self {
enabled: true,
intensity: 0.05,
speed: 10.0,
time_offset: 0.0,
}
}
}
#[derive(Component, Debug)]
pub struct BaseTransform(pub Transform);
pub fn plugin(app: &mut App) {
app.add_plugins(toolbar::plugin)
.add_systems(OnEnter(GameState::Playing), (init_player, hide_cursor))
.add_systems(
Update,
(move_camera, handle_input).run_if(in_state(GameState::Playing)),
(move_camera, handle_input, apply_head_bob, on_resize_system).run_if(in_state(GameState::Playing)),
)
.add_systems(
FixedUpdate,
@@ -39,7 +64,7 @@ pub fn plugin(app: &mut App) {
);
}
/// used by the view model camera and the player's arm.
// used by the view model camera and the player's arm.
const STATIC_LAYER: usize = 1;
pub fn init_player(
@@ -47,15 +72,14 @@ pub fn init_player(
flashlights: Res<FlashlightAssets>,
window: Query<&Window>,
) {
let window = window.single();
commands
.spawn((
Player,
Player::default(),
PlayerAction::default(),
CameraSensitivity::default(),
PlayerInput::default(),
toolbar::Item::none(),
HeadBob::default(),
// rapier
RigidBody::Dynamic,
Collider::capsule(Vec3::new(0.0, -0.5, 0.0), Vec3::new(0.0, 0.5, 0.0), 0.5),
@@ -88,24 +112,45 @@ pub fn init_player(
RenderLayers::layer(STATIC_LAYER),
));
let window_size = Vec2::new(window.resolution.width(), window.resolution.height());
let sprite_size = Vec2::new(101.0, 101.0);
let scale = window.resolution.width() / 600.0;
let world_size = sprite_size * scale;
let offset = window_size.x / 4.0 - 40.0;
let mut transform = Transform::from_translation(
Vec3::new(window_size.x / 2.0 - world_size.x / 2.0 - offset, -window_size.y / 2.0 + world_size.y / 2.0, 0.0)
);
transform.scale = Vec3::new(scale, scale, 1.0);
let window = window.single();
let transform = flashlight_base_transform(window.width(), window.height());
parent.spawn((
Sprite::from_image(flashlights.flash_hold_4.clone()),
transform.0.clone(),
transform,
RenderLayers::layer(STATIC_LAYER),
));
});
}
fn on_resize_system(
mut resize_reader: EventReader<WindowResized>,
mut sprites: Query<(&mut Transform, &mut BaseTransform), With<Sprite>>,
) {
for e in resize_reader.read() {
for (mut transform, mut base_transform) in sprites.iter_mut() {
let new_pos = flashlight_base_transform(e.width, e.height);
*transform = new_pos.0.clone();
*base_transform = new_pos;
}
}
}
fn flashlight_base_transform(width: f32, height: f32) -> BaseTransform {
let window_size = Vec2::new(width, height);
let sprite_size = Vec2::new(101.0, 101.0);
let scale = width / 600.0;
let world_size = sprite_size * scale;
let xoffset = window_size.x / 4.0 - 40.0;
let yoffset = 15.0;
let mut transform = Transform::from_translation(
Vec3::new(window_size.x / 2.0 - world_size.x / 2.0 - xoffset, -window_size.y / 2.0 + world_size.y / 2.0 - yoffset, 0.0)
);
transform.scale = Vec3::new(scale, scale, 1.0);
return BaseTransform(transform);
}
fn hide_cursor(mut windows: Query<&mut Window, With<PrimaryWindow>>) {
for mut window in windows.iter_mut() {
window.cursor_options.visible = false;
@@ -145,9 +190,9 @@ pub(crate) enum PlayerAction {
pub fn handle_input(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut query: Query<(&Transform, &mut PlayerInput, &mut PlayerAction), With<Player>>,
mut query: Query<(&Transform, &mut PlayerInput, &mut PlayerAction, &mut Player), With<Player>>,
) {
for (transform, mut input, mut action) in query.iter_mut() {
for (transform, mut input, mut action, mut player) in query.iter_mut() {
let forward = transform.forward();
let right = transform.right();
let mut movement_direction = Vec3::ZERO;
@@ -168,7 +213,11 @@ pub fn handle_input(
movement_direction += Vec3::Y;
}
if keyboard_input.pressed(KeyCode::ShiftLeft) {
movement_direction -= Vec3::Y;
player.speed_factor = 1.35;
} else if keyboard_input.pressed(KeyCode::ControlLeft) {
player.speed_factor = 0.65;
} else {
player.speed_factor = 1.0;
}
if keyboard_input.pressed(KeyCode::KeyE) {
*action = PlayerAction::Interact
@@ -178,15 +227,16 @@ pub fn handle_input(
}
}
pub fn apply_player_movement(mut player_query: Query<(&PlayerInput, &mut Velocity), With<Player>>) {
const SPEED: f32 = 3.0;
pub fn apply_player_movement(mut player_query: Query<(&PlayerInput, &mut Velocity, &Player), With<Player>>) {
const SPEED: f32 = 2.6;
const JUMP_FORCE: f32 = 4.0;
for (input, mut velocity) in player_query.iter_mut() {
for (input, mut velocity, player) in player_query.iter_mut() {
let speed = SPEED * player.speed_factor;
let horizontal_movement = Vec3::new(
input.movement_direction.x * SPEED,
input.movement_direction.x * speed,
0.0,
input.movement_direction.z * SPEED,
input.movement_direction.z * speed,
);
velocity.linvel.x = horizontal_movement.x;
@@ -194,8 +244,75 @@ pub fn apply_player_movement(mut player_query: Query<(&PlayerInput, &mut Velocit
if input.movement_direction.y > 0.0 {
velocity.linvel.y = JUMP_FORCE;
} else if input.movement_direction.y < 0.0 {
velocity.linvel.y = -SPEED;
}
}
}
}
pub fn apply_head_bob(
time: Res<Time>,
mut query: Query<(&PlayerInput, &mut HeadBob, &Player), With<Player>>,
mut camera_query: Query<&mut Transform, (With<WorldModelCamera>, Without<Player>)>,
mut sprite_query: Query<(&mut Transform, &Sprite, &BaseTransform), (With<Sprite>, Without<WorldModelCamera>, Without<Player>)>,
) {
let Ok((input, mut head_bob, player)) = query.get_single_mut() else {
return;
};
// bob when moving horizontally
let horizontal_movement = Vec3::new(input.movement_direction.x, 0.0, input.movement_direction.z);
let is_moving = horizontal_movement.length_squared() > 0.01;
let bobbing_speed = head_bob.speed * player.speed_factor;
let mut offset = bobbing_speed * time.delta_secs();
if !is_moving {
// decrease bobbing frequency when stationary
offset *= 0.2;
// bob returns to neutral position when stopped
if let Some(camera_transform) = camera_query.iter_mut().next() {
let current_offset = camera_transform.translation.y;
if current_offset.abs() < 0.005 {
// cancel out so head_bob.time_offset
offset = -head_bob.time_offset;
}
}
}
head_bob.time_offset += offset;
if head_bob.enabled {
// calculate vertical and horizontal offsets using sine and cosine
let vertical_offset = head_bob.intensity * f32::sin(head_bob.time_offset);
let horizontal_offset = (head_bob.intensity * 0.5) * f32::cos(head_bob.time_offset * 0.5);
// apply
for mut transform in camera_query.iter_mut() {
transform.translation.y = vertical_offset;
if is_moving {
transform.translation.x = horizontal_offset;
} else {
// decrease bobbing magnitued
transform.translation.x *= 0.8;
}
}
// apply offsets to flashlight
for (mut transform, _sprite, base_transform) in sprite_query.iter_mut() {
let scale_factor = 40.0 * player.speed_factor;
if is_moving {
transform.translation.x = base_transform.0.translation.x + horizontal_offset * scale_factor;
transform.translation.y = base_transform.0.translation.y + vertical_offset * scale_factor;
transform.rotation = Quat::from_euler(
EulerRot::XYZ,
0.0,
0.0,
horizontal_offset * 0.1,
);
} else {
transform.translation = transform.translation.lerp(base_transform.0.translation, 0.1);
transform.rotation = transform.rotation.slerp(Quat::IDENTITY, 0.1);
}
}
}
}