用设计模式与 Pub/Sub 构建游戏原型:Web-Dev-For-Beginners 空间游戏系列实战作业完全指南
用设计模式与 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 空间游戏课程第一课Introduction的配套作业展开要求你用继承或组合两种架构模式配合发布-订阅Pub/Sub事件系统从零搭建一个可运行、可扩展的简单游戏原型。读完本文你将掌握 EventEmitter 的完整实现思路、消息类型的设计规范、键盘/鼠标输入与游戏事件的解耦连接方式并拿到一份可直接执行的验收清单与评分标准。作业定位为什么第一课就要搭原型空间游戏系列共六课第一课的主题是Introduction核心是两件事用代码组织游戏对象继承 vs 组合和用 Pub/Sub 解耦系统间通信。作业要求你立刻把这两项能力落地为一个模拟游戏Mock up a Game——它不必有复杂美术但必须在架构上自洽选择一种架构模式组织游戏对象用EventEmitter搭建事件总线定义 2~3 种以上的消息类型并让它们真正驱动游戏逻辑让用户输入键盘/鼠标通过事件系统影响游戏世界。这与后续课程Canvas 绘制、移动、碰撞检测、计分、结束条件形成递进关系最终目标是在本系列结束时完成一个完整的太空射击游戏参考实现位于 6-space-game/solution/app.js 与 6-space-game/solution/index.html。第一步选定你的架构模式游戏对象Hero、Enemy、PowerUp、Bullet 等都拥有坐标x、y、类型type且普遍具备存在某处、可移动、有生命周期、可交互四类共性行为。组织这些行为有两种主流方式作业要求二选一。方案 A类继承is-a 关系对应课程文档 6-space-game/1-introduction/README.md 中的GameObject→Movable→Hero示例逐层扩展// 基类所有游戏对象的共同底座 class GameObject { constructor(x, y, type) { this.x x; this.y y; this.type type; } } // 继承在基类上追加移动能力 class Movable extends GameObject { constructor(x, y, type) { super(x, y, type); // 调用父类构造函数初始化 x/y/type } moveTo(x, y) { this.x x; this.y y; } } // 具体对象自动把 type 设为 Hero class Hero extends Movable { constructor(x, y) { super(x, y, Hero); } } const hero new Hero(0, 0); hero.moveTo(5, 5); // 正常Hero 可以移动继承的好处是is-a关系清晰、类型层级明确代价是层级一旦过深就僵化——比如一棵树Tree直接继承GameObject而非Movable它天然就没有moveTo()这既是约束也是保护。方案 B组合has-a 关系工厂函数组合思路是把行为拆成独立对象用展开语法按需拼装对应课程中的混入行为写法// 基础属性与独立行为 const gameObject { x: 0, y: 0, type: }; const movable { moveTo(x, y) { this.x x; this.y y; } }; // 工厂函数按需组合出具体对象 function createHero(x, y) { return { ...movable, ...gameObject, x, y, type: Hero }; } function createStatic(x, y, type) { return { ...gameObject, x, y, type }; // 没有 movable不能移动 } const hero createHero(10, 10); hero.moveTo(5, 5); // 正常 const tree createStatic(0, 0, Tree); // tree.moveTo is undefined —— 未组合进移动行为组合的优势是灵活、无层级负担一个既能移动又能飞行的对象只需{ ...movable, ...flyable, ...gameObject }即可拼出课程文档也强调可以在同一应用里混合使用两种思路。选型建议存在清晰的is-a层级如 Hero 就是一种 Movable时用继承需要has-a灵活拼装如一个对象同时具备多种能力时用组合。作业中无论选哪种都要在代码注释里写清理由。第二步搭建 Pub/Sub 通信系统课程把 Pub/Sub 比作广播电台发布者不知道谁在收听订阅者按消息类型各取所需中间由事件系统负责路由。三个角色缺一不可Messages描述发生了什么的文本常量如PLAYER_SCOREDPublishers发出事件的源头如键盘、碰撞检测Subscribers对某类事件感兴趣的监听者如计分 UI、音效系统。实现 EventEmitter作业要求包含一个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 消息机制核心。定义消息类型常量为防止字符串拼写错误用常量对象集中管理消息类型。课程示例const Messages { HERO_MOVE_LEFT: HERO_MOVE_LEFT, HERO_MOVE_RIGHT: HERO_MOVE_RIGHT, ENEMY_SPOTTED: ENEMY_SPOTTED };作业要求至少 2~3 种消息类型可参考PLAYER_MOVE、ENEMY_SPAWN、SCORE_UPDATE这类命名——统一用大写蛇形命名语义上动词对象最清晰。连接输入与游戏事件键盘事件只做发布不做任何游戏逻辑这正是解耦的关键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); }); // 发布键盘只负责 emit不知道谁在听 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; } });后续要加音效、镜头跟随或屏幕震动只需再on()一个监听者键盘与移动代码一行都不用改。第三步设计游戏元素与交互作业对游戏本体提出三条硬性要求至少一个玩家控制角色至少一个其他游戏对象敌人、可收集物或环境元素对象间存在基本交互碰撞、收集或通信。仓库完整实现 6-space-game/solution/app.js 可以作为架构参考它用player对象保存坐标x/y、速度speed、半径r等属性app.js#L81-L89用bullets、enemies、particles三个数组管理实体并在update()中以碰撞检测驱动得分、护盾、生命值等状态流转app.js#L345-L399——这种输入收集 → 状态更新 → 碰撞交互 → HUD 刷新的主循环正是作业原型需要的最小闭环。作业也给出了四个可选的游戏点子按实现难度从低到高排列贪吃蛇Snake蛇身段跟随蛇头食物随机生成Pong 变体挡板响应输入球在墙壁间反弹收集游戏Collector玩家移动拾取物品、躲避障碍基础塔防Tower Defense防御塔探测并射击移动中的敌人。第四步按骨架代码起步作业提供了一段起始结构建议直接在此基础上填充// Example starting structure const Messages { // Define your game messages here }; class EventEmitter { // Your event system implementation } // Choose either class-based OR composition approach // Class-based example: class GameObject { /* base properties */ } class Player extends GameObject { /* player-specific behavior */ } // OR Composition example: const gameObject { /* base properties */ }; const movable { /* movement behavior */ }; function createPlayer() { /* combine behaviors */ }注意作业中类继承示例用的是Player extends GameObject——这提示你不需要照搬课程的Movable中间层只要继承关系清晰即可组合路线则要求用工厂函数把行为拼装成完整对象。第五步测试你的实现作业要求从四个维度验证代码确实工作事件触发时对象发生移动或状态变化——在控制台观察hero坐标随按键改变多个对象能响应同一事件——给同一消息挂两个以上监听者确认都被调用新增行为无需改动既有代码——新功能只通过新增on()监听实现不改发布端键盘/鼠标输入正确触发游戏事件——把真实按键/点击映射到emit()手动验证端到端链路。课程文档对此有精辟总结因为每个组件独立工作测试变简单了出问题时你也知道该去哪里查。提交清单与评分标准作业的交付物共四件JavaScript 文件——包含完整的游戏实现HTML 文件——用于运行和测试游戏可以很简陋注释——说明你选择了哪种模式以及为什么简短文档——列出你的消息类型及其作用。评分表按四个维度、每项 1~3 分标准优秀3 分合格2 分待改进1 分架构模式正确实现继承或组合类/对象层级清晰使用了所选模式但有小问题或不一致尝试使用模式但实现有明显问题Pub/Sub 实现EventEmitter 工作正常多种消息类型且事件流正确基础 pub/sub 可用部分事件处理事件系统存在但不可靠游戏功能三个以上通过事件通信的交互元素两个交互元素、基础事件通信仅一个元素响应事件或基础交互代码质量干净、注释充分、逻辑组织良好、现代 JavaScript大体组织良好、注释足够能运行但缺乏组织或清晰注释另有三个加分项创意游戏机制、多种输入方式键盘和鼠标、易于扩展新功能的可伸缩架构。想在最后一项拿分一个可实践的技巧是把 HUD 更新计分、生命值全部写成SCORE_UPDATE、PLAYER_DAMAGED等消息的订阅者而不是在游戏逻辑里直接改 DOM——这正是课程所倡导的松散耦合。写在最后这个作业的价值不在于画面多华丽而在于让你亲手验证两件事继承与组合如何影响代码的可扩展性以及事件驱动架构如何让加功能变成挂监听。完成它之后你就可以带着这套架构进入空间游戏系列的后续课程用 Canvas 绘制、碰撞检测与计分系统把它一步步充实成完整的游戏。继续参考 6-space-game/1-introduction/README.md 的课后挑战用 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创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考