第一篇是关于‘为什么要制作’的故事,
第二篇是‘怎么制作’的故事。

对于我个人而言,在制作web应用时,我更喜欢处理Django、PostgreSQL、Redis、Docker等后端基础系统。
然而,这个项目几乎完全基于纯JavaScript + HTML5 Canvas进行。
平时我绝对不会深入触及的前端技术在这个游戏中成为了核心。

而令人惊讶的是…
非常有趣。
这是让我更加沉浸的技术因素。

平时我对前端的工作用“不是很喜欢”来形容,可能用“讨厌”更为合适,但对豆柴的爱让我变得喜欢“讨厌的事情”。

角色选择屏幕截图


1. HTML5 Canvas – “画板 + 动画”的世界



MAME RUN!!的核心是HTML5 Canvas
这是一个在网页上创建的像‘画板’一样的区域,通过直接绘制像素的方式制作游戏。

通常的web开发是

  • 创建按钮(HTML)

  • 着色(CSS)

  • 增加一些行为(JS)

这样的流程,但是Canvas则完全不同。

Canvas是以30~60fps不断重绘的结构
每一帧:

  1. 清除背景

  2. 重新绘制

  3. 绘制角色

  4. 绘制障碍物

  5. 计算碰撞

  6. 增加分数

  7. 预定下一帧

这个过程在每秒重复数十次。

现在的web浏览器真的强大得不亚于游戏引擎


2. 跳跃与重力: “仅用3行代码创造的魔法”

游戏中的基本“跳跃”其实是通过简单的物理实现的。

dino.vy += gravity * dt;  // 应用重力
dino.y  += dino.vy * dt;  // 改变位置

if (dino.y >= ground) {
    dino.y = ground;
    dino.vy = 0;
}

仅凭这个简单的逻辑
就能让<强>雷欧娜自然地跳跃、着陆和下落

在没有游戏引擎的情况下,
“啊……仅凭这种基本物理原理就能产生如此流畅的动作”
让我感动不已。

雷欧娜跳跃的游戏画面


3. 精灵动画 – 雷欧娜奔跑时赋予了生命



雷欧娜的奔跑动作是如下一张6帧的精灵表

(※ 建议在博客中插入精灵图片)

在JS中,只需将精灵切割并在每一帧绘制即可:

const frameWidth = sprite.width / 6;
ctx.drawImage(
  sprite,
  frameIndex * frameWidth, 0, frameWidth, sprite.height,
  dino.x, dino.y, dino.width, dino.height
);

只要不断变化帧索引
雷欧娜就会可爱地奔跑。
这个时刻感觉游戏被赋予了‘生命’。

在游戏开发中,动画是非常重要的。


4. 障碍物的随机生成 – “消除单调性的关键”

障碍物有多种类型。

  • 橙色锥

  • 红色锥

  • 黄色锥

  • 摩托车(两种颜色)

  • 汽车(两种类型)

  • 标志(种类共三种)

如果只是简单地重复1~2个,很快就会厌倦,
但通过随机间隔混合多种类型,游戏的节奏感就大大改善了。

const key = OBSTACLE_KEYS[Math.random() * length];
const interval = 0.8 + Math.random() * 1.2;

虽然是很简单的随机,但
玩法感受完全不同。


5. 碰撞判定 – 最困难而又最有趣的部分

碰撞判定通常称为“AABB盒子碰撞”的方式。

if (dino.x < ob.x + ob.width &&
    dino.x + dino.width > ob.x &&
    dino.y < ob.y + ob.height &&
    dino.y + dino.height > ob.y) {
    // 碰撞!
}

这里重要的是

  • 如果判定得太严格,游戏会让人厌烦

  • 如果判定得过于宽松,游戏难度就会下降

因此,我将雷欧娜和障碍物的碰撞盒缩小10~15%,创造了“带来快感的难度”。

这种微调相当有趣。
我有点理解为什么游戏开发者会在难度平衡上投入热情。


6. 距离基础的关卡系统 – 50秒内通关

第一关设计为约50秒内通关。
因此假设雷欧娜的移动速度为每秒16米。

目标距离: 800m  
速度: 16 m/s → 约50秒  

这不仅仅是通过数字来决定,实际上我玩了30~40次,
“这个长度是否使人愉快,想再来一局?”
不断进行测试。

这是一种虽小却影响游戏体验的重要因素。


7. 服务器联动 – 分数排名与记录保存

作为后端开发者,服务器部分自然是使用Django + DRF构建的。

  • 通过POST提交分数

  • 在序列化器中进行有效性检查

  • 未登录用户使用guest_id记录

  • 用户的最高分由UserScore表管理

  • 前五名通过HTML片段实时返回给JS进行替换

因此在游戏结束时,排名TOP5会实时更新

有趣的是,即使在Canvas游戏中,
后端的稳定性和结构化的数据流也确实提供了强大的支持

这是通过系统化地整理角色的图片和台词数据,并将这些组合发送给浏览器的方式。


8. 移动端适配 – 比预想的要困难得多

在制作web游戏过程中,最意想不到的障碍是移动环境

  • 分辨率适配

  • 触控操作

  • 画布比例

  • 渲染性能

  • 背景滚动速度校正

在桌面上表现得非常流畅,但在移动端则出现了小的帧丢失、触控响应性、canvas大小计算等问题。

最终解决了这个问题,不过在这个过程中,我不禁感慨“前端开发者真是值得敬佩……”。


9. 第二篇 – 技术篇完

我喜爱后端,所以主要从事API开发,而以设计为主的游戏用JavaScript制作在web上的工作是第一次。 平时也得做一点前端,所以也无奈地做了些,尤其是如此深入使用HTML5 Canvas的经历几乎没有。

然而通过这个项目,

  • 网页比想象中强大

  • 那个曾经觉得烦的JavaScript其实是挺有趣的语言

  • 游戏开发是“小技术的组合”这点

我学到了这些。

一个出于粉丝之情开始的项目,
但是作为开发者却也获得了相当多的学习与成长。


下篇预告 (第三篇: 故事篇)

在下一篇文章中,
“为什么从涩谷开始?”、“为什么东京巨蛋?”、“各个成员的角色设定是怎么确定的?”
我打算整理与世界观/故事制作相关的幕后故事。