原创文章

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

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

从0到1全纪录配图

项目名称:微信小游戏 - 飞机大战
开发方式:AI 辅助开发(Trae IDE + Qwen3.5-Plus)
开发周期:3 天
文档版本:1.0
生成时间:2026-03-29


📖 目录


🎯 项目背景

项目起源

这是一个基于微信小游戏平台的飞机大战游戏项目。项目已经完成了基础框架,但存在诸多问题需要完善:

  • 敌机系统不够丰富,缺乏挑战性
  • 关卡系统单一,只有基础关卡
  • 视觉效果简陋,缺乏特色
  • 游戏平衡性需要调整
  • 存在多个 BUG 需要修复

开发目标

  1. 完善敌机系统:每关 5 种不同特性的敌机
  2. 构建十关关卡系统:每关独特的主题和视觉效果
  3. 优化视觉体验:每个关卡独特的背景粒子效果
  4. 调整游戏平衡:难度曲线、奖励机制
  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:无敌技能撞毁飞机后不再生成敌机

问题描述

"程序对于界面飞机的判定有问题,当我使用无敌把所有飞机都撞摧毁后,不再生成飞机了"

问题分析

  1. 敌人生成逻辑检查当前敌人数组长度
  2. 使用无敌技能时调用 enemy.forceDestroy()
  3. forceDestroy() 只播放爆炸动画,没有立即移除敌人
  4. 敌人仍然在数组中,导致数量检查失败

问题流程

无敌状态撞飞机
    ↓
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:敌机被摧毁后没有爆炸动画

问题描述

"目前程序,地方飞机被摧毁后,没有爆炸的动画效果,需要修复下"

问题分析

  1. destroy()forceDestroy() 都调用了 playAnimation()
  2. 但动画播放需要时间,而敌人被立即移除
  3. 或者 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:粒子效果性能优化

问题描述
大量粒子效果会导致性能下降,需要优化。

解决方案

  1. 控制粒子数量:每关粒子数量在 20-100 之间
  2. 简化渲染逻辑:使用简单图形(圆形、线条)
  3. 透明度优化:使用 globalAlpha 而非复杂渐变
  4. 对象复用:粒子到达边界后重置而非重新创建
// 优化前(性能差)
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:关卡难度曲线设计

问题描述
如何设计合理的难度曲线,让玩家既不会感到无聊也不会挫败。

解决方案

  1. 敌人数上限递增[10, 15, 20, 25, 30, 35, 40, 45, 50, 999]
  2. 敌人属性递增:血量、速度、移动复杂度
  3. 特殊机制:第 10 关需要击毁 10 个敌机才能获胜
  4. 奖励平衡:合理分布道具掉落率
// 敌机配置示例
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:多轮需求迭代的沟通技巧

问题描述
用户需求经常变化,如何高效处理多轮迭代。

解决方案

  1. 快速原型:先实现基本功能,再根据反馈调整
  2. 小步迭代:每次修改一个小点,便于回滚
  3. 详细记录:记录每次修改的原因和效果
  4. 主动确认:修改后主动询问用户是否满意

示例对话流程

用户:"第一关的风格切换一下"
  ↓
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 周)

  1. 性能优化
    - 粒子效果 LOD(根据设备性能调整数量)
    - 对象池预分配,减少 GC
    - 渲染批次合并

  2. 音效优化
    - 添加背景音乐
    - 不同关卡不同 BGM
    - 音效音量平衡

  3. UI 优化
    - 更精美的开始界面
    - 关卡选择界面
    - 结算界面动画

中期优化(1-2 月)

  1. 游戏模式
    - 无尽模式
    - 时间挑战模式
    - BOSS 战模式

  2. 社交功能
    - 好友排行榜
    - 成就系统
    - 每日任务

  3. 商业化
    - 皮肤系统
    - 道具内购
    - 广告激励视频

长期优化(3-6 月)

  1. 引擎升级
    - 考虑迁移到 Cocos Creator
    - 或使用 Phaser 引擎
    - 提升开发效率

  2. 跨平台
    - 抖音小游戏
    - 支付宝小游戏
    - Web 版本

  3. 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 辅助开发实践,我们成功完成了一个功能完整的微信小游戏飞机大战项目。

关键成功因素

  1. 明确的需求:清晰的关卡设计和视觉风格定位
  2. 高效的沟通:与 AI 的良好互动,快速迭代
  3. 合理的架构:对象池、数据总线、类继承体系
  4. 持续的优化:多轮视觉调整,性能优化
  5. 完善的测试:及时发现问题并修复

AI 辅助开发的价值

  • 开发效率提升:3 天完成传统开发需要 2-3 周的工作量
  • 代码质量提升:自动添加注释、错误处理
  • 创意实现加速:快速原型,多方案对比
  • 学习成本降低:AI 解释代码,帮助理解

未来展望

AI 辅助开发不是替代开发者,而是增强开发者的能力。掌握与 AI 高效协作的技巧,将成为未来软件开发的核心竞争力。


文档结束

作者:AI 辅助开发团队
日期:2026-03-29
版本:1.0
联系方式:[待添加]


本文档记录了从 0 到 1 使用 AI 开发小游戏的完整过程,包括技术决策、问题解决、经验总结等内容,可作为类似项目的参考指南。

评论

发表评论