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,142 @@
|
||||
/*
|
||||
* spranim — тест авто-анимаций спрайтового движка (§9г):
|
||||
* sprite_anim (LOOP/PINGPONG/ONCE) + sprite_moveto + статусы + стопы.
|
||||
*
|
||||
* Логика проверяется ПРОГРАММНО по полям sprite_t: спрайты невидимы,
|
||||
* тикер работает независимо от видимости, блитов нет — sprite_update
|
||||
* гоняется в текстовом режиме (скобка W3 безвредна). Ожидания:
|
||||
*
|
||||
* T1 LOOP 0..3 speed=2: смена раз в 2 тика: после 8 тиков frame
|
||||
* снова 0 (0→1→2→3→0), sy вернулся в 0.
|
||||
* T2 PINGPONG 0..3 speed=1: 1,2,3,2,1,0,1 — на тике 7 frame==1,
|
||||
* разворот на границе без удвоения краёв.
|
||||
* T3 ONCE 0..2 speed=1: после 2 тиков frame==2 и ANIM_ON ещё стоит,
|
||||
* после 3-го — ON снят, DONE взведён, frame==2 (замер на last).
|
||||
* T4 MOVETO (0,0)→(100,50) step=5 interval=4 (пример из §9г):
|
||||
* тик 4: (5,3); тик 8: (10,5); тик 79: НЕ DONE; тик 80: (100,50),
|
||||
* MOVE_ON снят, MOVE_DONE взведён. Параллельно у спрайта идёт
|
||||
* LOOP-анимация (одновременность) — frame успел смениться.
|
||||
* T5 sprite_anim_stop(1): ON снят, frame==1, ось = 16.
|
||||
* sprite_move_stop(at_target=1) на полпути: прыжок в цель + DONE.
|
||||
*
|
||||
* Кадры: RAM-лента 16×16×4 (getimage), кадр k залит k — содержимое
|
||||
* не важно (блитов нет), важен размер h=16 → an_step.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sprite.h>
|
||||
|
||||
static uint8_t strip[4 + 16 * 16 * 4];
|
||||
static sprite_t sp[4];
|
||||
|
||||
static void build_strip(void)
|
||||
{
|
||||
memset(strip, 0, sizeof strip);
|
||||
strip[0] = 16; strip[2] = 64; /* w=16, h=64 (4 кадра) */
|
||||
}
|
||||
|
||||
static void tick(sprite_t *arr, uint8_t n, uint16_t times)
|
||||
{
|
||||
while (times--)
|
||||
sprite_update(arr, n);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t ok;
|
||||
|
||||
puts("SPRANIM: frame/tween auto-animation");
|
||||
build_strip();
|
||||
|
||||
/* ---- T1: LOOP ---------------------------------------------------- */
|
||||
sprite_init(&sp[0], strip);
|
||||
sp[0].w = 16; sp[0].h = 16;
|
||||
sprite_anim(&sp[0], 0, 3, 2, ANIM_LOOP);
|
||||
tick(sp, 1, 2);
|
||||
ok = (sprite_anim_frame(&sp[0]) == 1 && sp[0].sy == 16);
|
||||
tick(sp, 1, 4); /* всего 6: 0→1→2→3 */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 3 && sp[0].sy == 48);
|
||||
tick(sp, 1, 2); /* всего 8: wrap → 0 */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 0 && sp[0].sy == 0);
|
||||
printf("T1 LOOP: %s\n", ok ? "PASS" : "FAIL");
|
||||
|
||||
/* ---- T2: PINGPONG ------------------------------------------------ */
|
||||
sprite_init(&sp[0], strip);
|
||||
sp[0].w = 16; sp[0].h = 16;
|
||||
sprite_anim(&sp[0], 0, 3, 1, ANIM_PINGPONG);
|
||||
tick(sp, 1, 3); /* 1,2,3 */
|
||||
ok = (sprite_anim_frame(&sp[0]) == 3);
|
||||
tick(sp, 1, 1); /* разворот: 2 */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 2 && sp[0].sy == 32);
|
||||
tick(sp, 1, 2); /* 1,0 */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 0);
|
||||
tick(sp, 1, 1); /* разворот: 1 */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 1 && sp[0].sy == 16);
|
||||
printf("T2 PINGPONG: %s\n", ok ? "PASS" : "FAIL");
|
||||
|
||||
/* ---- T3: ONCE ---------------------------------------------------- */
|
||||
sprite_init(&sp[0], strip);
|
||||
sp[0].w = 16; sp[0].h = 16;
|
||||
sprite_anim(&sp[0], 0, 2, 1, ANIM_ONCE);
|
||||
tick(sp, 1, 2); /* 1,2 — дошли до last */
|
||||
ok = (sprite_anim_frame(&sp[0]) == 2 &&
|
||||
(sprite_anim_status(&sp[0]) & SPR_ANIM_ON) &&
|
||||
!(sprite_anim_status(&sp[0]) & SPR_ANIM_DONE));
|
||||
tick(sp, 1, 1); /* фиксация: ON→off,DONE */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 2 && sp[0].sy == 32 &&
|
||||
!(sprite_anim_status(&sp[0]) & SPR_ANIM_ON) &&
|
||||
(sprite_anim_status(&sp[0]) & SPR_ANIM_DONE));
|
||||
tick(sp, 1, 5); /* стоит на месте */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 2);
|
||||
printf("T3 ONCE: %s\n", ok ? "PASS" : "FAIL");
|
||||
|
||||
/* ---- T4: MOVETO + одновременная кадровая ------------------------- */
|
||||
sprite_init(&sp[0], strip);
|
||||
sp[0].w = 16; sp[0].h = 16; /* x=y=0 после init */
|
||||
sprite_anim(&sp[0], 0, 3, 3, ANIM_LOOP);
|
||||
sprite_moveto(&sp[0], 100, 50, 5, 4);
|
||||
ok = (sprite_moving(&sp[0]) != 0);
|
||||
tick(sp, 1, 4);
|
||||
ok = ok && (sp[0].x == 5 && sp[0].y == 3); /* Y: 3/2/3/2 */
|
||||
tick(sp, 1, 4);
|
||||
ok = ok && (sp[0].x == 10 && sp[0].y == 5);
|
||||
tick(sp, 1, 71); /* всего 79 — ещё едет */
|
||||
ok = ok && sprite_moving(&sp[0]) &&
|
||||
!(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE);
|
||||
tick(sp, 1, 1); /* 80: приехали */
|
||||
ok = ok && (sp[0].x == 100 && sp[0].y == 50 &&
|
||||
!sprite_moving(&sp[0]) &&
|
||||
(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE));
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) != 0 ||
|
||||
sp[0].an_timer != 3); /* кадровая тоже тикала */
|
||||
printf("T4 MOVETO: %s (x=%d y=%d)\n",
|
||||
ok ? "PASS" : "FAIL", sp[0].x, sp[0].y);
|
||||
|
||||
/* ---- T5: стопы ---------------------------------------------------- */
|
||||
sprite_init(&sp[0], strip);
|
||||
sp[0].w = 16; sp[0].h = 16;
|
||||
sprite_anim(&sp[0], 0, 3, 1, ANIM_LOOP);
|
||||
tick(sp, 1, 2); /* frame 2 */
|
||||
sprite_anim_stop(&sp[0], 1); /* остановить на кадре 1 */
|
||||
ok = (!(sprite_anim_status(&sp[0]) & SPR_ANIM_ON) &&
|
||||
sprite_anim_frame(&sp[0]) == 1 && sp[0].sy == 16);
|
||||
tick(sp, 1, 4); /* не двигается */
|
||||
ok = ok && (sprite_anim_frame(&sp[0]) == 1);
|
||||
|
||||
sprite_moveto(&sp[0], 60, 60, 3, 1);
|
||||
tick(sp, 1, 5); /* на полпути */
|
||||
sprite_move_stop(&sp[0], 1); /* добить в цель */
|
||||
ok = ok && (sp[0].x == 60 && sp[0].y == 60 &&
|
||||
!sprite_moving(&sp[0]) &&
|
||||
(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE));
|
||||
|
||||
sprite_moveto(&sp[0], 0, 0, 3, 1);
|
||||
tick(sp, 1, 5);
|
||||
sprite_move_stop(&sp[0], 0); /* замереть где есть */
|
||||
ok = ok && (sp[0].x != 0 && !sprite_moving(&sp[0]) &&
|
||||
!(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE));
|
||||
printf("T5 STOPS: %s\n", ok ? "PASS" : "FAIL");
|
||||
|
||||
puts("done");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user