Skip to content

Commit

Permalink
add third-person view camera & player box
Browse files Browse the repository at this point in the history
  • Loading branch information
meyerzinn committed May 4, 2023
1 parent d6e7846 commit afb0cf8
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 71 deletions.
64 changes: 54 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::f32::consts::PI;

use bevy::{core_pipeline::fxaa::Fxaa, prelude::*};
use voxel::player::{self, CameraMode};

mod debug;
mod voxel;
Expand All @@ -20,17 +21,60 @@ fn main() {
.run();
}

fn setup(mut cmds: Commands) {
cmds.spawn(Camera3dBundle {
projection: bevy::render::camera::Projection::Perspective(PerspectiveProjection {
fov: PI / 2.,
far: 2048.0,
..Default::default()
}),
transform: Transform::from_xyz(2.0, 160.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
fn setup(
mut cmds: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
cmds.spawn((
player::Player,
VisibilityBundle {
visibility: Visibility::Visible,
..default()
},
TransformBundle {
local: Transform::from_xyz(2.0, 170.0, 2.0).looking_to(Vec3::Z, Vec3::Y),
..default()
},
))
.with_children(|player| {
player.spawn(player::Body).insert(MaterialMeshBundle {
mesh: meshes.add(Mesh::from(shape::Box::new(0.5, 1.8, 0.5))),
material: materials.add(StandardMaterial {
base_color: Color::WHITE,
..default()
}),
transform: Transform::IDENTITY.looking_to(Vec3::Z, Vec3::Y),
..default()
});

player
.spawn((
player::Head,
TransformBundle {
// head is 1.8m above feet
local: Transform::from_translation(Vec3::new(0.0, 0.9, 0.0))
.looking_to(Vec3::Z, Vec3::Y),
..default()
},
))
.with_children(|head| {
// spawn camera as a child of head
head.spawn(Camera3dBundle {
projection: bevy::render::camera::Projection::Perspective(
PerspectiveProjection {
fov: PI / 2.,
far: 2048.0,
..Default::default()
},
),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, -5.0))
.looking_to(Vec3::Z, Vec3::Y),
..Default::default()
})
.insert(CameraMode::ThirdPersonForward);
});
})
.insert(voxel::player::PlayerController::default())
.insert(Fxaa::default())
.insert(bevy_atmosphere::plugin::AtmosphereCamera::default());

Expand Down
4 changes: 2 additions & 2 deletions src/voxel/world/chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ use bevy::{
};
use float_ord::FloatOrd;

use super::{player::PlayerController, Chunk, ChunkShape, CHUNK_LENGTH};
use super::{player, Chunk, ChunkShape, CHUNK_LENGTH};
use crate::voxel::storage::ChunkMap;
use crate::voxel::Voxel;

/// Updates the current chunk position for the current player.
fn update_player_pos(
player: Query<&GlobalTransform, (With<PlayerController>, Changed<GlobalTransform>)>,
player: Query<&GlobalTransform, (With<player::Body>, Changed<GlobalTransform>)>,
mut chunk_pos: ResMut<CurrentLocalPlayerChunk>,
) {
if let Ok(ply) = player.get_single() {
Expand Down
153 changes: 108 additions & 45 deletions src/voxel/world/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,108 @@ use std::f32::consts::FRAC_PI_2;

use crate::debug::DebugUISet;

// Reusing the player controller impl for now.
const BODY_ROTATION_SLERP: f32 = 0.5;

pub const DEFAULT_CAMERA_SENS: f32 = 0.005;
#[derive(Component)]
pub struct Player;

/// Marker component for player body.
#[derive(Component)]
pub struct Body;

#[derive(Default, Component)]
pub struct PlayerController {
yaw: f32,
pitch: f32,
cursor_locked: bool,
#[derive(Component)]
pub struct Head;

#[derive(Component, Debug, Clone, Copy)]
pub enum CameraMode {
FirstPerson,
ThirdPersonForward,
}

pub fn handle_player_mouse_move(
mut query: Query<(&mut PlayerController, &mut Transform)>,
mut mouse_motion_event_reader: EventReader<MouseMotion>,
mut window: Query<&mut Window>,
) {
let (mut controller, mut transform) = query.single_mut();
let mut delta = Vec2::ZERO;
impl CameraMode {
fn next(self) -> Self {
match self {
Self::FirstPerson => Self::ThirdPersonForward,
Self::ThirdPersonForward => Self::FirstPerson,
}
}

if controller.cursor_locked {
for mouse_move in mouse_motion_event_reader.iter() {
delta += mouse_move.delta;
fn translation(self) -> Vec3 {
match self {
Self::FirstPerson => Vec3::ZERO,
Self::ThirdPersonForward => Vec3::Z * -5.0,
}
}
}

let mut first_win = window.single_mut();
first_win.cursor.visible = !controller.cursor_locked;
first_win.cursor.grab_mode = if controller.cursor_locked {
CursorGrabMode::Locked
} else {
CursorGrabMode::None
};
// Reusing the player controller impl for now.

if delta == Vec2::ZERO {
return;
}
pub const DEFAULT_CAMERA_SENS: f32 = 0.005;

let mut new_pitch = delta.y.mul_add(DEFAULT_CAMERA_SENS, controller.pitch);
let new_yaw = delta.x.mul_add(-DEFAULT_CAMERA_SENS, controller.yaw);
fn handle_player_mouse_move(
mut head: Query<&mut Transform, With<Head>>,
mut mouse_motion_event_reader: EventReader<MouseMotion>,
windows: Query<&Window>,
) {
let window = windows.single();
let mut head_transform = head.single_mut();
let mut delta = Vec2::ZERO;

new_pitch = new_pitch.clamp(-FRAC_PI_2, FRAC_PI_2);
for mouse_move in mouse_motion_event_reader.iter() {
delta -= mouse_move.delta;
}

controller.yaw = new_yaw;
controller.pitch = new_pitch;
if !matches!(window.cursor.grab_mode, CursorGrabMode::Locked) {
return;
}

transform.rotation =
Quat::from_axis_angle(Vec3::Y, new_yaw) * Quat::from_axis_angle(-Vec3::X, new_pitch);
let (yaw, pitch, _roll) = head_transform.rotation.to_euler(EulerRot::YXZ);
let yaw = delta.x.mul_add(DEFAULT_CAMERA_SENS, yaw);
let pitch = delta
.y
.mul_add(-DEFAULT_CAMERA_SENS, pitch)
// ensure that the look direction always has a component in the xz plane:
.clamp(-FRAC_PI_2 + 1e-5, FRAC_PI_2 - 1e-5);
head_transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, 0.);
}

pub fn handle_player_input(
fn handle_player_keyboard_input(
mut egui: EguiContexts,
mut query: Query<(&mut PlayerController, &mut Transform)>,
// mut queries: ParamSet<Query<&mut Transform, With<Body>>>,
mut queries: ParamSet<(
Query<&mut Transform, With<Player>>,
Query<&Transform, With<Body>>,
)>,
keys: Res<Input<KeyCode>>,
btns: Res<Input<MouseButton>>,
mut windows: Query<&mut Window>,
) {
let (mut controller, mut transform) = query.single_mut();
let mut window = windows.single_mut();

// cursor grabbing
// @todo: this should prevent cursor grabbing when the user is interacting with a debug UI. Why doesn't this work?
if btns.just_pressed(MouseButton::Left) && !egui.ctx_mut().wants_pointer_input() {
controller.cursor_locked = true;
window.cursor.grab_mode = CursorGrabMode::Locked;
window.cursor.visible = false;
}

// cursor ungrabbing
if keys.just_pressed(KeyCode::Escape) {
controller.cursor_locked = false;
window.cursor.grab_mode = CursorGrabMode::None;
window.cursor.visible = true;
}
let mut direction = Vec3::ZERO;

let forward = transform.rotation.mul_vec3(Vec3::Z).normalize() * Vec3::new(1.0, 0., 1.0);
let right = transform.rotation.mul_vec3(Vec3::X).normalize();
let (forward, right) = {
let body = queries.p1();
let body_transform = body.single();
let forward = body_transform.rotation.mul_vec3(Vec3::Z).normalize();
let right = Vec3::Y.cross(forward); // @todo(meyerzinn): not sure why this is the correct orientation
(forward, right)
};

let mut body = queries.p0();
let mut body_transform = body.single_mut();

let mut direction = Vec3::ZERO;
let mut acceleration = 1.0f32;

if keys.pressed(KeyCode::W) {
Expand Down Expand Up @@ -111,11 +141,39 @@ pub fn handle_player_input(
}

// hardcoding 0.10 as a factor for now to not go zoomin across the world.
transform.translation += direction.x * right * acceleration
body_transform.translation += direction.x * right * acceleration
+ direction.z * forward * acceleration
+ direction.y * Vec3::Y * acceleration;
}

fn handle_player_change_camera_mode(
keys: Res<Input<KeyCode>>,
mut cameras: Query<(&mut CameraMode, &mut Transform)>,
) {
if keys.just_pressed(KeyCode::F5) {
let (mut mode, mut transform) = cameras.single_mut();
*mode = mode.next();
transform.translation = mode.translation();
}
}

fn update_player_body_rotation(
mut queries: ParamSet<(
Query<&mut Transform, With<Body>>,
Query<&Transform, With<Head>>,
)>,
) {
let yaw = {
let head = queries.p1();
let (yaw, _pitch, _roll) = head.single().rotation.to_euler(EulerRot::YXZ);
yaw
};
let mut body = queries.p0();
let mut body_transform = body.single_mut();
let desired = Quat::from_euler(EulerRot::YXZ, yaw, 0., 0.);
body_transform.rotation = body_transform.rotation.slerp(desired, BODY_ROTATION_SLERP);
}

#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, SystemSet)]
/// Systems related to player controls.
pub struct PlayerControllerSet;
Expand All @@ -125,7 +183,12 @@ pub struct VoxelWorldPlayerControllerPlugin;
impl Plugin for VoxelWorldPlayerControllerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
(handle_player_input, handle_player_mouse_move)
(
handle_player_mouse_move,
update_player_body_rotation,
handle_player_keyboard_input,
handle_player_change_camera_mode,
)
.chain()
.in_base_set(CoreSet::Update)
.after(DebugUISet::Display),
Expand Down
16 changes: 2 additions & 14 deletions src/voxel/world/sky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use bevy::prelude::{
Query, Res, Resource, Transform, Vec3, With,
};

use super::player::PlayerController;
use super::player::CameraMode;

#[derive(Resource, Deref)]
struct SkyLightEntity(Entity);
Expand All @@ -17,15 +17,6 @@ fn setup_sky_lighting(mut cmds: Commands) {
directional_light: DirectionalLight {
color: Color::WHITE,
shadows_enabled: true,
// shadow_projection: OrthographicProjection {
// // left: -SIZE,
// // right: SIZE,
// // bottom: -SIZE,
// // top: SIZE,
// near: -SIZE,
// far: SIZE,
// ..Default::default()
// },
..Default::default()
},
..Default::default()
Expand All @@ -37,10 +28,7 @@ fn setup_sky_lighting(mut cmds: Commands) {

fn update_light_position(
sky_light_entity: Res<SkyLightEntity>,
mut queries: ParamSet<(
Query<&mut Transform>,
Query<&Transform, With<PlayerController>>,
)>,
mut queries: ParamSet<(Query<&mut Transform>, Query<&Transform, With<CameraMode>>)>,
) {
let sky_light_entity = **sky_light_entity;
let player_translation = queries
Expand Down

0 comments on commit afb0cf8

Please sign in to comment.