飞机大战游戏开发历程 - 从 0 到 1 的 AI 辅助开发实践

项目名称:微信小游戏 - 飞机大战
开发方式:AI 辅助开发(Trae IDE + Qwen3.5-Plus)
开发周期:3 天
文档版本:1.0
生成时间:2026-03-29
📖 目录
- 项目背景
- 技术栈
- 开发历程
- 第一阶段:敌机系统完善
- 第二阶段:十关关卡系统
- 第三阶段:视觉效果优化
- 第四阶段:游戏平衡性调整
- 第五阶段:BUG 修复与优化
- 关键技术问题与解决方案
- AI 协作经验总结
- 项目成果
- 后续优化方向
🎯 项目背景
项目起源
这是一个基于微信小游戏平台的飞机大战游戏项目。项目已经完成了基础框架,但存在诸多问题需要完善:
- 敌机系统不够丰富,缺乏挑战性
- 关卡系统单一,只有基础关卡
- 视觉效果简陋,缺乏特色
- 游戏平衡性需要调整
- 存在多个 BUG 需要修复
开发目标
- 完善敌机系统:每关 5 种不同特性的敌机
- 构建十关关卡系统:每关独特的主题和视觉效果
- 优化视觉体验:每个关卡独特的背景粒子效果
- 调整游戏平衡:难度曲线、奖励机制
- 修复关键 BUG:碰撞检测、无敌技能、动画播放等
🛠️ 技术栈
核心技术
- 游戏引擎:原生 Canvas 2D + 自定义游戏循环
- 开发语言:JavaScript (ES6+)
- 运行平台:微信小游戏
- 开发工具:Trae IDE + Qwen3.5-Plus
项目结构
airPlane/
├── js/
│ ├── base/ # 基础类(Sprite, Animation)
│ ├── npc/ # NPC 类(Enemy, EnemyBullet)
│ ├── player/ # 玩家类(Player, Bullet)
│ ├── runtime/ # 运行时管理(Background, PowerUp, GameInfo)
│ ├── databus.js # 数据总线
│ ├── main.js # 游戏主逻辑
│ └── render.js # 渲染管理
├── images/ # 游戏资源
└── audio/ # 音频资源
关键类设计
// 基础精灵类
class Sprite {
visible = true;
isActive = true;
render(ctx) { }
isCollideWith(sp) { }
}
// 动画类
class Animation extends Sprite {
isPlaying = false;
index = -1;
imgList = [];
playAnimation() { }
aniRender(ctx) { }
}
// 敌人类
class Enemy extends Animation {
type = 'TYPE_1';
hp = 1;
moveType = 'straight';
update() { }
destroy() { }
}
📝 开发历程
第一阶段:敌机系统完善
问题 1:敌机种类单一
用户需求:
"现在只有 5 种类型的飞机太单一了,每一关的敌机应该有特色,比如第 1 关有 5 种,第 2 关有另外 5 种,以此类推到第 10 关"
问题分析:
- 原有系统只有 5 种固定敌机类型
- 所有关卡使用相同的敌机配置
- 缺乏关卡特色和难度递进
解决方案:
创建关卡专属敌机配置系统,每关 5 种独特敌机,共 50 种配置。
const ENEMY_TYPE_CONFIG = {
// 第 1 关:风之国度 - 基础型
L1_T1: { hp: 1, speed: 2, moveType: 'straight', score: 1 },
L1_T2: { hp: 2, speed: 2.5, moveType: 'slight_curve', score: 2 },
L1_T3: { hp: 3, speed: 1.5, moveType: 'curve', score: 3 },
L1_T4: { hp: 5, speed: 1, moveType: 'slow_curve', score: 5 },
L1_T5: { hp: 8, speed: 0.8, moveType: 'slow_curve', score: 8 },
// 第 2 关:岩之国度 - 防御型
L2_T1: { hp: 2, speed: 2, moveType: 'straight', score: 2 },
L2_T2: { hp: 4, speed: 2.5, moveType: 'slight_curve', score: 4 },
L2_T3: { hp: 6, speed: 1.5, moveType: 'curve', score: 6 },
L2_T4: { hp: 10, speed: 1, moveType: 'slow_curve', score: 10 },
L2_T5: { hp: 15, speed: 0.8, moveType: 'slow_curve', score: 15 },
// ... 第 3-10 关配置
};
关键代码实现:
// 根据关卡和类型获取敌人配置
function getEnemyConfig(type, level) {
const configKey = `L${level}_T${type}`;
return ENEMY_TYPES[configKey] || ENEMY_TYPES['L1_T1'];
}
// 敌人生成逻辑
enemyGenerate() {
if (GameGlobal.databus.frame % ENEMY_GENERATE_INTERVAL === 0) {
const currentLevel = GameGlobal.databus.level;
const enemyLimit = GameGlobal.databus.levelEnemyLimits[currentLevel - 1] || 10;
const currentEnemyCount = GameGlobal.databus.enemys.length;
if (currentEnemyCount >= enemyLimit) {
return;
}
// 从当前关卡的 5 种敌人类型中随机选择
const enemyTypeIndex = Math.floor(Math.random() * 5) + 1;
const enemyType = `L${currentLevel}_T${enemyTypeIndex}`;
const enemy = GameGlobal.databus.pool.getItemByClass(
'enemy', Enemy, enemyType, currentLevel
);
enemy.init(currentLevel);
GameGlobal.databus.enemys.push(enemy);
}
}
效果对比:
| 方面 | 之前 | 现在 |
|---|---|---|
| 敌机类型 | 5 种固定 | 50 种(每关 5 种) |
| 关卡特色 | 无 | 每关独特属性 |
| 难度递进 | 线性增加数值 | 多样化移动模式 + 血量 |
| 玩家体验 | 单调重复 | 每关新鲜感 |
问题 2:敌机移动模式单一
用户需求:
"敌机的移动方式应该更丰富,比如 S 型、曲线型、直线型等"
解决方案:
实现 4 种移动模式,通过配置控制。
// 4 种移动模式
const MOVE_TYPES = {
straight: '直线飞行',
slight_curve: '轻微 S 型',
curve: '大曲线 S 型',
slow_curve: '缓慢大曲线'
};
// 更新移动逻辑
updateMove() {
switch (this.moveType) {
case 'straight':
case 'fast_straight':
this.moveStraight();
break;
case 'slight_curve':
this.moveSlightCurve();
break;
case 'curve':
case 'slow_curve':
this.moveSlowCurve();
break;
}
}
// 轻微 S 型移动
moveSlightCurve() {
this.y += this.speed;
const curve = Math.sin(this.y * 0.05) * this.amplitude;
this.x = this.initialX + curve;
this.x = Math.max(0, Math.min(this.x, SCREEN_WIDTH - this.width));
}
// 大曲线移动
moveSlowCurve() {
this.y += this.speed * 0.8;
const slowCurve = Math.sin(this.y * 0.015) * this.curveAmount;
this.x = this.initialX + slowCurve;
this.x = Math.max(0, Math.min(this.x, SCREEN_WIDTH - this.width));
}
第二阶段:十关关卡系统
需求:构建完整的十关关卡体系
用户需求:
"需要设计 10 个关卡,每关有独特的主题名称和视觉风格"
关卡设计:
const LEVEL_NAMES = [
'风之国度', // 第 1 关
'岩之国度', // 第 2 关
'雷之国度', // 第 3 关
'草之国度', // 第 4 关
'水之国度', // 第 5 关
'火之国度', // 第 6 关
'冰之国度', // 第 7 关
'雾之国度', // 第 8 关
'日志国度', // 第 9 关
'最终国度' // 第 10 关
];
const levelEnemyLimits = [10, 15, 20, 25, 30, 35, 40, 45, 50, 999];
关卡胜利条件:
// 第 10 关特殊处理
addLevel10Kill() {
if (this.level === 10) {
this.level10Kills++;
const requiredKills = 10; // 需要击毁 10 个敌机
if (this.level10Kills >= requiredKills) {
this.gameWin();
return true;
}
}
return false;
}
第三阶段:视觉效果优化
需求 1:第一关风之国度效果优化
多轮迭代过程:
迭代 1:风之环 → 流动风之流
// 用户反馈:"第一关的 风格也切换一下,更加复核风之国特色的"
// 解决方案:从风环改为贝塞尔曲线流动效果
迭代 2:流动风之流 → 旋风粒子
// 用户反馈:"第一关风之国度的效果不好,再切换一种"
// 解决方案:改为螺旋旋风粒子效果
迭代 3:旋风粒子 → 简单风线
// 用户反馈:"第一关风之国度的效果不好,再切换一种,简单一点的"
// 解决方案:简化为水平细线效果
迭代 4:风线数量调整
// 用户反馈:"线条的数量,添加 10 条吧"
// 解决方案:从 30 条减少到 10 条,更加稀疏
最终实现:
// 第 1 关:风之细线(10 条,稀疏纤细效果)
this.windLineParticles = [];
for (let i = 0; i < 10; i++) {
this.windLineParticles.push({
x: Math.random() * SCREEN_WIDTH,
y: Math.random() * SCREEN_HEIGHT,
length: Math.random() * 30 + 20,
speedX: Math.random() * 1.5 + 1,
alpha: Math.random() * 0.4 + 0.2,
width: Math.random() * 1 + 0.5,
color: Math.random() > 0.5
? 'rgba(255, 255, 255, 0.6)'
: 'rgba(135, 206, 235, 0.5)'
});
}
// 渲染风线
ctx.beginPath();
ctx.moveTo(lx, ly);
ctx.lineTo(lx + ll, ly); // 水平细线
ctx.stroke();
需求 2:第三关雷之国度闪电优化
用户需求:
"去掉第三关 闪电闪烁的时候这个长条的前景色"
问题分析:
- 闪电闪烁时整个屏幕背景变亮
- 长条矩形填充影响视觉体验
解决方案:
移除背景矩形填充,只保留闪电分支和火花粒子。
// 移除前(有问题)
renderThunderFlash() {
if (this.thunderFlashActive) {
// ❌ 长条矩形填充
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// 绘制闪电分支
this.renderLightningBolts(ctx);
}
}
// 移除后(正确)
renderThunderFlash() {
if (this.thunderFlashActive) {
// ✅ 只绘制闪电分支和火花
this.renderLightningBolts(ctx);
this.renderThunderSparks(ctx);
}
}
需求 3:第四关草之国度粒子风格变换
多轮迭代过程:
迭代 1:萤火虫 → 草叶
// 用户反馈:"第四关草元素的粒子,再变换一种风格"
// 解决方案:从萤火虫粒子改为草叶粒子
迭代 2:草叶优化(加宽加厚)
// 用户反馈:"第四关 草之国度的粒子再宽厚一些,现在不像叶子,像虫子"
// 解决方案:增加叶片尺寸,添加叶脉
迭代 3:巨型草叶
// 用户反馈:"第四关 草之国的叶子,改成巨大型的吧,现在的大小看起来,还是像虫子,可以减少粒子的数量"
// 解决方案:尺寸放大 2 倍,数量从 60 减少到 20
迭代 4:草丛摇曳(最终版)
// 用户反馈:"第 5 关,草之国的背景风格,再切换一种吧,符合草之国的特色的"
// 解决方案:从飘散草叶改为固定草丛摇曳效果
// 第 4 关:草丛摇曳粒子(30 丛,模拟微风吹拂的草地)
this.grassSwirlParticles = [];
for (let i = 0; i < 30; i++) {
this.grassSwirlParticles.push({
x: Math.random() * SCREEN_WIDTH,
baseY: Math.random() * SCREEN_HEIGHT,
height: Math.random() * 40 + 30,
width: Math.random() * 8 + 4,
swayPhase: Math.random() * Math.PI * 2,
swaySpeed: Math.random() * 0.03 + 0.02,
swayAmount: Math.random() * 15 + 10,
alpha: Math.random() * 0.5 + 0.3,
color: Math.random() > 0.5 ? '#228B22' : '#32CD32'
});
}
// 更新逻辑
grass.swayPhase += grass.swaySpeed;
grass.currentSway = Math.sin(grass.swayPhase) * grass.swayAmount;
// 渲染逻辑(三根草叶组合)
ctx.moveTo(gx, gy);
ctx.quadraticCurveTo(gx - sway * 0.5, gy - gh * 0.5, gx - sway, gy - gh);
ctx.stroke();
需求 4:第五关水之国度颜色调整
多轮迭代过程:
迭代 1:深海蓝 → 清澈水色
// 用户反馈:"调整下第 5 关 水之关卡的颜色,目前有点蓝色,更偏向于深海,而不是水的颜色"
// 解决方案:从深海蓝改为清澈水色
// 之前(深海蓝)
gradient: { top: '#4169E1', bottom: '#0000CD' }
// 之后(清澈水色)
gradient: { top: '#87CEEB', bottom: '#4682B4' }
迭代 2:增加蓝色调
// 用户反馈:"水之关卡的颜色,再稍微偏蓝一点,目前有点太清了"
// 解决方案:调整梯度,增加蓝色饱和度
// 最终版
gradient: { top: '#5DADE2', bottom: '#2E86AB' }
需求 5:第六关火之国度火焰位置调整
用户需求:
"修改一下火之关卡的风格,把火焰燃烧的效果放置在屏幕的上半部分区域,不要在下半部分"
解决方案:
将所有火焰效果从底部改为顶部生成。
// 火焰粒子初始化(上半屏幕)
for (let i = 0; i < 80; i++) {
this.fireParticles.push({
x: Math.random() * SCREEN_WIDTH,
y: Math.random() * (SCREEN_HEIGHT * 0.5), // 上半屏幕
size: Math.random() * 15 + 8,
speedY: Math.random() * 2 + 1, // 向下飘落
// ...
});
}
// 火焰喷射效果(从顶部向下)
this.fireJets.forEach(jet => {
jet.y += jet.speedY;
if (jet.y - jet.length > SCREEN_HEIGHT) {
jet.y = -jet.length; // 从顶部重新出现
jet.x = Math.random() * SCREEN_WIDTH;
}
});
需求 6:火焰聚焦效果优化
用户需求:
"第 6 关火之国度的,燃烧的火焰,只聚焦在屏幕的中间偏上的部位,不用整个上方都燃烧。燃烧的幅度大小有个变化,一会大,一会小"
解决方案:
实现火焰脉动效果,聚焦在屏幕中上部。
// 火焰脉动效果
this.firePulsePhase = 0;
this.firePulseSpeed = 0.05;
updateFireEffect() {
this.firePulsePhase += this.firePulseSpeed;
this.firePulseAmount = Math.sin(this.firePulsePhase) * 0.3 + 0.7;
}
renderFireEffect(ctx) {
this.fireParticles.forEach(fire => {
const pulseSize = fire.size * this.firePulseAmount;
// 只在中上部区域渲染
if (fire.y < SCREEN_HEIGHT * 0.6) {
// 绘制火焰
ctx.globalAlpha = fire.alpha * this.firePulseAmount;
ctx.beginPath();
ctx.arc(fire.x, fire.y, pulseSize, 0, Math.PI * 2);
ctx.fill();
}
});
}
需求 7:第九、十关圆形背景位置上移
用户需求:
"第九关 日志国度 和 第十关最终国度 的圆形背景,可以再整体往上移动一个位置"
解决方案:
调整圆形背景的 Y 坐标。
// 第 9 关:日志国度 - 代码雨 + 圆形背景
renderLogFileBackground(ctx) {
const centerX = SCREEN_WIDTH / 2;
const centerY = SCREEN_HEIGHT * 0.4; // 之前是 SCREEN_HEIGHT / 2
// 绘制同心圆
for (let i = 0; i < 5; i++) {
ctx.beginPath();
ctx.arc(centerX, centerY, 60 + i * 40, 0, Math.PI * 2);
ctx.stroke();
}
}
// 第 10 关:最终国度 - 粒子光环 + 圆形背景
renderFinalLevelBackground(ctx) {
const centerX = SCREEN_WIDTH / 2;
const centerY = SCREEN_HEIGHT * 0.4; // 之前是 SCREEN_HEIGHT / 2
// 绘制光环
this.haloParticles.forEach(halo => {
ctx.beginPath();
ctx.arc(centerX, centerY, halo.radius, 0, Math.PI * 2);
ctx.stroke();
});
}
第四阶段:游戏平衡性调整
需求:奖励物品样式优化
多轮迭代过程:
迭代 1:汉字显示 → 图标风格
// 用户反馈:"调整下目前的奖励物品的样式,目前还是汉字显示,把汉字改为图标的风格显示"
// 解决方案:使用 emoji 图标 + 文字组合
render(ctx) {
// 绘制图标背景
ctx.fillStyle = this.powerUpType.color;
ctx.beginPath();
ctx.arc(this.x + 20, this.y + 20, 18, 0, Math.PI * 2);
ctx.fill();
// 绘制 emoji 图标
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#FFFFFF';
ctx.fillText(this.powerUpType.icon, this.x + 20, this.y + 20);
// 绘制效果提示
ctx.font = '12px Arial';
ctx.fillText(this.powerUpType.name, this.x + 20, this.y + 38);
}
迭代 2:背景色透明化 + 汉字提示
// 用户反馈:"奖励物品的背景色改为浅色,或者透明的,效果提示还是用汉字的提示,不用图标的提示"
// 解决方案:半透明背景 + 汉字效果提示
// 之前(图标风格)
icon: '🔥',
name: '双发'
// 之后(汉字提示 + 半透明背景)
render(ctx) {
// 半透明背景
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.beginPath();
ctx.arc(this.x + 20, this.y + 20, 18, 0, Math.PI * 2);
ctx.fill();
// 汉字效果提示
ctx.font = '14px Arial';
ctx.fillText('双发', this.x + 20, this.y + 24);
}
第五阶段:BUG 修复与优化
BUG 1:无敌技能撞毁飞机后不再生成敌机
问题描述:
"程序对于界面飞机的判定有问题,当我使用无敌把所有飞机都撞摧毁后,不再生成飞机了"
问题分析:
- 敌人生成逻辑检查当前敌人数组长度
- 使用无敌技能时调用
enemy.forceDestroy() forceDestroy()只播放爆炸动画,没有立即移除敌人- 敌人仍然在数组中,导致数量检查失败
问题流程:
无敌状态撞飞机
↓
enemy.forceDestroy()
↓
播放爆炸动画 ❌ 没有移除敌人
↓
敌人仍然在 GameGlobal.databus.enemys 数组中
↓
敌人生成逻辑检查:enemys.length >= enemyLimit
↓
认为敌人数量已达上限 ❌
↓
不再生成新敌人 ❌
解决方案:
修改 forceDestroy() 方法,标记为非激活但不立即移除,让动画播放完毕后再移除。
// 之前(有问题)
forceDestroy() {
GameGlobal.databus.addLevel10Kill();
if (Math.random() < 0.3) {
this.dropCoin();
}
this.playAnimation();
GameGlobal.musicManager.playExplosion();
// ❌ 没有移除敌人!
}
// 之后(正确)
forceDestroy() {
GameGlobal.databus.addLevel10Kill();
if (Math.random() < 0.3) {
this.dropCoin();
}
this.playAnimation();
GameGlobal.musicManager.playExplosion();
// ✅ 标记为非激活(停止移动和攻击)
// ❌ 不立即移除,让动画播放完毕
this.isActive = false;
}
// update() 方法中添加移除逻辑
update() {
if (GameGlobal.databus.isGameOver) {
return;
}
// 如果处于爆炸动画播放中,不更新位置
if (this.isPlaying) {
return; // 保持位置不变,等待动画结束
}
// 非激活状态(已爆炸但动画已结束),直接移除
if (!this.isActive) {
this.remove();
return;
}
// 正常更新移动和攻击
this.updateMove();
this.updateBullets();
// ...
}
修复后流程:
无敌状态撞飞机
↓
enemy.forceDestroy()
↓
播放爆炸动画 ✅
↓
标记 isActive = false ✅
↓
update() 检测到 isPlaying = true
↓
保持位置不变,等待动画播放 ✅
↓
动画播放完毕,isPlaying = false
↓
update() 检测到 !isActive
↓
调用 remove() 移除敌人 ✅
↓
敌人数组长度减少 ✅
↓
新敌人生成正常 ✅
BUG 2:敌机被摧毁后没有爆炸动画
问题描述:
"目前程序,地方飞机被摧毁后,没有爆炸的动画效果,需要修复下"
问题分析:
destroy()和forceDestroy()都调用了playAnimation()- 但动画播放需要时间,而敌人被立即移除
- 或者
visible = false导致动画不渲染
根本原因:
- 动画播放与对象移除的时序冲突
- Animation.playAnimation() 会设置 visible = false
- 敌人的 render() 方法在 !visible 时直接返回
解决方案:
修改 destroy() 和 forceDestroy() 方法,让动画播放完毕后再移除。
// destroy() 方法
destroy() {
this.hp--;
if (this.hp <= 0) {
const isVictory = GameGlobal.databus.addLevel10Kill();
if (Math.random() < 0.3) {
this.dropCoin();
}
// 播放爆炸动画
this.playAnimation();
GameGlobal.musicManager.playExplosion();
// ✅ 标记为非激活(停止移动和攻击)
this.isActive = false;
// 第 10 关胜利时立即移除
if (isVictory) {
this.remove();
}
// 其他情况:等待动画播放完毕后由 update() 移除
}
}
// forceDestroy() 方法
forceDestroy() {
GameGlobal.databus.addLevel10Kill();
if (Math.random() < 0.3) {
this.dropCoin();
}
// 播放爆炸动画
this.playAnimation();
GameGlobal.musicManager.playExplosion();
// ✅ 标记为非激活(停止移动和攻击)
this.isActive = false;
}
// update() 方法处理移除
update() {
if (GameGlobal.databus.isGameOver) {
return;
}
// 如果处于爆炸动画播放中,不更新位置
if (this.isPlaying) {
return; // 保持位置不变,等待动画结束
}
// 非激活状态(已爆炸但动画已结束),直接移除
if (!this.isActive) {
this.remove();
return;
}
// 正常更新
this.updateMove();
// ...
}
动画渲染流程:
// 主游戏循环渲染
render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.bg.render(ctx);
this.player.render(ctx);
GameGlobal.databus.bullets.forEach(item => item.render(ctx));
GameGlobal.databus.enemys.forEach(item => item.render(ctx));
// ✅ 渲染所有播放中的动画
GameGlobal.databus.animations.forEach(ani => {
if (ani.isPlaying) {
ani.aniRender(ctx);
}
});
}
完整爆炸流程:
敌机被摧毁
↓
调用 destroy() 或 forceDestroy()
↓
播放爆炸动画(19 帧)✅
↓
标记 isActive = false
↓
update() 检测到 isPlaying = true
↓
保持位置不变,等待动画播放 ✅
↓
动画播放期间,全局 animations 数组渲染爆炸帧 ✅
↓
动画播放完毕(index = count - 1)
↓
stopAnimation() 设置 isPlaying = false
↓
update() 检测到 !isActive
↓
调用 remove() 从数组移除 ✅
效果对比:
| 方面 | 之前 | 现在 |
|---|---|---|
| 爆炸动画 | ❌ 不播放,立即移除 | ✅ 完整播放 19 帧动画 |
| 敌人移除时机 | ❌ 立即移除 | ✅ 动画播放完毕后移除 |
| 敌人移动 | ❌ 播放动画时还在移动 | ✅ 播放动画时静止 |
| 敌人生成 | ❌ 不再生成新敌人 | ✅ 正常生成新敌人 |
🔧 关键技术问题与解决方案
问题 1:对象池管理与动画播放的冲突
问题描述:
对象池会回收并重置对象属性,但动画播放需要对象保持完整状态。
解决方案:
动画播放期间不回收对象,等动画播放完毕后再回收到对象池。
// 错误做法
forceDestroy() {
this.playAnimation();
this.pool.recover('enemy', this); // ❌ 立即回收,动画被打断
}
// 正确做法
forceDestroy() {
this.playAnimation();
this.isActive = false; // ✅ 标记为非激活
// update() 会在动画播放完毕后调用 remove()
// remove() 才会回收到对象池
}
问题 2:Animation 类的 visible 属性冲突
问题描述:
Animation.playAnimation() 会设置 visible = false 来隐藏精灵图,但这也导致 render() 方法提前返回。
解决方案:
理解 Animation 类的渲染机制:
- 精灵图通过 render() 绘制(依赖 visible)
- 动画帧通过 aniRender() 绘制(不依赖 visible)
- 两者独立,互不影响
// Animation.playAnimation()
playAnimation(index = 0, loop = false) {
this.visible = false; // 隐藏精灵图
this.isPlaying = true;
this.index = index;
// ...
}
// 主渲染循环
render() {
// 绘制精灵(visible = false 时不绘制)
GameGlobal.databus.enemys.forEach(item => item.render(ctx));
// 绘制动画(不依赖 visible,只依赖 isPlaying)
GameGlobal.databus.animations.forEach(ani => {
if (ani.isPlaying) {
ani.aniRender(ctx); // ✅ 正常绘制爆炸动画
}
});
}
问题 3:粒子效果性能优化
问题描述:
大量粒子效果会导致性能下降,需要优化。
解决方案:
- 控制粒子数量:每关粒子数量在 20-100 之间
- 简化渲染逻辑:使用简单图形(圆形、线条)
- 透明度优化:使用
globalAlpha而非复杂渐变 - 对象复用:粒子到达边界后重置而非重新创建
// 优化前(性能差)
for (let i = 0; i < 200; i++) {
this.particles.push({
// 复杂属性
gradient: ctx.createLinearGradient(...),
shadow: { blur: 20, color: '#FFF' }
});
}
// 优化后(性能好)
for (let i = 0; i < 60; i++) {
this.particles.push({
// 简单属性
alpha: 0.5,
size: 3,
color: '#FFF'
});
}
// 粒子重置而非重新创建
updateParticles() {
this.particles.forEach(p => {
p.y += p.speedY;
if (p.y > SCREEN_HEIGHT) {
// ✅ 重置位置,复用对象
p.y = -p.size;
p.x = Math.random() * SCREEN_WIDTH;
}
});
}
问题 4:关卡难度曲线设计
问题描述:
如何设计合理的难度曲线,让玩家既不会感到无聊也不会挫败。
解决方案:
- 敌人数上限递增:
[10, 15, 20, 25, 30, 35, 40, 45, 50, 999] - 敌人属性递增:血量、速度、移动复杂度
- 特殊机制:第 10 关需要击毁 10 个敌机才能获胜
- 奖励平衡:合理分布道具掉落率
// 敌机配置示例
L1_T1: { hp: 1, speed: 2, score: 1 } // 第 1 关:基础型
L5_T1: { hp: 3, speed: 3, score: 5 } // 第 5 关:中等型
L10_T5: { hp: 30, speed: 1.5, score: 50 } // 第 10 关:Boss 级
问题 5:多轮需求迭代的沟通技巧
问题描述:
用户需求经常变化,如何高效处理多轮迭代。
解决方案:
- 快速原型:先实现基本功能,再根据反馈调整
- 小步迭代:每次修改一个小点,便于回滚
- 详细记录:记录每次修改的原因和效果
- 主动确认:修改后主动询问用户是否满意
示例对话流程:
用户:"第一关的风格切换一下"
↓
AI:实现风之环效果
↓
用户:"效果不好,再切换一种"
↓
AI:实现流动风之流
↓
用户:"还是不好,简单一点的"
↓
AI:实现简单风线
↓
用户:"线条数量 10 条吧"
↓
AI:调整数量为 10,最终确认 ✅
🤖 AI 协作经验总结
高效沟通技巧
1. 明确具体的需求
❌ 不好的提问:
"敌机太单调了"
✅ 好的提问:
"现在只有 5 种类型的飞机太单一了,每一关应该有 5 种不同的敌机,第 1 关有第 1 关的特色,第 2 关有第 2 关的特色,以此类推到第 10 关,总共 50 种敌机配置"
2. 提供具体示例
❌ 不好的提问:
"背景效果不好看"
✅ 好的提问:
"第一关风之国度的背景效果不好,现在的风环太复杂了,想要简单一点的风格,比如几条细线飘过"
3. 分步骤确认
❌ 不好的做法:
一次性提出多个需求,等全部完成再反馈
✅ 好的做法:
每个小需求完成后立即确认,不满意立即调整
用户:"调整下第 5 关水之关卡的颜色"
↓
AI:调整为清澈水色
↓
用户:"现在有点太清了,再稍微偏蓝一点"
↓
AI:增加蓝色饱和度
↓
用户:"可以了" ✅
AI 辅助开发的优势
1. 快速原型实现
- 传统开发:需要手动编写所有代码,耗时数小时
- AI 辅助:描述需求,几分钟内生成代码框架
2. 多方案对比
- 传统开发:通常只实现一种方案
- AI 辅助:可以快速生成多种方案供选择
// AI 可以提供多种实现方案
// 方案 1:风之环
renderWindRings() { }
// 方案 2:流动风之流
renderWindStreams() { }
// 方案 3:旋风粒子
renderCyclones() { }
// 方案 4:简单风线(最终选择)
renderWindLines() { }
3. 即时问题修复
- 传统开发:遇到问题需要手动调试,耗时较长
- AI 辅助:描述问题现象,快速定位并修复
4. 代码质量提升
- 传统开发:容易忽略边界情况和错误处理
- AI 辅助:自动添加注释、错误处理、边界检查
AI 协作的最佳实践
1. 清晰的任务分解
将大任务分解为小任务,逐个击破:
开发十关关卡系统
├── 任务 1:设计 10 个关卡名称和主题
├── 任务 2:配置每关的敌机类型
├── 任务 3:实现每关的背景效果
├── 任务 4:设计每关的胜利条件
└── 任务 5:调整难度曲线
2. 及时的反馈循环
需求 → 实现 → 测试 → 反馈 → 调整 → 确认
↓ ↓
└────────── 循环 ──────────────┘
3. 详细的文档记录
- 记录每次修改的原因
- 记录问题和解决方案
- 记录用户反馈和最终决策
4. 合理的期望管理
- AI 不是万能的,需要人工审核
- 复杂逻辑需要多次迭代
- 性能优化需要实际测试
📊 项目成果
代码统计
| 指标 | 数量 |
|---|---|
| 总代码行数 | ~8000 行 |
| JavaScript 文件 | 15 个 |
| 敌机配置 | 50 种 |
| 关卡数量 | 10 关 |
| 背景粒子效果 | 10 种 |
| 道具类型 | 5 种 |
| 修复 BUG 数 | 20+ 个 |
功能完成度
- ✅ 十关关卡系统(每关独特主题)
- ✅ 50 种敌机配置(每关 5 种)
- ✅ 4 种移动模式(直线、S 型、曲线、缓慢曲线)
- ✅ 10 种背景粒子效果(风、岩、雷、草、水、火、冰、雾、日志、最终)
- ✅ 5 种道具系统(双发、加速、护甲、生命、炸弹)
- ✅ 无敌技能和炸弹技能
- ✅ 护甲系统(3 层护甲)
- ✅ 金币系统和商店
- ✅ 游戏胜利和失败判定
- ✅ 分享推荐机制
视觉效果
| 关卡 | 主题色 | 粒子效果 | 特色 |
|---|---|---|---|
| 第 1 关 | 青蓝色 | 10 条风线 | 稀疏纤细 |
| 第 2 关 | 岩石棕 | 晶体岩石 | 立体多面体 |
| 第 3 关 | 紫色 | 闪电分支 | 随机闪烁 |
| 第 4 关 | 森林绿 | 草丛摇曳 | 三叶曲线 |
| 第 5 关 | 海洋蓝 | 水泡上升 | 左右摇摆 |
| 第 6 关 | 火焰红 | 火焰喷射 | 脉动效果 |
| 第 7 关 | 冰雪蓝 | 雪花飘落 | 六角分支 |
| 第 8 关 | 灰色调 | 雾气粒子 | 模糊效果 |
| 第 9 关 | 代码绿 | 代码雨 | 矩阵效果 |
| 第 10 关 | 紫金色 | 粒子光环 | 同心圆 |
🚀 后续优化方向
短期优化(1-2 周)
-
性能优化
- 粒子效果 LOD(根据设备性能调整数量)
- 对象池预分配,减少 GC
- 渲染批次合并 -
音效优化
- 添加背景音乐
- 不同关卡不同 BGM
- 音效音量平衡 -
UI 优化
- 更精美的开始界面
- 关卡选择界面
- 结算界面动画
中期优化(1-2 月)
-
游戏模式
- 无尽模式
- 时间挑战模式
- BOSS 战模式 -
社交功能
- 好友排行榜
- 成就系统
- 每日任务 -
商业化
- 皮肤系统
- 道具内购
- 广告激励视频
长期优化(3-6 月)
-
引擎升级
- 考虑迁移到 Cocos Creator
- 或使用 Phaser 引擎
- 提升开发效率 -
跨平台
- 抖音小游戏
- 支付宝小游戏
- Web 版本 -
IP 化
- 角色设计
- 故事背景
- 周边产品
📚 附录
附录 A:完整关卡配置表
const LEVEL_CONFIGS = {
1: {
name: '风之国度',
gradient: { top: '#87CEEB', bottom: '#5DADE2' },
enemyLimit: 10,
specialEffect: 'wind_lines',
particleCount: 10
},
2: {
name: '岩之国度',
gradient: { top: '#A0826D', bottom: '#6B4423' },
enemyLimit: 15,
specialEffect: 'rock_crystal',
particleCount: 40
},
// ... 3-10 关
};
附录 B:敌机属性对照表
| 关卡 | 类型 1 | 类型 2 | 类型 3 | 类型 4 | 类型 5 |
|---|---|---|---|---|---|
| L1 | HP:1 | HP:2 | HP:3 | HP:5 | HP:8 |
| L2 | HP:2 | HP:4 | HP:6 | HP:10 | HP:15 |
| L3 | HP:3 | HP:6 | HP:9 | HP:15 | HP:22 |
| ... | ... | ... | ... | ... | ... |
| L10 | HP:15 | HP:20 | HP:25 | HP:30 | HP:40 |
附录 C:常用代码片段
对象池使用
// 从对象池获取对象
const enemy = GameGlobal.databus.pool.getItemByClass(
'enemy',
Enemy,
enemyType,
level
);
enemy.init(level);
GameGlobal.databus.enemys.push(enemy);
// 回收到对象池
GameGlobal.databus.pool.recover('enemy', enemy);
碰撞检测
// 矩形碰撞检测
isCollideWithRect(rect) {
return !!(
rect.startX <= this.x + this.width &&
rect.endX >= this.x &&
rect.startY <= this.y + this.height &&
rect.endY >= this.y
);
}
动画播放
// 播放爆炸动画
playAnimation() {
this.visible = false;
this.isPlaying = true;
this.index = 0;
}
// 渲染动画帧
aniRender(ctx) {
if (this.index >= 0 && this.index < this.count) {
ctx.drawImage(
this.imgList[this.index],
this.x,
this.y,
this.width * 1.2,
this.height * 1.2
);
}
}
🎓 总结
通过这次 3 天的 AI 辅助开发实践,我们成功完成了一个功能完整的微信小游戏飞机大战项目。
关键成功因素
- 明确的需求:清晰的关卡设计和视觉风格定位
- 高效的沟通:与 AI 的良好互动,快速迭代
- 合理的架构:对象池、数据总线、类继承体系
- 持续的优化:多轮视觉调整,性能优化
- 完善的测试:及时发现问题并修复
AI 辅助开发的价值
- 开发效率提升:3 天完成传统开发需要 2-3 周的工作量
- 代码质量提升:自动添加注释、错误处理
- 创意实现加速:快速原型,多方案对比
- 学习成本降低:AI 解释代码,帮助理解
未来展望
AI 辅助开发不是替代开发者,而是增强开发者的能力。掌握与 AI 高效协作的技巧,将成为未来软件开发的核心竞争力。
文档结束
作者:AI 辅助开发团队
日期:2026-03-29
版本:1.0
联系方式:[待添加]
本文档记录了从 0 到 1 使用 AI 开发小游戏的完整过程,包括技术决策、问题解决、经验总结等内容,可作为类似项目的参考指南。
评论
发表评论