6.3 综合实战:装配完整的游戏帧循环


6.3 综合实战:装配完整的游戏帧循环

本节摘要:一个小游戏是流水线知识的毕业答辩——场景树搭结构、材质管外观、动画与物理驱动运动、拾取接输入、GUI 与音频做伴随、后期造气氛、预算表卡成本。本节从零装配一个"接金币"小游戏,每一步都标注它对应前文的哪一站,最后给同一套装配在产品可视化场景里的变式。

从零装配一个循环

游戏规则故意简单:玩家用键盘左右移动接金币,接到得分、漏接扣命,60 秒计时。简单是为了让注意力落在装配顺序上——顺序本身就是流水线知识的应用题。先看全貌,一帧在游戏里的完整往返:

图 6-2 游戏帧循环:六个子系统在帧内的驻扎顺序

图 6-2 游戏帧循环:六个子系统在帧内的驻扎顺序

背景:给线下活动做一个 60 秒小游戏,要求三天内上线、中端手机可玩。操作:按依赖顺序装配。

第一步,结构与观察(2.1 与 2.2 的知识):

// 地面、玩家、相机、灯光:结构与数据源 const ground = BABYLON.MeshBuilder.CreateGround("g", { width: 12, depth: 20 }, scene); const player = BABYLON.MeshBuilder.CreateBox("p", { size: 1.2 }, scene); player.position.set(0, 0.6, 8); const camera = new BABYLON.ArcRotateCamera("cam", -Math.PI / 2, 1.25, 22, BABYLON.Vector3.Zero(), scene); camera.detachControl(); // 游戏里视角锁死:相机控制权收回(6.2 的教训) const sun = new BABYLON.DirectionalLight("sun", new BABYLON.Vector3(-0.4, -1, -0.2), scene);

第二步,金币复用(2.4 的选型):金币不断生成又消失,但形状材质完全一致——实例化的教科书场景:

// 金币母体与实例池:复用 2.4 的结论 const coinProto = BABYLON.MeshBuilder.CreateCylinder("coin", { diameter: 0.6, height: 0.1 }, scene); const coinMat = new BABYLON.StandardMaterial("cm", scene); // 5.1 的材质共享:全场就这一个实例 coinMat.diffuseColor = new BABYLON.Color3(0.95, 0.75, 0.1); coinMat.emissiveColor = new BABYLON.Color3(0.35, 0.25, 0); // 3.1:暗金自发光,暗处也认得出 coinProto.material = coinMat; coinProto.setEnabled(false); // 母体只当模板不参战 const coinPool = []; // 对象池:5.2 的零新建纪律 for (let i = 0; i < 12; i++) { const c = coinProto.createInstance("c" + i); c.setEnabled(false); coinPool.push(c); }

第三步,输入与帧逻辑(4.1 的帧时间纪律加 4.2 的拾取换算)。键盘状态进表、帧回调查表,速度按秒:

const keys = {}; window.addEventListener("keydown", (e) => keys[e.key] = true); window.addEventListener("keyup", (e) => keys[e.key] = false); let score = 0, timeLeft = 60; scene.onBeforeRenderObservable.add(() => { const dt = engine.getDeltaTime() / 1000; // 4.1:速度按秒,帧率无关 if (keys["ArrowLeft"] || keys["a"]) player.position.x -= 6 * dt; if (keys["ArrowRight"] || keys["d"]) player.position.x += 6 * dt; player.position.x = Math.max(-5.5, Math.min(5.5, player.position.x)); // 边界钳制 for (const c of coinPool) { // 简化碰撞:距离判定代替物理引擎 if (!c.isEnabled()) continue; c.position.y -= 4 * dt; if (BABYLON.Vector3.Distance(c.position, player.position) < 1.1) { c.setEnabled(false); score += 10; coinSound.play(); // 4.4:事件发声 } else if (c.position.y < 0) { c.setEnabled(false); } } });

第四步,伴随与气氛(4.4 与 6.1):计分文本走"值没变不赋值"纪律,后期只开调色加轻泛光——按中端手机档配置。

结果:三天里的实际耗时分布是装配一天半、调手感半天、性能验收半天、缓冲半天;中端手机实测 58 到 60 帧,绘制调用 48 次,全部在 5.4 预算表红线内。

解读:游戏没有用物理引擎——金币下落是匀速直线,用帧时间推进比接物理插件便宜且手感可控;4.3 的分工边界在这里兑现:"有剧本的运动交动画或手写,物理留给真碰撞"。这就是装配式思维的价值:每个子系统是"要不要用"的选择题,而不是"必须接上"的必答题。

变式:同一套装配换成产品可视化——玩家盒换成产品模型(2.4 的 glTF 装载)、金币换成部件热点(4.2 的拾取高亮)、计分板换成参数面板(4.4 的动态纹理)、后期保留调色、砍掉泛光(展品要的是准确不是气氛)。七个岗位一个不少,换的只是每个岗位上的"人"。

⚠️ 常见坑:把计分逻辑写进渲染后回调——逻辑必须在渲染前定稿,渲染后才算出的分数会让 UI 与画面错开一帧,肉眼可见的"分数慢半拍"。

小结

  • 装配顺序即依赖顺序:结构、数据源、复用、交互、逻辑、伴随、气氛,七步不颠倒
  • 实例池配材质共享:高频生成销毁的对象的标准解
  • 子系统是选择题:物理引擎不是必答题,剧本化运动手写更省
  • 逻辑在渲染前定稿:伴随系统的更新纪律各归各位
  • 变式证明通用性:换七个岗位上的角色,同一套流水线跑通另一类应用

实战通关。最后一节抬头看路:流水线本身正在发生什么变化,WebGPU 与云渲染会把这份地图改写多少。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U