examples/space: демо полного спрайтового стека (атлас W0 + авто-анимации)
Всё сразу: .atl с диска (mkatlas.py из генерённых лент) → atlas_load в EMM-страницу (W0, ISR-стаб) → движок sprite_t.page → авто-анимации: 5 астероидов (ANIM_LOOP вращение + sprite_moveto к случайным целям разных скоростей; по прибытии — взрыв ANIM_ONCE в точке + новая цель, по SPR_ANIM_DONE взрыв прячется), маяк ANIM_PINGPONG; дабл-буфер, FPS-метр, ESC. ~48 fps (vsync-кап). Грабли по дороге (оба — прикладные, не библиотека): - фон рисовался rand()'ом с разными последовательностями на страницах → мерцание звёзд; фикс — фиксированный seed на draw_space; - НЕ была скопирована палитра страницы 0 → 1 (у каждой страницы своя, см. gfx.h) — страница 1 показывалась чёрной, флип мигал «сцена/чёрный»; fps-плашка теперь рисуется на ОБЕИХ страницах (fps_draw=2 кадра при смене секунды). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Генератор спрайтов для examples/space (raw-ленты для mkatlas.py).
|
||||
|
||||
asteroid.raw — 16×16×4 (LOOP): серый астероид, кратеры вращаются по
|
||||
фазам (шаг 22.5°).
|
||||
explode.raw — 16×16×4 (ONCE): расширяющийся взрыв (жёлтое ядро →
|
||||
кольца → разлёт искр).
|
||||
beacon.raw — 16×16×4 (PINGPONG): пульсирующий маяк (крест с
|
||||
растущим свечением).
|
||||
|
||||
Палитра — дефолтная EGA (индексы 0..15), 0xFF = прозрачно.
|
||||
"""
|
||||
import math
|
||||
|
||||
T = 0xFF # прозрачный
|
||||
W = H = 16
|
||||
CX = CY = 7.5
|
||||
|
||||
|
||||
def blank():
|
||||
return [[T] * W for _ in range(H)]
|
||||
|
||||
|
||||
def disc(px, cx, cy, r, c):
|
||||
for y in range(H):
|
||||
for x in range(W):
|
||||
if (x - cx) ** 2 + (y - cy) ** 2 <= r * r:
|
||||
px[y][x] = c
|
||||
|
||||
|
||||
def ring(px, cx, cy, r, c):
|
||||
for a in range(0, 360, 4):
|
||||
x = int(round(cx + r * math.cos(math.radians(a))))
|
||||
y = int(round(cy + r * math.sin(math.radians(a))))
|
||||
if 0 <= x < W and 0 <= y < H:
|
||||
px[y][x] = c
|
||||
|
||||
|
||||
def dot(px, x, y, c):
|
||||
if 0 <= x < W and 0 <= y < H:
|
||||
px[int(y)][int(x)] = c
|
||||
|
||||
|
||||
def asteroid(phase):
|
||||
px = blank()
|
||||
disc(px, CX, CY, 6.8, 7) # LIGHTGRAY тело
|
||||
ring(px, CX, CY, 6.8, 8) # DARKGRAY контур
|
||||
for i in range(3): # кратеры на орбите
|
||||
a = math.radians(phase * 22.5 + i * 120)
|
||||
disc(px, CX + 3.6 * math.cos(a), CY + 3.6 * math.sin(a), 1.4, 8)
|
||||
dot(px, CX - 2, CY - 3, 15) # блик WHITE
|
||||
return px
|
||||
|
||||
|
||||
def explode(phase):
|
||||
px = blank()
|
||||
if phase == 0:
|
||||
disc(px, CX, CY, 2.2, 14) # YELLOW ядро
|
||||
elif phase == 1:
|
||||
disc(px, CX, CY, 3.8, 14)
|
||||
ring(px, CX, CY, 4.6, 12) # LIGHTRED
|
||||
elif phase == 2:
|
||||
ring(px, CX, CY, 4.2, 14)
|
||||
ring(px, CX, CY, 6.0, 12)
|
||||
ring(px, CX, CY, 7.0, 4) # RED
|
||||
else:
|
||||
for a in range(0, 360, 30): # разлёт искр
|
||||
r = 6.5
|
||||
dot(px, CX + r * math.cos(math.radians(a)),
|
||||
CY + r * math.sin(math.radians(a)), 4)
|
||||
dot(px, CX + 4 * math.cos(math.radians(a + 15)),
|
||||
CY + 4 * math.sin(math.radians(a + 15)), 6) # BROWN
|
||||
return px
|
||||
|
||||
|
||||
def beacon(phase):
|
||||
px = blank()
|
||||
r = 2 + phase * 1.6
|
||||
for d in range(int(r) + 1): # крест-лучи
|
||||
for xx, yy in ((CX - d, CY), (CX + d, CY), (CX, CY - d), (CX, CY + d)):
|
||||
dot(px, round(xx), round(yy), 11) # LIGHTCYAN
|
||||
disc(px, CX, CY, 1.4, 15) # WHITE ядро
|
||||
if phase >= 2:
|
||||
ring(px, CX, CY, r + 1, 9) # LIGHTBLUE ореол
|
||||
return px
|
||||
|
||||
|
||||
def save(name, frames):
|
||||
with open(name, "wb") as f:
|
||||
for fr in frames:
|
||||
for row in fr:
|
||||
f.write(bytes(row))
|
||||
print(f"gen_sprites: {name} ({len(frames)} кадров)")
|
||||
|
||||
|
||||
save("asteroid.raw", [asteroid(k) for k in range(4)])
|
||||
save("explode.raw", [explode(k) for k in range(4)])
|
||||
save("beacon.raw", [beacon(k) for k in range(4)])
|
||||
Reference in New Issue
Block a user