|
|
# 构建太空游戏第6部分:结束与重启
|
|
|
|
|
|
```mermaid
|
|
|
journey
|
|
|
title 你的游戏完成旅程
|
|
|
section 结束条件
|
|
|
定义胜负状态: 3: Student
|
|
|
实现条件检测: 4: Student
|
|
|
处理状态转换: 4: Student
|
|
|
section 玩家体验
|
|
|
设计反馈系统: 4: Student
|
|
|
创建重启机制: 5: Student
|
|
|
优化用户界面: 5: Student
|
|
|
section 系统集成
|
|
|
管理游戏生命周期: 5: Student
|
|
|
处理内存清理: 5: Student
|
|
|
创建完整体验: 5: Student
|
|
|
```
|
|
|
每个伟大的游戏都需要清晰的结束条件和流畅的重启机制。你已经打造了一个具有移动、战斗和计分功能的令人印象深刻的太空游戏——现在是时候添加最终的部分,让它感觉完整了。
|
|
|
|
|
|
你的游戏当前无限运行,就像美国宇航局1977年发射的旅行者探测器——几十年后仍在太空中旅行。虽然这对于太空探索很好,但游戏需要定义好的终点来创造令人满意的体验。
|
|
|
|
|
|
今天,我们将实现合适的胜负条件和重启系统。课程结束时,你将拥有一个抛光完善的游戏,玩家可以完成并重玩,就像定义了媒介的经典街机游戏一样。
|
|
|
|
|
|
```mermaid
|
|
|
mindmap
|
|
|
root((游戏完成))
|
|
|
End Conditions
|
|
|
Victory States[胜利状态]
|
|
|
Defeat Conditions[失败条件]
|
|
|
Progress Tracking[进度跟踪]
|
|
|
State Validation[状态验证]
|
|
|
Player Feedback
|
|
|
Visual Messages[视觉信息]
|
|
|
Color Psychology[色彩心理学]
|
|
|
Clear Communication[清晰传达]
|
|
|
Emotional Response[情感反应]
|
|
|
State Management
|
|
|
Game Loop Control[游戏循环控制]
|
|
|
Memory Cleanup[内存清理]
|
|
|
Object Lifecycle[对象生命周期]
|
|
|
Event Handling[事件处理]
|
|
|
Restart Systems
|
|
|
Input Handling[输入处理]
|
|
|
State Reset[状态重置]
|
|
|
Fresh Initialization[全新初始化]
|
|
|
User Experience[用户体验]
|
|
|
Polish Elements
|
|
|
Message Display[消息显示]
|
|
|
Smooth Transitions[平滑过渡]
|
|
|
Error Prevention[错误预防]
|
|
|
Accessibility[无障碍]
|
|
|
```
|
|
|
## 课前测验
|
|
|
|
|
|
[课前测验](https://ff-quizzes.netlify.app/web/quiz/39)
|
|
|
|
|
|
## 理解游戏结束条件
|
|
|
|
|
|
你的游戏应该何时结束?这个基础问题自早期街机时代以来影响了游戏设计。吃豆人被鬼抓住或吃掉所有豆子即结束,太空入侵者则是在外星人到达底部或全部被击毁时结束。
|
|
|
|
|
|
作为游戏创作者,你定义胜利和失败条件。对于我们的太空游戏,以下是创造引人入胜玩法的常见方式:
|
|
|
|
|
|
```mermaid
|
|
|
flowchart TD
|
|
|
A["🎮 游戏开始"] --> B{"检查条件"}
|
|
|
|
|
|
B --> C["敌人数"]
|
|
|
B --> D["英雄生命"]
|
|
|
B --> E["得分阈值"]
|
|
|
B --> F["关卡进度"]
|
|
|
|
|
|
C --> C1{"敌人 = 0?"}
|
|
|
D --> D1{"生命 = 0?"}
|
|
|
E --> E1{"得分 ≥ 目标?"}
|
|
|
F --> F1{"目标完成?"}
|
|
|
|
|
|
C1 -->|是| G["🏆 胜利"]
|
|
|
D1 -->|是| H["💀 失败"]
|
|
|
E1 -->|是| G
|
|
|
F1 -->|是| G
|
|
|
|
|
|
C1 -->|否| B
|
|
|
D1 -->|否| B
|
|
|
E1 -->|否| B
|
|
|
F1 -->|否| B
|
|
|
|
|
|
G --> I["🔄 重新开始选项"]
|
|
|
H --> I
|
|
|
|
|
|
style G fill:#e8f5e8
|
|
|
style H fill:#ffebee
|
|
|
style I fill:#e3f2fd
|
|
|
```
|
|
|
- **已击毁 `N` 艘敌舰**:如果你将游戏分为不同关卡,通常需要击毁 `N` 艘敌舰以完成关卡
|
|
|
- **你的飞船被击毁**:有些游戏中,如果你的船被毁你就会输。一种常见方式是生命值机制。每当你的船被毁损失一条命,命数耗尽时游戏失败。
|
|
|
- **你获得了 `N` 分**:另一种常见的结束条件是你收集到足够的分数。分数来源由你决定,通常分数赋予消灭敌舰或收集敌舰被击毁后掉落的物品。
|
|
|
- **完成一个关卡**:这可能涉及多个条件,如击毁 `X` 艘敌舰、收集 `Y` 分数,或收集特定道具。
|
|
|
|
|
|
## 实现游戏重启功能
|
|
|
|
|
|
好的游戏通过流畅的重启机制提高重玩价值。当玩家完成游戏(或失败)时,通常希望立即再试一次——无论是为了超越分数还是提升表现。
|
|
|
|
|
|
```mermaid
|
|
|
stateDiagram-v2
|
|
|
[*] --> Playing: 游戏开始
|
|
|
Playing --> Victory: 消灭所有敌人
|
|
|
Playing --> Defeat: 生命值 = 0
|
|
|
|
|
|
Victory --> MessageDisplay: 显示胜利信息
|
|
|
Defeat --> MessageDisplay: 显示失败信息
|
|
|
|
|
|
MessageDisplay --> WaitingRestart: 按回车提示
|
|
|
WaitingRestart --> Resetting: 按下回车键
|
|
|
|
|
|
Resetting --> CleanupMemory: 清除定时器
|
|
|
CleanupMemory --> ClearEvents: 移除监听器
|
|
|
ClearEvents --> InitializeGame: 全新开始
|
|
|
InitializeGame --> Playing: 新游戏开始
|
|
|
|
|
|
note right of MessageDisplay
|
|
|
颜色编码反馈:
|
|
|
绿色 = 胜利
|
|
|
红色 = 失败
|
|
|
end note
|
|
|
|
|
|
note right of Resetting
|
|
|
完成状态重置
|
|
|
防止内存泄漏
|
|
|
end note
|
|
|
```
|
|
|
俄罗斯方块是这方面的典范:当方块堆满顶部时,你可以立即开始新游戏,无需复杂菜单操作。我们将构建类似的重启系统,清晰地重置游戏状态,让玩家迅速回归游戏。
|
|
|
|
|
|
✅ **思考**:想想你玩过的游戏。它们在什么情况下结束?如何提示你重启?什么样的重启体验让人感觉顺畅而非烦躁?
|
|
|
|
|
|
## 你将构建的内容
|
|
|
|
|
|
你将实现最终的功能,把项目打造成完整的游戏体验。这些元素让精致游戏区别于基础原型。
|
|
|
|
|
|
**今天我们添加的内容:**
|
|
|
|
|
|
1. **胜利条件**:消灭全部敌人并庆祝胜利(你应得的!)
|
|
|
2. **失败条件**:耗尽所有生命,面对失败画面
|
|
|
3. **重启机制**:按回车键即可重新开始——因为一场游戏永远不够
|
|
|
4. **状态管理**:每次都是崭新开始——无遗留敌人或奇怪故障
|
|
|
|
|
|
## 准备工作
|
|
|
|
|
|
让我们准备开发环境。你应该已经有上节课的所有太空游戏文件。
|
|
|
|
|
|
**你的项目文件结构应大致如下:**
|
|
|
|
|
|
```bash
|
|
|
-| assets
|
|
|
-| enemyShip.png
|
|
|
-| player.png
|
|
|
-| laserRed.png
|
|
|
-| life.png
|
|
|
-| index.html
|
|
|
-| app.js
|
|
|
-| package.json
|
|
|
```
|
|
|
|
|
|
**启动你的开发服务器:**
|
|
|
|
|
|
```bash
|
|
|
cd your-work
|
|
|
npm start
|
|
|
```
|
|
|
|
|
|
**该命令执行:**
|
|
|
- 在 `http://localhost:5000` 运行本地服务器
|
|
|
- 正确提供你的文件服务
|
|
|
- 当你修改时自动刷新浏览器
|
|
|
|
|
|
在浏览器打开 `http://localhost:5000` 并验证游戏是否能运行。你应该可以移动,发射并与敌人互动。确认后,我们即可开始实现功能。
|
|
|
|
|
|
> 💡 **专业建议**:为避免Visual Studio Code中警告,请在文件顶部声明 `let gameLoopId;`,而不是在 `window.onload` 函数内声明。这符合现代JavaScript变量声明最佳实践。
|
|
|
|
|
|
```mermaid
|
|
|
flowchart TD
|
|
|
A["1. 条件追踪"] --> B["2. 事件处理器"]
|
|
|
B --> C["3. 消息常量"]
|
|
|
C --> D["4. 重启控制"]
|
|
|
D --> E["5. 消息显示"]
|
|
|
E --> F["6. 重置系统"]
|
|
|
|
|
|
G["isHeroDead()\nisEnemiesDead()"] --> A
|
|
|
H["碰撞事件\n结束游戏事件"] --> B
|
|
|
I["游戏结束_胜利\n游戏结束_失败"] --> C
|
|
|
J["回车键\n重启触发"] --> D
|
|
|
K["胜利/失败\n颜色编码文本"] --> E
|
|
|
L["状态清理\n全新初始化"] --> F
|
|
|
|
|
|
F --> M["🎮 完整游戏"]
|
|
|
|
|
|
style A fill:#e3f2fd
|
|
|
style B fill:#e8f5e8
|
|
|
style C fill:#fff3e0
|
|
|
style D fill:#f3e5f5
|
|
|
style E fill:#e0f2f1
|
|
|
style F fill:#fce4ec
|
|
|
style M fill:#e1f5fe
|
|
|
```
|
|
|
## 实现步骤
|
|
|
|
|
|
### 步骤1:创建结束条件追踪函数
|
|
|
|
|
|
我们需要函数来监测游戏何时结束。就像国际空间站上的传感器不断监控关键系统一样,这些函数将持续检查游戏状态。
|
|
|
|
|
|
```javascript
|
|
|
function isHeroDead() {
|
|
|
return hero.life <= 0;
|
|
|
}
|
|
|
|
|
|
function isEnemiesDead() {
|
|
|
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
|
|
|
return enemies.length === 0;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**幕后发生的事情:**
|
|
|
- **检查** 主角是否已无生命(糟糕!)
|
|
|
- **计算** 当前还活着并在战斗的敌人数量
|
|
|
- **返回** 当战场清空敌人时返回 `true`
|
|
|
- **使用** 简单的真假逻辑保持清晰明了
|
|
|
- **过滤** 所有游戏对象,找出存活者
|
|
|
|
|
|
### 步骤2:更新事件处理器以检测结束条件
|
|
|
|
|
|
现在我们将把这些条件检测连接到游戏事件系统中。每当发生碰撞时,游戏会评估是否触发结束条件。这为关键游戏事件创建即时反馈。
|
|
|
|
|
|
```mermaid
|
|
|
sequenceDiagram
|
|
|
participant Collision
|
|
|
participant GameLogic
|
|
|
participant Conditions
|
|
|
participant EventSystem
|
|
|
participant Display
|
|
|
|
|
|
Collision->>GameLogic: 激光击中敌人
|
|
|
GameLogic->>GameLogic: 摧毁对象
|
|
|
GameLogic->>Conditions: 检查 isEnemiesDead()
|
|
|
|
|
|
alt 全部敌人被击败
|
|
|
Conditions->>EventSystem: 发送 GAME_END_WIN
|
|
|
EventSystem->>Display: 显示胜利消息
|
|
|
else 敌人仍然存在
|
|
|
Conditions->>GameLogic: 继续游戏
|
|
|
end
|
|
|
|
|
|
Collision->>GameLogic: 敌人攻击英雄
|
|
|
GameLogic->>GameLogic: 减少生命值
|
|
|
GameLogic->>Conditions: 检查 isHeroDead()
|
|
|
|
|
|
alt 生命值 = 0
|
|
|
Conditions->>EventSystem: 发送 GAME_END_LOSS
|
|
|
EventSystem->>Display: 显示失败消息
|
|
|
else 生命值仍然存在
|
|
|
GameLogic->>Conditions: 检查 isEnemiesDead()
|
|
|
alt 全部敌人被击败
|
|
|
Conditions->>EventSystem: 发送 GAME_END_WIN
|
|
|
end
|
|
|
end
|
|
|
```
|
|
|
```javascript
|
|
|
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
|
|
|
first.dead = true;
|
|
|
second.dead = true;
|
|
|
hero.incrementPoints();
|
|
|
|
|
|
if (isEnemiesDead()) {
|
|
|
eventEmitter.emit(Messages.GAME_END_WIN);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
|
|
|
enemy.dead = true;
|
|
|
hero.decrementLife();
|
|
|
if (isHeroDead()) {
|
|
|
eventEmitter.emit(Messages.GAME_END_LOSS);
|
|
|
return; // 胜利之前的代价
|
|
|
}
|
|
|
if (isEnemiesDead()) {
|
|
|
eventEmitter.emit(Messages.GAME_END_WIN);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
eventEmitter.on(Messages.GAME_END_WIN, () => {
|
|
|
endGame(true);
|
|
|
});
|
|
|
|
|
|
eventEmitter.on(Messages.GAME_END_LOSS, () => {
|
|
|
endGame(false);
|
|
|
});
|
|
|
```
|
|
|
|
|
|
**这里发生的事情:**
|
|
|
- **激光击中敌人**:双方消失,你得分,我们检查你是否获胜
|
|
|
- **敌人撞击你**:你失去一条命,我们检查你是否还活着
|
|
|
- **合理顺序**:先检查失败(没人想同时赢又输!)
|
|
|
- **即时反应**:重要事件发生时游戏立刻感知
|
|
|
|
|
|
### 步骤3:添加新消息常量
|
|
|
|
|
|
你需要为 `Messages` 常量对象添加新的消息类型。这些常量帮助保持一致性,避免事件系统中的拼写错误。
|
|
|
|
|
|
```javascript
|
|
|
GAME_END_LOSS: "GAME_END_LOSS",
|
|
|
GAME_END_WIN: "GAME_END_WIN",
|
|
|
```
|
|
|
|
|
|
**上面内容:**
|
|
|
- **添加** 代表游戏结束事件的常量以保持一致性
|
|
|
- **使用** 描述性名称清晰标识事件目的
|
|
|
- **遵循** 现有消息类型命名规范
|
|
|
|
|
|
### 步骤4:实现重启控制
|
|
|
|
|
|
接下来添加键盘控制,让玩家能重启游戏。回车键是自然选择,因其常用作确认与开始新游戏的按键。
|
|
|
|
|
|
**在现有keydown事件监听器中添加对回车键的检测:**
|
|
|
|
|
|
```javascript
|
|
|
else if(evt.key === "Enter") {
|
|
|
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**添加新的消息常量:**
|
|
|
|
|
|
```javascript
|
|
|
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
|
|
|
```
|
|
|
|
|
|
**你需要知道的:**
|
|
|
- **扩展** 你现有的键盘事件处理系统
|
|
|
- **使用** 回车键作为重启触发器,提升用户体验直观性
|
|
|
- **发布** 自定义事件,供游戏的其他部分监听
|
|
|
- **保持** 与其他键盘控制相同模式
|
|
|
|
|
|
### 步骤5:创建消息显示系统
|
|
|
|
|
|
游戏需要清楚地向玩家传达结果。我们将创建一个消息系统,使用颜色编码的文本显示胜利和失败状态,类似早期计算机终端界面绿色表示成功,红色表示错误。
|
|
|
|
|
|
**创建 `displayMessage()` 函数:**
|
|
|
|
|
|
```javascript
|
|
|
function displayMessage(message, color = "red") {
|
|
|
ctx.font = "30px Arial";
|
|
|
ctx.fillStyle = color;
|
|
|
ctx.textAlign = "center";
|
|
|
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**一步步,发生了什么:**
|
|
|
- **设置** 字号和字体,确保文字清晰易读
|
|
|
- **应用** 颜色参数,默认为“红色”表示警告
|
|
|
- **让** 文字在画布水平和垂直居中
|
|
|
- **利用** 现代JavaScript默认参数支持灵活颜色选择
|
|
|
- **借助** canvas 2D上下文直接渲染文本
|
|
|
|
|
|
**创建 `endGame()` 函数:**
|
|
|
|
|
|
```javascript
|
|
|
function endGame(win) {
|
|
|
clearInterval(gameLoopId);
|
|
|
|
|
|
// 设置延迟以确保所有待处理的渲染完成
|
|
|
setTimeout(() => {
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
ctx.fillStyle = "black";
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
if (win) {
|
|
|
displayMessage(
|
|
|
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
|
|
|
"green"
|
|
|
);
|
|
|
} else {
|
|
|
displayMessage(
|
|
|
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
|
|
|
);
|
|
|
}
|
|
|
}, 200)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**此函数做了什么:**
|
|
|
- **冻结** 所有移动——不再有舰船或激光动作
|
|
|
- **暂停** 200毫秒,让最后一帧完成绘制
|
|
|
- **清屏** 并将背景涂成黑色,效果更戏剧化
|
|
|
- **显示** 不同消息给胜利者和失败者
|
|
|
- **用色彩区分消息**——绿代表好消息,红代表……不太好
|
|
|
- **告诉** 玩家如何重新开始游戏
|
|
|
|
|
|
### 🔄 **教学反馈点**
|
|
|
**游戏状态管理**:在实现重置功能前确保理解:
|
|
|
- ✅ 结束条件如何创造清晰的游戏目标
|
|
|
- ✅ 为什么视觉反馈对玩家理解至关重要
|
|
|
- ✅ 合适清理对防止内存泄漏的重要性
|
|
|
- ✅ 事件驱动架构如何实现干净的状态切换
|
|
|
|
|
|
**自测速答**:如果重置时不清理事件监听器会怎样?
|
|
|
*答案:导致内存泄漏和重复事件处理器,引发不可预测的行为*
|
|
|
|
|
|
**游戏设计原则**:你正在实现的是:
|
|
|
- **清晰目标**:玩家明确什么是胜利、什么是失败
|
|
|
- **即时反馈**:游戏状态变化立即被传达
|
|
|
- **用户控制**:玩家准备好了就能重启
|
|
|
- **系统可靠性**:适当清理避免bug和性能损耗
|
|
|
|
|
|
### 步骤6:实现游戏重置功能
|
|
|
|
|
|
重置系统需彻底清理当前游戏状态,初始化清新的游戏会话。保证玩家得到干净的开局,无前一局遗留数据。
|
|
|
|
|
|
**创建 `resetGame()` 函数:**
|
|
|
|
|
|
```javascript
|
|
|
function resetGame() {
|
|
|
if (gameLoopId) {
|
|
|
clearInterval(gameLoopId);
|
|
|
eventEmitter.clear();
|
|
|
initGame();
|
|
|
gameLoopId = setInterval(() => {
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
ctx.fillStyle = "black";
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
drawPoints();
|
|
|
drawLife();
|
|
|
updateGameObjects();
|
|
|
drawGameObjects(ctx);
|
|
|
}, 100);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**逐部分理解:**
|
|
|
- **检查** 当前是否有游戏循环正在运行
|
|
|
- **清除** 现有游戏循环以停止所有当前游戏活动
|
|
|
- **移除** 所有事件监听器防止内存泄漏
|
|
|
- **重新初始化** 游戏状态,生成全新对象和变量
|
|
|
- **启动** 新的游戏循环,包含必需的游戏逻辑
|
|
|
- **保持** 100毫秒间隔,确保游戏性能一致
|
|
|
|
|
|
**将回车键事件处理器添加到 `initGame()` 函数:**
|
|
|
|
|
|
```javascript
|
|
|
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
|
|
|
resetGame();
|
|
|
});
|
|
|
```
|
|
|
|
|
|
**为你的 EventEmitter 类添加 `clear()` 方法:**
|
|
|
|
|
|
```javascript
|
|
|
clear() {
|
|
|
this.listeners = {};
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**关键点总结:**
|
|
|
- **将** 回车键按下连接到重置游戏功能
|
|
|
- **在** 游戏初始化时注册此事件监听器
|
|
|
- **提供** 一种清理所有事件监听器的干净方式,用于重置
|
|
|
- **通过** 清空事件处理器避免内存泄漏
|
|
|
- **将** 监听器对象重置为空以便重新初始化
|
|
|
|
|
|
## 恭喜!🎉
|
|
|
|
|
|
👽 💥 🚀 你成功从零构建了完整的游戏。像1970年代首批电子游戏程序员一样,你把代码变成了带有合适游戏机制和用户反馈的互动体验。🚀 💥 👽
|
|
|
|
|
|
**你完成了:**
|
|
|
- **实现** 完整的胜负条件并向用户反馈
|
|
|
- **创建** 无缝重启系统,支持连续游戏
|
|
|
- **设计** 明确的游戏状态视觉传达
|
|
|
- **管理** 复杂的游戏状态切换和清理
|
|
|
- **整合** 所有组件成一个连贯、可玩的游戏
|
|
|
|
|
|
### 🔄 **教学反馈点**
|
|
|
**完整游戏开发系统**:庆祝你掌握了完整开发流程:
|
|
|
- ✅ 结束条件如何创造满足感玩家体验?
|
|
|
- ✅ 为什么正确的状态管理对游戏稳定性关键?
|
|
|
- ✅ 视觉反馈如何增强玩家理解?
|
|
|
- ✅ 重启系统在玩家留存中扮演什么角色?
|
|
|
|
|
|
**系统掌控力**:你的完整游戏展现了:
|
|
|
- **全栈游戏开发**:涵盖图形、输入和状态管理
|
|
|
- **专业架构**:事件驱动系统加上适当清理
|
|
|
- **用户体验设计**:清晰反馈与直观控制
|
|
|
- **性能优化**:高效渲染和内存管理
|
|
|
- **打磨完善**:所有细节让游戏感觉完美
|
|
|
|
|
|
**行业级技能**:你实现了:
|
|
|
- **游戏循环架构**:实时系统性能稳定
|
|
|
- **事件驱动编程**:解耦系统便于扩展
|
|
|
- **状态管理**:复杂数据处理与生命周期管控
|
|
|
- **用户界面设计**:清晰沟通与响应式操作
|
|
|
- **测试与调试**:迭代开发和问题解决
|
|
|
|
|
|
### ⚡ **下5分钟你可以做什么**
|
|
|
- [ ] 玩你的完整游戏,测试所有胜负条件
|
|
|
- [ ] 尝试修改不同结束条件参数
|
|
|
- [ ] 加入 console.log 语句追踪游戏状态变化
|
|
|
- [ ] 与朋友分享游戏并收集反馈
|
|
|
|
|
|
### 🎯 **这小时你可以完成什么**
|
|
|
- [ ] 完成课后测验,反思你的开发历程
|
|
|
- [ ] 为胜负状态添加音效效果
|
|
|
- [ ] 实现额外的结束条件,如限时或奖励目标
|
|
|
- [ ] 制作不同难度,调整敌人数量
|
|
|
- [ ] 美化视觉效果,使用更好字体和颜色
|
|
|
|
|
|
### 📅 **你的周度游戏开发精通计划**
|
|
|
- [ ] 完成增强版太空游戏,多关卡和进度系统
|
|
|
- [ ] 增加高级功能,如增强道具、多样敌人类型和特殊武器
|
|
|
- [ ] 创建保存的高分榜系统
|
|
|
- [ ] 设计菜单、设置和选项的用户界面
|
|
|
- [ ] 优化性能,适配不同设备和浏览器
|
|
|
- [ ] 在线部署你的游戏,与社区分享
|
|
|
### 🌟 **您的一个月游戏开发职业规划**
|
|
|
- [ ] 制作多个完整游戏,探索不同的类型和机制
|
|
|
- [ ] 学习高级游戏开发框架,如 Phaser 或 Three.js
|
|
|
- [ ] 参与开源游戏开发项目的贡献
|
|
|
- [ ] 研究游戏设计原则和玩家心理
|
|
|
- [ ] 创建展示您游戏开发技能的作品集
|
|
|
- [ ] 与游戏开发社区连接,持续学习
|
|
|
|
|
|
## 🎯 您的完整游戏开发精通时间表
|
|
|
|
|
|
```mermaid
|
|
|
timeline
|
|
|
title 完整游戏开发学习进度
|
|
|
|
|
|
section 基础(课程1-2)
|
|
|
游戏架构:项目结构
|
|
|
:资源管理
|
|
|
:画布基础
|
|
|
:事件系统
|
|
|
|
|
|
section 交互系统(课程3-4)
|
|
|
玩家控制:输入处理
|
|
|
:移动机制
|
|
|
:碰撞检测
|
|
|
:物理模拟
|
|
|
|
|
|
section 游戏机制(课程5)
|
|
|
反馈系统:得分机制
|
|
|
:生命管理
|
|
|
:视觉传达
|
|
|
:玩家激励
|
|
|
|
|
|
section 游戏完成(课程6)
|
|
|
精细化与流程:结束条件
|
|
|
:状态管理
|
|
|
:重启系统
|
|
|
:用户体验
|
|
|
|
|
|
section 高级功能(一周)
|
|
|
增强技能:音频整合
|
|
|
:视觉特效
|
|
|
:关卡进度
|
|
|
:性能优化
|
|
|
|
|
|
section 职业发展(一月)
|
|
|
行业准备:框架精通
|
|
|
:团队协作
|
|
|
:作品集开发
|
|
|
:社区参与
|
|
|
|
|
|
section 职业晋升(三月)
|
|
|
专业化:高级游戏引擎
|
|
|
:平台部署
|
|
|
:盈利策略
|
|
|
:行业网络
|
|
|
```
|
|
|
### 🛠️ 您的完整游戏开发工具包总结
|
|
|
|
|
|
完成整个太空游戏系列后,您现已掌握:
|
|
|
- **游戏架构**:事件驱动系统、游戏循环和状态管理
|
|
|
- **图形编程**:Canvas API,精灵渲染和视觉效果
|
|
|
- **输入系统**:键盘处理、碰撞检测和响应式控制
|
|
|
- **游戏设计**:玩家反馈、进度系统和参与机制
|
|
|
- **性能优化**:高效渲染、内存管理和帧率控制
|
|
|
- **用户体验**:清晰沟通、直观控制和细节打磨
|
|
|
- **专业模式**:整洁代码、调试技术和项目组织
|
|
|
|
|
|
**现实应用**:您的游戏开发技能直接适用于:
|
|
|
- **交互式网络应用**:动态界面和实时系统
|
|
|
- **数据可视化**:动画图表和交互图形
|
|
|
- **教育技术**:游戏化和吸引人的学习体验
|
|
|
- **移动开发**:基于触摸的交互和性能优化
|
|
|
- **模拟软件**:物理引擎和实时建模
|
|
|
- **创意产业**:互动艺术、娱乐和数字体验
|
|
|
|
|
|
**获得的专业技能**:您现在可以:
|
|
|
- **架构**复杂的交互系统从零开始
|
|
|
- **调试**使用系统化方法的实时应用
|
|
|
- **优化**性能以流畅用户体验
|
|
|
- **设计**吸引人的用户界面和交互模式
|
|
|
- **高效协作**处理技术项目并合理组织代码
|
|
|
|
|
|
**掌握的游戏开发概念**:
|
|
|
- **实时系统**:游戏循环、帧率管理和性能
|
|
|
- **事件驱动架构**:解耦系统和消息传递
|
|
|
- **状态管理**:复杂数据处理和生命周期管理
|
|
|
- **用户界面编程**:Canvas 图形和响应式设计
|
|
|
- **游戏设计理论**:玩家心理和参与机制
|
|
|
|
|
|
**下一阶段**:您已准备好探索高级游戏框架、3D 图形、多玩家系统,或转向专业游戏开发职位!
|
|
|
|
|
|
🌟 **成就解锁**:您已完成完整的游戏开发旅程,从零打造出专业品质的互动体验!
|
|
|
|
|
|
**欢迎加入游戏开发社区!** 🎮✨
|
|
|
|
|
|
## GitHub Copilot Agent 挑战 🚀
|
|
|
|
|
|
使用 Agent 模式完成以下挑战:
|
|
|
|
|
|
**描述:** 通过实现一个关卡进度系统,增加难度和奖励功能,增强太空游戏。
|
|
|
|
|
|
**提示:** 创建一个多关卡太空游戏系统,每个关卡中敌舰数量增加,速度和生命值也提高。增加随关卡增长的得分倍增器,并实现当敌人被摧毁时随机出现的强化道具(如速射或护盾)。包括关卡完成奖励,并在屏幕上显示当前关卡与现有的得分和生命值。
|
|
|
|
|
|
了解更多关于[agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)的信息。
|
|
|
|
|
|
## 🚀 可选增强挑战
|
|
|
|
|
|
**给您的游戏添加音效**:通过实现音效来增强游戏体验!考虑添加以下音效:
|
|
|
|
|
|
- **激光射击**,玩家开火时
|
|
|
- **敌舰摧毁**,敌舰被击中时
|
|
|
- **英雄受伤**,玩家受击时
|
|
|
- **胜利音乐**,游戏胜利时
|
|
|
- **失败音效**,游戏失败时
|
|
|
|
|
|
**音频实现示例:**
|
|
|
|
|
|
```javascript
|
|
|
// 创建音频对象
|
|
|
const laserSound = new Audio('assets/laser.wav');
|
|
|
const explosionSound = new Audio('assets/explosion.wav');
|
|
|
|
|
|
// 在游戏事件中播放声音
|
|
|
function playLaserSound() {
|
|
|
laserSound.currentTime = 0; // 重置到开始处
|
|
|
laserSound.play();
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**您需要了解:**
|
|
|
- **创建**用于不同音效的 Audio 对象
|
|
|
- **重置**`currentTime`以支持快速连续音效播放
|
|
|
- **处理**浏览器自动播放策略,通过用户交互触发声音
|
|
|
- **管理**音量和时序以提升游戏体验
|
|
|
|
|
|
> 💡 **学习资源**:探索这个[audio sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play),了解更多 JavaScript 游戏中音频实现。
|
|
|
|
|
|
## 课后测验
|
|
|
|
|
|
[课后测验](https://ff-quizzes.netlify.app/web/quiz/40)
|
|
|
|
|
|
## 复习与自学
|
|
|
|
|
|
您的作业是创建一个新的示例游戏,您可以探索一些有趣的游戏,看看想制作哪种类型的游戏。
|
|
|
|
|
|
## 任务
|
|
|
|
|
|
[构建示例游戏](assignment.md)
|
|
|
|
|
|
---
|
|
|
|
|
|
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
|
|
|
**免责声明**:
|
|
|
本文件由 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译而成。尽管我们努力确保准确性,但请注意自动翻译可能包含错误或不准确之处。原始文本的原文版本应视为权威来源。对于重要信息,建议采用专业人工翻译。对于因使用本翻译而产生的任何误解或曲解,我们概不负责。
|
|
|
<!-- CO-OP TRANSLATOR DISCLAIMER END --> |