Skip to content

Commit

Permalink
feat: add repeat-from animation mode (#40)
Browse files Browse the repository at this point in the history
deprecate: deprecate the `AnimationMode` enum which is not used in any public API
  • Loading branch information
cristicbz authored Mar 2, 2022
1 parent e9c4de6 commit 586859c
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 30 deletions.
45 changes: 37 additions & 8 deletions src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ pub struct SpriteSheetAnimation {
/// Frames
pub(crate) frames: Vec<Frame>,
/// Animation mode
pub(crate) mode: AnimationMode,
pub(crate) mode: Mode,
}

/// Animation mode (run once, repeat or ping-pong)
///
/// Deprecated
/// ---
/// This is not exposed in any of the public APIs of the crate so there is no reason to depend on
/// it. Use 'builder-style' methods like [`SpriteSheetAnimation::repeat`] instead.
#[deprecated]
#[non_exhaustive]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum AnimationMode {
Expand Down Expand Up @@ -45,7 +51,7 @@ impl SpriteSheetAnimation {
pub fn from_frames(frames: Vec<Frame>) -> Self {
Self {
frames,
mode: AnimationMode::default(),
mode: Mode::default(),
}
}

Expand Down Expand Up @@ -76,24 +82,32 @@ impl SpriteSheetAnimation {
.collect()
}

/// Set the animation mode to [`AnimationMode::Once`]
/// Runs the animation once and then stop playing
#[must_use]
pub fn once(mut self) -> Self {
self.mode = AnimationMode::Once;
self.mode = Mode::Once;
self
}

/// Set the animation mode to [`AnimationMode::Repeat`]
/// Repeat the animation forever
#[must_use]
pub fn repeat(mut self) -> Self {
self.mode = AnimationMode::Repeat;
self.mode = Mode::RepeatFrom(0);
self
}

/// Set the animation mode to [`AnimationMode::PingPong`]
/// Repeat the animation forever, from a given frame index (loop back to it at the end of the
/// animation)
#[must_use]
pub fn repeat_from(mut self, frame_index: usize) -> Self {
self.mode = Mode::RepeatFrom(frame_index);
self
}

/// Repeat the animation forever, going back and forth between the first and last frame.
#[must_use]
pub fn ping_pong(mut self) -> Self {
self.mode = AnimationMode::PingPong;
self.mode = Mode::PingPong;
self
}

Expand All @@ -102,12 +116,27 @@ impl SpriteSheetAnimation {
}
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum Mode {
Once,
RepeatFrom(usize),
PingPong,
}

impl FromIterator<Frame> for SpriteSheetAnimation {
fn from_iter<T: IntoIterator<Item = Frame>>(iter: T) -> Self {
Self::from_frames(iter.into_iter().collect())
}
}

impl Default for Mode {
#[inline]
fn default() -> Self {
Self::RepeatFrom(0)
}
}

#[allow(deprecated)]
impl Default for AnimationMode {
#[inline]
fn default() -> Self {
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
//! 0..=2, // Indices of the sprite atlas
//! Duration::from_secs_f64(1.0 / 12.0), // Duration of each frame
//! ));
//!
//!
//! commands
//! .spawn_bundle(SpriteSheetBundle {
//! // TODO: Configure the sprite sheet
Expand Down Expand Up @@ -117,9 +117,12 @@ use bevy_ecs::component::SparseStorage;
use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;

pub use animation::{AnimationMode, Frame, SpriteSheetAnimation};
pub use animation::{Frame, SpriteSheetAnimation};
pub use state::SpriteSheetAnimationState;

#[allow(deprecated)]
pub use animation::AnimationMode;

mod animation;
mod state;

Expand Down
51 changes: 31 additions & 20 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bevy_core::prelude::*;
use bevy_ecs::prelude::*;
use bevy_sprite::prelude::*;

use crate::{AnimationMode, Play, SpriteSheetAnimation};
use crate::{animation::Mode, Play, SpriteSheetAnimation};

pub(crate) fn maintenance_systems() -> SystemSet {
SystemSet::new()
Expand Down Expand Up @@ -66,14 +66,14 @@ impl SpriteSheetAnimationState {
self.elapsed_in_frame += delta;
if self.elapsed_in_frame >= frame.duration {
match animation.mode {
AnimationMode::Repeat => {
Mode::RepeatFrom(loop_from) => {
if self.current_frame < animation.frames.len() - 1 {
self.current_frame += 1;
} else {
self.current_frame = 0;
self.current_frame = loop_from;
}
}
AnimationMode::PingPong => {
Mode::PingPong => {
if self.going_backward {
if self.current_frame == 0 {
self.going_backward = false;
Expand All @@ -88,7 +88,7 @@ impl SpriteSheetAnimationState {
self.current_frame -= 1;
}
}
AnimationMode::Once => {
Mode::Once => {
if self.current_frame < animation.frames.len() - 1 {
self.current_frame += 1;
} else {
Expand Down Expand Up @@ -305,45 +305,51 @@ mod tests {
}
}

mod repeat {
mod repeat_from {
use crate::Frame;

use super::*;

#[fixture]
fn mode() -> AnimationMode {
AnimationMode::Repeat
fn mode() -> Mode {
Mode::RepeatFrom(2)
}

mod on_last_frame {
use super::*;

#[fixture]
fn animation(frame_duration: Duration, mode: AnimationMode) -> SpriteSheetAnimation {
fn animation(frame_duration: Duration, mode: Mode) -> SpriteSheetAnimation {
SpriteSheetAnimation {
frames: vec![Frame::new(0, frame_duration), Frame::new(1, frame_duration)],
frames: vec![
Frame::new(0, frame_duration),
Frame::new(1, frame_duration),
Frame::new(2, frame_duration),
Frame::new(3, frame_duration),
Frame::new(4, frame_duration),
],
mode,
}
}

#[fixture]
fn state() -> SpriteSheetAnimationState {
SpriteSheetAnimationState {
current_frame: 1,
current_frame: 4,
elapsed_in_frame: Duration::from_nanos(0),
going_backward: false,
}
}

#[rstest]
fn returns_to_first_frame(
fn returns_to_loop_frame(
mut state: SpriteSheetAnimationState,
mut sprite_at_second_frame: TextureAtlasSprite,
animation: SpriteSheetAnimation,
frame_duration: Duration,
) {
state.update(&mut sprite_at_second_frame, &animation, frame_duration);
assert_eq!(sprite_at_second_frame.index, 0);
assert_eq!(sprite_at_second_frame.index, 2);
}

#[rstest]
Expand All @@ -361,17 +367,22 @@ mod tests {
use super::*;

#[fixture]
fn animation(frame_duration: Duration, mode: AnimationMode) -> SpriteSheetAnimation {
fn animation(frame_duration: Duration, mode: Mode) -> SpriteSheetAnimation {
SpriteSheetAnimation {
frames: vec![Frame::new(0, frame_duration), Frame::new(1, frame_duration)],
frames: vec![
Frame::new(0, frame_duration),
Frame::new(1, frame_duration),
Frame::new(2, frame_duration),
Frame::new(3, frame_duration),
],
mode,
}
}

#[fixture]
fn state() -> SpriteSheetAnimationState {
SpriteSheetAnimationState {
current_frame: 2,
current_frame: 4,
elapsed_in_frame: Duration::from_nanos(0),
going_backward: false,
}
Expand All @@ -385,7 +396,7 @@ mod tests {
frame_duration: Duration,
) {
state.update(&mut sprite_at_second_frame, &animation, frame_duration);
assert_eq!(sprite_at_second_frame.index, 0);
assert_eq!(sprite_at_second_frame.index, 2);
}

#[rstest]
Expand All @@ -404,8 +415,8 @@ mod tests {
use super::*;

#[fixture]
fn mode() -> AnimationMode {
AnimationMode::Once
fn mode() -> Mode {
Mode::Once
}

mod on_last_frame {
Expand All @@ -414,7 +425,7 @@ mod tests {
use super::*;

#[fixture]
fn animation(frame_duration: Duration, mode: AnimationMode) -> SpriteSheetAnimation {
fn animation(frame_duration: Duration, mode: Mode) -> SpriteSheetAnimation {
SpriteSheetAnimation {
frames: vec![Frame::new(0, frame_duration), Frame::new(1, frame_duration)],
mode,
Expand Down

0 comments on commit 586859c

Please sign in to comment.