implement animations

This commit is contained in:
Michael Zhang 2019-08-10 00:27:29 -05:00
parent 027d487adc
commit 0fdab97044
No known key found for this signature in database
GPG key ID: 5BAEFE5D04F0CE6C
3 changed files with 133 additions and 58 deletions

View file

@ -1,20 +1,24 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration; use std::time::Duration;
pub type BlockOffsets = HashMap<usize, (i32, i32)>; use crate::enums::Board;
use crate::level::{ChangeSet, Entity, FailSet};
pub type MoveResult = Result<ChangeSet, FailSet>;
pub type BlockOffsets = HashMap<Entity, (f32, f32)>;
// TODO: don't yeet around a HashMap all the time // TODO: don't yeet around a HashMap all the time
pub type AnimationFn = Box<Fn(BlockOffsets, f32) -> BlockOffsets>; pub type AnimationFn = Box<Fn(MoveResult, BlockOffsets, f32) -> BlockOffsets>;
// in seconds
const ANIMATION_DURATION: f32 = 1.0 / 6.0; const ANIMATION_DURATION: f32 = 1.0 / 6.0;
#[derive(Default, Serialize)] #[derive(Default)]
pub struct AnimationState { pub struct AnimationState {
pub is_animating: bool, pub is_animating: bool,
pub last_move_success: bool, pub last_move_result: Option<MoveResult>,
pub progress: f32, pub progress: f32,
pub block_offsets: BlockOffsets, pub block_offsets: BlockOffsets,
#[serde(skip)]
progress_function: Option<AnimationFn>, progress_function: Option<AnimationFn>,
} }
@ -22,13 +26,46 @@ impl AnimationState {
pub fn new() -> Self { pub fn new() -> Self {
AnimationState { AnimationState {
is_animating: false, is_animating: false,
last_move_success: true, last_move_result: None,
progress: 0.0, progress: 0.0,
block_offsets: BlockOffsets::new(), block_offsets: BlockOffsets::new(),
progress_function: None, progress_function: None,
} }
} }
pub fn begin_move_transition(&mut self, result: MoveResult) {
self.last_move_result = Some(result);
self.is_animating = true;
self.progress = 0.0;
let func = |last_move_result: MoveResult, mut offsets: BlockOffsets, progress: f32| {
use std::f32::consts::PI;
match last_move_result {
// transition
Ok(change_set) => {
for (entity, direction) in change_set {
// TODO: implement ease-out?
let pair = direction.as_pair();
let offset = (pair.0 as f32 * progress, pair.1 as f32 * progress);
println!(
"|| entity: {:?}, direction: {:?} => {:?}",
entity, direction, offset
);
offsets.insert(entity.clone(), offset);
}
}
// vibrate all blocking pieces
Err(fail_set) => {
for index in fail_set {
let delta = 0.05 * (4.0 * PI * progress).sin() / (progress + 0.5);
offsets.insert(Entity::Block(index), (delta, delta));
}
}
}
offsets
};
self.progress_function = Some(Box::new(func));
}
pub fn begin_transition(&mut self, f: AnimationFn) { pub fn begin_transition(&mut self, f: AnimationFn) {
self.is_animating = true; self.is_animating = true;
self.progress = 0.0; self.progress = 0.0;
@ -36,26 +73,45 @@ impl AnimationState {
} }
pub fn make_progress(&mut self, delta: Duration) { pub fn make_progress(&mut self, delta: Duration) {
let progress = self.progress + delta.as_millis() as f32 / 1000.0; let progress = self.progress + (delta.as_millis() as f32 / ANIMATION_DURATION) / 1000.0;
let block_offsets = if let Some(f) = &self.progress_function { let block_offsets = if let Some(f) = &self.progress_function {
Some(f(self.block_offsets.clone(), progress)) Some(f(
self.last_move_result.clone().unwrap(),
self.block_offsets.clone(),
progress,
))
} else { } else {
None None
}; };
// this should always work
if let Some(block_offsets) = block_offsets { if let Some(block_offsets) = block_offsets {
self.block_offsets = block_offsets; self.block_offsets = block_offsets;
self.progress = progress; self.progress = progress;
} }
if self.progress > 1.0 {
self.is_animating = false;
self.block_offsets = BlockOffsets::new();
}
} }
pub fn is_done(&self) -> bool { pub fn is_done(&self) -> bool {
self.progress > 1.0 self.progress > 1.0
} }
pub fn get_offset(&self, index: usize) -> (i32, i32) { pub fn get_block_offset(&self, index: usize) -> (f32, f32) {
self.block_offsets self.block_offsets
.get(&index) .get(&Entity::Block(index))
.cloned() .cloned()
.unwrap_or_else(|| (0, 0)) .unwrap_or_else(|| (0.0, 0.0))
}
pub fn get_player_offset(&self, board: Board) -> (f32, f32) {
self.block_offsets
.get(&Entity::Player(board))
.cloned()
.unwrap_or_else(|| (0.0, 0.0))
} }
} }

View file

@ -100,18 +100,29 @@ impl<'a> Game<'a> {
println!("pushed: {:?}", $key); println!("pushed: {:?}", $key);
let level = self.get_current_level_mut(); let level = self.get_current_level_mut();
let result = level.try_move($board, $direction); let result = level.try_move($board, $direction);
println!("game result: {:?}", result); self.animations.begin_move_transition(result);
self.keymap.insert($key, false); self.keymap.insert($key, false);
} }
}; };
} }
if self.animations.is_animating { if self.animations.is_animating {
if self.animations.last_move_success { println!("animating. {:?}", self.animations.progress);
if self.animations.is_done() {
self.animations.make_progress(delta); self.animations.make_progress(delta);
}
// we just finished!
if !self.animations.is_animating {
// apply the changes to the entities
// this indirection is used to dodge a concurrent borrow
let change_set = if let Some(Ok(change_set)) = &self.animations.last_move_result {
Some(change_set.clone())
} else { } else {
None
};
if let Some(change_set) = change_set {
let level = self.get_current_level_mut();
level.apply_change_set(change_set.clone());
}
} }
} else { } else {
shit!(VirtualKeyCode::W, Board::Left, PushDir::Up); shit!(VirtualKeyCode::W, Board::Left, PushDir::Up);
@ -125,13 +136,7 @@ impl<'a> Game<'a> {
shit!(VirtualKeyCode::L, Board::Right, PushDir::Right); shit!(VirtualKeyCode::L, Board::Right, PushDir::Right);
// failed a move // failed a move
if !self.animations.last_move_success { if let Some(Err(fail_set)) = &self.animations.last_move_result {}
let func = |mut offsets: HashMap<_, _>, prog| {
offsets.insert(0, (0, 0));
offsets
};
self.animations.begin_transition(Box::new(func));
}
} }
} }

View file

@ -96,15 +96,7 @@ impl Level {
} }
} }
pub fn try_move(&mut self, board: Board, direction: PushDir) { pub fn apply_change_set(&mut self, change_set: ChangeSet) {
let mut change_set = ChangeSet::default();
change_set.insert(Entity::Player(board), direction);
let result = self.player_can_move(board, direction, change_set);
match result {
Ok(change_set) => {
println!("change_set: {:?}", change_set);
for (entity, direction) in change_set { for (entity, direction) in change_set {
let direction = direction.as_pair(); let direction = direction.as_pair();
match entity { match entity {
@ -126,8 +118,11 @@ impl Level {
} }
} }
} }
Err(fail_set) => {}
}; pub fn try_move(&mut self, board: Board, direction: PushDir) -> Result<ChangeSet, FailSet> {
let mut change_set = ChangeSet::default();
change_set.insert(Entity::Player(board), direction);
self.player_can_move(board, direction, change_set)
} }
fn player_can_move( fn player_can_move(
@ -369,9 +364,9 @@ impl Level {
offset.0 + segment.position.0 * scale, offset.0 + segment.position.0 * scale,
offset.1 + segment.position.1 * scale, offset.1 + segment.position.1 * scale,
); );
let animation_offset = animations.get_offset(i); let animation_offset = animations.get_block_offset(i);
location.0 += animation_offset.0; location.0 += (animation_offset.0 * scale as f32) as i32;
location.1 += animation_offset.1; location.1 += (animation_offset.1 * scale as f32) as i32;
renderer.render_segment( renderer.render_segment(
location, location,
scale, scale,
@ -383,21 +378,40 @@ impl Level {
} }
// render player // render player
self.render_player(renderer, &self.player1, scale, left_off); self.render_player(
self.render_player(renderer, &self.player2, scale, right_off); renderer,
Board::Left,
&self.player1,
scale,
animations,
left_off,
);
self.render_player(
renderer,
Board::Right,
&self.player2,
scale,
animations,
right_off,
);
} }
fn render_player( fn render_player(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
board: Board,
player: &Player, player: &Player,
scale: i32, scale: i32,
animations: &AnimationState,
offset: (i32, i32), offset: (i32, i32),
) { ) {
let location = ( let mut location = (
offset.0 + player.position.0 * scale + 4, offset.0 + player.position.0 * scale + 4,
offset.1 + player.position.1 * scale + 4, offset.1 + player.position.1 * scale + 4,
); );
let animation_offset = animations.get_player_offset(board);
location.0 += (animation_offset.0 * scale as f32) as i32;
location.1 += (animation_offset.1 * scale as f32) as i32;
renderer.render_segment( renderer.render_segment(
location, location,
(scale - 8), (scale - 8),