mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
add player movement with basic physics ecs system
Former-commit-id: f2e151971a42b25bfd1971311f5a06535a577007
This commit is contained in:
parent
993ee68037
commit
6f5f80f749
@ -7,10 +7,7 @@ use vek::*;
|
||||
use threadpool;
|
||||
|
||||
// Project
|
||||
use common::{
|
||||
state::State,
|
||||
terrain::TerrainChunk,
|
||||
};
|
||||
use common::{comp::phys::Vel, state::State, terrain::TerrainChunk};
|
||||
use world::World;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -21,6 +18,7 @@ pub enum Error {
|
||||
|
||||
pub struct Input {
|
||||
// TODO: Use this type to manage client input
|
||||
pub move_vec: Vec2<f32>,
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
@ -61,6 +59,7 @@ impl Client {
|
||||
// TODO: Get rid of this
|
||||
pub fn with_test_state(mut self) -> Self {
|
||||
self.chunk = Some(self.world.generate_chunk(Vec3::zero()));
|
||||
self.player = Some(self.state.new_test_player());
|
||||
self
|
||||
}
|
||||
|
||||
@ -76,6 +75,11 @@ impl Client {
|
||||
/// Get a mutable reference to the client's game state.
|
||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
||||
|
||||
/// Get the player entity
|
||||
pub fn player(&self) -> Option<EcsEntity> {
|
||||
self.player
|
||||
}
|
||||
|
||||
/// Get the current tick number.
|
||||
pub fn get_tick(&self) -> u64 {
|
||||
self.tick
|
||||
@ -95,6 +99,27 @@ impl Client {
|
||||
// 4) Go through the terrain update queue and apply all changes to the terrain
|
||||
// 5) Finish the tick, passing control of the main thread back to the frontend
|
||||
|
||||
// (step 1)
|
||||
if let Some(p) = self.player {
|
||||
let vel = input.move_vec;
|
||||
|
||||
const MIN_LOOKING: f32 = 0.5;
|
||||
const LEANING_FAC: f32 = 0.05;
|
||||
|
||||
let dir = Vec3::from([
|
||||
// Rotation
|
||||
match vel.magnitude() > MIN_LOOKING {
|
||||
true => vel[0].atan2(vel[1]),
|
||||
_ => 0.0,
|
||||
},
|
||||
// Lean
|
||||
Vec2::new(vel[0], vel[1]).magnitude() * LEANING_FAC,
|
||||
]);
|
||||
|
||||
// TODO: Set acceleration instead and adjust dir calculations accordingly
|
||||
self.state.write_component(p, Vel(Vec3::from(vel)));
|
||||
}
|
||||
|
||||
// Tick the client's LocalState (step 3)
|
||||
self.state.tick(dt);
|
||||
|
||||
|
@ -4,6 +4,7 @@ pub mod clock;
|
||||
pub mod comp;
|
||||
pub mod figure;
|
||||
pub mod state;
|
||||
pub mod sys;
|
||||
pub mod terrain;
|
||||
pub mod util;
|
||||
pub mod volumes;
|
||||
|
@ -2,15 +2,12 @@
|
||||
use std::time::Duration;
|
||||
|
||||
// Library
|
||||
use specs::World as EcsWorld;
|
||||
use shred::{Fetch, FetchMut};
|
||||
use specs::{Builder, Component, DispatcherBuilder, Entity as EcsEntity, World as EcsWorld};
|
||||
use vek::*;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
comp,
|
||||
terrain::TerrainMap,
|
||||
};
|
||||
use crate::{comp, sys, terrain::TerrainMap};
|
||||
|
||||
/// How much faster should an in-game day be compared to a real day?
|
||||
// TODO: Don't hard-code this
|
||||
@ -22,6 +19,10 @@ struct TimeOfDay(f64);
|
||||
/// A resource to store the tick (i.e: physics) time
|
||||
struct Time(f64);
|
||||
|
||||
/// A resource used to store the time since the last tick
|
||||
#[derive(Default)]
|
||||
pub struct DeltaTime(pub f64);
|
||||
|
||||
pub struct Changes {
|
||||
pub new_chunks: Vec<Vec3<i32>>,
|
||||
pub changed_chunks: Vec<Vec3<i32>>,
|
||||
@ -59,6 +60,7 @@ impl State {
|
||||
// Register resources used by the ECS
|
||||
ecs_world.add_resource(TimeOfDay(0.0));
|
||||
ecs_world.add_resource(Time(0.0));
|
||||
ecs_world.add_resource(DeltaTime(0.0));
|
||||
ecs_world.add_resource(TerrainMap::new());
|
||||
|
||||
// Register common components with the state
|
||||
@ -70,15 +72,36 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Get rid of this
|
||||
pub fn new_test_player(&mut self) -> EcsEntity {
|
||||
self.ecs_world
|
||||
.create_entity()
|
||||
.with(comp::phys::Pos(Vec3::default()))
|
||||
.with(comp::phys::Vel(Vec3::default()))
|
||||
.with(comp::phys::Dir(Vec3::default()))
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Write a component
|
||||
pub fn write_component<C: Component>(&mut self, e: EcsEntity, c: C) {
|
||||
let _ = self.ecs_world.write_storage().insert(e, c);
|
||||
}
|
||||
|
||||
/// Get a reference to the internal ECS world
|
||||
pub fn ecs_world(&self) -> &EcsWorld { &self.ecs_world }
|
||||
pub fn ecs_world(&self) -> &EcsWorld {
|
||||
&self.ecs_world
|
||||
}
|
||||
|
||||
/// Get a reference to the `Changes` structure of the state. This contains
|
||||
/// information about state that has changed since the last game tick.
|
||||
pub fn changes(&self) -> &Changes { &self.changes }
|
||||
pub fn changes(&self) -> &Changes {
|
||||
&self.changes
|
||||
}
|
||||
|
||||
// TODO: Get rid of this since it shouldn't be needed
|
||||
pub fn changes_mut(&mut self) -> &mut Changes { &mut self.changes }
|
||||
pub fn changes_mut(&mut self) -> &mut Changes {
|
||||
&mut self.changes
|
||||
}
|
||||
|
||||
/// Get the current in-game time of day.
|
||||
///
|
||||
@ -95,12 +118,12 @@ impl State {
|
||||
}
|
||||
|
||||
/// Get a reference to this state's terrain.
|
||||
pub fn terrain<'a>(&'a self) -> Fetch<'a, TerrainMap> {
|
||||
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
||||
self.ecs_world.read_resource::<TerrainMap>()
|
||||
}
|
||||
|
||||
// TODO: Get rid of this since it shouldn't be needed
|
||||
pub fn terrain_mut<'a>(&'a mut self) -> FetchMut<'a, TerrainMap> {
|
||||
pub fn terrain_mut(&mut self) -> FetchMut<TerrainMap> {
|
||||
self.ecs_world.write_resource::<TerrainMap>()
|
||||
}
|
||||
|
||||
@ -109,6 +132,17 @@ impl State {
|
||||
// Change the time accordingly
|
||||
self.ecs_world.write_resource::<TimeOfDay>().0 += dt.as_float_secs() * DAY_CYCLE_FACTOR;
|
||||
self.ecs_world.write_resource::<Time>().0 += dt.as_float_secs();
|
||||
|
||||
// Run systems to update the world
|
||||
self.ecs_world.write_resource::<DeltaTime>().0 = dt.as_float_secs();
|
||||
|
||||
// Create and run dispatcher for ecs systems
|
||||
let mut dispatch_builder = DispatcherBuilder::new();
|
||||
sys::add_local_systems(&mut dispatch_builder);
|
||||
// This dispatches all the systems in parallel
|
||||
dispatch_builder.build().dispatch(&self.ecs_world.res);
|
||||
|
||||
self.ecs_world.maintain();
|
||||
}
|
||||
|
||||
/// Clean up the state after a tick
|
||||
|
11
common/src/sys/mod.rs
Normal file
11
common/src/sys/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
pub mod phys;
|
||||
|
||||
// External
|
||||
use specs::DispatcherBuilder;
|
||||
|
||||
// System names
|
||||
const MOVEMENT_SYS: &str = "movement_sys";
|
||||
|
||||
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
dispatch_builder.add(phys::MovementSys, MOVEMENT_SYS, &[]);
|
||||
}
|
25
common/src/sys/phys.rs
Normal file
25
common/src/sys/phys.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// Library
|
||||
use specs::{Join, Read, ReadStorage, System, WriteStorage};
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
comp::phys::{Pos, Vel},
|
||||
state::DeltaTime,
|
||||
};
|
||||
|
||||
// Basic ECS physics system
|
||||
pub struct MovementSys;
|
||||
|
||||
impl<'a> System<'a> for MovementSys {
|
||||
type SystemData = (
|
||||
WriteStorage<'a, Pos>,
|
||||
ReadStorage<'a, Vel>,
|
||||
Read<'a, DeltaTime>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (mut positions, velocities, dt): Self::SystemData) {
|
||||
(&mut positions, &velocities)
|
||||
.join() // this can be parallelized with par_join()
|
||||
.for_each(|(pos, vel)| pos.0 += vel.0 * dt.0 as f32 * 100.0);
|
||||
}
|
||||
}
|
32
voxygen/src/key_state.rs
Normal file
32
voxygen/src/key_state.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use vek::Vec2;
|
||||
|
||||
pub struct KeyState {
|
||||
pub right: bool,
|
||||
pub left: bool,
|
||||
pub up: bool,
|
||||
pub down: bool,
|
||||
pub jump: bool,
|
||||
}
|
||||
|
||||
impl KeyState {
|
||||
pub fn new() -> KeyState {
|
||||
KeyState {
|
||||
right: false,
|
||||
left: false,
|
||||
up: false,
|
||||
down: false,
|
||||
jump: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dir_vec(&self) -> Vec2<f32> {
|
||||
Vec2::<f32>::new(
|
||||
if self.right { 1.0 } else { 0.0 } + if self.left { -1.0 } else { 0.0 },
|
||||
if self.up { 1.0 } else { 0.0 } + if self.down { -1.0 } else { 0.0 },
|
||||
)
|
||||
}
|
||||
|
||||
pub fn jump(&self) -> bool {
|
||||
self.jump
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
pub mod anim;
|
||||
pub mod error;
|
||||
pub mod key_state;
|
||||
pub mod menu;
|
||||
pub mod mesh;
|
||||
pub mod render;
|
||||
|
@ -67,11 +67,14 @@ impl Vertex {
|
||||
}
|
||||
|
||||
impl Locals {
|
||||
pub fn default() -> Self {
|
||||
pub fn new(model_mat: Mat4<f32>) -> Self {
|
||||
Self {
|
||||
model_mat: arr_to_mat(Mat4::identity().into_col_array()),
|
||||
model_mat: arr_to_mat(model_mat.into_col_array()),
|
||||
}
|
||||
}
|
||||
pub fn default() -> Self {
|
||||
Self::new(Mat4::identity())
|
||||
}
|
||||
}
|
||||
|
||||
impl BoneData {
|
||||
@ -82,9 +85,7 @@ impl BoneData {
|
||||
}
|
||||
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
bone_mat: arr_to_mat(Mat4::identity().into_col_array()),
|
||||
}
|
||||
Self::new(Mat4::identity())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,8 @@ impl Camera {
|
||||
/// Rotate the camera about its focus by the given delta, limiting the input accordingly.
|
||||
pub fn rotate_by(&mut self, delta: Vec3<f32>) {
|
||||
self.ori += delta;
|
||||
// Wrap camera roll
|
||||
self.ori.x = self.ori.x % (2.0 * PI);
|
||||
// Clamp camera pitch to the vertical limits
|
||||
self.ori.y = self.ori.y
|
||||
.min(PI / 2.0)
|
||||
@ -75,4 +77,7 @@ impl Camera {
|
||||
pub fn get_aspect_ratio(&self) -> f32 { self.aspect }
|
||||
/// Set the aspect ratio of the camera.
|
||||
pub fn set_aspect_ratio(&mut self, aspect: f32) { self.aspect = aspect; }
|
||||
|
||||
/// Get the orientation of the camera
|
||||
pub fn get_orientation(&self) -> Vec3<f32> { self.ori }
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ use vek::*;
|
||||
use dot_vox;
|
||||
|
||||
// Project
|
||||
use common::figure::Segment;
|
||||
use client::Client;
|
||||
use common::{comp::phys::Pos as PosComp, figure::Segment};
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
@ -138,6 +138,22 @@ impl Scene {
|
||||
|
||||
/// Maintain data such as GPU constant buffers, models, etc. To be called once per tick.
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
// Get player position
|
||||
let player_pos = match client.player() {
|
||||
Some(entity) => {
|
||||
client
|
||||
.state()
|
||||
.ecs_world()
|
||||
.read_storage::<PosComp>()
|
||||
.get(entity)
|
||||
.expect("There was no position component on the player entity!")
|
||||
.0
|
||||
}
|
||||
None => Vec3::default(),
|
||||
};
|
||||
// Alter camera position to match player
|
||||
self.camera.set_focus_pos(player_pos);
|
||||
|
||||
// Compute camera matrices
|
||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents();
|
||||
|
||||
@ -161,7 +177,15 @@ impl Scene {
|
||||
&mut self.test_figure.skeleton,
|
||||
client.state().get_time(),
|
||||
);
|
||||
self.test_figure.update_locals(renderer, FigureLocals::default()).unwrap();
|
||||
|
||||
// Calculate entity model matrix
|
||||
let model_mat = Mat4::<f32>::translation_3d(player_pos);
|
||||
//* Mat4::rotation_z(PI - entity.look_dir().x)
|
||||
//* Mat4::rotation_x(entity.look_dir().y);
|
||||
|
||||
self.test_figure
|
||||
.update_locals(renderer, FigureLocals::new(model_mat))
|
||||
.unwrap();
|
||||
self.test_figure.update_skeleton(renderer).unwrap();
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ use crate::{
|
||||
PlayState,
|
||||
PlayStateResult,
|
||||
GlobalState,
|
||||
key_state::KeyState,
|
||||
window::{Event, Key},
|
||||
render::Renderer,
|
||||
scene::Scene,
|
||||
@ -27,6 +28,7 @@ const FPS: u64 = 60;
|
||||
pub struct SessionState {
|
||||
scene: Scene,
|
||||
client: Client,
|
||||
key_state: KeyState,
|
||||
}
|
||||
|
||||
/// Represents an active game session (i.e: one that is being played)
|
||||
@ -38,6 +40,7 @@ impl SessionState {
|
||||
// Create a scene for this session. The scene handles visible elements of the game world
|
||||
scene: Scene::new(renderer, &client),
|
||||
client,
|
||||
key_state: KeyState::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,7 +51,16 @@ const BG_COLOR: Rgba<f32> = Rgba { r: 0.0, g: 0.3, b: 1.0, a: 1.0 };
|
||||
impl SessionState {
|
||||
/// Tick the session (and the client attached to it)
|
||||
pub fn tick(&mut self, dt: Duration) -> Result<(), Error> {
|
||||
self.client.tick(client::Input {}, dt)?;
|
||||
// Calculate the movement input vector of the player from the current key presses and the camera direction
|
||||
let ori = self.scene.camera().get_orientation();
|
||||
let unit_vecs = (
|
||||
Vec2::new(ori[0].cos(), -ori[0].sin()),
|
||||
Vec2::new(ori[0].sin(), ori[0].cos()),
|
||||
);
|
||||
let dir_vec = self.key_state.dir_vec();
|
||||
let move_vec = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
||||
|
||||
self.client.tick(client::Input { move_vec }, dt)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -101,6 +113,16 @@ impl PlayState for SessionState {
|
||||
Event::KeyDown(Key::ToggleCursor) => {
|
||||
global_state.window.grab_cursor(!global_state.window.is_cursor_grabbed());
|
||||
},
|
||||
// Movement Key Pressed
|
||||
Event::KeyDown(Key::MoveForward) => self.key_state.up = true,
|
||||
Event::KeyDown(Key::MoveBack) => self.key_state.down = true,
|
||||
Event::KeyDown(Key::MoveLeft) => self.key_state.left = true,
|
||||
Event::KeyDown(Key::MoveRight) => self.key_state.right = true,
|
||||
// Movement Key Released
|
||||
Event::KeyUp(Key::MoveForward) => self.key_state.up = false,
|
||||
Event::KeyUp(Key::MoveBack) => self.key_state.down = false,
|
||||
Event::KeyUp(Key::MoveLeft) => self.key_state.left = false,
|
||||
Event::KeyUp(Key::MoveRight) => self.key_state.right = false,
|
||||
// Pass all other events to the scene
|
||||
event => { self.scene.handle_input_event(event); },
|
||||
};
|
||||
|
@ -2,6 +2,7 @@
|
||||
use glutin;
|
||||
use gfx_window_glutin;
|
||||
use vek::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
@ -18,6 +19,7 @@ pub struct Window {
|
||||
renderer: Renderer,
|
||||
window: glutin::GlWindow,
|
||||
cursor_grabbed: bool,
|
||||
key_map: HashMap<glutin::VirtualKeyCode, Key>,
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +48,13 @@ impl Window {
|
||||
&events_loop,
|
||||
).map_err(|err| Error::BackendError(Box::new(err)))?;
|
||||
|
||||
let mut key_map = HashMap::new();
|
||||
key_map.insert(glutin::VirtualKeyCode::Escape, Key::ToggleCursor);
|
||||
key_map.insert(glutin::VirtualKeyCode::W, Key::MoveForward);
|
||||
key_map.insert(glutin::VirtualKeyCode::A, Key::MoveLeft);
|
||||
key_map.insert(glutin::VirtualKeyCode::S, Key::MoveBack);
|
||||
key_map.insert(glutin::VirtualKeyCode::D, Key::MoveRight);
|
||||
|
||||
let tmp = Ok(Self {
|
||||
events_loop,
|
||||
renderer: Renderer::new(
|
||||
@ -56,6 +65,7 @@ impl Window {
|
||||
)?,
|
||||
window,
|
||||
cursor_grabbed: false,
|
||||
key_map,
|
||||
});
|
||||
tmp
|
||||
}
|
||||
@ -69,6 +79,7 @@ impl Window {
|
||||
let cursor_grabbed = self.cursor_grabbed;
|
||||
let renderer = &mut self.renderer;
|
||||
let window = &mut self.window;
|
||||
let key_map = &self.key_map;
|
||||
|
||||
let mut events = vec![];
|
||||
self.events_loop.poll_events(|event| match event {
|
||||
@ -85,15 +96,17 @@ impl Window {
|
||||
},
|
||||
glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)),
|
||||
glutin::WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode {
|
||||
Some(glutin::VirtualKeyCode::Escape) => events.push(if input.state == glutin::ElementState::Pressed {
|
||||
Event::KeyDown(Key::ToggleCursor)
|
||||
} else {
|
||||
Event::KeyUp(Key::ToggleCursor)
|
||||
Some(keycode) => match key_map.get(&keycode) {
|
||||
Some(&key) => events.push(match input.state {
|
||||
glutin::ElementState::Pressed => Event::KeyDown(key),
|
||||
_ => Event::KeyUp(key),
|
||||
}),
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
glutin::Event::DeviceEvent { event, .. } => match event {
|
||||
glutin::DeviceEvent::MouseMotion { delta: (dx, dy), .. } if cursor_grabbed =>
|
||||
events.push(Event::CursorPan(Vec2::new(dx as f32, dy as f32))),
|
||||
@ -126,8 +139,13 @@ impl Window {
|
||||
}
|
||||
|
||||
/// Represents a key that the game recognises after keyboard mapping
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Key {
|
||||
ToggleCursor,
|
||||
MoveForward,
|
||||
MoveBack,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
}
|
||||
|
||||
/// Represents an incoming event from the window
|
||||
|
Loading…
Reference in New Issue
Block a user