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

日期: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}`);
}
}
优化点说明:
- 数量限制检查:防止敌人过多导致性能问题
- 关卡专属类型:每关只生成该关的 5 种敌机
- 对象池复用:避免频繁创建销毁对象
- 调试日志:便于开发调试(已注释)
实现成果对比
| 指标 | 重构前 | 重构后 | 提升 |
|---|---|---|---|
| 敌机类型数量 | 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:配置对象污染
问题描述:
// ❌ 错误示例
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;
// 清晰易读,易于扩展
渐进式开发
今天的开发遵循了渐进式原则:
- 先设计数据结构 → ENEMY_TYPE_CONFIG
- 再实现获取函数 → getEnemyConfig()
- 然后重构生成逻辑 → enemyGenerate()
- 最后测试验证 → 确保每关敌机正确生成
这种开发方式的好处是:
- ✅ 每一步都清晰明确
- ✅ 便于调试和回滚
- ✅ 降低出错概率
📝 明日计划
优先级任务
- [P0] 视觉效果优化 - 10 关背景粒子效果
- [P1] 关卡主题色设计 - 每关独特的渐变背景
- [P1] 特殊效果实现 - 闪电、火焰、雪花等
- [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 个技术问题
文档记录:本文档
明天将继续完善视觉效果,让每个关卡都有独特的视觉风格!🚀
评论
发表评论