用 400 行代码做一个极简坦克大战:从零到发布的完整实践
📄 摘要
本文记录了一个极简风格坦克大战游戏的完整开发与发布过程。游戏采用纯 HTML + Canvas + JavaScript 实现,零外部依赖,黑白灰三色配色,涵盖玩家操控、敌方 AI、碰撞检测、墙壁障碍、计分与生命系统等核心玩法。最终通过 GitCode 开发者作品展览馆开源发布。文章从设计思路、技术选型、核心代码解析到发布流程逐一展开,适合对 Canvas 游戏开发和轻量级项目发布感兴趣的开发者参考。
📖 正文
一、为什么做这个项目
经典坦克大战是很多人的童年回忆。市面上的复刻版本往往追求像素还原、音效还原,代码量动辄上千行。但如果只保留最核心的玩法——移动、射击、躲避、消灭敌人——其实几百行代码就够了。
这个项目的目标很明确:用最少的代码,实现能玩的坦克大战,界面极简到极致。
二、设计思路
2.1 极简美学
整个游戏只用 4 个颜色:
| 颜色 | 用途 | 色值 |
|---|---|---|
| 纯黑 | 背景 | #000 |
| 白色 | 玩家坦克、子弹、文字 | #fff |
| 灰色 | 敌方坦克 | #888 |
| 深灰 | 墙壁、画布边框 | #333 |
没有渐变、没有阴影、没有粒子特效、没有音效。等宽字体 Courier New 贯穿全局,营造出一种终端/复古的视觉气质。
2.2 玩法设计
只保留坦克大战最本质的元素:
- 玩家:方向键/WASD 四向移动,空格射击
- 敌人:3 辆敌方坦克,随机移动 + 周期射击
- 墙壁:网格地图中的砖墙障碍,阻挡坦克和子弹
- 胜负:消灭全部敌人 = 胜利;3 条命用完 = 游戏结束
- HUD:顶部实时显示分数、剩余生命、剩余敌人数
三、技术实现
3.1 整体架构
index.html ← 页面结构 + CSS 样式(602 字节)
game.js ← 游戏全部逻辑(约 400 行)
零依赖、零构建工具。用 python3 -m http.server 8080 即可启动预览,浏览器打开直接游玩。
3.2 核心数据结构
方向系统——用 4 个向量描述上下左右:
var DIRS = [
{ x: 0, y: -1 }, // 上
{ x: 1, y: 0 }, // 右
{ x: 0, y: 1 }, // 下
{ x: -1, y: 0 } // 左
];
地图——用字符串数组表示 20×20 网格,# 代表墙壁:
var mapStr = [
" ",
" ## ## ## ",
" ",
" ## ## ",
" #### ",
// ...
];
这种设计的好处是:地图布局一目了然,修改关卡只需改字符串。
3.3 坦克对象
用构造函数 + 原型方法实现坦克实体:
function Tank(x, y, dir, isEnemy) {
this.x = x;
this.y = y;
this.dir = dir;
this.isEnemy = isEnemy;
this.alive = true;
this.cooldown = 0; // 射击冷却
this.changeDirTimer = ...; // AI 换方向计时器
this.shootTimer = ...; // AI 射击计时器
}
每个坦克维护自己的位置、方向、存活状态和冷却计时器。敌方坦克额外有 changeDirTimer 和 shootTimer,用于控制 AI 行为节奏。
3.4 碰撞检测
采用 AABB(轴对齐包围盒)检测,一个函数搞定所有碰撞:
function overlap(ax, ay, aw, ah, bx, by, bw, bh) {
return ax < bx + bw && ax + aw > bx
&& ay < by + bh && ay + ah > by;
}
这个函数被复用于:
- 坦克 vs 墙壁(移动前检测)
- 坦克 vs 坦克(防止重叠)
- 子弹 vs 墙壁(子弹销毁)
- 子弹 vs 坦克(命中判定)
3.5 敌方 AI
敌方坦克的 AI 逻辑非常简单,但效果不错:
function updateEnemy(e) {
// 1. 定时换方向
e.changeDirTimer--;
if (e.changeDirTimer <= 0) {
e.dir = (Math.random() * 4) | 0; // 随机方向
e.changeDirTimer = 30 + ((Math.random() * 60) | 0);
}
// 2. 撞墙了也换方向
if (!e.tryMove(e.dir)) {
e.dir = (Math.random() * 4) | 0;
}
// 3. 定时射击
e.shootTimer--;
if (e.shootTimer <= 0) {
e.shoot();
e.shootTimer = 50 + ((Math.random() * 80) | 0);
}
}
两个随机计时器控制行为节奏:一个管换方向(30~90 帧),一个管射击(50~130 帧)。撞墙时立即换方向,避免卡死。简单但有效——玩起来敌人确实像个"有想法的对手"。
3.6 游戏循环
标准的 requestAnimationFrame 循环:
function loop() {
update(); // 处理输入、更新位置、碰撞判定
draw(); // 清屏、画墙、画坦克、画子弹、画 HUD
requestAnimationFrame(loop);
}
update() 和 draw() 完全分离,逻辑清晰。
3.7 状态管理
用简单的字符串状态机控制游戏流程:
var state = 'start'; // → 'playing' → 'win' / 'gameover'
start:显示标题画面和"开始游戏"按钮playing:正常游戏循环win:全部敌人消灭,显示胜利画面gameover:生命归零,显示结束画面
每个状态在 draw() 中对应不同的渲染逻辑,点击按钮调用 startGame() 重置一切。
四、发布到 GitCode 开发者作品展览馆
项目完成后,通过 GitCode API 创建公开仓库并推送代码:
仓库地址: https://gitcode.com/2301_81769094/tank-battle
发布内容包含:
index.html— 页面入口game.js— 游戏逻辑README.md— 项目说明
整个过程通过 DevSpace 连接器的 GitCode API 完成,无需手动操作 Git 命令行。
五、总结与思考
这个项目的核心收获:
- 极简不等于简陋。去掉所有装饰后,游戏的核心乐趣——移动、瞄准、躲避、击杀——反而更加纯粹。
- AABB 碰撞检测足够用。对于网格类游戏,不需要物理引擎,一个
overlap函数解决所有碰撞。 - 随机计时器是好用的 AI 骨架。两个
Math.random()驱动的计时器,就能让敌人表现出"巡逻 + 交火"的行为模式。 - 单文件部署的威力。零依赖意味着任何能跑 HTTP 服务器的地方都能部署——
python3 -m http.server一行命令即可。
完整代码已在 GitCode 开源,欢迎 Fork、修改、扩展。下一步可以尝试加入:关卡系统、道具掉落、双人对战、音效……但那就不是"极简"了。
项目仓库:https://gitcode.com/2301_81769094/tank-battle
技术栈:HTML5 Canvas + 原生 JavaScript,零依赖
- 点赞
- 收藏
- 关注作者
评论(0)