拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Web-Dev-For-Beginners 实战:用 JavaScript 继承与 Pub/Sub 模式构建太空游戏的第一课

Web-Dev-For-Beginners 实战用 JavaScript 继承与 Pub/Sub 模式构建太空游戏的第一课【免费下载链接】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」六课系列的开篇对应仓库 6-space-game/1-introduction/README.md本文章基于德语译本整理。读者将掌握两类最核心的代码组织方式——继承Inheritance与组合Composition——以及一套让组件互相通信却互不依赖的Pub/Sub 发布-订阅事件系统。学完本课你将能用纯 JavaScript 设计出可扩展、易维护的游戏对象模型并为后续在 Canvas 上绘制飞船、处理碰撞、计分打下架构基础。整个系列最终会产出一个类似《Space Invaders》的可玩太空射击游戏成品效果见 6-space-game/images/pewpew.gif六课完整清单见 6-space-game/README.md。课程定位软件架构不只是游戏的专利就像 NASA 的任务控制中心要在发射时协调多个子系统一个太空游戏也要让程序的各个部分无缝协作。本课讨论的两种代码组织方式——继承与组合——并不是学术概念它们正是从电子游戏到银行系统都在使用的真实模式而 Pub/Sub 通信系统的工作原理则类似航天器上的通信网络允许不同组件共享信息却不产生依赖。系列导航本课为 Theory理论部分后续 Practice实践部分包括 绘制到 Canvas、让元素移动、碰撞检测、计分 与 结束与重启游戏。游戏对象它们在屏幕上到底是什么在任何游戏中都存在game objects游戏对象——英雄、敌人、能量道具、视觉特效它们都通过x与y坐标存在于屏幕上就像在坐标系中描点。尽管外观千差万别这些对象往往共享四类基础行为它们存在于某处每个对象都有 x、y 坐标游戏据此决定在哪里绘制它许多对象可以移动英雄奔跑、敌人追击、子弹划过屏幕它们有生命周期有的常驻有的如爆炸短暂出现后消失它们会对事物作出反应碰撞发生时能量道具被收集、血条被更新。可以设想一下《Pac-Man》其中的幽灵、豆子、能量豆、水果分别对应上面哪一类对象在仓库的最终实现 6-space-game/solution/app.js 中可以看到真实落地玩家被建模为一个带x、y、半径r、速度speed和速度分量vx/vy的实体对象而子弹、敌人、粒子则各自用数组bullets、enemies、particles管理——这正是「对象有属性、有行为、有生命周期」的直观体现。用类与继承表达行为类的分类思想与林奈的生物分类系统类似先有一个包含公共属性的基类再由子类继承基础并追加特定能力。以 JavaScript 为例// 步骤 1创建基类 GameObject class GameObject { constructor(x, y, type) { this.x x; this.y y; this.type type; } }这个类保存了对象的位置x、y和类型type是所有游戏对象的共同地基。// 步骤 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()更新对象位置。// 步骤 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() 会报错 —— 树不能移动这一小段代码揭示了继承的四个要点创建继承合适行为的特化对象类型继承允许选择性加入功能类层级天然阻止不合适的操作树不暴露moveTo英雄可动而树保持静止。可以花几分钟尝试用 JavaScript 重新设计一个《Pac-Man》幽灵如 Inky、Pinky、Blinky。用组合与工厂函数表达行为组合遵循模块化设计哲学——像工程师用可互换组件设计航天器一样不靠继承父类而是把特定行为拼装成对象按需拥有恰好所需的功能摆脱僵化的层级约束。// 步骤 1创建基础行为对象 const gameObject { x: 0, y: 0, type: }; const movable { moveTo(x, y) { this.x x; this.y y; } };这里把位置数据与移动逻辑拆开各自独立。// 步骤 2用 Spread 语法组合对象 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() 是 undefined —— 没有组合进移动行为组合方案的记忆要点用行为混合behavior mixing而非继承来组装对象比僵化的继承层级更灵活让对象精确拥有所需功能依托现代 JavaScript 的 Spread 语法实现干净的对象合并。什么时候选哪种一个象限决策框架场景推荐方案原因存在清晰的「is-a」关系英雄是一个可移动对象继承class Hero extends Movable类层级天然表达父子关系需要「has-a」关系英雄拥有移动能力组合{ ...movable, ...flyable, ...gameObject }灵活拼装、避免深层级需要最大灵活性、频繁扩展行为组合 工厂函数无需改动已有类即可增删行为团队约定与项目规范明确依团队/项目而定两种模式可以在同一应用内混用从源码实现看6-space-game/solution/app.js 中的settings、difficultyTable、graphicsTable等配置对象正是「数据与行为分离、按需组合」思路的延续难度、画质等能力通过查表而非多层继承来实现。通信模式Pub/Sub 发布-订阅系统当应用变复杂组件间的通信管理会成为难题。发布-订阅模式Publish-Subscribe即 Pub/Sub借鉴无线电广播的原理——一个发射端可以到达多个接收端而无需知道谁在收听。设想英雄受伤血条要更新、音效要播放、视觉反馈要出现。与其让英雄对象直接耦合这些系统Pub/Sub 允许英雄广播一条「受到伤害」的消息任何需要响应的系统只需订阅该消息类型并做出反应。Pub/Sub 中的四个关键角色消息Messages简单的文本标签如PLAYER_SCORED描述发生了什么可附带额外信息发布者Publishers向所有听众喊出「有事发生了」的对象订阅者Subscribers声明「我关心这个事件」并在事件发生时做出反应的对象事件系统Event System确保消息送达正确听众的中介。这种模式让应用各部分松散耦合loosely coupled它们能协同工作却不互相依赖代码因此更易维护、更易测试、更抗变更。亲手构建 EventEmitter 事件系统下面这个简单却强大的事件系统完整演示了上述概念// 步骤 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参数用于传递相关信息。实战串联键盘输入 事件驱动移动把前面学到的概念组装成一个可运行的移动系统// 步骤 1定义消息类型常量可防止拼写错误 const Messages { HERO_MOVE_LEFT: HERO_MOVE_LEFT, HERO_MOVE_RIGHT: HERO_MOVE_RIGHT, ENEMY_SPOTTED: ENEMY_SPOTTED }; // 步骤 2创建事件系统与游戏对象 const eventEmitter new EventEmitter(); const hero createHero(0, 0); // 步骤 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}); }); // 步骤 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; } });这段代码的价值键盘输入与游戏事件之间无紧密耦合输入系统可间接与游戏对象通信多个系统能响应同一键盘事件而互不干扰更换按键映射或新增输入方式都很容易。这个模式在实际仓库中得到了充分印证6-space-game/solution/app.js 用Set维护按键状态、用keyMap映射 WASD/方向键/空格/Shift 等控制update(dt)见 6-space-game/solution/app.js再依据按键集合计算玩家速度并更新坐标主循环loop(ts)见 6-space-game/solution/app.js通过requestAnimationFrame驱动「更新世界 → 渲染」的每帧流程。输入、逻辑、渲染被拆成相互独立的部分这正是事件驱动与松散耦合在真实游戏中的体现。为什么 Pub/Sub 能良好扩展随着应用复杂度上升Pub/Sub 依然保持简单无论管理几十个敌人、动态 UI 更新还是音效系统模式都能承载规模增长而无需架构调整新功能接入既有事件系统即可不会破坏已有功能。⚠️常见误区不要在早期就创建过多过于具体的消息类型。先从宽泛的类别开始等游戏需求清晰后再细化。最佳实践把相关消息归类到有意义的类别使用能清楚表明「发生了什么」的描述性命名保持消息 payload 简单聚焦为团队协作而文档化消息类型。设计挑战自测——如何用 Pub/Sub 处理这些场景敌人死亡更新分数、播放音效、生成能量道具、从屏幕移除关卡完成停止音乐、显示 UI、保存进度、加载下一关拾取能量道具增强能力、更新 UI、播放特效、启动计时器。课后实践与作业GitHub Copilot Agent 挑战在 Agent 模式下让 Copilot 生成一个同时使用继承与 Pub/Sub 的小型游戏对象系统——要求包括带x/y与type的GameObject基类、可移动的Hero类、能追踪英雄的Enemy类、EventEmitter类以及「英雄移动时附近敌人收到HERO_MOVED事件并向英雄靠近」的监听逻辑并输出console.log展示对象间通信。作业Assignment完成 6-space-game/1-introduction/assignment.md 中的「Mock up a Game」任务可选类继承GameObject → Movable → Hero或组合工厂函数混合行为任一路线实现至少 23 种消息类型与一种玩家交互并撰写说明文档解释所选模式。5 分钟速练用 DevTools 检查任意在线 HTML5 游戏的代码创建一个简单 Canvas 元素并绘制基础图形尝试用setInterval搭建简易动画循环。一小时目标搭建由 HTML/CSS/JS 文件组成的游戏项目结构编写持续更新与渲染的游戏循环在 Canvas 上绘制第一批精灵实现图片与音频的基础资源加载。学习路线从架构模式到完整游戏本课对应时间线中的「对象模式20 分钟」与「通信系统25 分钟」两段。后续可按 6-space-game/README.md 的总览推进对象模式之后是游戏对象设计属性管理、行为组合、状态与生命周期、架构模式组件系统、观察者模式、命令模式、状态机进而延伸到性能优化、场景图、渲染管线、物理集成直至 Phaser/Three.js 等框架与 PWA/WebGL 进阶方向。本课收获小结掌握继承与组合的取舍拥有可落地的 Pub/Sub 事件驱动架构理解类层级与行为组合两种面向对象设计熟悉工厂函数、Spread 语法等现代 JavaScript 手法建立起松散耦合与模块化设计的可扩展架构意识。这些模式可直接迁移到前端框架React/Vue 组件架构与状态管理、后端微服务通信、移动端通知系统乃至企业级事件溯源与分布式架构之中。【免费下载链接】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),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门