原创文章

飞机大战开发日志 - 第一天

飞机大战开发日志 - 第一天

第一天配图

日期:Day 1
开发方式:AI 辅助开发(Trae IDE + Qwen3.5-Plus)
今日目标:完善敌机系统,构建十关关卡基础架构
文档版本:1.0


📋 今日概要

第一天主要聚焦于游戏的核心战斗系统完善,包括:
1. 敌机系统从 5 种扩展到 50 种
2. 实现多样化的敌机移动模式
3. 构建十关关卡基础架构
4. 设计关卡难度曲线


🎯 开发背景

项目初始状态

接手项目时,游戏已经完成了基础框架,但存在以下问题:

核心问题清单
- ❌ 敌机系统单一:只有 5 种固定类型的敌机
- ❌ 关卡无特色:所有关卡使用相同的敌机配置
- ❌ 难度曲线平:只是线性增加数值,缺乏变化
- ❌ 视觉体验差:背景效果简陋,缺乏主题特色
- ❌ 隐藏 BUG:碰撞检测、对象池管理存在问题

今日开发目标

优先级排序
1. [P0] 敌机系统扩展 - 每关 5 种独特敌机
2. [P0] 移动模式丰富 - S 曲线型等
3. [P1] 十关关卡架构 - 基础配置和胜利条件
4. [P1] 难度曲线设计 - 敌人数上限和属性递增

🔧 核心任务一:敌机系统扩展

问题发现

用户反馈

"现在只有 5 种类型的飞机太单一了,每一关的敌机应该有特色,比如第 1 关有 5 种,第 2 关有另外 5 种,以此类推到第 10 关"

现状分析

原有代码结构:

// ❌ 问题代码:只有 5 种固定配置
const ENEMY_TYPES = {
  TYPE_1: { hp: 1, speed: 2, moveType: 'straight', score: 1 },
  TYPE_2: { hp: 2, speed: 2.5, moveType: 'slight_curve', score: 2 },
  TYPE_3: { hp: 3, speed: 1.5, moveType: 'curve', score: 3 },
  TYPE_4: { hp: 5, speed: 1, moveType: 'slow_curve', score: 5 },
  TYPE_5: { hp: 8, speed: 0.8, moveType: 'slow_curve', score: 8 }
};

// 所有关卡都使用这 5 种配置
function createEnemy(level) {
  const type = 'TYPE_' + Math.floor(Math.random() * 5) + 1;
  return new Enemy(ENEMY_TYPES[type]);
}

问题总结
1. 敌机类型固定为 5 种
2. 所有关卡使用相同配置
3. 无法体现关卡特色
4. 难度提升仅靠数值堆砌


解决方案设计

设计思路

关卡专属敌机系统
├── 第 1 关:风之国度 - 基础型敌机(5 种)
├── 第 2 关:岩之国度 - 防御型敌机(5 种)
├── 第 3 关:雷之国度 - 快速型敌机(5 种)
├── 第 4 关:草之国度 - 平衡型敌机(5 种)
├── 第 5 关:水之国度 - 灵活型敌机(5 种)
├── 第 6 关:火之国度 - 攻击型敌机(5 种)
├── 第 7 关:冰之国度 - 迟缓型敌机(5 种)
├── 第 8 关:雾之国度 - 隐身型敌机(5 种)
├── 第 9 关:日志国度 - 数据型敌机(5 种)
└── 第 10 关:最终国度 - Boss 型敌机(5 种)

总计:50 种独特敌机配置

实现方案

步骤 1:创建关卡专属配置系统

/**
 * 敌机类型配置表
 * 命名规则:L{关卡}_T{类型} 例如:L1_T1 表示第 1 关第 1 种敌机
 */
const ENEMY_TYPE_CONFIG = {
  // ==================== 第 1 关:风之国度 ====================
  L1_T1: { 
    hp: 1, 
    speed: 2, 
    moveType: 'straight',    // 直线飞行
    score: 1,
    color: '#87CEEB',        // 天空蓝
    width: 40,
    height: 40
  },
  L1_T2: { 
    hp: 2, 
    speed: 2.5, 
    moveType: 'slight_curve', // 轻微 S 型
    score: 2,
    color: '#5DADE2',
    width: 45,
    height: 45
  },
  L1_T3: { 
    hp: 3, 
    speed: 1.5, 
    moveType: 'curve',        // 大曲线 S 型
    score: 3,
    color: '#4682B4',
    width: 50,
    height: 50
  },
  L1_T4: { 
    hp: 5, 
    speed: 1, 
    moveType: 'slow_curve',   // 缓慢大曲线
    score: 5,
    color: '#2E86AB',
    width: 60,
    height: 60
  },
  L1_T5: { 
    hp: 8, 
    speed: 0.8, 
    moveType: 'slow_curve',
    score: 8,
    color: '#1E3A5F',
    width: 70,
    height: 70
  },

  // ==================== 第 2 关:岩之国度 ====================
  L2_T1: { 
    hp: 2,      // 血量提升
    speed: 2, 
    moveType: 'straight',
    score: 2,
    color: '#A0826D',  // 岩石棕
    width: 45,
    height: 45
  },
  L2_T2: { 
    hp: 4, 
    speed: 2.5, 
    moveType: 'slight_curve',
    score: 4,
    color: '#8B7355',
    width: 50,
    height: 50
  },
  L2_T3: { 
    hp: 6, 
    speed: 1.5, 
    moveType: 'curve',
    score: 6,
    color: '#6B4423',
    width: 55,
    height: 55
  },
  L2_T4: { 
    hp: 10, 
    speed: 1, 
    moveType: 'slow_curve',
    score: 10,
    color: '#5A4635',
    width: 65,
    height: 65
  },
  L2_T5: { 
    hp: 15,     // 高血量防御型
    speed: 0.8, 
    moveType: 'slow_curve',
    score: 15,
    color: '#4A3625',
    width: 80,
    height: 80
  },

  // ==================== 第 3 关:雷之国度 ====================
  L3_T1: { 
    hp: 3, 
    speed: 3,      // 速度提升
    moveType: 'straight',
    score: 3,
    color: '#8A2BE2',  // 紫色雷电
    width: 42,
    height: 42
  },
  L3_T2: { 
    hp: 6, 
    speed: 3.5, 
    moveType: 'slight_curve',
    score: 6,
    color: '#4B0082',
    width: 48,
    height: 48
  },
  L3_T3: { 
    hp: 9, 
    speed: 2.5, 
    moveType: 'curve',
    score: 9,
    color: '#9370DB',
    width: 54,
    height: 54
  },
  L3_T4: { 
    hp: 15, 
    speed: 1.5, 
    moveType: 'slow_curve',
    score: 15,
    color: '#6A5ACD',
    width: 66,
    height: 66
  },
  L3_T5: { 
    hp: 22, 
    speed: 1.2, 
    moveType: 'straight',  // 高速直线
    score: 22,
    color: '#483D8B',
    width: 78,
    height: 78
  },

  // ... 第 4-10 关配置类似结构
};

配置设计原则

关卡 主题 敌机特色 数值倾向
L1 风之国度 基础型 平衡
L2 岩之国度 防御型 高血量
L3 雷之国度 快速型 高速度
L4 草之国度 平衡型 均衡
L5 水之国度 灵活型 高机动
L6 火之国度 攻击型 高伤害
L7 冰之国度 迟缓型 低速度高血量
L8 雾之国度 隐身型 特殊能力
L9 日志国度 数据型 诡异移动
L10 最终国度 Boss 型 全能型

步骤 2:实现动态配置获取函数

/**
 * 根据关卡和类型获取敌机配置
 * @param {string} type - 敌机类型标识 (1-5)
 * @param {number} level - 关卡数 (1-10)
 * @returns {Object} 敌机配置对象
 */
function getEnemyConfig(type, level) {
  // 构建配置键名,例如:L1_T1, L2_T3, L10_T5
  const configKey = `L${level}_T${type}`;

  // 返回对应配置,如果不存在则返回默认配置
  const config = ENEMY_TYPES[configKey] || ENEMY_TYPES['L1_T1'];

  // 深拷贝配置对象,避免污染原始配置
  return JSON.parse(JSON.stringify(config));
}

/**
 * 敌机工厂函数
 * @param {number} level - 关卡数
 * @param {number} typeIndex - 类型索引 (1-5)
 * @returns {Enemy} 敌机实例
 */
function createEnemy(level, typeIndex) {
  const config = getEnemyConfig(typeIndex, level);
  return new Enemy(config, level);
}

技术要点
1. 使用模板字符串构建配置键名
2. 提供默认配置兜底
3. 深拷贝避免配置污染
4. 工厂函数封装创建逻辑


步骤 3:重构敌人生成逻辑

原有问题代码

// ❌ 所有关卡使用相同的 5 种类型
enemyGenerate() {
  if (frame % 30 === 0) {
    const typeIndex = Math.floor(Math.random() * 5) + 1;
    const enemy = new Enemy(ENEMY_TYPES['TYPE_' + typeIndex]);
    databus.enemys.push(enemy);
  }
}

重构后的代码

/**
 * 敌人生成逻辑
 * 每 30 帧生成一个敌人,从当前关卡的 5 种类型中随机选择
 */
enemyGenerate() {
  // 每 30 帧生成一个敌机
  if (GameGlobal.databus.frame % ENEMY_GENERATE_INTERVAL === 0) {

    // 1. 检查当前关卡敌人数量上限
    const currentLevel = GameGlobal.databus.level;
    const enemyLimit = GameGlobal.databus.levelEnemyLimits[currentLevel - 1] || 10;
    const currentEnemyCount = GameGlobal.databus.enemys.length;

    // 2. 如果已达上限,停止生成
    if (currentEnemyCount >= enemyLimit) {
      return;
    }

    // 3. 从当前关卡的 5 种敌人类型中随机选择
    const enemyTypeIndex = Math.floor(Math.random() * 5) + 1; // 1-5
    const enemyType = `L${currentLevel}_T${enemyTypeIndex}`;

    // 4. 从对象池获取敌人(性能优化)
    const enemy = GameGlobal.databus.pool.getItemByClass(
      'enemy', 
      Enemy, 
      enemyType, 
      currentLevel
    );

    // 5. 初始化位置和生命值
    enemy.init(currentLevel);

    // 6. 添加到敌人数组
    GameGlobal.databus.enemys.push(enemy);

    // 调试日志
    // console.log(`[生成] 第${currentLevel}关 - 类型${enemyTypeIndex} - 当前敌人数量:${currentEnemyCount + 1}`);
  }
}

优化点说明

  1. 数量限制检查:防止敌人过多导致性能问题
  2. 关卡专属类型:每关只生成该关的 5 种敌机
  3. 对象池复用:避免频繁创建销毁对象
  4. 调试日志:便于开发调试(已注释)

实现成果对比

指标 重构前 重构后 提升
敌机类型数量 5 种 50 种 10 倍
关卡特色 每关独特 质的飞跃
配置可维护性 硬编码 配置化 易于扩展
玩家体验 单调重复 新鲜感强 显著提升

🔧 核心任务二:移动模式系统

需求分析

用户反馈

"敌机的移动方式应该更丰富,比如 S 型、曲线型、直线型等"

原有问题
- 所有敌机都是直线飞行
- 缺乏变化和趣味性
- 无法体现不同敌机的特色


移动模式设计

4 种基础移动模式

/**
 * 敌机移动模式枚举
 */
const MOVE_TYPES = {
  // 1. 直线飞行 - 最简单,适合基础敌机
  STRAIGHT: 'straight',

  // 2. 轻微 S 型 - 小幅度摇摆,适合初级敌机
  SLIGHT_CURVE: 'slight_curve',

  // 3. 大曲线 S 型 - 大幅度摇摆,适合中级敌机
  CURVE: 'curve',

  // 4. 缓慢大曲线 - 慢速大幅度摇摆,适合高级敌机
  SLOW_CURVE: 'slow_curve'
};

移动模式实现

模式 1:直线飞行

/**
 * 直线移动 - 最简单直接的移动方式
 * 适用于:基础敌机、高速突袭敌机
 */
moveStraight() {
  // 简单的向下移动
  this.y += this.speed;

  // 边界检查(可选,直线飞行通常不需要)
  // this.x = Math.max(0, Math.min(this.x, SCREEN_WIDTH - this.width));
}

特点
- ✅ 性能最优
- ✅ 易于预测
- ❌ 缺乏变化


模式 2:轻微 S 型

/**
 * 轻微 S 型移动 - 小幅度左右摇摆
 * 适用于:初级敌机,增加一定难度
 * 
 * 数学原理:正弦波
 * x = A * sin(ω * y)
 * A: 振幅 (amplitude)
 * ω: 角频率 (frequency)
 */
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));
}

// 初始化时设置参数
this.amplitude = 50;      // 振幅 50px
this.frequency = 0.05;    // 频率系数
this.initialX = this.x;   // 记录初始 X 坐标

效果演示

生成点 → ↓
        ↘
         ↙
        ↘
         ↙
          ↓

特点
- ✅ 有一定变化
- ✅ 仍可预测
- ✅ 性能良好


模式 3:大曲线 S 型

/**
 * 大曲线 S 型移动 - 大幅度左右摇摆
 * 适用于:中级敌机,显著增加难度
 * 
 * 与轻微 S 型的区别:
 * 1. 振幅更大(100px vs 50px)
 * 2. 频率更低(0.03 vs 0.05),波形更平缓
 */
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));
}

// 初始化时设置参数
this.curveAmount = 100;   // 曲线幅度 100px
this.initialX = this.x;

效果演示

生成点 →  ↓
        ↘
         ↙
        ↘
         ↙
        ↘
         ↙
          ↓

特点
- ✅ 变化明显
- ✅ 视觉效果好
- ⚠️ 需要更多计算


模式 4:统一更新接口

/**
 * 每一帧更新敌人位置
 * 根据 moveType 自动选择移动方式
 */
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;

    default:
      this.moveStraight();
  }
}

移动模式配置示例

// 不同关卡的敌机移动模式配置

const ENEMY_MOVE_CONFIGS = {
  // 第 1 关:以直线和轻微 S 型为主
  L1: {
    T1: { moveType: 'straight' },      // 基础直线
    T2: { moveType: 'slight_curve' },  // 轻微 S 型
    T3: { moveType: 'curve' },         // 大曲线
    T4: { moveType: 'slow_curve' },    // 缓慢曲线
    T5: { moveType: 'slow_curve' }     // 缓慢曲线
  },

  // 第 2 关:增加曲线比例
  L2: {
    T1: { moveType: 'straight' },
    T2: { moveType: 'straight' },
    T3: { moveType: 'slight_curve' },
    T4: { moveType: 'curve' },
    T5: { moveType: 'slow_curve' }
  },

  // 第 3 关:高速直线 + 曲线混合
  L3: {
    T1: { moveType: 'fast_straight' }, // 高速直线
    T2: { moveType: 'straight' },
    T3: { moveType: 'slight_curve' },
    T4: { moveType: 'curve' },
    T5: { moveType: 'fast_straight' }
  }

  // ... 更多关卡配置
};

🏗️ 核心任务三:十关关卡架构

关卡设计文档

10 个关卡的主题设计

/**
 * 关卡名称配置
 */
const LEVEL_NAMES = [
  '风之国度',    // 第 1 关 - 清新开场
  '岩之国度',    // 第 2 关 - 防御提升
  '雷之国度',    // 第 3 关 - 速度挑战
  '草之国度',    // 第 4 关 - 平衡发展
  '水之国度',    // 第 5 关 - 灵活多变
  '火之国度',    // 第 6 关 - 激烈战斗
  '冰之国度',    // 第 7 关 - 迟缓考验
  '雾之国度',    // 第 8 关 - 神秘诡异
  '日志国度',    // 第 9 关 - 数据风暴
  '最终国度'     // 第 10 关 - 终极挑战
];

关卡难度曲线

敌人数上限设计

/**
 * 每关敌人数上限配置
 * 设计原则:渐进式提升,第 10 关特殊处理
 */
const levelEnemyLimits = [
  10,   // 第 1 关:10 个敌机 - 教学关
  15,   // 第 2 关:15 个敌机 - 入门
  20,   // 第 3 关:20 个敌机 - 初级
  25,   // 第 4 关:25 个敌机 - 中级
  30,   // 第 5 关:30 个敌机 - 中高级
  35,   // 第 6 关:35 个敌机 - 高级
  40,   // 第 7 关:40 个敌机 - 专家
  45,   // 第 8 关:45 个敌机 - 大师
  50,   // 第 9 关:50 个敌机 - 准 Boss
  999   // 第 10 关:无上限 - Boss 战(击毁 10 个敌机获胜)
];

难度曲线可视化

敌机数量
  50 |                       □
  45 |                   □
  40 |               □
  35 |           □
  30 |       □
  25 |   □
  20 | □
  15 |□
  10 |□
     +--+--+--+--+--+--+--+--+--+--
      L1 L2 L3 L4 L5 L6 L7 L8 L9 L10

关卡胜利条件

普通关卡(1-9 关)

/**
 * 普通关卡胜利条件
 * 击毁所有敌机后自动进入下一关
 */
updateLevel() {
  if (this.level >= 10) return;

  // 检查是否所有敌机都被击毁
  if (this.enemys.length === 0 && this.frame > 100) {
    this.level++;
    this.resetLevel();
  }
}

最终关卡(第 10 关)

/**
 * 第 10 关特殊胜利条件
 * 需要击毁 10 个敌机才能获胜
 */
addLevel10Kill() {
  if (this.level === 10) {
    this.level10Kills++;
    const requiredKills = 10;

    if (this.level10Kills >= requiredKills) {
      this.gameWin();
      return true;
    }
  }
  return false;
}

/**
 * 游戏胜利处理
 */
gameWin() {
  this.isGameOver = true;
  this.gameState = GAME_STATE.VICTORY;

  // 显示胜利界面
  if (GameGlobal.game && GameGlobal.game.gameInfo) {
    GameGlobal.game.gameInfo.showVictoryDialog = true;
  }
}

第 10 关设计思路
- 敌机无上限生成
- 必须击毁 10 个敌机
- 考验玩家持久战能力
- 通关后显示胜利界面


📊 今日开发成果

代码统计

指标 数量
新增代码行数 ~1500 行
修改文件数 5 个
敌机配置数 50 种
移动模式数 4 种
关卡配置数 10 关

功能完成度

  • ✅ 敌机系统从 5 种扩展到 50 种
  • ✅ 实现 4 种移动模式(直线、S 型、曲线、缓慢曲线)
  • ✅ 完成 10 关基础架构设计
  • ✅ 设计难度曲线(敌人数上限递增)
  • ✅ 实现第 10 关特殊胜利条件

技术亮点

  1. 配置化设计:所有敌机属性通过配置控制
  2. 工厂模式:统一的敌机创建接口
  3. 对象池优化:避免频繁创建销毁对象
  4. 策略模式:移动模式的可扩展设计

🐛 遇到的问题与解决

问题 1:配置对象污染

问题描述

// ❌ 错误示例
function createEnemy(level, type) {
  const config = ENEMY_TYPES[`L${level}_T${type}`];
  return new Enemy(config);  // 直接传入配置对象
}

// 问题:Enemy 构造函数可能修改 config,污染原始配置

解决方案

// ✅ 正确示例
function createEnemy(level, type) {
  const config = ENEMY_TYPES[`L${level}_T${type}`];
  // 深拷贝配置对象
  return new Enemy(JSON.parse(JSON.stringify(config)));
}

问题 2:对象池类型不匹配

问题描述

// ❌ 错误示例
const enemy = GameGlobal.databus.pool.getItemByClass('enemy', Enemy);
enemy.init();  // 缺少关卡参数,无法正确初始化

// 问题:对象池返回的敌机没有关卡信息

解决方案

// ✅ 正确示例
const enemy = GameGlobal.databus.pool.getItemByClass(
  'enemy', 
  Enemy, 
  enemyType,    // 传入类型
  currentLevel  // 传入关卡
);
enemy.init(currentLevel);  // 使用关卡参数初始化

💡 开发心得

配置化思维

通过今天的开发,深刻体会到配置化设计的重要性:

硬编码方式(❌)

if (level === 1) {
  enemy.hp = 1;
  enemy.speed = 2;
} else if (level === 2) {
  enemy.hp = 2;
  enemy.speed = 2.5;
}
// ... 难以维护

配置化方式(✅)

const config = ENEMY_TYPES[`L${level}_T${type}`];
enemy.hp = config.hp;
enemy.speed = config.speed;
// 清晰易读,易于扩展

渐进式开发

今天的开发遵循了渐进式原则:

  1. 先设计数据结构 → ENEMY_TYPE_CONFIG
  2. 再实现获取函数 → getEnemyConfig()
  3. 然后重构生成逻辑 → enemyGenerate()
  4. 最后测试验证 → 确保每关敌机正确生成

这种开发方式的好处是:
- ✅ 每一步都清晰明确
- ✅ 便于调试和回滚
- ✅ 降低出错概率


📝 明日计划

优先级任务

  1. [P0] 视觉效果优化 - 10 关背景粒子效果
  2. [P1] 关卡主题色设计 - 每关独特的渐变背景
  3. [P1] 特殊效果实现 - 闪电、火焰、雪花等
  4. [P2] 性能优化 - 粒子数量控制

预期目标

  • 完成所有关卡的视觉效果
  • 每关都有独特的背景粒子效果
  • 保证性能流畅(60 FPS)

📚 技术参考

正弦波在游戏中的应用

// 通用公式
x = A * sin(ω * t + φ)

// 参数说明
A: 振幅幅度大小
ω: 角频率波动快慢
t: 时间或位置
φ: 初相位起始位置

// 实际应用:S 型移动
this.x = this.initialX + Math.sin(this.y * 0.05) * 50;
//            ↑                    ↑         ↑
//          初始位置              频率      振幅

对象池模式

/**
 * 对象池的核心思想
 * 1. 预先创建一批对象
 * 2. 使用时从池中获取
 * 3. 使用后回收到池中
 * 4. 避免频繁创建销毁
 */

// 获取对象
const enemy = pool.getItemByClass('enemy', Enemy, type, level);

// 使用对象
enemy.init(level);
databus.enemys.push(enemy);

// 回收对象(在 remove() 方法中)
pool.recover('enemy', this);

第一天开发结束

开发时长:约 8 小时
代码提交:3 次 commit
问题解决:5 个技术问题
文档记录:本文档

明天将继续完善视觉效果,让每个关卡都有独特的视觉风格!🚀

评论

发表评论