129 lines
3.9 KiB
Rust
129 lines
3.9 KiB
Rust
use crate::{
|
|
game::{GameState, WorldEntity},
|
|
maze::{self, Maze},
|
|
settings::Settings,
|
|
};
|
|
use bevy::{
|
|
input::mouse::AccumulatedMouseMotion,
|
|
prelude::*,
|
|
window::{CursorGrabMode, CursorOptions},
|
|
};
|
|
|
|
#[derive(Component)]
|
|
pub struct Player {
|
|
pub yaw: f32,
|
|
pub pitch: f32,
|
|
pub distance: f32,
|
|
}
|
|
pub const SPAWN_YAW: f32 = std::f32::consts::FRAC_PI_2;
|
|
pub fn spawn(mut commands: Commands, maze: Res<Maze>, settings: Res<Settings>) {
|
|
commands.spawn((
|
|
WorldEntity,
|
|
Player {
|
|
yaw: SPAWN_YAW,
|
|
pitch: 0.,
|
|
distance: 0.,
|
|
},
|
|
Camera3d::default(),
|
|
bevy::render::view::NoIndirectDrawing,
|
|
bevy::core_pipeline::tonemapping::Tonemapping::None,
|
|
if settings.low {
|
|
Msaa::Off
|
|
} else {
|
|
Msaa::Sample4
|
|
},
|
|
Projection::Perspective(PerspectiveProjection {
|
|
fov: 80_f32.to_radians(),
|
|
near: 0.05,
|
|
far: 90.,
|
|
..default()
|
|
}),
|
|
Transform::from_translation(maze::position(maze.spawn()) + Vec3::Y * 1.15)
|
|
.with_rotation(Quat::from_rotation_y(SPAWN_YAW)),
|
|
));
|
|
}
|
|
pub fn cursor(state: Res<State<GameState>>, mut windows: Query<(&Window, &mut CursorOptions)>) {
|
|
for (window, mut c) in &mut windows {
|
|
let grab = *state.get() == GameState::Playing && window.focused;
|
|
c.visible = !grab;
|
|
c.grab_mode = if grab {
|
|
CursorGrabMode::Locked
|
|
} else {
|
|
CursorGrabMode::None
|
|
};
|
|
}
|
|
}
|
|
pub fn move_player(
|
|
time: Res<Time>,
|
|
keys: Res<ButtonInput<KeyCode>>,
|
|
mouse: Res<AccumulatedMouseMotion>,
|
|
pads: Query<&Gamepad>,
|
|
settings: Res<Settings>,
|
|
maze: Res<Maze>,
|
|
mut players: Query<(&mut Player, &mut Transform)>,
|
|
) {
|
|
let dt = time.delta_secs().min(0.1);
|
|
for (mut p, mut t) in &mut players {
|
|
let mut walk = Vec2::ZERO;
|
|
if keys.pressed(KeyCode::KeyW) {
|
|
walk.y += 1.;
|
|
}
|
|
if keys.pressed(KeyCode::KeyS) {
|
|
walk.y -= 1.;
|
|
}
|
|
if keys.pressed(KeyCode::KeyD) {
|
|
walk.x += 1.;
|
|
}
|
|
if keys.pressed(KeyCode::KeyA) {
|
|
walk.x -= 1.;
|
|
}
|
|
let mut turn = Vec2::ZERO;
|
|
if keys.pressed(KeyCode::ArrowLeft) {
|
|
turn.x -= 1.;
|
|
}
|
|
if keys.pressed(KeyCode::ArrowRight) {
|
|
turn.x += 1.;
|
|
}
|
|
for pad in &pads {
|
|
let left = pad.left_stick();
|
|
if left.length() > 0.18 {
|
|
walk += left;
|
|
}
|
|
let right = pad.right_stick();
|
|
if right.length() > 0.18 {
|
|
turn += Vec2::new(right.x, -right.y);
|
|
}
|
|
}
|
|
p.yaw -= turn.x * dt * 2.;
|
|
p.pitch = (p.pitch - turn.y * dt * 1.5).clamp(-1.1, 1.1);
|
|
if !settings.keyboard_only {
|
|
p.yaw -= mouse.delta.x * settings.sensitivity;
|
|
p.pitch = (p.pitch - mouse.delta.y * settings.sensitivity).clamp(-1.1, 1.1);
|
|
}
|
|
let rotation = Quat::from_rotation_y(p.yaw);
|
|
let velocity =
|
|
rotation * Vec3::new(walk.x, 0., -walk.y).clamp_length_max(1.) * settings.speed * dt;
|
|
// Small swept substeps prevent tunneling even after a long frame.
|
|
let steps = (velocity.length() / 0.08).ceil().max(1.) as u32;
|
|
let delta = velocity / steps as f32;
|
|
for _ in 0..steps {
|
|
let x = t.translation + Vec3::X * delta.x;
|
|
if maze.fits(x, 0.25) {
|
|
t.translation = x;
|
|
}
|
|
let z = t.translation + Vec3::Z * delta.z;
|
|
if maze.fits(z, 0.25) {
|
|
t.translation = z;
|
|
}
|
|
}
|
|
p.distance += velocity.length();
|
|
t.translation.y = 1.15
|
|
+ if settings.bob && !settings.low {
|
|
(p.distance * 7.).sin() * 0.025
|
|
} else {
|
|
0.
|
|
};
|
|
t.rotation = rotation * Quat::from_rotation_x(p.pitch);
|
|
}
|
|
}
|