|
|
本帖最后由 飞向狙沙 于 2026-11-4 10:38 编辑
驱动屏幕一般就是先单色刷屏,再移植些效果图,挺麻烦也不好看,就让ai生成了两个动画图
旋转花瓶
激光炮
花瓶代码
- #ifndef VASE3D_H
- #define VASE3D_H
- /* 3D 线框花瓶旋转动画测试 */
- void vase3dInit(void);
- void vase3dFrame(void);
- #endif
复制代码- /**
- * @file vase3d.c
- * [url=home.php?mod=space&uid=650075]@brief[/url] 最简单的三维动画测试:屏幕上旋转的线框花瓶
- *
- * 实现思路:
- * 1. 花瓶用一张 {y, r} 轮廓表描述(y 为高度,r 为该高度处的半径);
- * 2. 轮廓绕自身轴(Y 轴)旋转一圈,取若干条经线 + 若干条纬线组成线框;
- * 3. 每个顶点依次做 绕Y轴自转 -> 绕X轴倾斜 -> 透视投影,得到屏幕坐标;
- * 4. 用 SCGUI 的分带缓冲(sc_pfb_t)逐带绘制并刷屏,整帧只需一次 SPI 传输。
- */
- #include "vase3d.h"
- #include "sc_gui.h"
- /* ---------- 参数 ---------- */
- #define VASE_N_PROF 14 /* 轮廓点数 */
- #define VASE_N_MER 12 /* 经线条数(每 30 度一条) */
- #define VASE_N_RING 6 /* 纬线(圆环)条数 */
- #define VASE_ANG_STEP (360 / VASE_N_MER)
- #define VASE_CX 160 /* 屏幕中心 X */
- #define VASE_CY 78 /* 屏幕中心 Y */
- #define VASE_CAM 300 /* 虚拟摄像机距离(越小透视越强) */
- #define VASE_TILT (-18) /* 绕 X 轴倾斜角(度),负值表示略微俯视 */
- #define VASE_ROT_STEP 5 /* 每帧自转角度(度) */
- /* 只重绘花瓶所在矩形区域,避免全屏清屏拖慢帧率。
- * 按上面的投影公式实算包围盒:X 129~191、Y 30~133,四周各留 4 像素。 */
- #define VASE_BX 123
- #define VASE_BY 25
- #define VASE_BW 74
- #define VASE_BH 114
- #define VASE_C_FRONT C_CYAN /* 近侧线框 */
- #define VASE_C_BACK C_STEEL_BLUE /* 远侧线框(压暗体现纵深) */
- /* 花瓶轮廓表:{ y, r },模型坐标(像素单位) */
- static const int8_t vase_prof[VASE_N_PROF][2] = {
- { -54, 14 }, /* 瓶底 */
- { -50, 15 },
- { -45, 21 },
- { -38, 27 },
- { -29, 32 },
- { -18, 32 }, /* 腹部最宽 */
- { -7, 31 },
- { 2, 25 },
- { 9, 18 },
- { 16, 13 }, /* 颈部 */
- { 23, 11 },
- { 31, 12 },
- { 38, 15 },
- { 45, 19 }, /* 瓶口外翻 */
- };
- /* 需要绘制成圆环的轮廓点下标 */
- static const uint8_t vase_rings[VASE_N_RING] = { 0, 3, 5, 8, 11, 13 };
- static int16_t vase_rot = 0; /* 当前自转角度 */
- /**
- * @brief 顶点变换 + 透视投影
- * @param ang 绕 Y 轴的角度
- * @param r 顶点半径
- * @param y 顶点高度
- * @param sx 输出屏幕 X
- * @param sy 输出屏幕 Y
- * @param depth 输出深度(<0 表示位于近侧)
- */
- static void vaseProject(int16_t ang, int16_t r, int16_t y,
- int16_t *sx, int16_t *sy, int32_t *depth)
- {
- /* 绕 Y 轴自转 */
- int32_t x = (r * sc_cos(ang)) >> 15;
- int32_t z = (r * sc_sin(ang)) >> 15;
- /* 绕 X 轴倾斜 */
- int32_t ct = sc_cos(VASE_TILT);
- int32_t st = sc_sin(VASE_TILT);
- int32_t y2 = (y * ct - z * st) >> 15;
- int32_t z2 = (y * st + z * ct) >> 15;
- /* 透视缩放(Q8 定点) */
- int32_t k = (256L * VASE_CAM) / (VASE_CAM + z2);
- *sx = VASE_CX + (int16_t)((x * k) >> 8);
- *sy = VASE_CY - (int16_t)((y2 * k) >> 8);
- *depth = z; /* 自转后的 z:负值在近侧,正值在远侧 */
- }
- /* 在分带缓冲上打点(带裁剪) */
- static void vasePlot(sc_pfb_t *pfb, int x, int y, color_t c)
- {
- if (x < pfb->x || x >= pfb->x + pfb->w) return;
- if (y < pfb->y || y >= pfb->y + pfb->h) return;
- pfb->buf[(y - pfb->y) * pfb->w + (x - pfb->x)] = c;
- }
- /* 整数 Bresenham 画线,直接写入分带缓冲 */
- static void vaseLine(sc_pfb_t *pfb, int x1, int y1, int x2, int y2, color_t c)
- {
- int dx = SC_ABS(x2 - x1);
- int dy = -SC_ABS(y2 - y1);
- int sx = (x1 < x2) ? 1 : -1;
- int sy = (y1 < y2) ? 1 : -1;
- int err = dx + dy;
- for (;;) {
- vasePlot(pfb, x1, y1, c);
- if (x1 == x2 && y1 == y2) break;
- int e2 = err << 1;
- if (e2 >= dy) { err += dy; x1 += sx; }
- if (e2 <= dx) { err += dx; y1 += sy; }
- }
- }
- /**
- * @brief 绘制一帧线框
- * @param front true 只画近侧线段,false 只画远侧线段(先画远侧,近侧覆盖)
- */
- static void vaseDrawWire(sc_pfb_t *pfb, bool front)
- {
- int16_t px, py, qx, qy;
- int32_t pd, qd;
- color_t color = front ? VASE_C_FRONT : VASE_C_BACK;
- /* ---------- 经线:沿轮廓走一圈 ---------- */
- for (int m = 0; m < VASE_N_MER; m++) {
- int16_t ang = (vase_rot + m * VASE_ANG_STEP) % 360;
- vaseProject(ang, vase_prof[0][1], vase_prof[0][0], &px, &py, &pd);
- for (int i = 1; i < VASE_N_PROF; i++) {
- vaseProject(ang, vase_prof[i][1], vase_prof[i][0], &qx, &qy, &qd);
- if (((pd + qd) < 0) == front) {
- vaseLine(pfb, px, py, qx, qy, color);
- }
- px = qx; py = qy; pd = qd;
- }
- }
- /* ---------- 纬线:若干条水平圆环 ---------- */
- for (int k = 0; k < VASE_N_RING; k++) {
- int16_t idx = vase_rings[k];
- int16_t r = vase_prof[idx][1];
- int16_t y = vase_prof[idx][0];
- vaseProject(vase_rot % 360, r, y, &px, &py, &pd);
- for (int m = 1; m <= VASE_N_MER; m++) {
- int16_t ang = (vase_rot + (m % VASE_N_MER) * VASE_ANG_STEP) % 360;
- vaseProject(ang, r, y, &qx, &qy, &qd);
- if (((pd + qd) < 0) == front) {
- vaseLine(pfb, px, py, qx, qy, color);
- }
- px = qx; py = qy; pd = qd;
- }
- }
- }
- void vase3dInit(void)
- {
- vase_rot = 0;
- sc_clear(VASE_BX, VASE_BY, VASE_BW, VASE_BH, C_BLACK);
- }
- void vase3dFrame(void)
- {
- sc_rect_t rect = { VASE_BX, VASE_BY, VASE_BW, VASE_BH };
- sc_pfb_t pfb;
- /* 以花瓶区域为脏矩形,初始化第一个分带缓冲(底色黑色) */
- sc_pfb_init_slices(&pfb, &rect, C_BLACK);
- do {
- vaseDrawWire(&pfb, false); /* 先画远侧 */
- vaseDrawWire(&pfb, true); /* 再画近侧,形成前后遮挡 */
- } while (sc_pfb_next_slice(&pfb)); /* 刷屏并切到下一带 */
- vase_rot += VASE_ROT_STEP;
- if (vase_rot >= 360) vase_rot -= 360;
- }
复制代码
激光炮代码
- #ifndef LASERGAME_H
- #define LASERGAME_H
- /* 屏幕测试动画:激光炮打障碍物
- * 激光炮固定在屏幕底部中间,障碍物从顶部随机掉落,
- * 炮口自动瞄准最低的障碍物发射激光,命中后产生爆炸与火花效果。 */
- void laserGameInit(void);
- void laserGameFrame(void);
- #endif
复制代码- /**
- * @file lasergame.c
- * @brief 屏幕测试动画:激光炮打障碍物
- *
- * 场景:
- * 1. 激光炮固定在屏幕底部中间,炮管跟随目标转动;
- * 2. 障碍物从顶部随机位置、随机尺寸、随机速度掉落;
- * 3. 炮口自动瞄准最靠下的障碍物发射激光,激光由炮口向目标生长;
- * 4. 命中后障碍物消失,命中点产生爆炸:白热核心 + 扩散冲击波环 + 火花粒子。
- *
- * 实现要点:
- * - 全部使用 SCGUI 的分带缓冲(sc_pfb_t)绘制;
- * - 只重绘发生变化的脏矩形(背景按程序化方式重算),避免全屏刷新带来的卡顿;
- * - 全部为整数 Bresenham / 中点圆算法,无浮点运算;
- * - 时间步长来自 GetTick(),动画速度与帧率解耦。
- */
- #include "lasergame.h"
- #include "sc_gui.h"
- #include <Arduino.h>
- /* ================= 场景参数 ================= */
- #define LG_W SC_SCREEN_WIDTH
- #define LG_H SC_SCREEN_HEIGHT
- #define LG_BASE_Y 156 /* 地平线 Y */
- #define LG_PIVOT_X 160 /* 炮管转轴 X(底部中间) */
- #define LG_PIVOT_Y 150 /* 炮管转轴 Y */
- #define LG_BARREL 20 /* 炮管长度 */
- #define LG_MAX_OBST 6 /* 障碍物池 */
- #define LG_MAX_BOOM 3 /* 爆炸池 */
- #define LG_SPARK_N 12 /* 每次爆炸的火花数 */
- #define LG_STARS 36 /* 背景星星数 */
- #define LG_SPAWN_MS 780 /* 障碍物生成间隔 */
- #define LG_FIRE_MS 520 /* 开火间隔 */
- #define LG_BEAM_MS 170 /* 激光持续时间 */
- #define LG_BEAM_GROW 70 /* 激光生长时间 */
- #define LG_BOOM_MS 420 /* 爆炸持续时间 */
- #define LG_SPARK_MS 220 /* 火花最长寿命 */
- #define LG_BOOM_R 38 /* 爆炸最大半径 */
- #define LG_CANNON_R 30 /* 炮台脏矩形半径 */
- #define LG_MAX_BOX 24 /* 每帧合并型脏矩形上限 */
- #define LG_MAX_RAW 24 /* 每帧激光分段脏矩形上限 */
- #define LG_BEAM_PAD 11 /* 激光脏矩形外扩量(半宽) */
- #define LG_BEAM_STEP 18 /* 激光脏矩形分段步长 */
- #define LG_BEAM_SEG_MAX 11 /* 单段激光最多切几块 */
- /* 背景渐变(顶部深蓝 → 底部暗紫) */
- #define LG_BG_TOP_R 3
- #define LG_BG_TOP_G 5
- #define LG_BG_TOP_B 14
- #define LG_BG_BOT_R 26
- #define LG_BG_BOT_G 10
- #define LG_BG_BOT_B 44
- /* ================= 数据结构 ================= */
- typedef struct
- {
- int16_t x;
- int16_t y;
- int16_t w;
- int16_t h;
- } lg_box_t;
- typedef struct
- {
- int16_t x;
- int16_t w;
- int16_t h;
- int32_t y; /* 左上角 Y,Q8 定点 */
- int16_t vy; /* 下落速度,Q8 像素/毫秒 */
- color_t col;
- uint8_t alive;
- } lg_obst_t;
- typedef struct
- {
- int16_t x; /* Q4 定点像素 */
- int16_t y;
- int16_t vx; /* Q4 像素/毫秒 */
- int16_t vy;
- int16_t life;
- int16_t max;
- uint8_t size;
- color_t col;
- } lg_spark_t;
- typedef struct
- {
- uint8_t active;
- int16_t x;
- int16_t y;
- int16_t life;
- int16_t max;
- lg_spark_t sp[LG_SPARK_N];
- lg_box_t dirty; /* 上一帧脏矩形 */
- } lg_boom_t;
- typedef struct
- {
- uint8_t active;
- int8_t idx;
- int16_t elapsed;
- int16_t last_ex, last_ey; /* 上一帧的终点 */
- int16_t last_mx, last_my; /* 上一帧的炮口 */
- } lg_beam_t;
- /* ================= 全局状态 ================= */
- static lg_obst_t lg_obst[LG_MAX_OBST];
- static lg_boom_t lg_boom[LG_MAX_BOOM];
- static lg_beam_t lg_beam;
- static struct
- {
- int16_t x;
- int16_t y;
- color_t c;
- } lg_stars[LG_STARS];
- static lg_box_t lg_boxes[LG_MAX_BOX]; /* 会互相合并的脏矩形 */
- static int lg_box_cnt;
- static lg_box_t lg_raw[LG_MAX_RAW]; /* 激光分段脏矩形,不参与合并 */
- static int lg_raw_cnt;
- static uint32_t lg_rng;
- static uint32_t lg_last_ms;
- static uint32_t lg_spawn_ms;
- static uint32_t lg_fire_ms;
- static int16_t lg_aim_x, lg_aim_y;
- static int16_t lg_mx, lg_my;
- static int16_t lg_sweep_deg;
- static int16_t lg_recoil;
- static uint16_t lg_charge;
- static uint16_t lg_score;
- static uint16_t lg_miss;
- static uint8_t lg_hud_dirty;
- /* ================================================================
- * 基础工具
- * ================================================================ */
- static uint16_t lgRand(void)
- {
- lg_rng = lg_rng * 1103515245u + 12345u;
- return (uint16_t)((lg_rng >> 16) & 0x7FFF);
- }
- /* 整数开方(结果向下取整) */
- static int lgSqrt(int v)
- {
- if (v <= 0)
- return 0;
- uint32_t num = (uint32_t)v;
- uint32_t r = 0;
- uint32_t bit = 1u << 30;
- while (bit > num)
- bit >>= 2;
- while (bit)
- {
- if (num >= r + bit)
- {
- num -= r + bit;
- r = (r >> 1) + bit;
- }
- else
- {
- r >>= 1;
- }
- bit >>= 2;
- }
- return (int)r;
- }
- static color_t lgLighter(color_t c) { return alphaBlend(C_WHITE, c, 90); }
- static color_t lgDarker(color_t c) { return alphaBlend(C_BLACK, c, 90); }
- static color_t lgBgColor(int y)
- {
- int t = SC_CLAMP(y, 0, LG_H - 1) * 64 / LG_H;
- int r = LG_BG_TOP_R + ((LG_BG_BOT_R - LG_BG_TOP_R) * t) / 64;
- int g = LG_BG_TOP_G + ((LG_BG_BOT_G - LG_BG_TOP_G) * t) / 64;
- int b = LG_BG_TOP_B + ((LG_BG_BOT_B - LG_BG_TOP_B) * t) / 64;
- return C_RGB(r, g, b);
- }
- /* ================================================================
- * 脏矩形管理
- * ================================================================ */
- static void lgBoxAdd(int x, int y, int w, int h)
- {
- if (w <= 0 || h <= 0)
- return;
- /* 裁剪到屏幕范围 */
- int x0 = SC_MAX(x, 0);
- int y0 = SC_MAX(y, 0);
- int x1 = SC_MIN(x + w, LG_W);
- int y1 = SC_MIN(y + h, LG_H);
- if (x0 >= x1 || y0 >= y1)
- return;
- lg_box_t b = {(int16_t)x0, (int16_t)y0, (int16_t)(x1 - x0), (int16_t)(y1 - y0)};
- /* 与已有矩形相交(外扩 2 像素)则合并,最多迭代两轮 */
- for (int round = 0; round < 2; round++)
- {
- for (int i = 0; i < lg_box_cnt; i++)
- {
- lg_box_t *a = &lg_boxes[i];
- if (b.x > a->x + a->w + 2 || a->x > b.x + b.w + 2 ||
- b.y > a->y + a->h + 2 || a->y > b.y + b.h + 2)
- continue;
- int ax0 = SC_MIN(a->x, b.x);
- int ay0 = SC_MIN(a->y, b.y);
- int ax1 = SC_MAX(a->x + a->w, b.x + b.w);
- int ay1 = SC_MAX(a->y + a->h, b.y + b.h);
- b.x = (int16_t)ax0;
- b.y = (int16_t)ay0;
- b.w = (int16_t)(ax1 - ax0);
- b.h = (int16_t)(ay1 - ay0);
- /* 移除被合并的矩形 */
- lg_boxes[i] = lg_boxes[--lg_box_cnt];
- i = -1;
- }
- }
- if (lg_box_cnt < LG_MAX_BOX)
- {
- lg_boxes[lg_box_cnt++] = b;
- }
- else
- {
- /* 池满:并入最后一个矩形 */
- lg_box_t *a = &lg_boxes[LG_MAX_BOX - 1];
- int ax0 = SC_MIN(a->x, b.x);
- int ay0 = SC_MIN(a->y, b.y);
- int ax1 = SC_MAX(a->x + a->w, b.x + b.w);
- int ay1 = SC_MAX(a->y + a->h, b.y + b.h);
- a->x = (int16_t)ax0;
- a->y = (int16_t)ay0;
- a->w = (int16_t)(ax1 - ax0);
- a->h = (int16_t)(ay1 - ay0);
- }
- }
- /* 直接追加一个脏矩形,不与已有矩形合并。
- * 用于激光沿线段切分出的多块小矩形,避免被合并成一整块大区域。 */
- static void lgBoxAddRaw(int x, int y, int w, int h)
- {
- if (w <= 0 || h <= 0)
- return;
- int x0 = SC_MAX(x, 0);
- int y0 = SC_MAX(y, 0);
- int x1 = SC_MIN(x + w, LG_W);
- int y1 = SC_MIN(y + h, LG_H);
- if (x0 >= x1 || y0 >= y1)
- return;
- if (lg_raw_cnt >= LG_MAX_RAW)
- {
- lgBoxAdd(x, y, w, h); /* 池满则退回合并逻辑 */
- return;
- }
- lg_raw[lg_raw_cnt].x = (int16_t)x0;
- lg_raw[lg_raw_cnt].y = (int16_t)y0;
- lg_raw[lg_raw_cnt].w = (int16_t)(x1 - x0);
- lg_raw[lg_raw_cnt].h = (int16_t)(y1 - y0);
- lg_raw_cnt++;
- }
- /* ================================================================
- * 分带缓冲绘制原语(自带裁剪)
- * ================================================================ */
- static inline void lgPut(sc_pfb_t *pfb, int x, int y, color_t c)
- {
- if (x < pfb->x || x >= pfb->x + pfb->w) return;
- if (y < pfb->y || y >= pfb->y + pfb->h) return;
- pfb->buf[(y - pfb->y) * pfb->w + (x - pfb->x)] = c;
- }
- static inline void lgPutA(sc_pfb_t *pfb, int x, int y, color_t c, uint16_t a)
- {
- if (a == 0) return;
- if (x < pfb->x || x >= pfb->x + pfb->w) return;
- if (y < pfb->y || y >= pfb->y + pfb->h) return;
- color_t *p = &pfb->buf[(y - pfb->y) * pfb->w + (x - pfb->x)];
- *p = (a > 251) ? c : alphaBlend(c, *p, a);
- }
- /* 水平线段:整行批量直写,避免逐点边界判断 */
- static void lgHLine(sc_pfb_t *pfb, int x0, int x1, int y, color_t c, uint16_t a)
- {
- if (a == 0) return;
- if (y < pfb->y || y >= pfb->y + pfb->h) return;
- if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
- if (x0 < pfb->x) x0 = pfb->x;
- if (x1 > pfb->x + pfb->w - 1) x1 = pfb->x + pfb->w - 1;
- if (x0 > x1) return;
- color_t *p = &pfb->buf[(y - pfb->y) * pfb->w + (x0 - pfb->x)];
- int n = x1 - x0 + 1;
- if (a > 251)
- {
- while (n--) *p++ = c;
- }
- else
- {
- while (n--) { *p = alphaBlend(c, *p, a); p++; }
- }
- }
- static void lgRectFill(sc_pfb_t *pfb, int x, int y, int w, int h, color_t c, uint16_t a)
- {
- if (w <= 0 || h <= 0) return;
- for (int i = 0; i < h; i++)
- lgHLine(pfb, x, x + w - 1, y + i, c, a);
- }
- /* Bresenham 画线 */
- static void lgLine(sc_pfb_t *pfb, int x0, int y0, int x1, int y1, color_t c, uint16_t a)
- {
- int dx = SC_ABS(x1 - x0);
- int dy = -SC_ABS(y1 - y0);
- int sx = (x0 < x1) ? 1 : -1;
- int sy = (y0 < y1) ? 1 : -1;
- int err = dx + dy;
- for (;;)
- {
- lgPutA(pfb, x0, y0, c, a);
- if (x0 == x1 && y0 == y1) break;
- int e2 = err << 1;
- if (e2 >= dy) { err += dy; x0 += sx; }
- if (e2 <= dx) { err += dx; y0 += sy; }
- }
- }
- /* 粗线:沿法线平移若干条细线 */
- static void lgThickLine(sc_pfb_t *pfb, int x0, int y0, int x1, int y1, int half, color_t c, uint16_t a)
- {
- int dx = x1 - x0;
- int dy = y1 - y0;
- int len = lgSqrt(dx * dx + dy * dy);
- if (len == 0)
- {
- for (int oy = -half; oy <= half; oy++)
- lgHLine(pfb, x0 - half, x0 + half, y0 + oy, c, a);
- return;
- }
- int px = (-dy * 256) / len; /* 法线单位向量 Q8 */
- int py = (dx * 256) / len;
- for (int i = -half; i <= half; i++)
- {
- int ox = (px * i) >> 8;
- int oy = (py * i) >> 8;
- lgLine(pfb, x0 + ox, y0 + oy, x1 + ox, y1 + oy, c, a);
- }
- }
- /* 实心圆(中点圆逐行填充) */
- static void lgDisc(sc_pfb_t *pfb, int cx, int cy, int r, color_t c, uint16_t a)
- {
- if (r <= 0)
- {
- lgPutA(pfb, cx, cy, c, a);
- return;
- }
- int x = r, y = 0, err = 1 - r;
- while (x >= y)
- {
- lgHLine(pfb, cx - x, cx + x, cy + y, c, a);
- lgHLine(pfb, cx - x, cx + x, cy - y, c, a);
- lgHLine(pfb, cx - y, cx + y, cy + x, c, a);
- lgHLine(pfb, cx - y, cx + y, cy - x, c, a);
- y++;
- if (err < 0)
- err += 2 * y + 1;
- else
- {
- x--;
- err += 2 * (y - x) + 1;
- }
- }
- }
- /* 圆环 */
- static void lgRing(sc_pfb_t *pfb, int cx, int cy, int r, color_t c, uint16_t a)
- {
- if (r <= 0) return;
- int x = r, y = 0, err = 1 - r;
- while (x >= y)
- {
- lgPutA(pfb, cx + x, cy + y, c, a);
- lgPutA(pfb, cx - x, cy + y, c, a);
- lgPutA(pfb, cx + x, cy - y, c, a);
- lgPutA(pfb, cx - x, cy - y, c, a);
- lgPutA(pfb, cx + y, cy + x, c, a);
- lgPutA(pfb, cx - y, cy + x, c, a);
- lgPutA(pfb, cx + y, cy - x, c, a);
- lgPutA(pfb, cx - y, cy - x, c, a);
- y++;
- if (err < 0)
- err += 2 * y + 1;
- else
- {
- x--;
- err += 2 * (y - x) + 1;
- }
- }
- }
- /* ================================================================
- * 逻辑更新
- * ================================================================ */
- static const color_t lg_obst_colors[] = {
- C_TOMATO, C_ORANGE, C_GOLD, C_MEDIUM_ORCHID, C_HOT_PINK, C_LIGHT_GREEN, C_CYAN,
- };
- static const color_t lg_spark_colors[] = {
- C_WHITE, C_YELLOW, C_GOLD, C_ORANGE, C_ORANGE_RED, C_TOMATO,
- };
- static void lgSpawnObst(void)
- {
- for (int i = 0; i < LG_MAX_OBST; i++)
- {
- lg_obst_t *o = &lg_obst[i];
- if (o->alive) continue;
- int w = 14 + lgRand() % 16;
- int h = 12 + lgRand() % 12;
- o->w = (int16_t)w;
- o->h = (int16_t)h;
- o->x = (int16_t)(8 + lgRand() % (LG_W - 16 - w));
- o->y = -((int32_t)h << 8);
- o->vy = (int16_t)(30 + lgRand() % 26); /* 0.12~0.22 像素/毫秒 */
- o->col = lg_obst_colors[lgRand() % (sizeof(lg_obst_colors) / sizeof(color_t))];
- o->alive = 1;
- return;
- }
- }
- static void lgBoomSpawnSpark(lg_boom_t *b)
- {
- for (int k = 0; k < LG_SPARK_N; k++)
- {
- lg_spark_t *p = &b->sp[k];
- if (p->max != 0) continue;
- int ang = (int)(lgRand() % 360);
- int spd = 1 + (int)(lgRand() % 3); /* Q4 像素/毫秒,约 0.06~0.19 */
- p->x = (int16_t)(b->x << 4);
- p->y = (int16_t)(b->y << 4);
- p->vx = (int16_t)((spd * sc_cos(ang)) >> 15);
- p->vy = (int16_t)((spd * sc_sin(ang)) >> 15);
- p->max = (int16_t)(150 + lgRand() % (LG_SPARK_MS - 150));
- p->life = 0;
- p->size = (lgRand() & 1) ? 2 : 1;
- p->col = lg_spark_colors[lgRand() % (sizeof(lg_spark_colors) / sizeof(color_t))];
- return;
- }
- }
- static void lgSpawnBoom(int x, int y)
- {
- for (int i = 0; i < LG_MAX_BOOM; i++)
- {
- lg_boom_t *b = &lg_boom[i];
- if (b->active) continue;
- memset(b, 0, sizeof(*b));
- b->active = 1;
- b->x = (int16_t)x;
- b->y = (int16_t)y;
- b->life = 0;
- b->max = LG_BOOM_MS;
- b->dirty.x = (int16_t)(x - 4);
- b->dirty.y = (int16_t)(y - 4);
- b->dirty.w = 8;
- b->dirty.h = 8;
- for (int k = 0; k < LG_SPARK_N; k++)
- lgBoomSpawnSpark(b);
- return;
- }
- }
- static void lgDestroy(int idx)
- {
- if (idx < 0 || idx >= LG_MAX_OBST) return;
- lg_obst_t *o = &lg_obst[idx];
- if (!o->alive) return;
- int cx = o->x + o->w / 2;
- int cy = (int)(o->y >> 8) + o->h / 2;
- lgBoxAdd(o->x - 2, (o->y >> 8) - 2, o->w + 4, o->h + 4);
- lgSpawnBoom(cx, cy);
- o->alive = 0;
- lg_score++;
- lg_hud_dirty = 1;
- }
- /* 目标:已进入屏幕、最靠下(最危险)的障碍物 */
- static int lgPickTarget(void)
- {
- int best = -1;
- int32_t best_y = -1;
- for (int i = 0; i < LG_MAX_OBST; i++)
- {
- lg_obst_t *o = &lg_obst[i];
- if (!o->alive) continue;
- if (o->y < 0) continue;
- if ((o->y >> 8) + o->h >= LG_BASE_Y) continue;
- if (o->y > best_y)
- {
- best_y = o->y;
- best = i;
- }
- }
- return best;
- }
- static void lgAimUpdate(void)
- {
- int dx = lg_aim_x - LG_PIVOT_X;
- int dy = lg_aim_y - LG_PIVOT_Y;
- int len = lgSqrt(dx * dx + dy * dy);
- if (len < 8) len = 8;
- int ux = (dx << 8) / len;
- int uy = (dy << 8) / len;
- int bl = LG_BARREL - lg_recoil;
- lg_mx = (int16_t)(LG_PIVOT_X + ((ux * bl) >> 8));
- lg_my = (int16_t)(LG_PIVOT_Y + ((uy * bl) >> 8));
- }
- static void lgUpdateObstacles(uint32_t dt)
- {
- for (int i = 0; i < LG_MAX_OBST; i++)
- {
- lg_obst_t *o = &lg_obst[i];
- if (!o->alive) continue;
- int16_t old_y = (int16_t)(o->y >> 8);
- o->y += (int32_t)o->vy * (int32_t)dt;
- int16_t new_y = (int16_t)(o->y >> 8);
- if (new_y + o->h >= LG_BASE_Y)
- {
- /* 落地:判漏,扬一点尘土 */
- lgBoxAdd(o->x - 2, old_y - 2, o->w + 4, o->h + 4);
- lgBoxAdd(o->x - 8, LG_BASE_Y - 10, o->w + 16, 14);
- o->alive = 0;
- lg_miss++;
- lg_hud_dirty = 1;
- continue;
- }
- lgBoxAdd(o->x - 2, SC_MIN(old_y, new_y) - 2, o->w + 4, SC_ABS(new_y - old_y) + o->h + 4);
- }
- }
- static void lgUpdateBoom(uint32_t dt)
- {
- for (int i = 0; i < LG_MAX_BOOM; i++)
- {
- lg_boom_t *b = &lg_boom[i];
- if (!b->active) continue;
- b->life += (int16_t)dt;
- if (b->life >= b->max)
- {
- lgBoxAdd(b->dirty.x, b->dirty.y, b->dirty.w, b->dirty.h);
- b->active = 0;
- continue;
- }
- /* 火花推进 */
- for (int k = 0; k < LG_SPARK_N; k++)
- {
- lg_spark_t *p = &b->sp[k];
- if (p->max == 0) continue;
- p->life += (int16_t)dt;
- if (p->life >= p->max)
- {
- p->max = 0;
- continue;
- }
- p->x = (int16_t)(p->x + (p->vx * (int32_t)dt));
- p->y = (int16_t)(p->y + (p->vy * (int32_t)dt));
- }
- /* 脏矩形:爆炸中心 ± 半径,并覆盖所有火花位置(含上一帧累计区域) */
- int t = b->life * 255 / b->max;
- int r = 6 + (LG_BOOM_R - 6) * t / 255;
- int x0 = SC_MIN(b->dirty.x, b->x - r);
- int y0 = SC_MIN(b->dirty.y, b->y - r);
- int x1 = SC_MAX(b->dirty.x + b->dirty.w, b->x + r);
- int y1 = SC_MAX(b->dirty.y + b->dirty.h, b->y + r);
- for (int k = 0; k < LG_SPARK_N; k++)
- {
- lg_spark_t *p = &b->sp[k];
- if (p->max == 0) continue;
- int sx = p->x >> 4;
- int sy = p->y >> 4;
- x0 = SC_MIN(x0, sx - 2);
- y0 = SC_MIN(y0, sy - 2);
- x1 = SC_MAX(x1, sx + 3);
- y1 = SC_MAX(y1, sy + 3);
- }
- b->dirty.x = (int16_t)x0;
- b->dirty.y = (int16_t)y0;
- b->dirty.w = (int16_t)(x1 - x0);
- b->dirty.h = (int16_t)(y1 - y0);
- lgBoxAdd(b->dirty.x, b->dirty.y, b->dirty.w, b->dirty.h);
- }
- }
- /* 激光终点:优先跟随锁定的障碍物 */
- static void lgBeamTarget(int *tx, int *ty)
- {
- *tx = lg_aim_x;
- *ty = lg_aim_y;
- if (lg_beam.idx >= 0 && lg_beam.idx < LG_MAX_OBST && lg_obst[lg_beam.idx].alive)
- {
- lg_obst_t *o = &lg_obst[lg_beam.idx];
- *tx = o->x + o->w / 2;
- *ty = (int)(o->y >> 8) + o->h / 2;
- }
- }
- /* 沿线段撒一串小脏矩形,避免整条激光的包围盒过大导致刷新耗时 */
- static void lgBeamMarkSeg(int x0, int y0, int x1, int y1)
- {
- int dx = x1 - x0;
- int dy = y1 - y0;
- int len = lgSqrt(dx * dx + dy * dy);
- int n = len / LG_BEAM_STEP;
- if (n < 1) n = 1;
- if (n > LG_BEAM_SEG_MAX) n = LG_BEAM_SEG_MAX;
- for (int i = 0; i <= n; i++)
- {
- int x = x0 + (dx * i) / n;
- int y = y0 + (dy * i) / n;
- lgBoxAddRaw(x - LG_BEAM_PAD, y - LG_BEAM_PAD, LG_BEAM_PAD * 2, LG_BEAM_PAD * 2);
- }
- }
- static void lgUpdateBeam(uint32_t dt)
- {
- if (!lg_beam.active) return;
- lg_beam.elapsed += (int16_t)dt;
- int tx, ty;
- lgBeamTarget(&tx, &ty);
- int p = lg_beam.elapsed * 256 / LG_BEAM_GROW;
- if (p > 255) p = 255;
- int ex = lg_mx + (((tx - lg_mx) * p) >> 8);
- int ey = lg_my + (((ty - lg_my) * p) >> 8);
- /* 上一帧的激光要擦除,本帧的激光要绘制,两段都标记 */
- lgBeamMarkSeg(lg_beam.last_mx, lg_beam.last_my, lg_beam.last_ex, lg_beam.last_ey);
- lgBeamMarkSeg(lg_mx, lg_my, ex, ey);
- lg_beam.last_mx = lg_mx;
- lg_beam.last_my = lg_my;
- lg_beam.last_ex = (int16_t)ex;
- lg_beam.last_ey = (int16_t)ey;
- if (lg_beam.elapsed >= LG_BEAM_MS)
- {
- lgDestroy(lg_beam.idx);
- lg_beam.active = 0;
- lg_beam.idx = -1;
- }
- }
- static void lgUpdate(uint32_t dt)
- {
- uint32_t now = lg_last_ms;
- /* ---- 障碍物下落 ---- */
- lgUpdateObstacles(dt);
- /* ---- 定时生成 ---- */
- if (now - lg_spawn_ms >= LG_SPAWN_MS)
- {
- lg_spawn_ms = now;
- lgSpawnObst();
- }
- /* ---- 瞄准 ---- */
- int tgt = lgPickTarget();
- if (tgt >= 0)
- {
- lg_aim_x = (int16_t)(lg_obst[tgt].x + lg_obst[tgt].w / 2);
- lg_aim_y = (int16_t)((lg_obst[tgt].y >> 8) + lg_obst[tgt].h / 2);
- }
- else
- {
- lg_sweep_deg += 6;
- if (lg_sweep_deg >= 360) lg_sweep_deg -= 360;
- lg_aim_x = (int16_t)(LG_PIVOT_X + ((70 * sc_sin(lg_sweep_deg)) >> 15));
- lg_aim_y = 80;
- }
- /* ---- 炮口位置(按最新瞄准点更新) ---- */
- lgAimUpdate();
- /* ---- 开火 ---- */
- if (!lg_beam.active && tgt >= 0 && now - lg_fire_ms >= LG_FIRE_MS)
- {
- lg_fire_ms = now;
- lg_beam.active = 1;
- lg_beam.idx = (int8_t)tgt;
- lg_beam.elapsed = 0;
- lg_beam.last_mx = lg_mx;
- lg_beam.last_my = lg_my;
- lg_beam.last_ex = lg_mx;
- lg_beam.last_ey = lg_my;
- lg_recoil = 6;
- }
- /* ---- 激光推进 / 命中判定 ---- */
- lgUpdateBeam(dt);
- if (lg_recoil > 0 && lg_beam.elapsed > 40) lg_recoil--;
- /* ---- 充能(炮口核心颜色) ---- */
- uint32_t charge_ms = now - lg_fire_ms;
- if (charge_ms > LG_FIRE_MS) charge_ms = LG_FIRE_MS;
- lg_charge = (uint16_t)(charge_ms * 255 / LG_FIRE_MS);
- /* ---- 爆炸 ---- */
- lgUpdateBoom(dt);
- /* ---- 炮台:转轴固定,用覆盖炮管旋转范围与底座的脏矩形 ---- */
- lgBoxAdd(LG_PIVOT_X - LG_CANNON_R, LG_PIVOT_Y - LG_CANNON_R,
- LG_CANNON_R * 2, LG_H - (LG_PIVOT_Y - LG_CANNON_R));
- /* ---- HUD ---- */
- if (lg_hud_dirty)
- {
- lg_hud_dirty = 0;
- lgBoxAdd(2, 0, 96, 44);
- }
- }
- /* ================================================================
- * 绘制
- * ================================================================ */
- static void lgDrawBackground(sc_pfb_t *pfb)
- {
- for (int y = pfb->y; y < pfb->y + pfb->h; y++)
- lgHLine(pfb, pfb->x, pfb->x + pfb->w - 1, y, lgBgColor(y), 255);
- for (int i = 0; i < LG_STARS; i++)
- lgPut(pfb, lg_stars[i].x, lg_stars[i].y, lg_stars[i].c);
- lgHLine(pfb, 0, LG_W - 1, LG_BASE_Y, C_RGB(70, 105, 180), 255);
- lgHLine(pfb, 0, LG_W - 1, LG_BASE_Y + 1, C_RGB(34, 50, 96), 255);
- lgRectFill(pfb, 0, LG_BASE_Y + 2, LG_W, LG_H - LG_BASE_Y - 2, C_RGB(8, 12, 26), 255);
- }
- static void lgDrawObstacles(sc_pfb_t *pfb)
- {
- for (int i = 0; i < LG_MAX_OBST; i++)
- {
- lg_obst_t *o = &lg_obst[i];
- if (!o->alive) continue;
- int x = o->x;
- int y = (int)(o->y >> 8);
- lgRectFill(pfb, x, y, o->w, o->h, o->col, 255);
- lgRectFill(pfb, x, y, o->w, 2, lgLighter(o->col), 255); /* 顶部高光 */
- lgRectFill(pfb, x, y + o->h - 2, o->w, 2, lgDarker(o->col), 255); /* 底部阴影 */
- lgRectFill(pfb, x + 1, y + 2, 2, o->h - 4, lgLighter(o->col), 130);/* 左侧反光 */
- lgRectFill(pfb, x + o->w - 1, y, 1, o->h, lgDarker(o->col), 255); /* 右侧暗边 */
- }
- }
- static void lgDrawBoom(sc_pfb_t *pfb)
- {
- for (int i = 0; i < LG_MAX_BOOM; i++)
- {
- lg_boom_t *b = &lg_boom[i];
- if (!b->active) continue;
- int t = b->life * 255 / b->max; /* 0~255 爆炸进度 */
- int fade = 255 - t;
- int r_core = 3 + (9 * t) / 255; /* 白热核心 */
- int r_ring = 5 + (26 * t) / 255; /* 冲击波 */
- color_t core = (t < 60) ? C_WHITE : ((t < 130) ? C_YELLOW : C_ORANGE_RED);
- lgDisc(pfb, b->x, b->y, r_core, core, (uint16_t)fade);
- lgDisc(pfb, b->x, b->y, SC_MAX(r_core - 3, 1), C_WHITE, (uint16_t)fade);
- lgRing(pfb, b->x, b->y, r_ring, C_ORANGE, (uint16_t)(fade * 3 / 4));
- if (r_ring > 7)
- lgRing(pfb, b->x, b->y, r_ring - 2, C_GOLD, (uint16_t)(fade / 2));
- /* 十字光芒,强化"打爆"感 */
- if (t < 120)
- {
- int g = 4 + (14 * t) / 120;
- lgHLine(pfb, b->x - g, b->x + g, b->y, C_WHITE, (uint16_t)(fade * 2 / 3));
- for (int k = -g; k <= g; k++)
- lgPutA(pfb, b->x, b->y + k, C_WHITE, (uint16_t)(fade * 2 / 3));
- }
- /* 火花 */
- for (int k = 0; k < LG_SPARK_N; k++)
- {
- lg_spark_t *p = &b->sp[k];
- if (p->max == 0) continue;
- int a = 255 * (p->max - p->life) / p->max;
- lgRectFill(pfb, p->x >> 4, p->y >> 4, p->size, p->size, p->col, (uint16_t)a);
- }
- }
- }
- static void lgDrawCannon(sc_pfb_t *pfb)
- {
- /* 底座 */
- lgRectFill(pfb, LG_PIVOT_X - 20, LG_BASE_Y + 4, 40, 12, C_RGB(34, 44, 68), 255);
- lgRectFill(pfb, LG_PIVOT_X - 20, LG_BASE_Y + 4, 40, 2, C_RGB(72, 92, 134), 255);
- lgRectFill(pfb, LG_PIVOT_X - 15, LG_BASE_Y + 1, 30, 4, C_RGB(56, 70, 102), 255);
- /* 炮管:外轮廓 + 内层高光 */
- lgThickLine(pfb, LG_PIVOT_X, LG_PIVOT_Y, lg_mx, lg_my, 3, C_RGB(64, 76, 104), 255);
- lgThickLine(pfb, LG_PIVOT_X, LG_PIVOT_Y, lg_mx, lg_my, 1, C_RGB(150, 168, 200), 255);
- /* 转轴与充能核心 */
- lgDisc(pfb, LG_PIVOT_X, LG_PIVOT_Y, 6, C_RGB(58, 72, 102), 255);
- lgDisc(pfb, LG_PIVOT_X, LG_PIVOT_Y, 4, alphaBlend(C_ORANGE_RED, C_YELLOW, lg_charge), 255);
- lgDisc(pfb, LG_PIVOT_X, LG_PIVOT_Y, 2, alphaBlend(C_YELLOW, C_WHITE, lg_charge), 255);
- /* 炮口 */
- lgDisc(pfb, lg_mx, lg_my, 2, C_ORANGE_RED, 255);
- }
- static void lgDrawBeam(sc_pfb_t *pfb)
- {
- if (!lg_beam.active) return;
- int tx, ty;
- lgBeamTarget(&tx, &ty);
- int p = lg_beam.elapsed * 256 / LG_BEAM_GROW;
- if (p > 255) p = 255;
- int ex = lg_mx + (((tx - lg_mx) * p) >> 8);
- int ey = lg_my + (((ty - lg_my) * p) >> 8);
- /* 外层辉光 → 中层 → 白色核心 */
- lgThickLine(pfb, lg_mx, lg_my, ex, ey, 4, C_ORANGE, 55);
- lgThickLine(pfb, lg_mx, lg_my, ex, ey, 2, C_YELLOW, 130);
- lgThickLine(pfb, lg_mx, lg_my, ex, ey, 0, C_WHITE, 255);
- /* 炮口闪光 */
- lgDisc(pfb, lg_mx, lg_my, 4, C_WHITE, 140);
- /* 命中点高亮 */
- if (p >= 255)
- {
- lgDisc(pfb, ex, ey, 6, C_WHITE, 170);
- lgDisc(pfb, ex, ey, 3, C_YELLOW, 255);
- }
- }
- static void lgDrawHud(sc_pfb_t *pfb)
- {
- char buf[24];
- sprintf(buf, "HIT %d", (int)lg_score);
- sc_draw_Text(pfb, 8, 4, gui->font, buf, C_GOLD, gui->bkc);
- sprintf(buf, "MISS %d", (int)lg_miss);
- sc_draw_Text(pfb, 8, 24, gui->font, buf, C_STEEL_BLUE, gui->bkc);
- }
- /* 绘制一个脏矩形 */
- static void lgRenderBox(const lg_box_t *b)
- {
- sc_rect_t rect = {b->x, b->y, b->w, b->h};
- sc_pfb_t pfb;
- sc_pfb_init_slices(&pfb, &rect, C_BLACK);
- do
- {
- lgDrawBackground(&pfb);
- lgDrawObstacles(&pfb);
- lgDrawBoom(&pfb);
- lgDrawCannon(&pfb);
- lgDrawBeam(&pfb);
- lgDrawHud(&pfb);
- } while (sc_pfb_next_slice(&pfb));
- }
- /* ================================================================
- * 对外接口
- * ================================================================ */
- void laserGameInit(void)
- {
- lg_rng = 0x13579BDFu;
- lg_last_ms = (uint32_t)GetTick();
- lg_spawn_ms = lg_last_ms;
- lg_fire_ms = lg_last_ms - LG_FIRE_MS;
- memset(lg_obst, 0, sizeof(lg_obst));
- memset(lg_boom, 0, sizeof(lg_boom));
- memset(&lg_beam, 0, sizeof(lg_beam));
- lg_beam.idx = -1;
- lg_aim_x = LG_PIVOT_X;
- lg_aim_y = 80;
- lg_sweep_deg = 0;
- lg_recoil = 0;
- lg_charge = 0;
- lg_score = 0;
- lg_miss = 0;
- lg_hud_dirty = 0;
- lg_box_cnt = 0;
- lg_raw_cnt = 0;
- /* 预生成星空 */
- for (int i = 0; i < LG_STARS; i++)
- {
- lg_stars[i].x = (int16_t)(lgRand() % LG_W);
- lg_stars[i].y = (int16_t)(lgRand() % (LG_BASE_Y - 6));
- uint8_t v = (uint8_t)(70 + lgRand() % 120);
- lg_stars[i].c = C_RGB(v, v, (uint8_t)SC_MIN(255, v + 40));
- }
- /* 预置两个障碍物,避免开场空等 */
- lgSpawnObst();
- lgSpawnObst();
- int k = 0;
- for (int i = 0; i < LG_MAX_OBST; i++)
- {
- if (!lg_obst[i].alive) continue;
- lg_obst[i].y = (int32_t)(18 + k * 36) << 8; /* 分散到屏幕上部不同高度 */
- k++;
- }
- lgAimUpdate();
- /* 首帧整屏绘制一次 */
- sc_clear(0, 0, LG_W, LG_H, C_BLACK);
- lg_box_t full = {0, 0, LG_W, LG_H};
- lgRenderBox(&full);
- }
- void laserGameFrame(void)
- {
- uint32_t now = (uint32_t)GetTick();
- uint32_t dt = now - lg_last_ms;
- if (dt > 120) dt = 120; /* 限制最大步长,防止卡顿后跳帧 */
- lg_last_ms = now;
- lg_box_cnt = 0;
- lg_raw_cnt = 0;
- lgUpdate(dt);
- for (int i = 0; i < lg_box_cnt; i++)
- lgRenderBox(&lg_boxes[i]);
- for (int i = 0; i < lg_raw_cnt; i++)
- lgRenderBox(&lg_raw[i]);
- }
复制代码
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|