libbgi: авто-анимация спрайтов — кадровая + tween (tests/spranim все PASS)
Реализация §9г по требованиям пользователя: - sprite_anim(first,last,speed,mode): ANIM_LOOP / ANIM_PINGPONG / ANIM_ONCE (one-shot замирает на last), | ANIM_HORIZ для горизонтальных лент. Смена кадра в тике = ±an_step к оси ленты — без умножений (осевое смещение first считается в setup циклом). - sprite_moveto(tx,ty,max_step,interval): Брезенхэм порциями ≤max_step вдоль большей оси (меньшая — err-аккумулятором, нелинейные шаги Y сами собой), деления нет. - Одновременность кадровой и tween — независимые поля/биты. - Статус: sprite_anim_status() — битовое поле SPR_ANIM_ON/DONE + SPR_MOVE_ON/DONE; sprite_anim_frame() — текущий индекс кадра; sprite_moving(). - Стопы: sprite_anim_stop(frame | -1 = текущий); sprite_move_stop(0 = замереть / 1 = прыжок в цель + DONE). - Тикер _sprite_tick — проход 0 sprite_update через funcptr _spr_tick_fn (DCE: без sprite_anim/moveto код не линкуется, цена — один if на кадр; +40 Б программе с движком, sprite_t +18 Б). tests/spranim (MAME dev, все PASS): LOOP/PINGPONG (разворот на границе без удвоения краёв)/ONCE+DONE; пример (0,0)→(100,50), шаг 5, интервал 4 → ровно 80 кадров, Y идёт 3/2/3/2; стопы обоих видов. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* _sprite_tick — тикер авто-анимаций (§9г): вызывается из sprite_update
|
||||
* (раз на КАДР, до heal/блит-проходов — dirty должен взводиться раньше).
|
||||
* Кадровая анимация и tween-перемещение НЕЗАВИСИМЫ (идут одновременно);
|
||||
* тикаются и у скрытых спрайтов («время идёт», ветка дороже).
|
||||
*
|
||||
* Кадровая: смена кадра = ±an_step к оси ленты (sy; при _SPR_ANIM_HORZ
|
||||
* — sx) — БЕЗ умножения; wrap цикла — прыжок оси в an_lo (посчитан
|
||||
* sprite_anim'ом один раз). Маятник разворачивается НА границе (…2,3,
|
||||
* 2,1,0,1…, без удвоения крайних). ANIM_ONCE замирает на last:
|
||||
* SPR_ANIM_ON снимается, SPR_ANIM_DONE взводится.
|
||||
*
|
||||
* Перемещение: раз в mv_speed кадров — порция ≤mv_step итераций
|
||||
* классического Брезенхэма (err-аккумулятор: большая ось шагает каждую
|
||||
* итерацию, меньшая подтягивается — нелинейные 2/3/2/3 сами собой,
|
||||
* деления нет). Цель достигнута — SPR_MOVE_ON→off, SPR_MOVE_DONE.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
#include <sprite.h>
|
||||
|
||||
void _sprite_tick(void *arr, uint8_t count)
|
||||
{
|
||||
sprite_t *s = (sprite_t *)arr;
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < count; i++, s++) {
|
||||
/* ---- кадровая анимация ---- */
|
||||
if ((s->an_flags & SPR_ANIM_ON) && --s->an_timer == 0) {
|
||||
int *axis = (s->an_flags & _SPR_ANIM_HORZ) ? &s->sx : &s->sy;
|
||||
uint8_t moved = 1;
|
||||
|
||||
s->an_timer = s->an_speed;
|
||||
if (!(s->an_flags & _SPR_ANIM_BACK)) {
|
||||
if (s->an_frame < s->an_last) {
|
||||
s->an_frame++;
|
||||
*axis += s->an_step;
|
||||
} else if (s->an_flags & _SPR_ANIM_ONCE) {
|
||||
s->an_flags &= (uint8_t)~SPR_ANIM_ON;
|
||||
s->an_flags |= SPR_ANIM_DONE;
|
||||
moved = 0;
|
||||
} else if (s->an_flags & _SPR_ANIM_PP) {
|
||||
s->an_flags |= _SPR_ANIM_BACK; /* разворот на границе */
|
||||
if (s->an_frame > s->an_first) {
|
||||
s->an_frame--;
|
||||
*axis -= s->an_step;
|
||||
} else
|
||||
moved = 0; /* лента из 1 кадра */
|
||||
} else { /* ANIM_LOOP: wrap */
|
||||
s->an_frame = s->an_first;
|
||||
*axis = s->an_lo;
|
||||
}
|
||||
} else { /* маятник, назад */
|
||||
if (s->an_frame > s->an_first) {
|
||||
s->an_frame--;
|
||||
*axis -= s->an_step;
|
||||
} else {
|
||||
s->an_flags &= (uint8_t)~_SPR_ANIM_BACK;
|
||||
if (s->an_frame < s->an_last) {
|
||||
s->an_frame++;
|
||||
*axis += s->an_step;
|
||||
} else
|
||||
moved = 0;
|
||||
}
|
||||
}
|
||||
if (moved)
|
||||
s->flags |= _SPR_DIRTY;
|
||||
}
|
||||
|
||||
/* ---- tween-перемещение ---- */
|
||||
if ((s->an_flags & SPR_MOVE_ON) && --s->mv_timer == 0) {
|
||||
uint8_t n = s->mv_step;
|
||||
int x = s->x, y = s->y;
|
||||
|
||||
s->mv_timer = s->mv_speed;
|
||||
while (n--) {
|
||||
int e2;
|
||||
if (x == s->mv_tx && y == s->mv_ty)
|
||||
break;
|
||||
e2 = s->mv_err * 2;
|
||||
if (e2 >= s->mv_dy) { s->mv_err += s->mv_dy; x += s->mv_sgnx; }
|
||||
if (e2 <= s->mv_dx) { s->mv_err += s->mv_dx; y += s->mv_sgny; }
|
||||
}
|
||||
if (x != s->x || y != s->y) {
|
||||
s->x = x;
|
||||
s->y = y;
|
||||
s->flags |= _SPR_DIRTY;
|
||||
}
|
||||
if (x == s->mv_tx && y == s->mv_ty) {
|
||||
s->an_flags &= (uint8_t)~SPR_MOVE_ON;
|
||||
s->an_flags |= SPR_MOVE_DONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user