Revamp control system

This commit is contained in:
timokoesters 2019-06-09 16:20:20 +02:00
parent 132d1a67a6
commit 69cb2ed84f
No known key found for this signature in database
GPG Key ID: CD80BE9AAEE78097
16 changed files with 775 additions and 172 deletions

View File

@ -29,7 +29,7 @@ fn main() {
client.send_chat("Hello!".to_string());
loop {
let events = match client.tick(comp::Control::default(), clock.get_last_delta()) {
let events = match client.tick(comp::Controller::default(), clock.get_last_delta()) {
Ok(events) => events,
Err(err) => {
error!("Error: {:?}", err);

View File

@ -171,52 +171,6 @@ impl Client {
self.postbox.send_message(ClientMsg::Chat(msg))
}
/// Jump locally, the new positions will be synced to the server
#[allow(dead_code)]
pub fn jump(&mut self) {
if self.client_state != ClientState::Character {
return;
}
self.state.write_component(self.entity, comp::Jumping);
}
/// Start to glide locally, animation will be synced
#[allow(dead_code)]
pub fn glide(&mut self, state: bool) {
if self.client_state != ClientState::Character {
return;
}
if state {
self.state.write_component(self.entity, comp::Gliding);
} else {
self.state
.ecs_mut()
.write_storage::<comp::Gliding>()
.remove(self.entity);
}
}
/// Start to attack
#[allow(dead_code)]
pub fn attack(&mut self) {
if self.client_state != ClientState::Character {
return;
}
// TODO: Test if attack is possible using timeout
self.state
.write_component(self.entity, comp::Attacking::start());
self.postbox.send_message(ClientMsg::Attack);
}
/// Tell the server the client wants to respawn.
#[allow(dead_code)]
pub fn respawn(&mut self) {
if self.client_state != ClientState::Dead {
return;
}
self.postbox.send_message(ClientMsg::Respawn)
}
/// Remove all cached terrain
#[allow(dead_code)]
pub fn clear_terrain(&mut self) {
@ -226,7 +180,11 @@ impl Client {
/// Execute a single client tick, handle input and update the game state by the given duration.
#[allow(dead_code)]
pub fn tick(&mut self, control: comp::Control, dt: Duration) -> Result<Vec<Event>, Error> {
pub fn tick(
&mut self,
controller: comp::Controller,
dt: Duration,
) -> Result<Vec<Event>, Error> {
// This tick function is the centre of the Veloren universe. Most client-side things are
// managed from here, and as such it's important that it stays organised. Please consult
// the core developers before making significant changes to this code. Here is the
@ -243,10 +201,8 @@ impl Client {
// 1) Handle input from frontend.
// Pass character actions from frontend input to the player's entity.
// TODO: Only do this if the entity already has a Inputs component!
if self.client_state == ClientState::Character {
self.state.write_component(self.entity, control.clone());
}
self.state.write_component(self.entity, controller.clone());
self.postbox.send_message(ClientMsg::Controller(controller));
// 2) Build up a list of events for this frame, to be passed to the frontend.
let mut frontend_events = Vec::new();

View File

@ -0,0 +1,15 @@
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
use vek::*;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Controller {
pub move_dir: Vec2<f32>,
pub jump: bool,
pub glide: bool,
pub attack: bool,
pub respawn: bool,
}
impl Component for Controller {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

View File

@ -1,11 +1,6 @@
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
use vek::*;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Control {
pub move_dir: Vec2<f32>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Respawning;
@ -14,16 +9,13 @@ pub struct Attacking {
pub time: f32,
pub applied: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Jumping;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Gliding;
impl Component for Control {
type Storage = VecStorage<Self>;
}
impl Component for Respawning {
type Storage = NullStorage<Self>;
}
@ -36,6 +28,7 @@ impl Attacking {
}
}
}
impl Component for Attacking {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

View File

@ -1,6 +1,7 @@
pub mod actor;
pub mod agent;
pub mod animation;
pub mod controller;
pub mod inputs;
pub mod phys;
pub mod player;
@ -15,8 +16,8 @@ pub use actor::QuadrupedMediumBody;
pub use agent::Agent;
pub use animation::Animation;
pub use animation::AnimationInfo;
pub use controller::Controller;
pub use inputs::Attacking;
pub use inputs::Control;
pub use inputs::Gliding;
pub use inputs::Jumping;
pub use inputs::Respawning;

View File

@ -11,8 +11,7 @@ pub enum ClientMsg {
name: String,
body: comp::Body,
},
Attack,
Respawn,
Controller(comp::Controller),
RequestState(ClientState),
SetViewDistance(u32),
Ping,

View File

@ -110,9 +110,9 @@ impl State {
ecs.register::<comp::phys::Vel>();
ecs.register::<comp::phys::Ori>();
ecs.register::<comp::AnimationInfo>();
ecs.register::<comp::Controller>();
// Register client-local components
ecs.register::<comp::Control>();
ecs.register::<comp::Jumping>();
// Register server-local components

View File

@ -18,7 +18,7 @@ impl<'a> System<'a> for Sys {
let finished_attacks = (&entities, &mut attacks)
.join()
.filter(|(_, a)| a.time > 0.25) // TODO: constant
.filter(|(_, a)| a.time > 0.50) // TODO: constant
.map(|(e, _)| e)
.collect::<Vec<_>>();

View File

@ -1,4 +1,4 @@
use crate::comp::{phys::Pos, Agent, Attacking, Control, Jumping};
use crate::comp::{phys::Pos, Agent, Attacking, Controller, Jumping};
use log::warn;
use rand::{seq::SliceRandom, thread_rng};
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
@ -12,17 +12,17 @@ impl<'a> System<'a> for Sys {
Entities<'a>,
WriteStorage<'a, Agent>,
ReadStorage<'a, Pos>,
WriteStorage<'a, Control>,
WriteStorage<'a, Controller>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Attacking>,
);
fn run(
&mut self,
(entities, mut agents, positions, mut controls, mut jumps, mut attacks): Self::SystemData,
(entities, mut agents, positions, mut controllers, mut jumps, mut attacks): Self::SystemData,
) {
for (entity, agent, pos, control) in
(&entities, &mut agents, &positions, &mut controls).join()
for (entity, agent, pos, controller) in
(&entities, &mut agents, &positions, &mut controllers).join()
{
match agent {
Agent::Wanderer(bearing) => {
@ -32,7 +32,7 @@ impl<'a> System<'a> for Sys {
- pos.0 * 0.0002;
if bearing.magnitude_squared() != 0.0 {
control.move_dir = bearing.normalized();
controller.move_dir = bearing.normalized();
}
}
Agent::Pet { target, offset } => {
@ -49,7 +49,7 @@ impl<'a> System<'a> for Sys {
// Move towards the target.
let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude();
control.move_dir = if dist > 5.0 {
controller.move_dir = if dist > 5.0 {
Vec2::from(tgt_pos - pos.0).normalized()
} else if dist < 1.5 && dist > 0.0 {
Vec2::from(pos.0 - tgt_pos).normalized()
@ -57,7 +57,7 @@ impl<'a> System<'a> for Sys {
Vec2::zero()
};
}
_ => control.move_dir = Vec2::zero(),
_ => controller.move_dir = Vec2::zero(),
}
// Change offset occasionally.
@ -72,7 +72,7 @@ impl<'a> System<'a> for Sys {
Some(tgt_pos) => {
let dist = Vec2::<f32>::from(tgt_pos.0 - pos.0).magnitude();
if dist < 2.0 {
control.move_dir = Vec2::zero();
controller.move_dir = Vec2::zero();
if rand::random::<f32>() < 0.2 {
attacks
@ -82,7 +82,7 @@ impl<'a> System<'a> for Sys {
false
} else if dist < 60.0 {
control.move_dir =
controller.move_dir =
Vec2::<f32>::from(tgt_pos.0 - pos.0).normalized() * 0.96;
false
@ -91,7 +91,7 @@ impl<'a> System<'a> for Sys {
}
}
None => {
control.move_dir = Vec2::one();
controller.move_dir = Vec2::one();
true
}
};

View File

@ -0,0 +1,114 @@
use crate::{
comp::{
phys::{ForceUpdate, Ori, Pos, Vel},
Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, Stats,
},
state::{DeltaTime, Uid},
terrain::TerrainMap,
vol::{ReadVol, Vox},
};
use log::warn;
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
use vek::*;
const HUMANOID_ACCEL: f32 = 100.0;
const HUMANOID_SPEED: f32 = 500.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_JUMP_ACCEL: f32 = 16.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
Read<'a, DeltaTime>,
ReadStorage<'a, Controller>,
ReadStorage<'a, Stats>,
ReadExpect<'a, TerrainMap>,
ReadStorage<'a, Pos>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Ori>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Attacking>,
WriteStorage<'a, Gliding>,
WriteStorage<'a, ForceUpdate>,
);
fn run(
&mut self,
(
entities,
dt,
controllers,
stats,
terrain,
positions,
mut velocities,
mut orientations,
mut jumps,
mut attacks,
mut glides,
mut force_updates,
): Self::SystemData,
) {
for (entity, controller, stats, pos, mut vel, mut ori) in (
&entities,
&controllers,
&stats,
&positions,
&mut velocities,
&mut orientations,
)
.join()
{
if stats.is_dead {
continue;
}
let on_ground = terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0;
let gliding = controller.glide && vel.0.z < 0.0;
let move_dir = if controller.move_dir.magnitude() > 1.0 {
controller.move_dir.normalized()
} else {
controller.move_dir
};
if on_ground {
// Move player according to move_dir
if vel.0.magnitude() < HUMANOID_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_ACCEL;
}
// Jump
if controller.jump && vel.0.z <= 0.0 {
vel.0.z = HUMANOID_JUMP_ACCEL;
}
} else if gliding && vel.0.magnitude() < GLIDE_SPEED {
let anti_grav = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
vel.0.z += dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
vel.0 += Vec2::broadcast(dt.0) * move_dir * GLIDE_ACCEL;
} else if vel.0.magnitude() < HUMANOID_AIR_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_AIR_ACCEL;
}
// Set direction based on velocity
if vel.0.magnitude_squared() != 0.0 {
ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
}
// Attack
if controller.attack && attacks.get(entity).is_none() {
attacks.insert(entity, Attacking::start());
}
}
}
}

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
phys::{ForceUpdate, Ori, Pos, Vel},
Animation, AnimationInfo, Attacking, Control, Gliding, HealthSource, Jumping, Stats,
Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, Stats,
},
state::{DeltaTime, Uid},
terrain::TerrainMap,
@ -13,17 +13,6 @@ use vek::*;
// Basic ECS AI agent system
pub struct Sys;
const HUMANOID_ACCEL: f32 = 100.0;
const HUMANOID_SPEED: f32 = 500.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_JUMP_ACCEL: f32 = 16.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
@ -35,7 +24,7 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Ori>,
WriteStorage<'a, AnimationInfo>,
WriteStorage<'a, Stats>,
ReadStorage<'a, Control>,
ReadStorage<'a, Controller>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Gliding>,
WriteStorage<'a, Attacking>,
@ -54,74 +43,38 @@ impl<'a> System<'a> for Sys {
mut orientations,
mut animation_infos,
mut stats,
controls,
controllers,
mut jumps,
glides,
mut attacks,
mut force_updates,
): Self::SystemData,
) {
for (entity, pos, control, stats, mut ori, mut vel) in (
for (entity, pos, controller, stats, mut ori, mut vel) in (
&entities,
&positions,
&controls,
&controllers,
&stats,
&mut orientations,
&mut velocities,
)
.join()
{
// Disable while dead TODO: Replace with client states
if stats.is_dead {
continue;
}
let on_ground = terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0;
let gliding = glides.get(entity).is_some() && vel.0.z < 0.0;
let move_dir = if control.move_dir.magnitude() > 1.0 {
control.move_dir.normalized()
} else {
control.move_dir
};
if on_ground {
// Move player according to move_dir
if vel.0.magnitude() < HUMANOID_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_ACCEL;
}
// Jump
if jumps.get(entity).is_some() && vel.0.z <= 0.0 {
vel.0.z = HUMANOID_JUMP_ACCEL;
jumps.remove(entity);
}
} else if gliding && vel.0.magnitude() < GLIDE_SPEED {
let anti_grav = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
vel.0.z += dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
vel.0 += Vec2::broadcast(dt.0) * move_dir * GLIDE_ACCEL;
} else if vel.0.magnitude() < HUMANOID_AIR_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_AIR_ACCEL;
}
// Set direction based on velocity
if vel.0.magnitude_squared() != 0.0 {
ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
}
let animation = if on_ground {
if control.move_dir.magnitude() > 0.01 {
if controller.move_dir.magnitude() > 0.01 {
Animation::Run
} else if attacks.get(entity).is_some() {
Animation::Attack
} else {
Animation::Idle
}
} else if glides.get(entity).is_some() {
} else if controller.glide {
Animation::Gliding
} else {
Animation::Jump

View File

@ -1,6 +1,7 @@
pub mod actions;
pub mod agent;
pub mod animation;
pub mod controller;
pub mod inputs;
pub mod phys;
mod stats;
@ -10,6 +11,7 @@ use specs::DispatcherBuilder;
// System names
const AGENT_SYS: &str = "agent_sys";
const CONTROLLER_SYS: &str = "controller_sys";
const INPUTS_SYS: &str = "inputs_sys";
const ACTIONS_SYS: &str = "actions_sys";
const PHYS_SYS: &str = "phys_sys";
@ -20,6 +22,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
dispatch_builder.add(phys::Sys, PHYS_SYS, &[]);
dispatch_builder.add(actions::Sys, ACTIONS_SYS, &[]);
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[]);
dispatch_builder.add(inputs::Sys, INPUTS_SYS, &[]);
dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[]);
dispatch_builder.add(stats::Sys, STATS_SYS, &[INPUTS_SYS]);

578
diff Normal file
View File

@ -0,0 +1,578 @@
diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs
index f68a5b4..8ebd46f 100644
--- a/chat-cli/src/main.rs
+++ b/chat-cli/src/main.rs
@@ -29,7 +29,7 @@ fn main() {
client.send_chat("Hello!".to_string());
loop {
- let events = match client.tick(comp::Control::default(), clock.get_last_delta()) {
+ let events = match client.tick(comp::Controller::default(), clock.get_last_delta()) {
Ok(events) => events,
Err(err) => {
error!("Error: {:?}", err);
diff --git a/client/src/lib.rs b/client/src/lib.rs
index f491359..ee3b62e 100644
--- a/client/src/lib.rs
+++ b/client/src/lib.rs
@@ -154,52 +154,6 @@ impl Client {
self.postbox.send_message(ClientMsg::Chat(msg))
}
- /// Jump locally, the new positions will be synced to the server
- #[allow(dead_code)]
- pub fn jump(&mut self) {
- if self.client_state != ClientState::Character {
- return;
- }
- self.state.write_component(self.entity, comp::Jumping);
- }
-
- /// Start to glide locally, animation will be synced
- #[allow(dead_code)]
- pub fn glide(&mut self, state: bool) {
- if self.client_state != ClientState::Character {
- return;
- }
- if state {
- self.state.write_component(self.entity, comp::Gliding);
- } else {
- self.state
- .ecs_mut()
- .write_storage::<comp::Gliding>()
- .remove(self.entity);
- }
- }
-
- /// Start to attack
- #[allow(dead_code)]
- pub fn attack(&mut self) {
- if self.client_state != ClientState::Character {
- return;
- }
- // TODO: Test if attack is possible using timeout
- self.state
- .write_component(self.entity, comp::Attacking::start());
- self.postbox.send_message(ClientMsg::Attack);
- }
-
- /// Tell the server the client wants to respawn.
- #[allow(dead_code)]
- pub fn respawn(&mut self) {
- if self.client_state != ClientState::Dead {
- return;
- }
- self.postbox.send_message(ClientMsg::Respawn)
- }
-
/// Remove all cached terrain
#[allow(dead_code)]
pub fn clear_terrain(&mut self) {
@@ -209,7 +163,7 @@ impl Client {
/// Execute a single client tick, handle input and update the game state by the given duration.
#[allow(dead_code)]
- pub fn tick(&mut self, control: comp::Control, dt: Duration) -> Result<Vec<Event>, Error> {
+ pub fn tick(&mut self, controller: comp::Controller, dt: Duration) -> Result<Vec<Event>, Error> {
// This tick function is the centre of the Veloren universe. Most client-side things are
// managed from here, and as such it's important that it stays organised. Please consult
// the core developers before making significant changes to this code. Here is the
@@ -226,10 +180,8 @@ impl Client {
// 1) Handle input from frontend.
// Pass character actions from frontend input to the player's entity.
- // TODO: Only do this if the entity already has a Inputs component!
- if self.client_state == ClientState::Character {
- self.state.write_component(self.entity, control.clone());
- }
+ self.state.write_component(self.entity, controller.clone());
+ self.postbox.send_message(ClientMsg::Controller(controller));
// 2) Build up a list of events for this frame, to be passed to the frontend.
let mut frontend_events = Vec::new();
diff --git a/common/src/comp/inputs.rs b/common/src/comp/inputs.rs
index ce55110..27b7b75 100644
--- a/common/src/comp/inputs.rs
+++ b/common/src/comp/inputs.rs
@@ -1,11 +1,6 @@
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
use vek::*;
-#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
-pub struct Control {
- pub move_dir: Vec2<f32>,
-}
-
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Respawning;
@@ -14,16 +9,13 @@ pub struct Attacking {
pub time: f32,
pub applied: bool,
}
+
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Jumping;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Gliding;
-impl Component for Control {
- type Storage = VecStorage<Self>;
-}
-
impl Component for Respawning {
type Storage = NullStorage<Self>;
}
@@ -36,6 +28,7 @@ impl Attacking {
}
}
}
+
impl Component for Attacking {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs
index ee351db..2ca2e12 100644
--- a/common/src/comp/mod.rs
+++ b/common/src/comp/mod.rs
@@ -2,6 +2,7 @@ pub mod actor;
pub mod agent;
pub mod animation;
pub mod inputs;
+pub mod controller;
pub mod phys;
pub mod player;
pub mod stats;
@@ -15,8 +16,8 @@ pub use actor::QuadrupedMediumBody;
pub use agent::Agent;
pub use animation::Animation;
pub use animation::AnimationInfo;
+pub use controller::Controller;
pub use inputs::Attacking;
-pub use inputs::Control;
pub use inputs::Gliding;
pub use inputs::Jumping;
pub use inputs::Respawning;
diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs
index aa765d0..1187831 100644
--- a/common/src/msg/client.rs
+++ b/common/src/msg/client.rs
@@ -11,8 +11,7 @@ pub enum ClientMsg {
name: String,
body: comp::Body,
},
- Attack,
- Respawn,
+ Controller(comp::Controller),
RequestState(ClientState),
SetViewDistance(u32),
Ping,
diff --git a/common/src/state.rs b/common/src/state.rs
index 9357c60..630f7c8 100644
--- a/common/src/state.rs
+++ b/common/src/state.rs
@@ -110,9 +110,9 @@ impl State {
ecs.register::<comp::phys::Vel>();
ecs.register::<comp::phys::Ori>();
ecs.register::<comp::AnimationInfo>();
+ ecs.register::<comp::Controller>();
// Register client-local components
- ecs.register::<comp::Control>();
ecs.register::<comp::Jumping>();
// Register server-local components
diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs
index 55e845c..0e5e2f8 100644
--- a/common/src/sys/agent.rs
+++ b/common/src/sys/agent.rs
@@ -1,4 +1,4 @@
-use crate::comp::{phys::Pos, Agent, Attacking, Control, Jumping};
+use crate::comp::{phys::Pos, Agent, Attacking, Controller, Jumping};
use log::warn;
use rand::{seq::SliceRandom, thread_rng};
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
@@ -12,17 +12,17 @@ impl<'a> System<'a> for Sys {
Entities<'a>,
WriteStorage<'a, Agent>,
ReadStorage<'a, Pos>,
- WriteStorage<'a, Control>,
+ WriteStorage<'a, Controller>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Attacking>,
);
fn run(
&mut self,
- (entities, mut agents, positions, mut controls, mut jumps, mut attacks): Self::SystemData,
+ (entities, mut agents, positions, mut controllers, mut jumps, mut attacks): Self::SystemData,
) {
- for (entity, agent, pos, control) in
- (&entities, &mut agents, &positions, &mut controls).join()
+ for (entity, agent, pos, controller) in
+ (&entities, &mut agents, &positions, &mut controllers).join()
{
match agent {
Agent::Wanderer(bearing) => {
@@ -32,7 +32,7 @@ impl<'a> System<'a> for Sys {
- pos.0 * 0.0002;
if bearing.magnitude_squared() != 0.0 {
- control.move_dir = bearing.normalized();
+ controller.move_dir = bearing.normalized();
}
}
Agent::Pet { target, offset } => {
@@ -49,7 +49,7 @@ impl<'a> System<'a> for Sys {
// Move towards the target.
let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude();
- control.move_dir = if dist > 5.0 {
+ controller.move_dir = if dist > 5.0 {
Vec2::from(tgt_pos - pos.0).normalized()
} else if dist < 1.5 && dist > 0.0 {
Vec2::from(pos.0 - tgt_pos).normalized()
@@ -57,7 +57,7 @@ impl<'a> System<'a> for Sys {
Vec2::zero()
};
}
- _ => control.move_dir = Vec2::zero(),
+ _ => controller.move_dir = Vec2::zero(),
}
// Change offset occasionally.
@@ -72,7 +72,7 @@ impl<'a> System<'a> for Sys {
Some(tgt_pos) => {
let dist = Vec2::<f32>::from(tgt_pos.0 - pos.0).magnitude();
if dist < 2.0 {
- control.move_dir = Vec2::zero();
+ controller.move_dir = Vec2::zero();
if rand::random::<f32>() < 0.2 {
attacks
@@ -82,7 +82,7 @@ impl<'a> System<'a> for Sys {
false
} else if dist < 60.0 {
- control.move_dir =
+ controller.move_dir =
Vec2::<f32>::from(tgt_pos.0 - pos.0).normalized() * 0.96;
false
@@ -91,7 +91,7 @@ impl<'a> System<'a> for Sys {
}
}
None => {
- control.move_dir = Vec2::one();
+ controller.move_dir = Vec2::one();
true
}
};
diff --git a/common/src/sys/inputs.rs b/common/src/sys/inputs.rs
index 288a0e0..6cadba5 100644
--- a/common/src/sys/inputs.rs
+++ b/common/src/sys/inputs.rs
@@ -1,7 +1,7 @@
use crate::{
comp::{
phys::{ForceUpdate, Ori, Pos, Vel},
- Animation, AnimationInfo, Attacking, Control, Gliding, HealthSource, Jumping, Stats,
+ Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, Stats,
},
state::{DeltaTime, Uid},
terrain::TerrainMap,
@@ -13,17 +13,6 @@ use vek::*;
// Basic ECS AI agent system
pub struct Sys;
-
-const HUMANOID_ACCEL: f32 = 100.0;
-const HUMANOID_SPEED: f32 = 500.0;
-const HUMANOID_AIR_ACCEL: f32 = 10.0;
-const HUMANOID_AIR_SPEED: f32 = 100.0;
-const HUMANOID_JUMP_ACCEL: f32 = 16.0;
-const GLIDE_ACCEL: f32 = 15.0;
-const GLIDE_SPEED: f32 = 45.0;
-// Gravity is 9.81 * 4, so this makes gravity equal to .15
-const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
-
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
@@ -35,7 +24,7 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Ori>,
WriteStorage<'a, AnimationInfo>,
WriteStorage<'a, Stats>,
- ReadStorage<'a, Control>,
+ ReadStorage<'a, Controller>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Gliding>,
WriteStorage<'a, Attacking>,
@@ -54,67 +43,31 @@ impl<'a> System<'a> for Sys {
mut orientations,
mut animation_infos,
mut stats,
- controls,
+ controllers,
mut jumps,
glides,
mut attacks,
mut force_updates,
): Self::SystemData,
) {
- for (entity, pos, control, stats, mut ori, mut vel) in (
+ for (entity, pos, controller, stats, mut ori, mut vel) in (
&entities,
&positions,
- &controls,
+ &controllers,
&stats,
&mut orientations,
&mut velocities,
)
.join()
{
- // Disable while dead TODO: Replace with client states
- if stats.is_dead {
- continue;
- }
-
let on_ground = terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0;
- let gliding = glides.get(entity).is_some() && vel.0.z < 0.0;
- let move_dir = if control.move_dir.magnitude() > 1.0 {
- control.move_dir.normalized()
- } else {
- control.move_dir
- };
-
- if on_ground {
- // Move player according to move_dir
- if vel.0.magnitude() < HUMANOID_SPEED {
- vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_ACCEL;
- }
-
- // Jump
- if jumps.get(entity).is_some() && vel.0.z <= 0.0 {
- vel.0.z = HUMANOID_JUMP_ACCEL;
- jumps.remove(entity);
- }
- } else if gliding && vel.0.magnitude() < GLIDE_SPEED {
- let anti_grav = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
- vel.0.z += dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
- vel.0 += Vec2::broadcast(dt.0) * move_dir * GLIDE_ACCEL;
- } else if vel.0.magnitude() < HUMANOID_AIR_SPEED {
- vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_AIR_ACCEL;
- }
-
- // Set direction based on velocity
- if vel.0.magnitude_squared() != 0.0 {
- ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
- }
-
let animation = if on_ground {
- if control.move_dir.magnitude() > 0.01 {
+ if controller.move_dir.magnitude() > 0.01 {
Animation::Run
} else if attacks.get(entity).is_some() {
Animation::Attack
@@ -149,6 +102,7 @@ impl<'a> System<'a> for Sys {
(&entities, &uids, &positions, &orientations, &mut attacks).join()
{
if !attacking.applied {
+ dbg!();
for (b, pos_b, stat_b, mut vel_b) in
(&entities, &positions, &mut stats, &mut velocities).join()
{
diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs
index da6f629..e6a5917 100644
--- a/common/src/sys/mod.rs
+++ b/common/src/sys/mod.rs
@@ -1,6 +1,7 @@
pub mod actions;
pub mod agent;
pub mod animation;
+pub mod controller;
pub mod inputs;
pub mod phys;
mod stats;
@@ -10,6 +11,7 @@ use specs::DispatcherBuilder;
// System names
const AGENT_SYS: &str = "agent_sys";
+const CONTROLLER_SYS: &str = "controller_sys";
const INPUTS_SYS: &str = "inputs_sys";
const ACTIONS_SYS: &str = "actions_sys";
const PHYS_SYS: &str = "phys_sys";
@@ -20,6 +22,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
dispatch_builder.add(phys::Sys, PHYS_SYS, &[]);
dispatch_builder.add(actions::Sys, ACTIONS_SYS, &[]);
+ dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[]);
dispatch_builder.add(inputs::Sys, INPUTS_SYS, &[]);
dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[]);
dispatch_builder.add(stats::Sys, STATS_SYS, &[INPUTS_SYS]);
diff --git a/server/src/lib.rs b/server/src/lib.rs
index ead806c..6260f0a 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -145,7 +145,7 @@ impl Server {
.with(pos)
.with(comp::phys::Vel(Vec3::zero()))
.with(comp::phys::Ori(Vec3::unit_y()))
- .with(comp::Control::default())
+ .with(comp::Controller::default())
.with(comp::AnimationInfo::default())
.with(comp::Actor::Character { name, body })
.with(comp::Stats::default())
@@ -164,6 +164,7 @@ impl Server {
state.write_component(entity, comp::Actor::Character { name, body });
state.write_component(entity, comp::Stats::default());
state.write_component(entity, comp::AnimationInfo::default());
+ state.write_component(entity, comp::Controller::default());
state.write_component(entity, comp::phys::Pos(spawn_point));
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Ori(Vec3::unit_y()));
@@ -492,25 +493,16 @@ impl Server {
}
ClientState::Pending => {}
},
- ClientMsg::Attack => match client.client_state {
- ClientState::Character => {
- if state
- .ecs()
- .read_storage::<comp::Attacking>()
- .get(entity)
- .is_none()
- {
- state.write_component(entity, comp::Attacking::start());
- }
+ ClientMsg::Controller(controller) => match client.client_state {
+ ClientState::Connected | ClientState::Registered | ClientState::Spectator => {
+ client.error_state(RequestStateError::Impossible)
}
- _ => client.error_state(RequestStateError::Impossible),
- },
- ClientMsg::Respawn => match client.client_state {
- ClientState::Dead => {
- state.write_component(entity, comp::Respawning);
+ ClientState::Dead
+ | ClientState::Character => {
+ state.write_component(entity, controller);
}
- _ => client.error_state(RequestStateError::Impossible),
- },
+ ClientState::Pending => {}
+ }
ClientMsg::Chat(msg) => match client.client_state {
ClientState::Connected => {
client.error_state(RequestStateError::Impossible)
diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs
index 2192ce7..1a3e3e3 100644
--- a/voxygen/src/menu/char_selection/mod.rs
+++ b/voxygen/src/menu/char_selection/mod.rs
@@ -110,7 +110,7 @@ impl PlayState for CharSelectionState {
if let Err(err) = self
.client
.borrow_mut()
- .tick(comp::Control::default(), clock.get_last_delta())
+ .tick(comp::Controller::default(), clock.get_last_delta())
{
error!("Failed to tick the scene: {:?}", err);
return PlayStateResult::Pop;
diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs
index 913e421..bd5d876 100644
--- a/voxygen/src/session.rs
+++ b/voxygen/src/session.rs
@@ -18,8 +18,9 @@ const FPS: u64 = 60;
pub struct SessionState {
scene: Scene,
client: Rc<RefCell<Client>>,
- key_state: KeyState,
hud: Hud,
+ key_state: KeyState,
+ controller: comp::Controller,
}
/// Represents an active game session (i.e., the one being played).
@@ -32,6 +33,7 @@ impl SessionState {
scene,
client,
key_state: KeyState::new(),
+ controller: comp::Controller::default(),
hud: Hud::new(window),
}
}
@@ -47,21 +49,11 @@ const BG_COLOR: Rgba<f32> = Rgba {
impl SessionState {
/// Tick the session (and the client attached to it).
- pub fn tick(&mut self, dt: Duration) -> Result<(), Error> {
- // 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_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
-
+ fn tick(&mut self, dt: Duration) -> Result<(), Error> {
for event in self
.client
.borrow_mut()
- .tick(comp::Control { move_dir }, dt)?
+ .tick(self.controller.clone(), dt)?
{
match event {
client::Event::Chat(msg) => {
@@ -121,19 +113,19 @@ impl PlayState for SessionState {
Event::Close => {
return PlayStateResult::Shutdown;
}
- Event::InputUpdate(GameInput::Attack, true) => {
- self.client.borrow_mut().attack();
- self.client.borrow_mut().respawn();
+ Event::InputUpdate(GameInput::Attack, state) => {
+ self.controller.attack = state;
+ self.controller.respawn = state; // TODO: Don't do both
}
- Event::InputUpdate(GameInput::Jump, true) => {
- self.client.borrow_mut().jump();
+ Event::InputUpdate(GameInput::Jump, state) => {
+ self.controller.jump = state;
}
Event::InputUpdate(GameInput::MoveForward, state) => self.key_state.up = state,
Event::InputUpdate(GameInput::MoveBack, state) => self.key_state.down = state,
Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state,
Event::InputUpdate(GameInput::MoveRight, state) => self.key_state.right = state,
Event::InputUpdate(GameInput::Glide, state) => {
- self.client.borrow_mut().glide(state)
+ self.controller.glide = state;
}
// Pass all other events to the scene
@@ -143,6 +135,17 @@ impl PlayState for SessionState {
}
}
+ // 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();
+ self.controller.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
+
+
// Perform an in-game tick.
if let Err(err) = self.tick(clock.get_last_delta()) {
error!("Failed to tick the scene: {:?}", err);

View File

@ -145,7 +145,7 @@ impl Server {
.with(pos)
.with(comp::phys::Vel(Vec3::zero()))
.with(comp::phys::Ori(Vec3::unit_y()))
.with(comp::Control::default())
.with(comp::Controller::default())
.with(comp::AnimationInfo::default())
.with(comp::Actor::Character { name, body })
.with(comp::Stats::default())
@ -164,6 +164,7 @@ impl Server {
state.write_component(entity, comp::Actor::Character { name, body });
state.write_component(entity, comp::Stats::default());
state.write_component(entity, comp::AnimationInfo::default());
state.write_component(entity, comp::Controller::default());
state.write_component(entity, comp::phys::Pos(spawn_point));
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Ori(Vec3::unit_y()));
@ -492,24 +493,16 @@ impl Server {
}
ClientState::Pending => {}
},
ClientMsg::Attack => match client.client_state {
ClientState::Character => {
if state
.ecs()
.read_storage::<comp::Attacking>()
.get(entity)
.is_none()
{
state.write_component(entity, comp::Attacking::start());
}
ClientMsg::Controller(controller) => match client.client_state {
ClientState::Connected
| ClientState::Registered
| ClientState::Spectator => {
client.error_state(RequestStateError::Impossible)
}
_ => client.error_state(RequestStateError::Impossible),
},
ClientMsg::Respawn => match client.client_state {
ClientState::Dead => {
state.write_component(entity, comp::Respawning);
ClientState::Dead | ClientState::Character => {
state.write_component(entity, controller);
}
_ => client.error_state(RequestStateError::Impossible),
ClientState::Pending => {}
},
ClientMsg::Chat(msg) => match client.client_state {
ClientState::Connected => {

View File

@ -108,7 +108,7 @@ impl PlayState for CharSelectionState {
if let Err(err) = self
.client
.borrow_mut()
.tick(comp::Control::default(), clock.get_last_delta())
.tick(comp::Controller::default(), clock.get_last_delta())
{
error!("Failed to tick the scene: {:?}", err);
return PlayStateResult::Pop;

View File

@ -16,8 +16,9 @@ use vek::*;
pub struct SessionState {
scene: Scene,
client: Rc<RefCell<Client>>,
key_state: KeyState,
hud: Hud,
key_state: KeyState,
controller: comp::Controller,
}
/// Represents an active game session (i.e., the one being played).
@ -30,6 +31,7 @@ impl SessionState {
scene,
client,
key_state: KeyState::new(),
controller: comp::Controller::default(),
hud: Hud::new(window),
}
}
@ -45,22 +47,8 @@ const BG_COLOR: Rgba<f32> = Rgba {
impl SessionState {
/// Tick the session (and the client attached to it).
pub fn tick(&mut self, dt: Duration) -> Result<(), Error> {
// 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_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
for event in self
.client
.borrow_mut()
.tick(comp::Control { move_dir }, dt)?
{
fn tick(&mut self, dt: Duration) -> Result<(), Error> {
for event in self.client.borrow_mut().tick(self.controller.clone(), dt)? {
match event {
client::Event::Chat(msg) => {
self.hud.new_message(msg);
@ -127,19 +115,19 @@ impl PlayState for SessionState {
Event::Close => {
return PlayStateResult::Shutdown;
}
Event::InputUpdate(GameInput::Attack, true) => {
self.client.borrow_mut().attack();
self.client.borrow_mut().respawn();
Event::InputUpdate(GameInput::Attack, state) => {
self.controller.attack = state;
self.controller.respawn = state; // TODO: Don't do both
}
Event::InputUpdate(GameInput::Jump, true) => {
self.client.borrow_mut().jump();
Event::InputUpdate(GameInput::Jump, state) => {
self.controller.jump = state;
}
Event::InputUpdate(GameInput::MoveForward, state) => self.key_state.up = state,
Event::InputUpdate(GameInput::MoveBack, state) => self.key_state.down = state,
Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state,
Event::InputUpdate(GameInput::MoveRight, state) => self.key_state.right = state,
Event::InputUpdate(GameInput::Glide, state) => {
self.client.borrow_mut().glide(state)
self.controller.glide = state;
}
// Pass all other events to the scene
@ -149,6 +137,16 @@ impl PlayState for SessionState {
}
}
// 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();
self.controller.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
// Perform an in-game tick.
if let Err(err) = self.tick(clock.get_last_delta()) {
error!("Failed to tick the scene: {:?}", err);