数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 134|回复: 4

[另类] AI生成了两个屏幕测试动画

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
本帖最后由 飞向狙沙 于 2026-11-4 10:38 编辑

驱动屏幕一般就是先单色刷屏,再移植些效果图,挺麻烦也不好看,就让ai生成了两个动画图

旋转花瓶


激光炮


花瓶代码
  1. #ifndef VASE3D_H
  2. #define VASE3D_H

  3. /* 3D 线框花瓶旋转动画测试 */
  4. void vase3dInit(void);
  5. void vase3dFrame(void);

  6. #endif
复制代码
  1. /**
  2. * @file    vase3d.c
  3. * [url=home.php?mod=space&uid=650075]@brief[/url]   最简单的三维动画测试:屏幕上旋转的线框花瓶
  4. *
  5. * 实现思路:
  6. *   1. 花瓶用一张 {y, r} 轮廓表描述(y 为高度,r 为该高度处的半径);
  7. *   2. 轮廓绕自身轴(Y 轴)旋转一圈,取若干条经线 + 若干条纬线组成线框;
  8. *   3. 每个顶点依次做 绕Y轴自转 -> 绕X轴倾斜 -> 透视投影,得到屏幕坐标;
  9. *   4. 用 SCGUI 的分带缓冲(sc_pfb_t)逐带绘制并刷屏,整帧只需一次 SPI 传输。
  10. */

  11. #include "vase3d.h"
  12. #include "sc_gui.h"

  13. /* ---------- 参数 ---------- */
  14. #define VASE_N_PROF   14        /* 轮廓点数 */
  15. #define VASE_N_MER    12        /* 经线条数(每 30 度一条) */
  16. #define VASE_N_RING   6         /* 纬线(圆环)条数 */
  17. #define VASE_ANG_STEP (360 / VASE_N_MER)

  18. #define VASE_CX       160       /* 屏幕中心 X */
  19. #define VASE_CY       78        /* 屏幕中心 Y */
  20. #define VASE_CAM      300       /* 虚拟摄像机距离(越小透视越强) */
  21. #define VASE_TILT     (-18)     /* 绕 X 轴倾斜角(度),负值表示略微俯视 */
  22. #define VASE_ROT_STEP 5         /* 每帧自转角度(度) */

  23. /* 只重绘花瓶所在矩形区域,避免全屏清屏拖慢帧率。
  24. * 按上面的投影公式实算包围盒:X 129~191、Y 30~133,四周各留 4 像素。 */
  25. #define VASE_BX       123
  26. #define VASE_BY       25
  27. #define VASE_BW       74
  28. #define VASE_BH       114

  29. #define VASE_C_FRONT  C_CYAN        /* 近侧线框 */
  30. #define VASE_C_BACK   C_STEEL_BLUE  /* 远侧线框(压暗体现纵深) */

  31. /* 花瓶轮廓表:{ y, r },模型坐标(像素单位) */
  32. static const int8_t vase_prof[VASE_N_PROF][2] = {
  33.     { -54, 14 },  /* 瓶底 */
  34.     { -50, 15 },
  35.     { -45, 21 },
  36.     { -38, 27 },
  37.     { -29, 32 },
  38.     { -18, 32 },  /* 腹部最宽 */
  39.     {  -7, 31 },
  40.     {   2, 25 },
  41.     {   9, 18 },
  42.     {  16, 13 },  /* 颈部 */
  43.     {  23, 11 },
  44.     {  31, 12 },
  45.     {  38, 15 },
  46.     {  45, 19 },  /* 瓶口外翻 */
  47. };

  48. /* 需要绘制成圆环的轮廓点下标 */
  49. static const uint8_t vase_rings[VASE_N_RING] = { 0, 3, 5, 8, 11, 13 };

  50. static int16_t vase_rot = 0;    /* 当前自转角度 */

  51. /**
  52. * @brief 顶点变换 + 透视投影
  53. * @param ang   绕 Y 轴的角度
  54. * @param r     顶点半径
  55. * @param y     顶点高度
  56. * @param sx    输出屏幕 X
  57. * @param sy    输出屏幕 Y
  58. * @param depth 输出深度(<0 表示位于近侧)
  59. */
  60. static void vaseProject(int16_t ang, int16_t r, int16_t y,
  61.                         int16_t *sx, int16_t *sy, int32_t *depth)
  62. {
  63.     /* 绕 Y 轴自转 */
  64.     int32_t x = (r * sc_cos(ang)) >> 15;
  65.     int32_t z = (r * sc_sin(ang)) >> 15;

  66.     /* 绕 X 轴倾斜 */
  67.     int32_t ct = sc_cos(VASE_TILT);
  68.     int32_t st = sc_sin(VASE_TILT);
  69.     int32_t y2 = (y * ct - z * st) >> 15;
  70.     int32_t z2 = (y * st + z * ct) >> 15;

  71.     /* 透视缩放(Q8 定点) */
  72.     int32_t k = (256L * VASE_CAM) / (VASE_CAM + z2);

  73.     *sx = VASE_CX + (int16_t)((x * k) >> 8);
  74.     *sy = VASE_CY - (int16_t)((y2 * k) >> 8);
  75.     *depth = z;                 /* 自转后的 z:负值在近侧,正值在远侧 */
  76. }

  77. /* 在分带缓冲上打点(带裁剪) */
  78. static void vasePlot(sc_pfb_t *pfb, int x, int y, color_t c)
  79. {
  80.     if (x < pfb->x || x >= pfb->x + pfb->w) return;
  81.     if (y < pfb->y || y >= pfb->y + pfb->h) return;
  82.     pfb->buf[(y - pfb->y) * pfb->w + (x - pfb->x)] = c;
  83. }

  84. /* 整数 Bresenham 画线,直接写入分带缓冲 */
  85. static void vaseLine(sc_pfb_t *pfb, int x1, int y1, int x2, int y2, color_t c)
  86. {
  87.     int dx = SC_ABS(x2 - x1);
  88.     int dy = -SC_ABS(y2 - y1);
  89.     int sx = (x1 < x2) ? 1 : -1;
  90.     int sy = (y1 < y2) ? 1 : -1;
  91.     int err = dx + dy;

  92.     for (;;) {
  93.         vasePlot(pfb, x1, y1, c);
  94.         if (x1 == x2 && y1 == y2) break;
  95.         int e2 = err << 1;
  96.         if (e2 >= dy) { err += dy; x1 += sx; }
  97.         if (e2 <= dx) { err += dx; y1 += sy; }
  98.     }
  99. }

  100. /**
  101. * @brief 绘制一帧线框
  102. * @param front true 只画近侧线段,false 只画远侧线段(先画远侧,近侧覆盖)
  103. */
  104. static void vaseDrawWire(sc_pfb_t *pfb, bool front)
  105. {
  106.     int16_t px, py, qx, qy;
  107.     int32_t pd, qd;
  108.     color_t color = front ? VASE_C_FRONT : VASE_C_BACK;

  109.     /* ---------- 经线:沿轮廓走一圈 ---------- */
  110.     for (int m = 0; m < VASE_N_MER; m++) {
  111.         int16_t ang = (vase_rot + m * VASE_ANG_STEP) % 360;

  112.         vaseProject(ang, vase_prof[0][1], vase_prof[0][0], &px, &py, &pd);
  113.         for (int i = 1; i < VASE_N_PROF; i++) {
  114.             vaseProject(ang, vase_prof[i][1], vase_prof[i][0], &qx, &qy, &qd);
  115.             if (((pd + qd) < 0) == front) {
  116.                 vaseLine(pfb, px, py, qx, qy, color);
  117.             }
  118.             px = qx; py = qy; pd = qd;
  119.         }
  120.     }

  121.     /* ---------- 纬线:若干条水平圆环 ---------- */
  122.     for (int k = 0; k < VASE_N_RING; k++) {
  123.         int16_t idx = vase_rings[k];
  124.         int16_t r = vase_prof[idx][1];
  125.         int16_t y = vase_prof[idx][0];

  126.         vaseProject(vase_rot % 360, r, y, &px, &py, &pd);
  127.         for (int m = 1; m <= VASE_N_MER; m++) {
  128.             int16_t ang = (vase_rot + (m % VASE_N_MER) * VASE_ANG_STEP) % 360;

  129.             vaseProject(ang, r, y, &qx, &qy, &qd);
  130.             if (((pd + qd) < 0) == front) {
  131.                 vaseLine(pfb, px, py, qx, qy, color);
  132.             }
  133.             px = qx; py = qy; pd = qd;
  134.         }
  135.     }
  136. }

  137. void vase3dInit(void)
  138. {
  139.     vase_rot = 0;
  140.     sc_clear(VASE_BX, VASE_BY, VASE_BW, VASE_BH, C_BLACK);
  141. }

  142. void vase3dFrame(void)
  143. {
  144.     sc_rect_t rect = { VASE_BX, VASE_BY, VASE_BW, VASE_BH };
  145.     sc_pfb_t pfb;

  146.     /* 以花瓶区域为脏矩形,初始化第一个分带缓冲(底色黑色) */
  147.     sc_pfb_init_slices(&pfb, &rect, C_BLACK);

  148.     do {
  149.         vaseDrawWire(&pfb, false);          /* 先画远侧 */
  150.         vaseDrawWire(&pfb, true);           /* 再画近侧,形成前后遮挡 */
  151.     } while (sc_pfb_next_slice(&pfb));      /* 刷屏并切到下一带 */

  152.     vase_rot += VASE_ROT_STEP;
  153.     if (vase_rot >= 360) vase_rot -= 360;
  154. }
复制代码



激光炮代码
  1. #ifndef LASERGAME_H
  2. #define LASERGAME_H

  3. /* 屏幕测试动画:激光炮打障碍物
  4. * 激光炮固定在屏幕底部中间,障碍物从顶部随机掉落,
  5. * 炮口自动瞄准最低的障碍物发射激光,命中后产生爆炸与火花效果。 */
  6. void laserGameInit(void);
  7. void laserGameFrame(void);

  8. #endif
复制代码
  1. /**
  2. * @file    lasergame.c
  3. * @brief   屏幕测试动画:激光炮打障碍物
  4. *
  5. * 场景:
  6. *   1. 激光炮固定在屏幕底部中间,炮管跟随目标转动;
  7. *   2. 障碍物从顶部随机位置、随机尺寸、随机速度掉落;
  8. *   3. 炮口自动瞄准最靠下的障碍物发射激光,激光由炮口向目标生长;
  9. *   4. 命中后障碍物消失,命中点产生爆炸:白热核心 + 扩散冲击波环 + 火花粒子。
  10. *
  11. * 实现要点:
  12. *   - 全部使用 SCGUI 的分带缓冲(sc_pfb_t)绘制;
  13. *   - 只重绘发生变化的脏矩形(背景按程序化方式重算),避免全屏刷新带来的卡顿;
  14. *   - 全部为整数 Bresenham / 中点圆算法,无浮点运算;
  15. *   - 时间步长来自 GetTick(),动画速度与帧率解耦。
  16. */

  17. #include "lasergame.h"
  18. #include "sc_gui.h"
  19. #include <Arduino.h>

  20. /* ================= 场景参数 ================= */
  21. #define LG_W            SC_SCREEN_WIDTH
  22. #define LG_H            SC_SCREEN_HEIGHT

  23. #define LG_BASE_Y       156             /* 地平线 Y */
  24. #define LG_PIVOT_X      160             /* 炮管转轴 X(底部中间) */
  25. #define LG_PIVOT_Y      150             /* 炮管转轴 Y */
  26. #define LG_BARREL       20              /* 炮管长度 */

  27. #define LG_MAX_OBST     6               /* 障碍物池 */
  28. #define LG_MAX_BOOM     3               /* 爆炸池 */
  29. #define LG_SPARK_N      12              /* 每次爆炸的火花数 */
  30. #define LG_STARS        36              /* 背景星星数 */

  31. #define LG_SPAWN_MS     780             /* 障碍物生成间隔 */
  32. #define LG_FIRE_MS      520             /* 开火间隔 */
  33. #define LG_BEAM_MS      170             /* 激光持续时间 */
  34. #define LG_BEAM_GROW    70              /* 激光生长时间 */
  35. #define LG_BOOM_MS      420             /* 爆炸持续时间 */
  36. #define LG_SPARK_MS     220             /* 火花最长寿命 */

  37. #define LG_BOOM_R       38              /* 爆炸最大半径 */
  38. #define LG_CANNON_R     30              /* 炮台脏矩形半径 */

  39. #define LG_MAX_BOX      24              /* 每帧合并型脏矩形上限 */
  40. #define LG_MAX_RAW      24              /* 每帧激光分段脏矩形上限 */
  41. #define LG_BEAM_PAD     11              /* 激光脏矩形外扩量(半宽) */
  42. #define LG_BEAM_STEP    18              /* 激光脏矩形分段步长 */
  43. #define LG_BEAM_SEG_MAX 11              /* 单段激光最多切几块 */

  44. /* 背景渐变(顶部深蓝 → 底部暗紫) */
  45. #define LG_BG_TOP_R     3
  46. #define LG_BG_TOP_G     5
  47. #define LG_BG_TOP_B     14
  48. #define LG_BG_BOT_R     26
  49. #define LG_BG_BOT_G     10
  50. #define LG_BG_BOT_B     44

  51. /* ================= 数据结构 ================= */
  52. typedef struct
  53. {
  54.     int16_t x;
  55.     int16_t y;
  56.     int16_t w;
  57.     int16_t h;
  58. } lg_box_t;

  59. typedef struct
  60. {
  61.     int16_t x;
  62.     int16_t w;
  63.     int16_t h;
  64.     int32_t y;      /* 左上角 Y,Q8 定点 */
  65.     int16_t vy;     /* 下落速度,Q8 像素/毫秒 */
  66.     color_t col;
  67.     uint8_t alive;
  68. } lg_obst_t;

  69. typedef struct
  70. {
  71.     int16_t x;      /* Q4 定点像素 */
  72.     int16_t y;
  73.     int16_t vx;     /* Q4 像素/毫秒 */
  74.     int16_t vy;
  75.     int16_t life;
  76.     int16_t max;
  77.     uint8_t size;
  78.     color_t col;
  79. } lg_spark_t;

  80. typedef struct
  81. {
  82.     uint8_t active;
  83.     int16_t x;
  84.     int16_t y;
  85.     int16_t life;
  86.     int16_t max;
  87.     lg_spark_t sp[LG_SPARK_N];
  88.     lg_box_t dirty;         /* 上一帧脏矩形 */
  89. } lg_boom_t;

  90. typedef struct
  91. {
  92.     uint8_t active;
  93.     int8_t  idx;
  94.     int16_t elapsed;
  95.     int16_t last_ex, last_ey;   /* 上一帧的终点 */
  96.     int16_t last_mx, last_my;   /* 上一帧的炮口 */
  97. } lg_beam_t;

  98. /* ================= 全局状态 ================= */
  99. static lg_obst_t lg_obst[LG_MAX_OBST];
  100. static lg_boom_t lg_boom[LG_MAX_BOOM];
  101. static lg_beam_t lg_beam;

  102. static struct
  103. {
  104.     int16_t x;
  105.     int16_t y;
  106.     color_t c;
  107. } lg_stars[LG_STARS];

  108. static lg_box_t lg_boxes[LG_MAX_BOX];      /* 会互相合并的脏矩形 */
  109. static int lg_box_cnt;

  110. static lg_box_t lg_raw[LG_MAX_RAW];        /* 激光分段脏矩形,不参与合并 */
  111. static int lg_raw_cnt;

  112. static uint32_t lg_rng;
  113. static uint32_t lg_last_ms;
  114. static uint32_t lg_spawn_ms;
  115. static uint32_t lg_fire_ms;

  116. static int16_t lg_aim_x, lg_aim_y;
  117. static int16_t lg_mx, lg_my;
  118. static int16_t lg_sweep_deg;
  119. static int16_t lg_recoil;
  120. static uint16_t lg_charge;
  121. static uint16_t lg_score;
  122. static uint16_t lg_miss;
  123. static uint8_t lg_hud_dirty;

  124. /* ================================================================
  125. * 基础工具
  126. * ================================================================ */

  127. static uint16_t lgRand(void)
  128. {
  129.     lg_rng = lg_rng * 1103515245u + 12345u;
  130.     return (uint16_t)((lg_rng >> 16) & 0x7FFF);
  131. }

  132. /* 整数开方(结果向下取整) */
  133. static int lgSqrt(int v)
  134. {
  135.     if (v <= 0)
  136.         return 0;

  137.     uint32_t num = (uint32_t)v;
  138.     uint32_t r = 0;
  139.     uint32_t bit = 1u << 30;

  140.     while (bit > num)
  141.         bit >>= 2;

  142.     while (bit)
  143.     {
  144.         if (num >= r + bit)
  145.         {
  146.             num -= r + bit;
  147.             r = (r >> 1) + bit;
  148.         }
  149.         else
  150.         {
  151.             r >>= 1;
  152.         }
  153.         bit >>= 2;
  154.     }
  155.     return (int)r;
  156. }

  157. static color_t lgLighter(color_t c) { return alphaBlend(C_WHITE, c, 90); }
  158. static color_t lgDarker(color_t c)  { return alphaBlend(C_BLACK, c, 90); }

  159. static color_t lgBgColor(int y)
  160. {
  161.     int t = SC_CLAMP(y, 0, LG_H - 1) * 64 / LG_H;
  162.     int r = LG_BG_TOP_R + ((LG_BG_BOT_R - LG_BG_TOP_R) * t) / 64;
  163.     int g = LG_BG_TOP_G + ((LG_BG_BOT_G - LG_BG_TOP_G) * t) / 64;
  164.     int b = LG_BG_TOP_B + ((LG_BG_BOT_B - LG_BG_TOP_B) * t) / 64;
  165.     return C_RGB(r, g, b);
  166. }

  167. /* ================================================================
  168. * 脏矩形管理
  169. * ================================================================ */

  170. static void lgBoxAdd(int x, int y, int w, int h)
  171. {
  172.     if (w <= 0 || h <= 0)
  173.         return;

  174.     /* 裁剪到屏幕范围 */
  175.     int x0 = SC_MAX(x, 0);
  176.     int y0 = SC_MAX(y, 0);
  177.     int x1 = SC_MIN(x + w, LG_W);
  178.     int y1 = SC_MIN(y + h, LG_H);
  179.     if (x0 >= x1 || y0 >= y1)
  180.         return;

  181.     lg_box_t b = {(int16_t)x0, (int16_t)y0, (int16_t)(x1 - x0), (int16_t)(y1 - y0)};

  182.     /* 与已有矩形相交(外扩 2 像素)则合并,最多迭代两轮 */
  183.     for (int round = 0; round < 2; round++)
  184.     {
  185.         for (int i = 0; i < lg_box_cnt; i++)
  186.         {
  187.             lg_box_t *a = &lg_boxes[i];
  188.             if (b.x > a->x + a->w + 2 || a->x > b.x + b.w + 2 ||
  189.                 b.y > a->y + a->h + 2 || a->y > b.y + b.h + 2)
  190.                 continue;

  191.             int ax0 = SC_MIN(a->x, b.x);
  192.             int ay0 = SC_MIN(a->y, b.y);
  193.             int ax1 = SC_MAX(a->x + a->w, b.x + b.w);
  194.             int ay1 = SC_MAX(a->y + a->h, b.y + b.h);
  195.             b.x = (int16_t)ax0;
  196.             b.y = (int16_t)ay0;
  197.             b.w = (int16_t)(ax1 - ax0);
  198.             b.h = (int16_t)(ay1 - ay0);

  199.             /* 移除被合并的矩形 */
  200.             lg_boxes[i] = lg_boxes[--lg_box_cnt];
  201.             i = -1;
  202.         }
  203.     }

  204.     if (lg_box_cnt < LG_MAX_BOX)
  205.     {
  206.         lg_boxes[lg_box_cnt++] = b;
  207.     }
  208.     else
  209.     {
  210.         /* 池满:并入最后一个矩形 */
  211.         lg_box_t *a = &lg_boxes[LG_MAX_BOX - 1];
  212.         int ax0 = SC_MIN(a->x, b.x);
  213.         int ay0 = SC_MIN(a->y, b.y);
  214.         int ax1 = SC_MAX(a->x + a->w, b.x + b.w);
  215.         int ay1 = SC_MAX(a->y + a->h, b.y + b.h);
  216.         a->x = (int16_t)ax0;
  217.         a->y = (int16_t)ay0;
  218.         a->w = (int16_t)(ax1 - ax0);
  219.         a->h = (int16_t)(ay1 - ay0);
  220.     }
  221. }

  222. /* 直接追加一个脏矩形,不与已有矩形合并。
  223. * 用于激光沿线段切分出的多块小矩形,避免被合并成一整块大区域。 */
  224. static void lgBoxAddRaw(int x, int y, int w, int h)
  225. {
  226.     if (w <= 0 || h <= 0)
  227.         return;

  228.     int x0 = SC_MAX(x, 0);
  229.     int y0 = SC_MAX(y, 0);
  230.     int x1 = SC_MIN(x + w, LG_W);
  231.     int y1 = SC_MIN(y + h, LG_H);
  232.     if (x0 >= x1 || y0 >= y1)
  233.         return;

  234.     if (lg_raw_cnt >= LG_MAX_RAW)
  235.     {
  236.         lgBoxAdd(x, y, w, h);   /* 池满则退回合并逻辑 */
  237.         return;
  238.     }

  239.     lg_raw[lg_raw_cnt].x = (int16_t)x0;
  240.     lg_raw[lg_raw_cnt].y = (int16_t)y0;
  241.     lg_raw[lg_raw_cnt].w = (int16_t)(x1 - x0);
  242.     lg_raw[lg_raw_cnt].h = (int16_t)(y1 - y0);
  243.     lg_raw_cnt++;
  244. }

  245. /* ================================================================
  246. * 分带缓冲绘制原语(自带裁剪)
  247. * ================================================================ */

  248. static inline void lgPut(sc_pfb_t *pfb, int x, int y, color_t c)
  249. {
  250.     if (x < pfb->x || x >= pfb->x + pfb->w) return;
  251.     if (y < pfb->y || y >= pfb->y + pfb->h) return;
  252.     pfb->buf[(y - pfb->y) * pfb->w + (x - pfb->x)] = c;
  253. }

  254. static inline void lgPutA(sc_pfb_t *pfb, int x, int y, color_t c, uint16_t a)
  255. {
  256.     if (a == 0) return;
  257.     if (x < pfb->x || x >= pfb->x + pfb->w) return;
  258.     if (y < pfb->y || y >= pfb->y + pfb->h) return;
  259.     color_t *p = &pfb->buf[(y - pfb->y) * pfb->w + (x - pfb->x)];
  260.     *p = (a > 251) ? c : alphaBlend(c, *p, a);
  261. }

  262. /* 水平线段:整行批量直写,避免逐点边界判断 */
  263. static void lgHLine(sc_pfb_t *pfb, int x0, int x1, int y, color_t c, uint16_t a)
  264. {
  265.     if (a == 0) return;
  266.     if (y < pfb->y || y >= pfb->y + pfb->h) return;
  267.     if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
  268.     if (x0 < pfb->x) x0 = pfb->x;
  269.     if (x1 > pfb->x + pfb->w - 1) x1 = pfb->x + pfb->w - 1;
  270.     if (x0 > x1) return;

  271.     color_t *p = &pfb->buf[(y - pfb->y) * pfb->w + (x0 - pfb->x)];
  272.     int n = x1 - x0 + 1;
  273.     if (a > 251)
  274.     {
  275.         while (n--) *p++ = c;
  276.     }
  277.     else
  278.     {
  279.         while (n--) { *p = alphaBlend(c, *p, a); p++; }
  280.     }
  281. }

  282. static void lgRectFill(sc_pfb_t *pfb, int x, int y, int w, int h, color_t c, uint16_t a)
  283. {
  284.     if (w <= 0 || h <= 0) return;
  285.     for (int i = 0; i < h; i++)
  286.         lgHLine(pfb, x, x + w - 1, y + i, c, a);
  287. }

  288. /* Bresenham 画线 */
  289. static void lgLine(sc_pfb_t *pfb, int x0, int y0, int x1, int y1, color_t c, uint16_t a)
  290. {
  291.     int dx = SC_ABS(x1 - x0);
  292.     int dy = -SC_ABS(y1 - y0);
  293.     int sx = (x0 < x1) ? 1 : -1;
  294.     int sy = (y0 < y1) ? 1 : -1;
  295.     int err = dx + dy;

  296.     for (;;)
  297.     {
  298.         lgPutA(pfb, x0, y0, c, a);
  299.         if (x0 == x1 && y0 == y1) break;
  300.         int e2 = err << 1;
  301.         if (e2 >= dy) { err += dy; x0 += sx; }
  302.         if (e2 <= dx) { err += dx; y0 += sy; }
  303.     }
  304. }

  305. /* 粗线:沿法线平移若干条细线 */
  306. static void lgThickLine(sc_pfb_t *pfb, int x0, int y0, int x1, int y1, int half, color_t c, uint16_t a)
  307. {
  308.     int dx = x1 - x0;
  309.     int dy = y1 - y0;
  310.     int len = lgSqrt(dx * dx + dy * dy);

  311.     if (len == 0)
  312.     {
  313.         for (int oy = -half; oy <= half; oy++)
  314.             lgHLine(pfb, x0 - half, x0 + half, y0 + oy, c, a);
  315.         return;
  316.     }
  317.     int px = (-dy * 256) / len;     /* 法线单位向量 Q8 */
  318.     int py = (dx * 256) / len;
  319.     for (int i = -half; i <= half; i++)
  320.     {
  321.         int ox = (px * i) >> 8;
  322.         int oy = (py * i) >> 8;
  323.         lgLine(pfb, x0 + ox, y0 + oy, x1 + ox, y1 + oy, c, a);
  324.     }
  325. }

  326. /* 实心圆(中点圆逐行填充) */
  327. static void lgDisc(sc_pfb_t *pfb, int cx, int cy, int r, color_t c, uint16_t a)
  328. {
  329.     if (r <= 0)
  330.     {
  331.         lgPutA(pfb, cx, cy, c, a);
  332.         return;
  333.     }
  334.     int x = r, y = 0, err = 1 - r;
  335.     while (x >= y)
  336.     {
  337.         lgHLine(pfb, cx - x, cx + x, cy + y, c, a);
  338.         lgHLine(pfb, cx - x, cx + x, cy - y, c, a);
  339.         lgHLine(pfb, cx - y, cx + y, cy + x, c, a);
  340.         lgHLine(pfb, cx - y, cx + y, cy - x, c, a);
  341.         y++;
  342.         if (err < 0)
  343.             err += 2 * y + 1;
  344.         else
  345.         {
  346.             x--;
  347.             err += 2 * (y - x) + 1;
  348.         }
  349.     }
  350. }

  351. /* 圆环 */
  352. static void lgRing(sc_pfb_t *pfb, int cx, int cy, int r, color_t c, uint16_t a)
  353. {
  354.     if (r <= 0) return;
  355.     int x = r, y = 0, err = 1 - r;
  356.     while (x >= y)
  357.     {
  358.         lgPutA(pfb, cx + x, cy + y, c, a);
  359.         lgPutA(pfb, cx - x, cy + y, c, a);
  360.         lgPutA(pfb, cx + x, cy - y, c, a);
  361.         lgPutA(pfb, cx - x, cy - y, c, a);
  362.         lgPutA(pfb, cx + y, cy + x, c, a);
  363.         lgPutA(pfb, cx - y, cy + x, c, a);
  364.         lgPutA(pfb, cx + y, cy - x, c, a);
  365.         lgPutA(pfb, cx - y, cy - x, c, a);
  366.         y++;
  367.         if (err < 0)
  368.             err += 2 * y + 1;
  369.         else
  370.         {
  371.             x--;
  372.             err += 2 * (y - x) + 1;
  373.         }
  374.     }
  375. }

  376. /* ================================================================
  377. * 逻辑更新
  378. * ================================================================ */

  379. static const color_t lg_obst_colors[] = {
  380.     C_TOMATO, C_ORANGE, C_GOLD, C_MEDIUM_ORCHID, C_HOT_PINK, C_LIGHT_GREEN, C_CYAN,
  381. };

  382. static const color_t lg_spark_colors[] = {
  383.     C_WHITE, C_YELLOW, C_GOLD, C_ORANGE, C_ORANGE_RED, C_TOMATO,
  384. };

  385. static void lgSpawnObst(void)
  386. {
  387.     for (int i = 0; i < LG_MAX_OBST; i++)
  388.     {
  389.         lg_obst_t *o = &lg_obst[i];
  390.         if (o->alive) continue;

  391.         int w = 14 + lgRand() % 16;
  392.         int h = 12 + lgRand() % 12;
  393.         o->w = (int16_t)w;
  394.         o->h = (int16_t)h;
  395.         o->x = (int16_t)(8 + lgRand() % (LG_W - 16 - w));
  396.         o->y = -((int32_t)h << 8);
  397.         o->vy = (int16_t)(30 + lgRand() % 26);   /* 0.12~0.22 像素/毫秒 */
  398.         o->col = lg_obst_colors[lgRand() % (sizeof(lg_obst_colors) / sizeof(color_t))];
  399.         o->alive = 1;
  400.         return;
  401.     }
  402. }

  403. static void lgBoomSpawnSpark(lg_boom_t *b)
  404. {
  405.     for (int k = 0; k < LG_SPARK_N; k++)
  406.     {
  407.         lg_spark_t *p = &b->sp[k];
  408.         if (p->max != 0) continue;

  409.         int ang = (int)(lgRand() % 360);
  410.         int spd = 1 + (int)(lgRand() % 3);      /* Q4 像素/毫秒,约 0.06~0.19 */
  411.         p->x = (int16_t)(b->x << 4);
  412.         p->y = (int16_t)(b->y << 4);
  413.         p->vx = (int16_t)((spd * sc_cos(ang)) >> 15);
  414.         p->vy = (int16_t)((spd * sc_sin(ang)) >> 15);
  415.         p->max = (int16_t)(150 + lgRand() % (LG_SPARK_MS - 150));
  416.         p->life = 0;
  417.         p->size = (lgRand() & 1) ? 2 : 1;
  418.         p->col = lg_spark_colors[lgRand() % (sizeof(lg_spark_colors) / sizeof(color_t))];
  419.         return;
  420.     }
  421. }

  422. static void lgSpawnBoom(int x, int y)
  423. {
  424.     for (int i = 0; i < LG_MAX_BOOM; i++)
  425.     {
  426.         lg_boom_t *b = &lg_boom[i];
  427.         if (b->active) continue;

  428.         memset(b, 0, sizeof(*b));
  429.         b->active = 1;
  430.         b->x = (int16_t)x;
  431.         b->y = (int16_t)y;
  432.         b->life = 0;
  433.         b->max = LG_BOOM_MS;
  434.         b->dirty.x = (int16_t)(x - 4);
  435.         b->dirty.y = (int16_t)(y - 4);
  436.         b->dirty.w = 8;
  437.         b->dirty.h = 8;
  438.         for (int k = 0; k < LG_SPARK_N; k++)
  439.             lgBoomSpawnSpark(b);
  440.         return;
  441.     }
  442. }

  443. static void lgDestroy(int idx)
  444. {
  445.     if (idx < 0 || idx >= LG_MAX_OBST) return;
  446.     lg_obst_t *o = &lg_obst[idx];
  447.     if (!o->alive) return;

  448.     int cx = o->x + o->w / 2;
  449.     int cy = (int)(o->y >> 8) + o->h / 2;

  450.     lgBoxAdd(o->x - 2, (o->y >> 8) - 2, o->w + 4, o->h + 4);
  451.     lgSpawnBoom(cx, cy);

  452.     o->alive = 0;
  453.     lg_score++;
  454.     lg_hud_dirty = 1;
  455. }

  456. /* 目标:已进入屏幕、最靠下(最危险)的障碍物 */
  457. static int lgPickTarget(void)
  458. {
  459.     int best = -1;
  460.     int32_t best_y = -1;
  461.     for (int i = 0; i < LG_MAX_OBST; i++)
  462.     {
  463.         lg_obst_t *o = &lg_obst[i];
  464.         if (!o->alive) continue;
  465.         if (o->y < 0) continue;
  466.         if ((o->y >> 8) + o->h >= LG_BASE_Y) continue;
  467.         if (o->y > best_y)
  468.         {
  469.             best_y = o->y;
  470.             best = i;
  471.         }
  472.     }
  473.     return best;
  474. }

  475. static void lgAimUpdate(void)
  476. {
  477.     int dx = lg_aim_x - LG_PIVOT_X;
  478.     int dy = lg_aim_y - LG_PIVOT_Y;
  479.     int len = lgSqrt(dx * dx + dy * dy);
  480.     if (len < 8) len = 8;

  481.     int ux = (dx << 8) / len;
  482.     int uy = (dy << 8) / len;
  483.     int bl = LG_BARREL - lg_recoil;

  484.     lg_mx = (int16_t)(LG_PIVOT_X + ((ux * bl) >> 8));
  485.     lg_my = (int16_t)(LG_PIVOT_Y + ((uy * bl) >> 8));
  486. }

  487. static void lgUpdateObstacles(uint32_t dt)
  488. {
  489.     for (int i = 0; i < LG_MAX_OBST; i++)
  490.     {
  491.         lg_obst_t *o = &lg_obst[i];
  492.         if (!o->alive) continue;

  493.         int16_t old_y = (int16_t)(o->y >> 8);
  494.         o->y += (int32_t)o->vy * (int32_t)dt;
  495.         int16_t new_y = (int16_t)(o->y >> 8);

  496.         if (new_y + o->h >= LG_BASE_Y)
  497.         {
  498.             /* 落地:判漏,扬一点尘土 */
  499.             lgBoxAdd(o->x - 2, old_y - 2, o->w + 4, o->h + 4);
  500.             lgBoxAdd(o->x - 8, LG_BASE_Y - 10, o->w + 16, 14);
  501.             o->alive = 0;
  502.             lg_miss++;
  503.             lg_hud_dirty = 1;
  504.             continue;
  505.         }

  506.         lgBoxAdd(o->x - 2, SC_MIN(old_y, new_y) - 2, o->w + 4, SC_ABS(new_y - old_y) + o->h + 4);
  507.     }
  508. }

  509. static void lgUpdateBoom(uint32_t dt)
  510. {
  511.     for (int i = 0; i < LG_MAX_BOOM; i++)
  512.     {
  513.         lg_boom_t *b = &lg_boom[i];
  514.         if (!b->active) continue;

  515.         b->life += (int16_t)dt;
  516.         if (b->life >= b->max)
  517.         {
  518.             lgBoxAdd(b->dirty.x, b->dirty.y, b->dirty.w, b->dirty.h);
  519.             b->active = 0;
  520.             continue;
  521.         }

  522.         /* 火花推进 */
  523.         for (int k = 0; k < LG_SPARK_N; k++)
  524.         {
  525.             lg_spark_t *p = &b->sp[k];
  526.             if (p->max == 0) continue;
  527.             p->life += (int16_t)dt;
  528.             if (p->life >= p->max)
  529.             {
  530.                 p->max = 0;
  531.                 continue;
  532.             }
  533.             p->x = (int16_t)(p->x + (p->vx * (int32_t)dt));
  534.             p->y = (int16_t)(p->y + (p->vy * (int32_t)dt));
  535.         }

  536.         /* 脏矩形:爆炸中心 ± 半径,并覆盖所有火花位置(含上一帧累计区域) */
  537.         int t = b->life * 255 / b->max;
  538.         int r = 6 + (LG_BOOM_R - 6) * t / 255;
  539.         int x0 = SC_MIN(b->dirty.x, b->x - r);
  540.         int y0 = SC_MIN(b->dirty.y, b->y - r);
  541.         int x1 = SC_MAX(b->dirty.x + b->dirty.w, b->x + r);
  542.         int y1 = SC_MAX(b->dirty.y + b->dirty.h, b->y + r);

  543.         for (int k = 0; k < LG_SPARK_N; k++)
  544.         {
  545.             lg_spark_t *p = &b->sp[k];
  546.             if (p->max == 0) continue;
  547.             int sx = p->x >> 4;
  548.             int sy = p->y >> 4;
  549.             x0 = SC_MIN(x0, sx - 2);
  550.             y0 = SC_MIN(y0, sy - 2);
  551.             x1 = SC_MAX(x1, sx + 3);
  552.             y1 = SC_MAX(y1, sy + 3);
  553.         }

  554.         b->dirty.x = (int16_t)x0;
  555.         b->dirty.y = (int16_t)y0;
  556.         b->dirty.w = (int16_t)(x1 - x0);
  557.         b->dirty.h = (int16_t)(y1 - y0);

  558.         lgBoxAdd(b->dirty.x, b->dirty.y, b->dirty.w, b->dirty.h);
  559.     }
  560. }

  561. /* 激光终点:优先跟随锁定的障碍物 */
  562. static void lgBeamTarget(int *tx, int *ty)
  563. {
  564.     *tx = lg_aim_x;
  565.     *ty = lg_aim_y;

  566.     if (lg_beam.idx >= 0 && lg_beam.idx < LG_MAX_OBST && lg_obst[lg_beam.idx].alive)
  567.     {
  568.         lg_obst_t *o = &lg_obst[lg_beam.idx];
  569.         *tx = o->x + o->w / 2;
  570.         *ty = (int)(o->y >> 8) + o->h / 2;
  571.     }
  572. }

  573. /* 沿线段撒一串小脏矩形,避免整条激光的包围盒过大导致刷新耗时 */
  574. static void lgBeamMarkSeg(int x0, int y0, int x1, int y1)
  575. {
  576.     int dx = x1 - x0;
  577.     int dy = y1 - y0;
  578.     int len = lgSqrt(dx * dx + dy * dy);
  579.     int n = len / LG_BEAM_STEP;
  580.     if (n < 1) n = 1;
  581.     if (n > LG_BEAM_SEG_MAX) n = LG_BEAM_SEG_MAX;

  582.     for (int i = 0; i <= n; i++)
  583.     {
  584.         int x = x0 + (dx * i) / n;
  585.         int y = y0 + (dy * i) / n;
  586.         lgBoxAddRaw(x - LG_BEAM_PAD, y - LG_BEAM_PAD, LG_BEAM_PAD * 2, LG_BEAM_PAD * 2);
  587.     }
  588. }

  589. static void lgUpdateBeam(uint32_t dt)
  590. {
  591.     if (!lg_beam.active) return;

  592.     lg_beam.elapsed += (int16_t)dt;

  593.     int tx, ty;
  594.     lgBeamTarget(&tx, &ty);

  595.     int p = lg_beam.elapsed * 256 / LG_BEAM_GROW;
  596.     if (p > 255) p = 255;

  597.     int ex = lg_mx + (((tx - lg_mx) * p) >> 8);
  598.     int ey = lg_my + (((ty - lg_my) * p) >> 8);

  599.     /* 上一帧的激光要擦除,本帧的激光要绘制,两段都标记 */
  600.     lgBeamMarkSeg(lg_beam.last_mx, lg_beam.last_my, lg_beam.last_ex, lg_beam.last_ey);
  601.     lgBeamMarkSeg(lg_mx, lg_my, ex, ey);

  602.     lg_beam.last_mx = lg_mx;
  603.     lg_beam.last_my = lg_my;
  604.     lg_beam.last_ex = (int16_t)ex;
  605.     lg_beam.last_ey = (int16_t)ey;

  606.     if (lg_beam.elapsed >= LG_BEAM_MS)
  607.     {
  608.         lgDestroy(lg_beam.idx);
  609.         lg_beam.active = 0;
  610.         lg_beam.idx = -1;
  611.     }
  612. }

  613. static void lgUpdate(uint32_t dt)
  614. {
  615.     uint32_t now = lg_last_ms;

  616.     /* ---- 障碍物下落 ---- */
  617.     lgUpdateObstacles(dt);

  618.     /* ---- 定时生成 ---- */
  619.     if (now - lg_spawn_ms >= LG_SPAWN_MS)
  620.     {
  621.         lg_spawn_ms = now;
  622.         lgSpawnObst();
  623.     }

  624.     /* ---- 瞄准 ---- */
  625.     int tgt = lgPickTarget();
  626.     if (tgt >= 0)
  627.     {
  628.         lg_aim_x = (int16_t)(lg_obst[tgt].x + lg_obst[tgt].w / 2);
  629.         lg_aim_y = (int16_t)((lg_obst[tgt].y >> 8) + lg_obst[tgt].h / 2);
  630.     }
  631.     else
  632.     {
  633.         lg_sweep_deg += 6;
  634.         if (lg_sweep_deg >= 360) lg_sweep_deg -= 360;
  635.         lg_aim_x = (int16_t)(LG_PIVOT_X + ((70 * sc_sin(lg_sweep_deg)) >> 15));
  636.         lg_aim_y = 80;
  637.     }

  638.     /* ---- 炮口位置(按最新瞄准点更新) ---- */
  639.     lgAimUpdate();

  640.     /* ---- 开火 ---- */
  641.     if (!lg_beam.active && tgt >= 0 && now - lg_fire_ms >= LG_FIRE_MS)
  642.     {
  643.         lg_fire_ms = now;
  644.         lg_beam.active = 1;
  645.         lg_beam.idx = (int8_t)tgt;
  646.         lg_beam.elapsed = 0;
  647.         lg_beam.last_mx = lg_mx;
  648.         lg_beam.last_my = lg_my;
  649.         lg_beam.last_ex = lg_mx;
  650.         lg_beam.last_ey = lg_my;
  651.         lg_recoil = 6;
  652.     }

  653.     /* ---- 激光推进 / 命中判定 ---- */
  654.     lgUpdateBeam(dt);

  655.     if (lg_recoil > 0 && lg_beam.elapsed > 40) lg_recoil--;

  656.     /* ---- 充能(炮口核心颜色) ---- */
  657.     uint32_t charge_ms = now - lg_fire_ms;
  658.     if (charge_ms > LG_FIRE_MS) charge_ms = LG_FIRE_MS;
  659.     lg_charge = (uint16_t)(charge_ms * 255 / LG_FIRE_MS);

  660.     /* ---- 爆炸 ---- */
  661.     lgUpdateBoom(dt);

  662.     /* ---- 炮台:转轴固定,用覆盖炮管旋转范围与底座的脏矩形 ---- */
  663.     lgBoxAdd(LG_PIVOT_X - LG_CANNON_R, LG_PIVOT_Y - LG_CANNON_R,
  664.              LG_CANNON_R * 2, LG_H - (LG_PIVOT_Y - LG_CANNON_R));

  665.     /* ---- HUD ---- */
  666.     if (lg_hud_dirty)
  667.     {
  668.         lg_hud_dirty = 0;
  669.         lgBoxAdd(2, 0, 96, 44);
  670.     }
  671. }

  672. /* ================================================================
  673. * 绘制
  674. * ================================================================ */

  675. static void lgDrawBackground(sc_pfb_t *pfb)
  676. {
  677.     for (int y = pfb->y; y < pfb->y + pfb->h; y++)
  678.         lgHLine(pfb, pfb->x, pfb->x + pfb->w - 1, y, lgBgColor(y), 255);

  679.     for (int i = 0; i < LG_STARS; i++)
  680.         lgPut(pfb, lg_stars[i].x, lg_stars[i].y, lg_stars[i].c);

  681.     lgHLine(pfb, 0, LG_W - 1, LG_BASE_Y, C_RGB(70, 105, 180), 255);
  682.     lgHLine(pfb, 0, LG_W - 1, LG_BASE_Y + 1, C_RGB(34, 50, 96), 255);
  683.     lgRectFill(pfb, 0, LG_BASE_Y + 2, LG_W, LG_H - LG_BASE_Y - 2, C_RGB(8, 12, 26), 255);
  684. }

  685. static void lgDrawObstacles(sc_pfb_t *pfb)
  686. {
  687.     for (int i = 0; i < LG_MAX_OBST; i++)
  688.     {
  689.         lg_obst_t *o = &lg_obst[i];
  690.         if (!o->alive) continue;

  691.         int x = o->x;
  692.         int y = (int)(o->y >> 8);

  693.         lgRectFill(pfb, x, y, o->w, o->h, o->col, 255);
  694.         lgRectFill(pfb, x, y, o->w, 2, lgLighter(o->col), 255);           /* 顶部高光 */
  695.         lgRectFill(pfb, x, y + o->h - 2, o->w, 2, lgDarker(o->col), 255); /* 底部阴影 */
  696.         lgRectFill(pfb, x + 1, y + 2, 2, o->h - 4, lgLighter(o->col), 130);/* 左侧反光 */
  697.         lgRectFill(pfb, x + o->w - 1, y, 1, o->h, lgDarker(o->col), 255); /* 右侧暗边 */
  698.     }
  699. }

  700. static void lgDrawBoom(sc_pfb_t *pfb)
  701. {
  702.     for (int i = 0; i < LG_MAX_BOOM; i++)
  703.     {
  704.         lg_boom_t *b = &lg_boom[i];
  705.         if (!b->active) continue;

  706.         int t = b->life * 255 / b->max;   /* 0~255 爆炸进度 */
  707.         int fade = 255 - t;

  708.         int r_core = 3 + (9 * t) / 255;   /* 白热核心 */
  709.         int r_ring = 5 + (26 * t) / 255;  /* 冲击波 */

  710.         color_t core = (t < 60) ? C_WHITE : ((t < 130) ? C_YELLOW : C_ORANGE_RED);
  711.         lgDisc(pfb, b->x, b->y, r_core, core, (uint16_t)fade);
  712.         lgDisc(pfb, b->x, b->y, SC_MAX(r_core - 3, 1), C_WHITE, (uint16_t)fade);

  713.         lgRing(pfb, b->x, b->y, r_ring, C_ORANGE, (uint16_t)(fade * 3 / 4));
  714.         if (r_ring > 7)
  715.             lgRing(pfb, b->x, b->y, r_ring - 2, C_GOLD, (uint16_t)(fade / 2));

  716.         /* 十字光芒,强化"打爆"感 */
  717.         if (t < 120)
  718.         {
  719.             int g = 4 + (14 * t) / 120;
  720.             lgHLine(pfb, b->x - g, b->x + g, b->y, C_WHITE, (uint16_t)(fade * 2 / 3));
  721.             for (int k = -g; k <= g; k++)
  722.                 lgPutA(pfb, b->x, b->y + k, C_WHITE, (uint16_t)(fade * 2 / 3));
  723.         }

  724.         /* 火花 */
  725.         for (int k = 0; k < LG_SPARK_N; k++)
  726.         {
  727.             lg_spark_t *p = &b->sp[k];
  728.             if (p->max == 0) continue;
  729.             int a = 255 * (p->max - p->life) / p->max;
  730.             lgRectFill(pfb, p->x >> 4, p->y >> 4, p->size, p->size, p->col, (uint16_t)a);
  731.         }
  732.     }
  733. }

  734. static void lgDrawCannon(sc_pfb_t *pfb)
  735. {
  736.     /* 底座 */
  737.     lgRectFill(pfb, LG_PIVOT_X - 20, LG_BASE_Y + 4, 40, 12, C_RGB(34, 44, 68), 255);
  738.     lgRectFill(pfb, LG_PIVOT_X - 20, LG_BASE_Y + 4, 40, 2, C_RGB(72, 92, 134), 255);
  739.     lgRectFill(pfb, LG_PIVOT_X - 15, LG_BASE_Y + 1, 30, 4, C_RGB(56, 70, 102), 255);

  740.     /* 炮管:外轮廓 + 内层高光 */
  741.     lgThickLine(pfb, LG_PIVOT_X, LG_PIVOT_Y, lg_mx, lg_my, 3, C_RGB(64, 76, 104), 255);
  742.     lgThickLine(pfb, LG_PIVOT_X, LG_PIVOT_Y, lg_mx, lg_my, 1, C_RGB(150, 168, 200), 255);

  743.     /* 转轴与充能核心 */
  744.     lgDisc(pfb, LG_PIVOT_X, LG_PIVOT_Y, 6, C_RGB(58, 72, 102), 255);
  745.     lgDisc(pfb, LG_PIVOT_X, LG_PIVOT_Y, 4, alphaBlend(C_ORANGE_RED, C_YELLOW, lg_charge), 255);
  746.     lgDisc(pfb, LG_PIVOT_X, LG_PIVOT_Y, 2, alphaBlend(C_YELLOW, C_WHITE, lg_charge), 255);

  747.     /* 炮口 */
  748.     lgDisc(pfb, lg_mx, lg_my, 2, C_ORANGE_RED, 255);
  749. }

  750. static void lgDrawBeam(sc_pfb_t *pfb)
  751. {
  752.     if (!lg_beam.active) return;

  753.     int tx, ty;
  754.     lgBeamTarget(&tx, &ty);

  755.     int p = lg_beam.elapsed * 256 / LG_BEAM_GROW;
  756.     if (p > 255) p = 255;

  757.     int ex = lg_mx + (((tx - lg_mx) * p) >> 8);
  758.     int ey = lg_my + (((ty - lg_my) * p) >> 8);

  759.     /* 外层辉光 → 中层 → 白色核心 */
  760.     lgThickLine(pfb, lg_mx, lg_my, ex, ey, 4, C_ORANGE, 55);
  761.     lgThickLine(pfb, lg_mx, lg_my, ex, ey, 2, C_YELLOW, 130);
  762.     lgThickLine(pfb, lg_mx, lg_my, ex, ey, 0, C_WHITE, 255);

  763.     /* 炮口闪光 */
  764.     lgDisc(pfb, lg_mx, lg_my, 4, C_WHITE, 140);

  765.     /* 命中点高亮 */
  766.     if (p >= 255)
  767.     {
  768.         lgDisc(pfb, ex, ey, 6, C_WHITE, 170);
  769.         lgDisc(pfb, ex, ey, 3, C_YELLOW, 255);
  770.     }
  771. }

  772. static void lgDrawHud(sc_pfb_t *pfb)
  773. {
  774.     char buf[24];
  775.     sprintf(buf, "HIT %d", (int)lg_score);
  776.     sc_draw_Text(pfb, 8, 4, gui->font, buf, C_GOLD, gui->bkc);

  777.     sprintf(buf, "MISS %d", (int)lg_miss);
  778.     sc_draw_Text(pfb, 8, 24, gui->font, buf, C_STEEL_BLUE, gui->bkc);
  779. }

  780. /* 绘制一个脏矩形 */
  781. static void lgRenderBox(const lg_box_t *b)
  782. {
  783.     sc_rect_t rect = {b->x, b->y, b->w, b->h};
  784.     sc_pfb_t pfb;

  785.     sc_pfb_init_slices(&pfb, &rect, C_BLACK);
  786.     do
  787.     {
  788.         lgDrawBackground(&pfb);
  789.         lgDrawObstacles(&pfb);
  790.         lgDrawBoom(&pfb);
  791.         lgDrawCannon(&pfb);
  792.         lgDrawBeam(&pfb);
  793.         lgDrawHud(&pfb);
  794.     } while (sc_pfb_next_slice(&pfb));
  795. }

  796. /* ================================================================
  797. * 对外接口
  798. * ================================================================ */

  799. void laserGameInit(void)
  800. {
  801.     lg_rng = 0x13579BDFu;
  802.     lg_last_ms = (uint32_t)GetTick();
  803.     lg_spawn_ms = lg_last_ms;
  804.     lg_fire_ms = lg_last_ms - LG_FIRE_MS;

  805.     memset(lg_obst, 0, sizeof(lg_obst));
  806.     memset(lg_boom, 0, sizeof(lg_boom));
  807.     memset(&lg_beam, 0, sizeof(lg_beam));

  808.     lg_beam.idx = -1;
  809.     lg_aim_x = LG_PIVOT_X;
  810.     lg_aim_y = 80;
  811.     lg_sweep_deg = 0;
  812.     lg_recoil = 0;
  813.     lg_charge = 0;
  814.     lg_score = 0;
  815.     lg_miss = 0;
  816.     lg_hud_dirty = 0;
  817.     lg_box_cnt = 0;
  818.     lg_raw_cnt = 0;

  819.     /* 预生成星空 */
  820.     for (int i = 0; i < LG_STARS; i++)
  821.     {
  822.         lg_stars[i].x = (int16_t)(lgRand() % LG_W);
  823.         lg_stars[i].y = (int16_t)(lgRand() % (LG_BASE_Y - 6));
  824.         uint8_t v = (uint8_t)(70 + lgRand() % 120);
  825.         lg_stars[i].c = C_RGB(v, v, (uint8_t)SC_MIN(255, v + 40));
  826.     }

  827.     /* 预置两个障碍物,避免开场空等 */
  828.     lgSpawnObst();
  829.     lgSpawnObst();

  830.     int k = 0;
  831.     for (int i = 0; i < LG_MAX_OBST; i++)
  832.     {
  833.         if (!lg_obst[i].alive) continue;
  834.         lg_obst[i].y = (int32_t)(18 + k * 36) << 8;   /* 分散到屏幕上部不同高度 */
  835.         k++;
  836.     }

  837.     lgAimUpdate();

  838.     /* 首帧整屏绘制一次 */
  839.     sc_clear(0, 0, LG_W, LG_H, C_BLACK);
  840.     lg_box_t full = {0, 0, LG_W, LG_H};
  841.     lgRenderBox(&full);
  842. }

  843. void laserGameFrame(void)
  844. {
  845.     uint32_t now = (uint32_t)GetTick();
  846.     uint32_t dt = now - lg_last_ms;
  847.     if (dt > 120) dt = 120;   /* 限制最大步长,防止卡顿后跳帧 */
  848.     lg_last_ms = now;

  849.     lg_box_cnt = 0;
  850.     lg_raw_cnt = 0;
  851.     lgUpdate(dt);

  852.     for (int i = 0; i < lg_box_cnt; i++)
  853.         lgRenderBox(&lg_boxes[i]);

  854.     for (int i = 0; i < lg_raw_cnt; i++)
  855.         lgRenderBox(&lg_raw[i]);
  856. }
复制代码




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
 楼主| 发表于 2 小时前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 1 小时前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|数码之家-技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2026-11-4 12:10 , Processed in 0.093600 second(s), 6 queries , Gzip On, Redis On.

Powered by Discuz!

© MyDigit.Net Since 2006

快速回复 返回顶部 返回列表