Merge branch 'anim' into 'master'

Animation States

See merge request veloren/veloren!31

Former-commit-id: 4f59aa8df9dbbcb9e89696a3b76b17a77a0f706a
This commit is contained in:
Joshua Barretto 2019-04-16 14:29:45 +00:00
commit 19d6b7b38a
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)); 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 { 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::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 // Request chunks from the server
if let Some(pos) = self.state.read_storage::<comp::phys::Pos>().get(self.player) { 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)); let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
@ -234,6 +242,12 @@ impl Client {
}, },
None => {}, 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 } => { ServerMsg::TerrainChunkUpdate { key, chunk } => {
self.state.insert_chunk(key, *chunk); self.state.insert_chunk(key, *chunk);
self.pending_chunks.remove(&key); self.pending_chunks.remove(&key);

View File

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

View File

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

View File

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

View File

@ -100,6 +100,7 @@ impl State {
ecs.internal_mut().register::<comp::phys::Pos>(); ecs.internal_mut().register::<comp::phys::Pos>();
ecs.internal_mut().register::<comp::phys::Vel>(); ecs.internal_mut().register::<comp::phys::Vel>();
ecs.internal_mut().register::<comp::phys::Dir>(); ecs.internal_mut().register::<comp::phys::Dir>();
ecs.internal_mut().register::<comp::Animation>();
// Register resources used by the ECS // Register resources used by the ECS
ecs.internal_mut().add_resource(TimeOfDay(0.0)); 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())); state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
if let Some(character) = character { if let Some(character) = character {
state.write_component(entity, character); state.write_component(entity, character);
} }
state.write_component(entity, comp::phys::ForceUpdate); state.write_component(entity, comp::phys::ForceUpdate);
@ -257,6 +258,7 @@ impl Server {
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong), ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
ClientMsg::Pong => {}, ClientMsg::Pong => {},
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)), ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
ClientMsg::PlayerAnimation(animation) => state.write_component(entity, animation),
ClientMsg::PlayerPhysics { pos, vel, dir } => { ClientMsg::PlayerPhysics { pos, vel, dir } => {
state.write_component(entity, pos); state.write_component(entity, pos);
state.write_component(entity, vel); state.write_component(entity, vel);
@ -347,11 +349,25 @@ impl Server {
}; };
match force_update { match force_update {
Some(_) => self.clients.notify_connected(msg), Some(_) => self.clients.notify_connected(msg),
None => self.clients.notify_connected_except(entity, 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 // Remove all force flags
self.state.ecs_mut().internal_mut().write_storage::<comp::phys::ForceUpdate>().clear(); 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 run;
pub mod idle;
// Reexports // Reexports
pub use self::run::RunAnimation; pub use self::run::RunAnimation;
pub use self::idle::IdleAnimation;
// Crate // Crate
use crate::render::FigureBoneData; use crate::render::FigureBoneData;

View File

@ -22,10 +22,10 @@ pub struct ClientInit {
impl ClientInit { impl ClientInit {
pub fn new( pub fn new(
connection_args: (String, u16, bool), 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 { ) -> Self {
let (server_address, default_port, prefer_ipv6) = connection_args; 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(); let (tx, rx) = channel();

View File

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

View File

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