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:
@@ -46,6 +46,26 @@
|
||||
#define _SPR_DIRTY1 0x08u
|
||||
#define _SPR_DIRTY (_SPR_DIRTY0 | _SPR_DIRTY1)
|
||||
|
||||
/* Статус-биты sprite_t.an_flags (чтение — sprite_anim_status()):
|
||||
* *_ON — анимация активна (тикается в sprite_update); *_DONE — дошла
|
||||
* до конца сама (one-shot до last / перемещение до цели) или была
|
||||
* добита sprite_move_stop(at_target=1). DONE сбрасывается новым
|
||||
* запуском соответствующей анимации. */
|
||||
#define SPR_ANIM_ON 0x01u /* кадровая анимация идёт */
|
||||
#define SPR_ANIM_DONE 0x02u /* one-shot дошёл до last */
|
||||
#define SPR_MOVE_ON 0x04u /* tween-перемещение идёт */
|
||||
#define SPR_MOVE_DONE 0x08u /* перемещение достигло цели */
|
||||
#define _SPR_ANIM_BACK 0x10u /* внутр.: маятник идёт назад */
|
||||
#define _SPR_ANIM_HORZ 0x20u /* внутр.: лента горизонтальная (ось sx) */
|
||||
#define _SPR_ANIM_PP 0x40u /* внутр.: режим маятника */
|
||||
#define _SPR_ANIM_ONCE 0x80u /* внутр.: режим one-shot */
|
||||
|
||||
/* Режимы sprite_anim(). */
|
||||
#define ANIM_LOOP 0u /* 0,1,..,last,first,.. (цикл) */
|
||||
#define ANIM_PINGPONG 1u /* 0,1,..,last,last-1,..,first,1,.. */
|
||||
#define ANIM_ONCE 2u /* first..last, замереть на last + DONE */
|
||||
#define ANIM_HORIZ 0x80u /* ИЛИ-флаг: кадры вдоль X (сетка nx>1) */
|
||||
|
||||
typedef struct {
|
||||
const void *img; /* getimage-формат / атлас-лента (вне W3) */
|
||||
int x, y; /* ЛОГИЧЕСКАЯ позиция левого-верхнего угла кадра */
|
||||
@@ -55,6 +75,19 @@ typedef struct {
|
||||
uint8_t page; /* 0 = img в обычной памяти; иначе физ. страница *
|
||||
* W0-атласа (img — адрес 0x0100-0x3FFF, движок *
|
||||
* сам мапит страницу в W0 на время блита) */
|
||||
/* --- авто-анимация (владеет тикер; настраивать ТОЛЬКО через
|
||||
* sprite_anim../sprite_moveto.., читать — inline-аксессорами) --- */
|
||||
uint8_t an_flags; /* статус: SPR_ANIM_.. | SPR_MOVE_.. */
|
||||
uint8_t an_frame; /* ТЕКУЩИЙ индекс кадра (0-based по ленте) */
|
||||
uint8_t an_first, an_last; /* границы анимации (индексы) */
|
||||
uint8_t an_speed, an_timer; /* кадров между сменами / счётчик */
|
||||
uint8_t an_step; /* шаг оси = fh (верт. лента) или fw (гориз.) */
|
||||
int an_lo; /* осевое смещение кадра first (для wrap) */
|
||||
uint8_t mv_speed, mv_timer; /* кадров между шагами / счётчик */
|
||||
uint8_t mv_step; /* макс. пикселей вдоль большей оси за шаг */
|
||||
int mv_tx, mv_ty; /* цель */
|
||||
int mv_dx, mv_dy, mv_err;/* Брезенхэм: dx, -dy, аккумулятор */
|
||||
int8_t mv_sgnx, mv_sgny; /* знаки шага осей */
|
||||
/* --- внутреннее (владеет движок) --- */
|
||||
struct {
|
||||
int x, y; /* где нарисован на странице p */
|
||||
@@ -107,6 +140,47 @@ inline void sprite_hide(sprite_t *s) {
|
||||
* активный спрайт прошёл над статиком — приложение помечает статик). */
|
||||
inline void sprite_touch(sprite_t *s) { s->flags |= _SPR_DIRTY; }
|
||||
|
||||
/* ==== Авто-анимация (§9г дизайна) ====================================
|
||||
* Тикается САМА в начале каждого sprite_update (раз на кадр), код
|
||||
* тикера подключается через funcptr — программа без анимаций его не
|
||||
* линкует. Кадровая анимация и tween-перемещение НЕЗАВИСИМЫ и могут
|
||||
* идти одновременно. Пока кадровая активна — не трогать sprite_frame
|
||||
* вручную (тикер ведёт ось сам); границы кадров должны лежать вдоль
|
||||
* ОДНОЙ оси ленты (вертикальная по умолчанию, ANIM_HORIZ — вдоль X). */
|
||||
|
||||
/* Запустить кадровую анимацию: кадры first..last (индексы вдоль оси
|
||||
* ленты), смена раз в speed кадров (1 = каждый кадр), mode = ANIM_LOOP/
|
||||
* ANIM_PINGPONG/ANIM_ONCE (| ANIM_HORIZ для горизонтальной ленты).
|
||||
* Старт с first; сбрасывает SPR_ANIM_DONE. */
|
||||
void sprite_anim(sprite_t *s, uint8_t first, uint8_t last,
|
||||
uint8_t speed, uint8_t mode);
|
||||
|
||||
/* Остановить кадровую анимацию: frame = индекс кадра, который останется
|
||||
* на экране, или -1 — замереть на текущем. */
|
||||
void sprite_anim_stop(sprite_t *s, int8_t frame);
|
||||
|
||||
/* Запустить перемещение к (tx,ty): раз в interval кадров — шаг до
|
||||
* max_step пикселей вдоль БОЛЬШЕЙ оси (меньшая тянется Брезенхэмом,
|
||||
* без деления). Сбрасывает SPR_MOVE_DONE. Пример: (0,0)→(100,50),
|
||||
* max_step 5, interval 4 → 20 шагов × 4 кадра = 80 кадров (1.6 с). */
|
||||
void sprite_moveto(sprite_t *s, int tx, int ty,
|
||||
uint8_t max_step, uint8_t interval);
|
||||
|
||||
/* Остановить перемещение: at_target = 0 — замереть где есть;
|
||||
* 1 — прыгнуть в цель (и взвести SPR_MOVE_DONE). */
|
||||
void sprite_move_stop(sprite_t *s, uint8_t at_target);
|
||||
|
||||
/* Статус анимаций: битовое поле SPR_ANIM_ON/DONE | SPR_MOVE_ON/DONE. */
|
||||
inline uint8_t sprite_anim_status(const sprite_t *s) { return s->an_flags; }
|
||||
|
||||
/* Текущий индекс кадра ленты (ведёт тикер; валиден и после stop). */
|
||||
inline uint8_t sprite_anim_frame(const sprite_t *s) { return s->an_frame; }
|
||||
|
||||
/* Перемещение ещё идёт? */
|
||||
inline uint8_t sprite_moving(const sprite_t *s) {
|
||||
return s->an_flags & SPR_MOVE_ON;
|
||||
}
|
||||
|
||||
/* ==== Атласы в EMM-страницах (W0) ====================================
|
||||
* Файл .atl (docs/sprite-api-design.md §3.1, вариант II): 0x100-байтовый
|
||||
* заголовок (магия 'SPA1', count; каталог с 0x68) + getimage-ленты;
|
||||
|
||||
Reference in New Issue
Block a user