implement animations
This commit is contained in:
parent
027d487adc
commit
0fdab97044
3 changed files with 133 additions and 58 deletions
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
src/game.rs
27
src/game.rs
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in a new issue