more advanced movement system

This commit is contained in:
Corwin 2023-08-30 17:43:24 +01:00
parent 025e76b8bd
commit 1bcbeb056e
No known key found for this signature in database
2 changed files with 84 additions and 108 deletions

View file

@ -6,8 +6,9 @@ use core::ops::{Deref, DerefMut};
use agb::{ use agb::{
display::object::{OamIterator, ObjectUnmanaged, SpriteLoader}, display::object::{OamIterator, ObjectUnmanaged, SpriteLoader},
fixnum::{num, Num, Vector2D}, fixnum::{Num, Vector2D},
}; };
use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use slotmap::SecondaryMap; use slotmap::SecondaryMap;
@ -26,11 +27,15 @@ struct AnimationEntity {
attached: Option<(Item, Num<i32, 10>)>, attached: Option<(Item, Num<i32, 10>)>,
} }
struct MovePoints {
sound_effect: Option<SoundEffect>,
points: Vec<Vector2D<Num<i32, 10>>>,
}
#[derive(Default)] #[derive(Default)]
struct ToPlay { struct ToPlay {
moves: Vec<Move>, move_points: SecondaryMap<EntityKey, MovePoints>,
attach_progress: Vec<AttachProgress>, attach_progress: Vec<AttachProgress>,
fakeout: Vec<FakeOutMove>,
detatch: Vec<Detatch>, detatch: Vec<Detatch>,
attach: Vec<Attach>, attach: Vec<Attach>,
change: Vec<Change>, change: Vec<Change>,
@ -50,11 +55,44 @@ impl ToPlay {
) { ) {
match instruction { match instruction {
AnimationInstruction::Move(e, p, s) => { AnimationInstruction::Move(e, p, s) => {
self.moves.push(Move(e, convert_to_real_space(p), s)); let move_points =
self.move_points
.entry(e)
.unwrap()
.or_insert_with(|| MovePoints {
sound_effect: s,
points: map
.get(e)
.map(|x| vec![x.start_position])
.unwrap_or_default(),
});
move_points.points.push(convert_to_real_space(p));
if let Some(sound_effect) = s {
move_points.sound_effect.get_or_insert(sound_effect);
} }
AnimationInstruction::FakeOutMove(e, d, p, s) => { }
self.fakeout AnimationInstruction::FakeOutMove(e, d, s) => {
.push(FakeOutMove(e, d, p.map(convert_to_real_space), s)) let move_points =
self.move_points
.entry(e)
.unwrap()
.or_insert_with(|| MovePoints {
sound_effect: s,
points: map
.get(e)
.map(|x| vec![x.start_position])
.unwrap_or_default(),
});
if let Some(sound_effect) = s {
move_points.sound_effect.get_or_insert(sound_effect);
}
let &most_recent_position = move_points.points.last().unwrap();
move_points
.points
.push(most_recent_position + convert_to_real_space(d.into()) / 2);
move_points.points.push(most_recent_position);
} }
AnimationInstruction::Detatch(e, nk, s) => self.detatch.push(Detatch(e, nk, s)), AnimationInstruction::Detatch(e, nk, s) => self.detatch.push(Detatch(e, nk, s)),
AnimationInstruction::Attach(e, o, s) => { AnimationInstruction::Attach(e, o, s) => {
@ -157,12 +195,6 @@ impl Map {
entity.start_position = destination; entity.start_position = destination;
} }
} }
fn set_entity_to_start_location(&mut self, entity: EntityKey) {
if let Some(entity) = self.map.get_mut(entity) {
entity.rendered_position = entity.start_position;
}
}
} }
impl Deref for Map { impl Deref for Map {
@ -179,6 +211,31 @@ impl DerefMut for Map {
} }
} }
fn lerp_points<N: Copy + core::ops::Mul<Num<i32, 10>, Output = N> + core::ops::Add<Output = N>>(
points: &[N],
t: Num<i32, 10>,
) -> N {
let number_of_points = points.len() as i32;
let slope_for_spike_fn = number_of_points - 1;
let relevant_points_pair_idx = (t * slope_for_spike_fn).floor();
let spike_function_for_first = t * -slope_for_spike_fn + relevant_points_pair_idx + 1;
let spike_function_for_second = t * slope_for_spike_fn - relevant_points_pair_idx;
let first_point_idx = relevant_points_pair_idx as usize;
let &first = points
.get(first_point_idx)
.expect("Maybe input to lerp is out of range?");
let second = points.get(first_point_idx + 1);
if let Some(&second) = second {
first * spike_function_for_first + second * spike_function_for_second
} else {
first
}
}
impl Animation { impl Animation {
pub fn populate(&mut self, instruction: AnimationInstruction, sfx: &mut Sfx) { pub fn populate(&mut self, instruction: AnimationInstruction, sfx: &mut Sfx) {
self.to_play.populate(instruction, &mut self.map, sfx); self.to_play.populate(instruction, &mut self.map, sfx);
@ -243,30 +300,16 @@ impl Animation {
} }
pub fn update(&mut self, sfx: &mut Sfx) -> bool { pub fn update(&mut self, sfx: &mut Sfx) -> bool {
if !self.to_play.moves.is_empty() if !self.to_play.move_points.is_empty() || !self.to_play.attach_progress.is_empty() {
|| !self.to_play.fakeout.is_empty()
|| !self.to_play.attach_progress.is_empty()
{
if self.time >= 1.into() { if self.time >= 1.into() {
// finalise animations // finalise animations
for m in self.to_play.moves.drain(0..) { for m in self.to_play.move_points.drain() {
let entity = m.0; let entity = m.0;
let destination = m.1; let &destination = m.1.points.last().unwrap();
self.map.set_entity_start_location(entity, destination); self.map.set_entity_start_location(entity, destination);
} }
for m in self.to_play.fakeout.drain(0..) {
let entity = m.0;
let destination = m.2;
if let Some(destination) = destination {
self.map.set_entity_start_location(entity, destination);
} else {
self.map.set_entity_to_start_location(entity);
}
}
for m in self.to_play.attach_progress.drain(0..) { for m in self.to_play.attach_progress.drain(0..) {
if let Some(ease) = self if let Some(ease) = self
.map .map
@ -279,49 +322,11 @@ impl Animation {
} }
} else { } else {
// play moves and fakeouts // play moves and fakeouts
for m in self.to_play.moves.iter_mut() { for (entity, move_points) in self.to_play.move_points.iter_mut() {
let entity = m.0; sfx.play_sound_effect(move_points.sound_effect.take());
let destination = m.1;
sfx.play_sound_effect(m.2.take());
if let Some(entity) = self.map.get_mut(entity) { if let Some(entity) = self.map.get_mut(entity) {
let location = entity.start_position * (Num::<i32, 10>::new(1) - self.ease) let location = lerp_points(&move_points.points, self.ease);
+ destination * self.ease;
entity.rendered_position = location;
}
}
for m in self.to_play.fakeout.iter_mut() {
let entity = m.0;
if let Some(entity) = self.map.get_mut(entity) {
let direction = m.1;
let destination = m.2.unwrap_or(entity.start_position);
let direction = convert_to_real_space(direction.into());
sfx.play_sound_effect(m.3.take());
let go_to = destination + direction / 2;
let mix = (self.ease * 2 - 1).abs();
let start_position_mix = if self.ease <= num!(0.5) {
mix
} else {
0.into()
};
let end_position_mix = if self.ease >= num!(0.5) {
mix
} else {
0.into()
};
let intermediate_position_mix = -mix + 1;
let location = entity.start_position * start_position_mix
+ go_to * intermediate_position_mix
+ destination * end_position_mix;
entity.rendered_position = location; entity.rendered_position = location;
} }
@ -363,9 +368,13 @@ impl Animation {
attached: None, attached: None,
}, },
); );
self.to_play self.to_play.move_points.insert(
.moves new_key,
.push(Move(new_key, destination_position, None)); MovePoints {
sound_effect: None,
points: vec![position, destination_position],
},
);
} }
} }
} else if !self.to_play.attach.is_empty() { } else if !self.to_play.attach.is_empty() {
@ -414,13 +423,6 @@ impl Animation {
} }
} }
struct Move(EntityKey, Vector2D<Num<i32, 10>>, Option<SoundEffect>);
struct FakeOutMove(
EntityKey,
Direction,
Option<Vector2D<Num<i32, 10>>>,
Option<SoundEffect>,
);
struct Detatch(EntityKey, EntityKey, Option<SoundEffect>); struct Detatch(EntityKey, EntityKey, Option<SoundEffect>);
struct Attach(EntityKey, Item, EntityKey, Option<SoundEffect>); struct Attach(EntityKey, Item, EntityKey, Option<SoundEffect>);
struct AttachProgress(EntityKey, Option<SoundEffect>); struct AttachProgress(EntityKey, Option<SoundEffect>);
@ -431,12 +433,7 @@ struct Change(EntityKey, Item, Option<SoundEffect>);
pub enum AnimationInstruction { pub enum AnimationInstruction {
Add(EntityKey, Item, Vector2D<i32>, Option<SoundEffect>), Add(EntityKey, Item, Vector2D<i32>, Option<SoundEffect>),
Move(EntityKey, Vector2D<i32>, Option<SoundEffect>), Move(EntityKey, Vector2D<i32>, Option<SoundEffect>),
FakeOutMove( FakeOutMove(EntityKey, Direction, Option<SoundEffect>),
EntityKey,
Direction,
Option<Vector2D<i32>>,
Option<SoundEffect>,
),
Detatch(EntityKey, EntityKey, Option<SoundEffect>), Detatch(EntityKey, EntityKey, Option<SoundEffect>),
Attach(EntityKey, EntityKey, Option<SoundEffect>), Attach(EntityKey, EntityKey, Option<SoundEffect>),
Change(EntityKey, Item, Option<SoundEffect>), Change(EntityKey, Item, Option<SoundEffect>),

View file

@ -44,23 +44,6 @@ impl ActionResult {
} }
} }
fn remove_move_animation_for_entity(
animations: &mut Vec<AnimationInstruction>,
entity_key: EntityKey,
) {
if let Some(existing_animation) = animations.iter().position(|x| {
if let AnimationInstruction::Move(entity, _, _) = x {
*entity == entity_key
} else if let AnimationInstruction::FakeOutMove(entity, _, _, _) = x {
*entity == entity_key
} else {
false
}
}) {
animations.swap_remove(existing_animation);
}
}
struct HasMoved(bool); struct HasMoved(bool);
struct WantsToMoveAgain(bool); struct WantsToMoveAgain(bool);
@ -190,7 +173,6 @@ impl EntityMap {
MoveAttemptResolution::AttemptPush => { MoveAttemptResolution::AttemptPush => {
let depth = push_depth - 1; let depth = push_depth - 1;
if depth >= 0 { if depth >= 0 {
remove_move_animation_for_entity(animations, other_entity_key);
let (can_move_result, action_result) = self.attempt_move_in_direction( let (can_move_result, action_result) = self.attempt_move_in_direction(
map, map,
animations, animations,
@ -270,7 +252,6 @@ impl EntityMap {
animations.push(AnimationInstruction::FakeOutMove( animations.push(AnimationInstruction::FakeOutMove(
entity_to_update_key, entity_to_update_key,
direction, direction,
self.map.get(entity_to_update_key).map(|e| e.location),
if explicit_stay_put { if explicit_stay_put {
self.map self.map
.get(entity_to_update_key) .get(entity_to_update_key)
@ -368,7 +349,6 @@ impl EntityMap {
break; break;
} }
OverlapResolution::MoveAgain => { OverlapResolution::MoveAgain => {
remove_move_animation_for_entity(animations, entity_to_update_key);
should_move_again = true; should_move_again = true;
} }
OverlapResolution::Teleport => { OverlapResolution::Teleport => {
@ -382,7 +362,6 @@ impl EntityMap {
let location_to_teleport_to = other_teleporter.location; let location_to_teleport_to = other_teleporter.location;
if self.whats_at(location_to_teleport_to).count() == 1 { if self.whats_at(location_to_teleport_to).count() == 1 {
//ok, we can teleport //ok, we can teleport
remove_move_animation_for_entity(animations, entity_to_update_key);
animations.push(AnimationInstruction::Move( animations.push(AnimationInstruction::Move(
entity_to_update_key, entity_to_update_key,
location_to_teleport_to, location_to_teleport_to,