
用继承、组合与 Pub/Sub 构建太空游戏Web-Dev-For-Beginners 游戏开发系列入门篇【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners本文将带你进入 Web-Dev-For-Beginners 项目中构建太空游戏Build a Space Game六课系列的第一课以浏览器 JavaScript 为语言环境系统讲解游戏对象Game Object的两种代码组织方式——类继承与组合以及让游戏组件松耦合协作的发布-订阅Pub/Sub通信模式。学完本篇你将掌握游戏对象的基础建模、EventEmitter 事件系统的完整实现并能结合仓库内完整的太空游戏源码理解这些模式在真实游戏如本仓库的 Space Invaders 风格游戏中是如何落地的。本课在太空游戏系列中的位置本仓库的太空游戏系列共六课本课是纯理论奠基课后续五课全部是动手实践本课1-introduction继承、组合与 Pub/Sub 架构模式绘制到画布与 Canvas 元素交互移动画面元素笛卡尔坐标系与对象移动碰撞检测对象相交判断计分利用 Async/Await 加载资源结束与重开游戏状态管理正如 NASA 任务控制中心在发射时协调多个系统太空游戏同样需要让玩家、敌人、子弹、爆炸粒子等不同部分无缝协作。本课学习的抽象模式是理解后续 Canvas 绘制、键盘事件、碰撞检测等具体实现的理论前提。游戏对象一切从坐标开始任何游戏中都存在game objects游戏对象——填充游戏世界的交互元素。英雄、敌人、道具、视觉特效都是游戏对象。每个对象都通过x、y坐标存在于屏幕上的特定位置类似于在坐标平面上标注点。尽管外观千差万别这些对象通常共享四类基础行为存在于某处——每个对象都有 x、y 坐标游戏据此决定在哪里绘制它多数可以移动——英雄奔跑、敌人追击、子弹划过屏幕拥有生命周期——有些常驻另一些如爆炸特效短暂出现后消失对外界作出反应——发生碰撞时道具被拾取、血条被更新。可以用一张类图Mermaid classDiagram概括这些共性GameObject作为基类持有x、y、type属性派生出MovableObject可移动、TemporaryObject临时对象含 lifespan、InteractiveObject交互对象含 onCollision而 Hero、Enemy、Bullet 继承 MovableObjectPowerUp、Explosion 继承 TemporaryObjectCollectible、Obstacle 继承 InteractiveObject。这正是后续所有对象建模的蓝本。类继承方式用is-a关系组织代码类与继承提供了一种结构化的对象组织方式。类似林奈的生物分类法你从一个包含公共属性的基类开始再创建继承这些基础并添加特定能力的子类。Step 1创建基类 GameObject// Krok 1: Vytvořte základní třídu GameObject class GameObject { constructor(x, y, type) { this.x x; this.y y; this.type type; } }构造函数保存对象位置x、y与种类type这是所有游戏对象向上构建的地基。Step 2通过继承添加移动能力class Movable extends GameObject { constructor(x, y, type) { super(x, y, type); // 调用父类构造函数 } // 添加移动到新位置的能力 moveTo(x, y) { this.x x; this.y y; } }这里完成了三件事用extends扩展 GameObject 增加移动功能用super()调用父类构造函数以初始化继承的属性新增moveTo()方法更新对象位置。super()是理解继承的关键——它负责把x、y、type在父类构造函数中正确初始化。Step 3创建具体游戏对象类型class Hero extends Movable { constructor(x, y) { super(x, y, Hero); // 自动设置 type } } class Tree extends GameObject { constructor(x, y) { super(x, y, Tree); // 树不需要移动 } } // Step 4: 使用你的游戏对象 const hero new Hero(0, 0); hero.moveTo(5, 5); // 英雄可以移动 const tree new Tree(10, 15); // tree.moveTo() 会报错——树不能移动这段示例展示了继承的核心价值选择性包含特性。Hero 因继承自 Movable 而获得moveTo()Tree 直接继承自 GameObject 则没有移动方法类层级天然阻止了不恰当的操作。仓库完整实现中Hero、Enemy、Laser同样继承自GameObject基类并各自携带width、height、img等属性和draw()、rectFromGameObject()方法见 6-end-condition/solution/app.js。组合方式用has-a关系按需组装行为组合遵循模块化设计哲学就像工程师用可更换部件设计航天器。它不依赖父类而是把具体行为拼装起来让对象恰好拥有所需功能规避僵化的层级约束。Step 1创建基础行为对象const gameObject { x: 0, y: 0, type: }; const movable { moveTo(x, y) { this.x x; this.y y; } };位置数据与移动逻辑被拆分为两个独立对象实现关注点分离。Step 2-3用 spread 语法组合 工厂函数生产对象// Step 2: 组合行为构建对象 const movableObject { ...gameObject, ...movable }; // Step 3: 为不同类型对象创建工厂函数 function createHero(x, y) { return { ...movableObject, x, y, type: Hero }; } function createStatic(x, y, type) { return { ...gameObject, x, y, type }; }Step 4创建并使用组合对象const hero createHero(10, 10); hero.moveTo(5, 5); // 完美工作 const tree createStatic(0, 0, Tree); // tree.moveTo() 未定义——没有组合进移动行为组合方式的关键点通过混合行为而非继承来组装对象比僵化的继承层级更灵活对象恰好拥有所需特性使用现代 JavaScript spread 语法实现干净的组合。如何选择继承还是组合两种模式在现代 JavaScript 开发中各有其位类适合明确定义的层级组合在需要最大灵活性时更胜一筹。实践中可参考象限图Class Inheritance 偏简单-僵化Pure Composition 偏复杂-灵活Factory Functions 与 Mixin Patterns 居于中位。选择依据可概括为存在清晰的is-a是什么关系时选继承——Hero是一个可移动对象需要has-a有什么关系时选组合——Hero拥有移动能力同时考虑团队偏好与项目需求两种方式可在同一应用中混用。自测题如何创建一个既能移动又能飞行的 FlyingEnemy继承方式class FlyingEnemy extends Movable组合方式{ ...movable, ...flyable, ...gameObject }。这些模式在现实世界无处不在React 组件的 props组合与类继承、游戏引擎的实体-组件系统组合、移动端 UI 框架继承层级。Pub/Sub 通信模式让组件互不相识也能协作随着应用复杂度上升组件间通信管理成为挑战。发布-订阅模式pub/sub即 publish-subscribe借鉴无线电广播的原理——一个发射器可以触达多个接收器而无需知道谁在收听。设想英雄受伤时的连锁反应血条更新、播放音效、出现视觉反馈。若把英雄对象直接耦合到这些系统代码将迅速腐化而 pub/sub 允许英雄仅广播一条受到伤害的消息任何需要响应的系统自行订阅该消息类型并作出反应。Pub/Sub 的四类角色消息Messages——描述发生了什么的简单文本标签如PLAYER_SCORED可附带额外信息发布者Publishers——向所有监听者喊话有事情发生了的对象订阅者Subscribers——声明我关心该事件并在其发生时反应的对象事件系统Event System——确保消息到达正确监听者的中间人。亲手实现一个 EventEmitter// Step 1: 创建 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()向所有感兴趣者广播消息支持可选的 payload 数据传递。综合示例基于事件的移动系统把前面学到的组合与 pub/sub 串起来构建一个完整的键盘移动示例// Step 1: 定义消息类型 const Messages { HERO_MOVE_LEFT: HERO_MOVE_LEFT, HERO_MOVE_RIGHT: HERO_MOVE_RIGHT, ENEMY_SPOTTED: ENEMY_SPOTTED }; // Step 2: 创建事件系统与游戏对象 const eventEmitter new EventEmitter(); const hero createHero(0, 0); // Step 3: 设置事件监听器订阅者 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}); }); // Step 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; } });用常量对象定义消息名可有效防止拼写错误键盘输入系统与游戏对象之间没有硬编码依赖多个系统可以响应同一键盘事件改键位或新增输入方式都无需触碰移动逻辑。消息常量message constants既防 bug 又提升可维护性。源码印证仓库中的真实 Pub/Sub 落地本课的理论模式并非纸上谈兵——仓库的太空游戏最终版就是完整的实践样本。在 6-end-condition/solution/app.js 中EventEmitter 与消息常量L4-L24、L135-L146class 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((l) l(message, payload)); } } clear() { this.listeners {}; } } const Messages { KEY_EVENT_ENTER: KEY_EVENT_ENTER, KEY_EVENT_UP: KEY_EVENT_UP, KEY_EVENT_DOWN: KEY_EVENT_DOWN, KEY_EVENT_LEFT: KEY_EVENT_LEFT, KEY_EVENT_RIGHT: KEY_EVENT_RIGHT, KEY_EVENT_SPACE: KEY_EVENT_SPACE, COLLISION_ENEMY_LASER: COLLISION_ENEMY_LASER, COLLISION_ENEMY_HERO: COLLISION_ENEMY_HERO, GAME_END_LOSS: GAME_END_LOSS, GAME_END_WIN: GAME_END_WIN, };对比可见这与本课教学的 EventEmitter 结构完全一致还额外提供了clear()用于重置监听器。发布端——键盘输入与碰撞检测L177-L191键盘事件被转换为KEY_EVENT_UP、KEY_EVENT_SPACE等语义化消息发布L214-L237 的updateGameObjects()用矩形相交检测intersectRect碰撞发生时发布COLLISION_ENEMY_LASER携带子弹与敌机两个 payload和COLLISION_ENEMY_HERO携带敌机。订阅端——游戏逻辑解耦L248-L300initGame()中集中注册所有订阅方向键移动英雄、空格触发开火受冷却时间cooldown控制、激光命中敌机则双方标记死亡并加分、敌机撞英雄则扣生命最终根据战况发布GAME_END_WIN或GAME_END_LOSS结束游戏。这个结构清晰地演示了本课的教学目标碰撞检测逻辑只负责发现事件并发布游戏规则只负责订阅事件并响应二者互不感知。想新增敌机死亡播放音效只需增加一个订阅者无需改动碰撞检测代码。完整太空游戏的架构印证除最终版外仓库根目录的 6-space-game/solution/app.js 还提供了一版功能更完整的现代实现同样印证本课的抽象游戏对象 数据 行为玩家飞船是携带x、y、r半径、angle、speed、vx、vy的对象L81-L89敌机、子弹、粒子分别以数组enemies、bullets、particles管理坐标驱动的世界更新update(dt)中基于dt每帧间隔被钳制在 33ms 内推进玩家、子弹、敌机位置用Math.atan2计算敌机追击玩家的角度L287-L415事件驱动输入keyMap把按键映射为语义动作wasd/方向键移动、空格开火、Shift 大招、P 暂停、F 全屏L119-L128keydown/keyup维护按键集合keys游戏循环读取后驱动状态更新——这正是输入系统与游戏逻辑松耦合的实践可配置的难度与画质difficultyTableeasy/normal/hard/insane 四档控制敌机生成速率、速度与上限L48-L53与graphicsTablelow/medium/high/ultra控制辉光、模糊、星空层数体现了参数驱动的模块化设计。可以看出无论哪一版实现坐标 行为 事件通信始终是贯穿始终的架构主线。设计挑战把模式用起来挑战 1用 pub/sub 处理以下游戏场景敌机死亡更新分数、播放音效、生成道具、从屏幕移除关卡完成停止音乐、显示 UI、保存进度、加载下一关拾取道具增强能力、更新 UI、播放特效、启动计时器。挑战 2GitHub Copilot Agent 模式用继承 pub/sub 实现一个简单游戏对象系统——创建带 x、y、type 的 GameObject 基类、可移动的 Hero 类、可追击英雄的 Enemy 类实现 EventEmitter并让英雄移动时附近的敌机收到HERO_MOVED事件向英雄靠拢用 console.log 展示对象间的通信。课后作业搭建一个游戏原型本课的配套作业详见 作业说明要求把模式付诸实践创建一个小游戏原型重点在架构与通信而非复杂画面。架构模式二选一Option A 类继承如GameObject→Movable→HeroOption B 组合工厂函数混合行为通信实现包含 EventEmitter 类定义 2-3 种消息类型如PLAYER_MOVE、ENEMY_SPAWN、SCORE_UPDATE把键盘/鼠标输入通过事件系统接入游戏必备元素至少一个玩家控制角色、至少一个其他游戏对象敌人/收集物/环境元素、对象间基础交互碰撞/收集/通信推荐选题贪吃蛇蛇身跟随蛇头、食物随机生成、乒乓变体挡板响应输入、球弹墙、收集游戏移动收集并躲避障碍、塔防基础塔检测并射击移动敌人验证标准事件触发时对象确实移动或变化多个对象能响应同一事件不修改既有代码即可新增行为输入正确触发游戏事件提交物JS 文件含实现、HTML 文件运行测试、注释说明所选模式及原因、消息类型文档。作业还提供了从基本继承/组合到三个以上事件交互元素 干净注释 现代 JavaScript的分档评分细则并设置了加分项创意玩法机制、多输入方式键盘和鼠标、易于扩展的可扩展架构。学习路线与能力清单本课给出的掌握路径约 20-45 分钟核心内容 长期拓展先掌握对象模式继承、组合、工厂函数、行为混合再学通信系统pub/sub 实现、消息设计、事件发射器、松耦合进而设计游戏对象属性管理、行为组合、状态处理、生命周期进阶到架构模式组件系统、观察者模式、命令模式、状态机最终触及引擎级概念场景图、资源管理、渲染管线、物理集成与框架掌握React 游戏模式、Canvas 优化、WebGL 基础、PWA 游戏。本课结束后你应具备设计模式掌控理解继承与组合的权衡事件驱动架构用 pub/sub 实现可扩展通信面向对象设计类层级与行为组合现代 JavaScript工厂函数、spread 语法与 ES6 模式可扩展架构松耦合与模块化设计原则游戏开发基础实体系统与组件模式。即练即用5 分钟快速实践用 DevTools 检查任意在线 HTML5 游戏的代码创建简单 Canvas 元素并绘制基础形状用setInterval搭建简单动画循环查阅 Canvas API 文档并尝试绘制方法。本课目标1 小时可达成完成课后测验理解游戏开发概念搭建 HTML/CSS/JS 游戏项目结构创建持续更新与渲染的基础游戏循环在画布上绘制第一批精灵实现图片与音效的基础资源加载。一周目标完成带全部功能的完整太空游戏加入精良图形、音效与流畅动画实现游戏状态开始/进行中/结束建立计分与进度追踪系统确保跨设备响应式与可访问性并分享在线收集玩家反馈。这些模式可直接迁移到前端框架React/Vue 组件架构与状态管理、后端服务微服务通信与事件驱动系统、移动开发iOS/Android 架构与通知系统、游戏引擎Unity、Unreal、Web 游戏、企业软件事件溯源、分布式系统与 API 设计RESTful 服务、实时通信。掌握它们你就握住了从简单游戏到复杂企业系统的通用架构钥匙。【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考