POST://canvas-pixel-game-fundamentals
带点橘子味的馒头的头像
带点橘子味的馒头

FRONTEND / DESKTOP DEV

返回文章列表

canvas 像素游戏入门:从字符画到 60fps 主循环

2 分钟· 876 10 次阅读

做了个小火箭游戏,最大的心得是:像素风 + 低分辨率画布,是业余开发者做"看起来不错"游戏的最低成本路径。这篇把几招核心技巧拆开讲。

一、低分辨率画布最省心

不要在高分辨率上硬画。用一个很小的内部分辨率(比如 160×240),然后 CSS 放大整数倍:

<canvas width="160" height="240" style="width:480px;height:720px"></canvas>

配合 image-rendering: pixelated,放大后是干净利落的像素颗粒,而不是糊成一团:

canvas {
  image-rendering: pixelated; /* 关键!不然放大全是锯齿渐变 */
}

好处立竿见影:

  • 绘制量极小:160×240 的 fillRect 比 1920×1080 快几个数量级
  • 天然像素风:不用精雕细琢,1px 就是 1px
  • 性能余量足:留给你做粒子、扫描线等特效

二、用字符画出素材

不想用图片资源?直接用字符串数组定义精灵——和写"画"是一回事:

const ROCKET = [
  "   O   ",
  "  OOO  ",
  " OOOOO ",
  " OOCOO ",
  "COOOOOC",
];

const PALETTE: Record<string, string> = {
  O: "#d9701a", // 橙
  C: "#0f9b8e", // 青
};

渲染时遍历字符,O→橙色方块,C→青色方块:

for (let r = 0; r < ROCKET.length; r++) {
  for (let c = 0; c < ROCKET[r].length; c++) {
    const ch = ROCKET[r][c];
    if (ch === " ") continue;
    const color = PALETTE[ch];
    if (color) {
      ctx.fillStyle = color;
      ctx.fillRect(x + c, y + r, 1, 1);
    }
  }
}

这种方法素材即代码:易改、易复用、零资源加载,还天然和"像素字体""像素图标"一个画风。

三、固定时间步长:让 60Hz 和 144Hz 手感一致

最怕的就是动画速度和帧率绑定——高刷屏的玩家会"外星人速度",低帧玩家"慢动作"。解法是固定时间步长 + 累加器

const STEP = 1000 / 60; // 目标 60 FPS

let acc = 0;
let last = performance.now();

function frame(now) {
  acc += now - last;
  last = now;
  if (acc > 250) acc = 250; // 防止长时间切后台回来积压爆炸

  // 把累加的时间按固定步长消费掉
  while (acc >= STEP) {
    update(); // 物理/逻辑只在这里跑
    acc -= STEP;
  }
  draw();     // 渲染每帧都跑,反正是重画
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

好处

  • 物理完全确定,和屏幕刷新率无关
  • 逻辑稳定跑 60 次/秒,不管屏幕是 60、120 还是 144Hz
  • 物体对象池、碰撞检测都只在 update() 里做一次

四、状态机管阶段切换

游戏不该是一坨 if 堆积,用状态机把阶段分清楚:

boot → ready → ascent → transition → descent → boom/landed → result
type Phase = "ready" | "ascent" | "descent" | "boom" | "result";

function update(s) {
  switch (s.phase) {
    case "ascent":
      // 升空:自动上升,玩家左右躲
      break;
    case "descent":
      // 下降:重力牵引,玩家喷气对抗
      break;
    case "boom":
      // 爆炸倒计时
      break;
  }
}

每个阶段只管自己的逻辑,切换阶段=改 phase。加新阶段不碰旧代码,可维护性直线上升。

五、HUD 也画进画布

分数、燃料条、速度这些别用 DOM 覆盖,直接塞进 Canvas 一起画。这样游戏循环完全自给自足,不依赖 React 的 setState,也就不会有"react 重渲染拖慢 60fps 循环"的问题。只有"游戏结束/重新开始"这种低频状态交给外层框架。

小结

做一个像素小游戏的效率公式:

  1. 低分辨率画布(像素风 + 高性能)
  2. 字符画素材(零资源 + 风格统一)
  3. 固定时间步长(多帧率手感一致)
  4. 状态机(可维护)
  5. HUD 上画布(不拖累循环)

这五招够你从零到一个能玩的像素游戏。剩下的就是把它变得好玩——那需要的是内容,不是技术了。

相关文章