Animation States

Former-commit-id: 769f09dc44143b5e54623ea819db2d6149429184
This commit is contained in:
Piotr 2019-04-16 14:29:44 +00:00 committed by Joshua Barretto
parent 0a2b0e98e8
commit ddb32eec30
12 changed files with 110 additions and 4 deletions

View File

@ -160,6 +160,9 @@ impl Client {
self.state.write_component(self.player, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY) * 0.1));
if input.move_dir.magnitude() > 0.01 {
self.state.write_component(self.player, comp::phys::Dir(input.move_dir.normalized().into()));
self.state.write_component(self.player, comp::Animation::Run);
} else {
self.state.write_component(self.player, comp::Animation::Idle);
}
}
@ -178,6 +181,11 @@ impl Client {
_ => {},
}
// Update the server about the player's currently playing animation
if let Some(animation) = self.state.read_storage().get(self.player).cloned() {
self.postbox.send_message(ClientMsg::PlayerAnimation(animation));
}
// Request chunks from the server
if let Some(pos) = self.state.read_storage::<comp::phys::Pos>().get(self.player) {
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
@ -234,6 +242,12 @@ impl Client {
},
None => {},
},
ServerMsg::EntityAnimation { entity, animation } => match self.state.ecs().entity_from_uid(entity) {
Some(entity) => {
self.state.write_component(entity, animation);
},
None => {},
},
ServerMsg::TerrainChunkUpdate { key, chunk } => {
self.state.insert_chunk(key, *chunk);
self.pending_chunks.remove(&key);

View File

@ -18,6 +18,12 @@ pub enum Gender {
Unspecified,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum Animation {
Idle,
Run,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Character {
race: Race,
@ -27,6 +33,7 @@ pub struct Character {
belt: (),
arms: (),
feet: (),
}
impl Character {
@ -47,3 +54,7 @@ impl Character {
impl Component for Character {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
impl Component for Animation {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

View File

@ -5,3 +5,4 @@ pub mod phys;
// Reexports
pub use character::Character;
pub use player::Player;
pub use character::Animation;

View File

@ -6,10 +6,12 @@ pub enum ClientMsg {
Connect {
player: comp::Player,
character: Option<comp::Character>,
},
Ping,
Pong,
Chat(String),
PlayerAnimation(comp::character::Animation),
PlayerPhysics {
pos: comp::phys::Pos,
vel: comp::phys::Vel,

View File

@ -23,6 +23,10 @@ pub enum ServerMsg {
vel: comp::phys::Vel,
dir: comp::phys::Dir,
},
EntityAnimation {
entity: u64,
animation: comp::Animation,
},
TerrainChunkUpdate {
key: Vec3<i32>,
chunk: Box<TerrainChunk>,

View File

@ -100,6 +100,7 @@ impl State {
ecs.internal_mut().register::<comp::phys::Pos>();
ecs.internal_mut().register::<comp::phys::Vel>();
ecs.internal_mut().register::<comp::phys::Dir>();
ecs.internal_mut().register::<comp::Animation>();
// Register resources used by the ECS
ecs.internal_mut().add_resource(TimeOfDay(0.0));

View File

@ -234,6 +234,7 @@ impl Server {
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
if let Some(character) = character {
state.write_component(entity, character);
}
state.write_component(entity, comp::phys::ForceUpdate);
@ -257,6 +258,7 @@ impl Server {
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
ClientMsg::Pong => {},
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
ClientMsg::PlayerAnimation(animation) => state.write_component(entity, animation),
ClientMsg::PlayerPhysics { pos, vel, dir } => {
state.write_component(entity, pos);
state.write_component(entity, vel);
@ -347,11 +349,25 @@ impl Server {
};
match force_update {
Some(_) => self.clients.notify_connected(msg),
None => self.clients.notify_connected_except(entity, msg),
}
}
// Sync animation states
for (entity, &uid, &animation) in (
&self.state.ecs().internal().entities(),
&self.state.ecs().internal().read_storage::<Uid>(),
&self.state.ecs().internal().read_storage::<comp::Animation>(),
).join() {
self.clients.notify_connected_except(entity, ServerMsg::EntityAnimation {
entity: uid.into(),
animation,
});
}
// Remove all force flags
self.state.ecs_mut().internal_mut().write_storage::<comp::phys::ForceUpdate>().clear();
}

View File

@ -0,0 +1,48 @@
// Standard
use std::f32::consts::PI;
// Library
use vek::*;
// Local
use super::{
CharacterSkeleton,
super::Animation,
};
pub struct IdleAnimation;
//TODO: Make it actually good, possibly add the head rotating slightly, add breathing, etc.
impl Animation for IdleAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = f64;
fn update_skeleton(
skeleton: &mut Self::Skeleton,
time: f64,
) {
skeleton.head.offset = Vec3::unit_z() * 13.0 / 11.0;
skeleton.head.ori = Quaternion::rotation_z(0.0);
skeleton.chest.offset = Vec3::unit_z() * 9.0 / 11.0;
skeleton.chest.ori = Quaternion::rotation_z(0.0);
skeleton.belt.offset = Vec3::unit_z() * 7.0 / 11.0;
skeleton.belt.ori = Quaternion::rotation_z(0.0);
skeleton.shorts.offset = Vec3::unit_z() * 4.0 / 11.0;
skeleton.shorts.ori = Quaternion::rotation_z(0.0);
skeleton.l_hand.offset = Vec3::new(-8.0, 0.0, 9.0) / 11.0;
skeleton.r_hand.offset = Vec3::new(8.0, 0.0, 9.0 ) / 11.0;
skeleton.l_foot.offset = Vec3::new(-3.5, 0.0, 3.0) / 11.0;
skeleton.l_foot.ori = Quaternion::rotation_x(0.0);
skeleton.r_foot.offset = Vec3::new(3.5, 0.0, 3.0) / 11.0;
skeleton.r_foot.ori = Quaternion::rotation_x(0.0);
skeleton.back.offset = Vec3::new(-9.0, 5.0, 18.0);
skeleton.back.ori = Quaternion::rotation_y(2.5);
skeleton.back.scale = Vec3::one();
}
}

View File

@ -1,7 +1,9 @@
pub mod run;
pub mod idle;
// Reexports
pub use self::run::RunAnimation;
pub use self::idle::IdleAnimation;
// Crate
use crate::render::FigureBoneData;

View File

@ -22,10 +22,10 @@ pub struct ClientInit {
impl ClientInit {
pub fn new(
connection_args: (String, u16, bool),
client_args: (comp::Player, Option<comp::Character>, u64),
client_args: (comp::Player, Option<comp::Character>, Option<comp::Animation>, u64),
) -> Self {
let (server_address, default_port, prefer_ipv6) = connection_args;
let (player, character, view_distance) = client_args;
let (player, character, animation, view_distance) = client_args;
let (tx, rx) = channel();

View File

@ -95,6 +95,7 @@ impl PlayState for MainMenuState {
(
comp::Player::new(username.clone()),
Some(comp::Character::test()),
Some(comp::Animation::Idle),
300,
),
)));

View File

@ -8,6 +8,7 @@ use client::Client;
use common::{
comp,
figure::Segment,
msg
};
use crate::{
Error,
@ -27,6 +28,7 @@ use crate::{
character::{
CharacterSkeleton,
RunAnimation,
IdleAnimation,
},
},
mesh::Meshable,
@ -81,17 +83,21 @@ impl Figures {
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
let time = client.state().get_time();
let ecs = client.state_mut().ecs_mut().internal_mut();
for (entity, pos, dir, character) in (
for (entity, pos, dir, character, animation) in (
&ecs.entities(),
&ecs.read_storage::<comp::phys::Pos>(),
&ecs.read_storage::<comp::phys::Dir>(),
&ecs.read_storage::<comp::Character>(),
&ecs.read_storage::<comp::Animation>(),
).join() {
let state = self.states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
RunAnimation::update_skeleton(&mut state.skeleton, time);
match animation {
comp::character::Animation::Idle => IdleAnimation::update_skeleton(&mut state.skeleton, time),
comp::character::Animation::Run => RunAnimation::update_skeleton(&mut state.skeleton, time),
}
state.update(renderer, pos.0, dir.0);
}