Dead Simple Sprite-Sheet Animations
8 декабря 2022 г.Я использовал Bevy для личного проекта, и это было восхитительно. По мере роста проекта я превратился в левел-дизайнера, аниматора, иллюстратора, наравне с обычным программистом.
Иногда я натыкаюсь на стены, и кроличьи норы вокруг этих стен слишком привлекательны, чтобы пройти мимо них. Одной из таких кроличьих норок было вычисление циклов анимации спрайтов из одного листа спрайтов. Единственная причина использования одного листа спрайтов заключается в том, что работа разработчика и иллюстратора требует очень много времени, и я хотел, чтобы мой рабочий процесс оставался максимально простым.
Создание анимационных страниц
Страница анимации – это группа кадров, образующих циклическую анимацию. В следующем примере страницы анимации представлены перечислением.
Реализация
Определение вариантов анимации в перечислении
<цитата>Я называю разделы анимации вариантами, а кортеж смещения/размера — страницей
#[derive(Clone, PartialEq)]
pub enum PlayerAnimationVariant {
Idle,
Rising,
Falling,
}
Создание трейта AnimationLoop
Черта проста, определяет функцию, которая будет возвращать смещение и размер страницы анимации.
pub trait AnimationLoop {
fn page(&self) -> (usize, usize);
}
Реализация трейта AnimationLoop
для перечисления
Черта проста, определяет функцию, которая будет возвращать смещение и размер страницы анимации.
impl AnimationLoop for PlayerAnimationVariant {
fn page(&self) -> (usize, usize) {
match self {
// return values (idx_offset, loop_size) describe the animation loop
PlayerAnimationVariant::Idle => (0, 3),
PlayerAnimationVariant::Rising => (2, 2),
PlayerAnimationVariant::Falling => (4, 4),
}
}
}
Переход между кадрами и страницами
Реализация
Реализация диспетчера состояний анимации
В структуре хранится вариант анимации для воспроизведения и текущий индекс кадра.
pub struct PlayerAnimationState {
pub variant: PlayerAnimationVariant,
pub idx: usize,
}
Реализация функций перехода
wrapping_next_idx
увеличивает idx
и выполняет перенос на границе страницы
impl PlayerAnimationState {
fn wrapping_next_idx(&mut self) -> usize {
let current_idx = self.idx;
let (offset, size) = self.variant.page();
self.idx = offset + (current_idx + 1) % size;
self.idx
}
}
transition_variant
обновляет диспетчер состояний анимации для воспроизведения варианта, переданного в качестве аргумента
impl PlayerAnimationState {
...
fn transition_variant(&mut self, to: PlayerAnimationVariant) {
let (offset, _) = to.page();
self.variant = to;
self.idx = offset;
}
}
Это может быть несколько утомительно, поэтому я создал крейт Rust, который упрощает процесс, предоставляя:
- черты
AnimationLoop
иAnimationTransition<T: AnimationLoop>
- удобный макрос
AnimationTransitionMacro
, реализующий необходимые функции манипулирования индексами для диспетчера состояний анимации
Посмотрите конечный продукт здесь! А вот репозиторий.
Оригинал