|
|
7 months ago | |
|---|---|---|
| .. | ||
| README.md | 7 months ago | |
| assignment.md | 7 months ago | |
README.md
构建一个太空游戏 第一部分: 介绍
journey
title 你的游戏开发旅程
section 基础
Learn game architecture: 3: Student
Understand inheritance: 4: Student
Explore composition: 4: Student
section 通信
Build pub/sub system: 4: Student
Design event flow: 5: Student
Connect components: 5: Student
section 应用
Create game objects: 5: Student
Implement patterns: 5: Student
Plan game structure: 5: Student
就像美国宇航局的任务控制中心在发射过程中协调多个系统一样,我们将构建一个太空游戏,展示程序的不同部分如何无缝协作。在创建一个你可以实际玩的项目时,你将学习适用于任何软件项目的基本编程概念。
我们将探索组织代码的两种基本方法:继承和组合。这不仅仅是学术概念——它们是驱动从视频游戏到银行系统所有内容的相同模式。我们还将实现一个名为发布/订阅(pub/sub)的通信系统,它类似于航天器中使用的通信网络,允许不同组件共享信息而不产生依赖关系。
通过本系列课程的结束,你将理解如何构建可扩展和演进的应用程序——无论你是开发游戏、Web应用程序,还是任何其他软件系统。
mindmap
root((游戏架构))
Object Organization
继承
组合
类层次结构
行为混合
Communication Patterns
发布/订阅系统
事件发射器
消息传递
松耦合
Game Objects
属性 (x, y)
行为 (移动, 碰撞)
生命周期管理
状态管理
Design Patterns
工厂函数
观察者模式
组件系统
事件驱动架构
Scalability
模块化设计
可维护代码
测试策略
性能优化
课前测验
游戏开发中的继承与组合
随着项目复杂度增大,代码组织变得尤为关键。最初简单的脚本如果没有良好的结构,维护会变得困难——就像阿波罗任务需要对上千个组件进行细致协作一样。
我们将探索两种组织代码的基本方式:继承和组合。每种都有独特的优点,理解它们有助于你在不同情况下选择合适的方式。我们将通过太空游戏进行演示,其中英雄、敌人、道具以及其他对象必须高效互动。
✅ 一本最著名的编程书籍就讲述了设计模式。
在任何游戏中,你都有 游戏对象——填充游戏世界的交互元素。英雄、敌人、道具和视觉特效都是游戏对象。它们都存在于特定屏幕坐标,用 x 和 y 值表示,类似于在坐标平面上绘点。
尽管外观不同,这些对象通常共享基本行为:
- 它们存在于某处 —— 每个对象都有 x 和 y 坐标,游戏才能知道在哪里绘制它
- 许多对象可以移动 —— 英雄奔跑,敌人追逐,子弹飞过屏幕
- 它们有生命周期 —— 有些永久存在,另一些(比如爆炸)短暂出现后消失
- 它们对事件做出反应 —— 碰撞发生时,道具被拾取,血条更新
✅ 想想像吃豆人这样的游戏。你能在游戏中识别上面提到的四种对象类型吗?
classDiagram
class GameObject {
+x: number
+y: number
+type: string
+exists_somewhere()
}
class MovableObject {
+moveTo(x, y)
+can_move_around()
}
class TemporaryObject {
+lifespan: number
+has_lifespan()
}
class InteractiveObject {
+onCollision()
+reacts_to_stuff()
}
GameObject <|-- MovableObject
GameObject <|-- TemporaryObject
GameObject <|-- InteractiveObject
MovableObject <|-- Hero
MovableObject <|-- Enemy
MovableObject <|-- Bullet
TemporaryObject <|-- PowerUp
TemporaryObject <|-- Explosion
InteractiveObject <|-- Collectible
InteractiveObject <|-- Obstacle
通过代码表达行为
现在你理解了游戏对象共有的行为,让我们探索如何用 JavaScript 实现这些行为。你可以通过添加到类或单个对象的方法来表达对象行为,且有多种实现方式。
基于类的方法
类和继承为组织游戏对象提供了一种结构化的方式。就像卡尔·林奈所构建的生物分类系统一样,你从一个含有共有属性的基础类开始,然后创建派生类继承这些基本功能并添加特定能力。
✅ 继承是一个重要概念,更多内容请见MDN关于继承的文章。
下面是如何使用类和继承实现游戏对象的示例:
// 第一步:创建基础的 GameObject 类
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
我们逐步拆解理解:
- 我们创建了一个基础模板,所有游戏对象都能使用它
- 构造函数保存对象的位置(
x,y)和类型 - 这成为所有游戏对象构建的基础
// 第2步:通过继承添加移动功能
class Movable extends GameObject {
constructor(x, y, type) {
super(x, y, type); // 调用父类构造函数
}
// 添加移动到新位置的能力
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
在上例中,我们:
- 扩展了 GameObject 类以添加移动功能
- 使用
super()调用父类构造函数初始化继承的属性 - 添加了
moveTo()方法来更新对象位置
// 第3步:创建特定的游戏对象类型
class Hero extends Movable {
constructor(x, y) {
super(x, y, 'Hero'); // 自动设置类型
}
}
class Tree extends GameObject {
constructor(x, y) {
super(x, y, 'Tree'); // 树木不需要移动
}
}
// 第4步:使用您的游戏对象
const hero = new Hero(0, 0);
hero.moveTo(5, 5); // 英雄可以移动!
const tree = new Tree(10, 15);
// tree.moveTo() 会导致错误——树木不能移动
理解这些概念:
- 创建继承适当行为的专门对象类型
- 展示继承如何选择性包含功能
- 说明英雄可以移动,而树木保持静止
- 演示类层级防止不恰当操作
✅ 花几分钟重新构思吃豆人中的英雄(比如 Inky、Pinky 或 Blinky)如何用 JavaScript 编写。
组合方法
组合遵循模块化设计理念,类似于工程师设计航天器时使用可互换组件。你不是继承父类,而是组合特定行为来创建具有所需功能的对象。这种方法提供了灵活性,不受严格层级限制。
// 第一步:创建基础行为对象
const gameObject = {
x: 0,
y: 0,
type: ''
};
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
};
这段代码的作用:
- 定义了一个带有位置和类型的基础
gameObject - 创建了一个独立的
movable行为对象,实现移动功能 - 分离了位置数据和移动逻辑,使职责独立
// 第2步:通过组合行为来组合对象
const movableObject = { ...gameObject, ...movable };
// 第3步:为不同类型的对象创建工厂函数
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
};
}
function createStatic(x, y, type) {
return {
...gameObject,
x,
y,
type
};
}
在上面,我们:
- 通过扩展语法组合基础对象属性和移动行为
- 创建工厂函数返回定制对象
- 实现了无需复杂类层级即可灵活创建对象
- 使对象恰好拥有所需行为
// 第4步:创建并使用你组合的对象
const hero = createHero(10, 10);
hero.moveTo(5, 5); // 工作完美!
const tree = createStatic(0, 0, 'Tree');
// tree.moveTo() 未定义 - 未组合任何移动行为
记住关键点:
- 通过混合行为组合对象,而非继承
- 提供比严格继承层级更大的灵活性
- 允许对象恰好拥有它们需要的特性
- 使用现代 JavaScript 扩展语法实现干净合并
**Which Pattern Should You Choose?**
**Which Pattern Should You Choose?**
```mermaid
quadrantChart
title Code Organization Patterns
x-axis Simple --> Complex
y-axis Rigid --> Flexible
quadrant-1 Advanced Composition
quadrant-2 Hybrid Approaches
quadrant-3 Basic Inheritance
quadrant-4 Modern Composition
Class Inheritance: [0.3, 0.2]
Interface Implementation: [0.6, 0.4]
Mixin Patterns: [0.7, 0.7]
Pure Composition: [0.8, 0.9]
Factory Functions: [0.5, 0.8]
Prototype Chain: [0.4, 0.3]
💡 专业提示:两种模式在现代 JavaScript 开发中各有用武之地。类适合明确定义的层级结构,而组合在需要最大灵活性时表现出色。
何时使用:
- 当存在明确的“是一个”关系时选择继承(例如,英雄 是一个 可移动对象)
- 当存在“有一个”关系时选择组合(例如,英雄 拥有 移动能力)
- 考虑团队偏好和项目需求
- 记住可以在同一应用中混合使用两者
🔄 教学自检
对象组织理解:在进入通信模式之前,确保你能:
- ✅ 解释继承与组合的区别
- ✅ 识别何时使用类还是工厂函数
- ✅ 理解继承中
super()关键字的作用 - ✅ 认识每种方法对游戏开发的优势
快速自测:你如何创建一个既可移动又能飞行的敌人?
- 继承方法:
class FlyingEnemy extends Movable - 组合方法:
{ ...movable, ...flyable, ...gameObject }
现实联系:这些模式无处不在:
- React 组件:属性(组合)与类继承
- 游戏引擎:实体组件系统使用组合
- 移动应用:UI 框架常用继承层级
通信模式:发布/订阅系统
随着应用复杂度提升,管理组件间通信变得困难。发布-订阅模式(pub/sub)用类似广播的原理解决这个问题——一个发射器能触达多个接收器,无需知道谁在监听。
考虑英雄受伤时的情况:血条更新,音效播放,视觉反馈出现。英雄对象不用直接耦合这些系统,而是广播一个“受伤”消息。任何需要响应的系统都能订阅该消息并做出反应。
✅ Pub/Sub 代表“发布-订阅”
flowchart TD
A[英雄受到伤害] --> B[发布:HERO_DAMAGED]
B --> C[事件系统]
C --> D[生命值条订阅者]
C --> E[声音系统订阅者]
C --> F[视觉效果订阅者]
C --> G[成就系统订阅者]
D --> H[更新生命值显示]
E --> I[播放伤害音效]
F --> J[显示红色闪光]
G --> K[检查存活成就]
style A fill:#ffebee
style B fill:#e1f5fe
style C fill:#e8f5e8
style H fill:#fff3e0
style I fill:#fff3e0
style J fill:#fff3e0
style K fill:#fff3e0
理解 Pub/Sub 架构
pub/sub 模式使应用的不同部分松散耦合,即它们能协作而不直接依赖彼此。这种分离让代码更易维护、测试和灵活应对变化。
pub/sub 的关键角色:
- 消息 —— 简单的文本标签,例如
'PLAYER_SCORED',描述发生的事件(含额外信息) - 发布者 —— 向所有监听者“大声宣布”发生了什么
- 订阅者 —— 说“我关注这个事件”,并在事件发生时响应
- 事件系统 —— 确保消息送达正确监听者的中间人
构建事件系统
让我们创建一个简单而强大的事件系统,演示这些概念:
// 第一步:创建 EventEmitter 类
class EventEmitter {
constructor() {
this.listeners = {}; // 存储所有事件监听器
}
// 为特定消息类型注册监听器
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
// 向所有注册的监听器发送消息
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(listener => {
listener(message, payload);
});
}
}
}
拆解发生的事情:
- 创建一个简单类实现的中心事件管理系统
- 按消息类型在对象中存储监听器
- 使用
on()方法注册新监听器 - 用
emit()方法广播消息到所有感兴趣的监听者 - 支持可选的数据负载以传递相关信息
综合示例演示
好,让我们看看实际应用!我们构建一个简单的移动系统,展示 pub/sub 的清晰灵活性:
// 第一步:定义你的消息类型
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT',
HERO_MOVE_RIGHT: 'HERO_MOVE_RIGHT',
ENEMY_SPOTTED: 'ENEMY_SPOTTED'
};
// 第二步:创建你的事件系统和游戏对象
const eventEmitter = new EventEmitter();
const hero = createHero(0, 0);
这段代码作用:
- 定义常量对象防止消息名拼写错误
- 创建事件发射器实例处理所有通信
- 初始化位于起始位置的英雄对象
// 第三步:设置事件监听器(订阅者)
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.moveTo(hero.x - 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
eventEmitter.on(Messages.HERO_MOVE_RIGHT, () => {
hero.moveTo(hero.x + 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
上述代码实现:
- 注册响应移动消息的事件监听器
- 根据移动方向更新英雄位置
- 添加控制台日志跟踪英雄位置变化
- 移动逻辑与输入处理分离
// 第4步:将键盘输入连接到事件(发布者)
window.addEventListener('keydown', (event) => {
switch(event.key) {
case 'ArrowLeft':
eventEmitter.emit(Messages.HERO_MOVE_LEFT);
break;
case 'ArrowRight':
eventEmitter.emit(Messages.HERO_MOVE_RIGHT);
break;
}
});
进一步理解:
- 连接键盘输入与游戏事件,避免紧耦合
- 使输入系统可间接与游戏对象通信
- 允许多个系统响应同一键盘事件
- 便于更改键绑定或添加新输入方法
sequenceDiagram
participant User
participant Keyboard
participant EventEmitter
participant Hero
participant SoundSystem
participant Camera
User->>Keyboard: 按下 左箭头
Keyboard->>EventEmitter: emit('HERO_MOVE_LEFT')
EventEmitter->>Hero: 向左移动5像素
EventEmitter->>SoundSystem: 播放脚步声
EventEmitter->>Camera: 跟随英雄
Hero->>Hero: 更新位置
SoundSystem->>SoundSystem: 播放音频
Camera->>Camera: 调整视口
💡 专业提示:此模式的美妙在于灵活性!你只需添加更多事件监听器即可添加音效、屏幕抖动或粒子效果——无需修改现有的键盘或移动代码。
你会喜欢此方法的原因:
- 新功能添加变得非常简单——只需监听感兴趣事件
- 多个模块可响应同一事件,互不干扰
- 测试更加简单,因为各部分独立工作
- 出现问题时,知道准确排查位置
Pub/Sub 为什么能有效扩展
随着应用复杂性增加,pub/sub 模式保持简洁。无论是管理数十个敌人、动态 UI 更新还是音效系统,该模式在不改变架构的情况下处理规模增大。新功能可无缝集成进现有事件系统,不影响已建立功能。
⚠️ 常见错误:早期不要创建过多具体消息类型。先用大类分类,随着游戏需求明确再细化。
最佳实践:
- 将相关消息分组归类
- 使用描述性名称清晰表达事件
- 保持消息负载简单聚焦
- 为团队协作记录消息类型文档
🔄 教学自检
事件驱动架构理解:确认你掌握整体系统:
- ✅ pub/sub 模式如何防止组件间紧耦合?
- ✅ 为什么事件驱动架构更易添加新功能?
- ✅ EventEmitter 在通信流程中起什么作用?
- ✅ 消息常量如何防止错误并提升可维护性?
设计挑战:你如何用 pub/sub 处理这些游戏场景?
- 敌人死亡:更新得分,播放音效,生成道具,移除屏幕
- 关卡完成:停止音乐,显示 UI,保存进度,加载下一关
- 道具收集:增强能力,更新 UI,播放效果,启动计时
专业联系:此模式应用于:
- 前端框架:React/Vue 事件系统
- 后端服务:微服务通信
- 游戏引擎:Unity 事件系统
- 移动开发:iOS/Android 通知系统
GitHub Copilot Agent 挑战 🚀
使用 Agent 模式完成以下挑战:
描述: 使用继承和发布/订阅模式创建一个简单的游戏对象系统。你将实现一个基本游戏,其中不同对象通过事件进行通信而无需直接了解彼此。
提示: 创建一个 JavaScript 游戏系统,要求如下:1)创建一个基础 GameObject 类,含 x, y 坐标和 type 属性。2)创建一个继承自 GameObject 且可以移动的 Hero 类。3)创建一个继承自 GameObject 且能追踪英雄的 Enemy 类。4)实现一个用于发布/订阅模式的 EventEmitter 类。5)设置事件监听器,当英雄移动时,附近敌人接收 'HERO_MOVED' 事件并更新位置向英雄移动。使用 console.log 展示对象间的通信。
了解更多Agent 模式。
🚀 挑战
考虑发布-订阅模式如何增强游戏架构。确定哪些组件应发出事件以及系统应如何响应。设计一个游戏概念并绘制其组件之间的通信模式。
课后测验
复习与自学
通过阅读相关内容深入了解发布/订阅。
⚡ 接下来5分钟你可以做的事情
- 打开任何在线HTML5游戏,使用开发者工具检查其代码
- 创建一个简单的HTML5 Canvas元素并绘制基本形状
- 尝试使用
setInterval创建简单动画循环 - 浏览Canvas API文档,尝试某个绘图方法
🎯 本小时你可以完成的任务
- 完成课后测验并理解游戏开发概念
- 搭建包含HTML、CSS和JavaScript文件的游戏项目结构
- 创建一个持续更新和渲染的基础游戏循环
- 在画布上绘制你的第一个游戏精灵
- 实现基础的图片和声音资源加载
📅 你的一周游戏创作计划
- 完成包含所有计划功能的完整太空游戏
- 添加精细的图形、音效和流畅动画
- 实现游戏状态(开始界面、游戏中、游戏结束)
- 创建计分系统和玩家进度追踪
- 让游戏响应式兼容多设备
- 在线分享游戏并收集玩家反馈
🌟 你的一月游戏开发计划
- 开发多个不同类型和机制的游戏
- 学习使用像Phaser或Three.js这样的游戏开发框架
- 参与开源游戏开发项目
- 精通高级游戏编程模式和优化技巧
- 创建展示游戏开发技能的作品集
- 指导对游戏开发和互动媒体感兴趣的新人
🎯 你的游戏开发精通时间表
timeline
title 游戏架构学习进度
section 对象模式(20分钟)
代码组织:类继承
:组合模式
:工厂函数
:行为混合
section 通信系统(25分钟)
事件架构:发布/订阅实现
:消息设计
:事件发射器
:松耦合
section 游戏对象设计(30分钟)
实体系统:属性管理
:行为组合
:状态处理
:生命周期管理
section 架构模式(35分钟)
系统设计:组件系统
:观察者模式
:命令模式
:状态机
section 高级概念(45分钟)
可扩展架构:性能优化
:内存管理
:模块化设计
:测试策略
section 游戏引擎概念(1周)
专业开发:场景图
:资源管理
:渲染管线
:物理整合
section 框架精通(2周)
现代游戏开发:React游戏模式
:Canvas优化
:WebGL基础
:PWA游戏
section 行业实践(1个月)
专业技能:团队协作
:代码审查
:游戏设计模式
:性能剖析
🛠️ 你的游戏架构工具包总结
完成本课后,你已有:
- 设计模式精通:理解继承与组合的权衡
- 事件驱动架构:发布/订阅实现的可扩展通信
- 面向对象设计:类层级和行为组合
- 现代JavaScript:工厂函数、展开语法以及ES6+模式
- 可扩展架构:低耦合和模块化设计原则
- 游戏开发基础:实体系统和组件模式
- 专业模式:业界标准代码组织方法
现实应用场景:这些模式适用于:
- 前端框架:React/Vue组件架构和状态管理
- 后端服务:微服务通信和事件驱动系统
- 移动开发:iOS/Android应用架构和通知系统
- 游戏引擎:Unity、Unreal及基于Web的游戏开发
- 企业软件:事件溯源和分布式系统设计
- API设计:RESTful服务和实时通信
获得的专业技能:你现在能:
- 设计可扩展的软件架构,使用成熟的设计模式
- 实现能够处理复杂交互的事件驱动系统
- 选择适合不同场景的代码组织策略
- 调试并维护低耦合系统
- 沟通使用业界标准术语表达技术决策
下一步:你已准备好在实际游戏中实现这些模式,探索高级游戏开发主题,或将这些架构理念应用于网页应用!
🌟 成就解锁:你已掌握支持从简单游戏到复杂企业系统的基础软件架构模式!
任务
免责声明: 本文件由 AI 翻译服务 Co-op Translator 翻译而成。尽管我们力求准确,但请注意自动翻译可能存在错误或不准确之处。原始语言版本的文件应被视为权威来源。对于重要信息,建议使用专业人工翻译。我们不对因使用本翻译而产生的任何误解或误释承担责任。
