在线体验: 点击进入《四分之一轮回:晶体铸造厂》Web 演示版

UE5.8 工程仓库: 查看 C++ 源码、完整资产与自动化内容管线

《四分之一轮回:晶体铸造厂》菜单界面

Web 演示版主菜单。

《四分之一轮回:晶体铸造厂》战斗画面

混合波次中的玩家、Crawler、Spitter 与 Bulwark。

《Quarter Run: Crystal Foundry》首先是一个 Unreal Engine 5.8 C++ 俯视角 Roguelite 玩法 Demo。它以一局完整的“四分之一流程”为目标:玩家经过教学波、混合波、精英压力波,在两次波次强化与局内升级之后进入三阶段 Boss 战,最终落到胜利或失败结算。

网页版本不是把 Unreal 画面串流到浏览器,也不是简单播放录屏,而是使用同一批模型、音频和玩法参数重新实现的一份在线演示。它的作用是让人不安装 UE 构建包也能直接体验核心循环;真正的工程主体、类划分和本文重点仍然是 UE5.8 版本。

Quarter Run UE5.8 系统架构图

图中蓝色表示流程编排,橙色表示战斗与伤害,绿色表示数据和成长,紫色表示反馈与音频。

1. 总体设计:GameMode 负责协调,而不是包办一切

工程的运行时中心是 AQRGameMode。它不亲自计算碰撞、伤害、敌人移动或音乐波形,而是把各个系统通过委托连接起来:

  • 创建并持有 AQRWaveDirectorAQRBGMDirector
  • 找到 AQRPlayerControllerAQRPlayerPawnAQRHUD,完成输入、玩家事件与 HUD 的绑定。
  • 接收波次状态、升级请求、Boss 生成请求、战斗强度和教程提示。
  • 在暂停、升级选择、胜利和失败时统一切换输入上下文、世界暂停状态和界面。
  • 把 Boss 血量和阶段转给 HUD 与 BGM,把结算数据转给 UQRGameInstance

这种拆分让流程控制只有一个权威入口,同时避免 WaveDirector、敌人、UI 和音频互相硬引用。多数跨系统通信采用委托:生产者只描述“发生了什么”,GameMode 决定“接下来通知谁”。

一次运行的主链路是:

  1. UQRGameInstance::StartNewRun 进入游戏地图并重置本局统计。
  2. AQRGameMode::BeginPlay 停止菜单音乐、重置升级状态并创建波次与 BGM 导演。
  3. GameMode 把玩家死亡、玩家升级、波次状态、Boss 阶段等委托绑定到自己的处理函数。
  4. AQRWaveDirector 按计划推进四段战斗,只在需要时广播生成、升级、Boss 或音乐强度请求。
  5. GameMode 在结算时关闭战斗输入、冻结运行状态,并让 HUD 展示本局用时、击杀、受伤和所选升级。

2. 输入与 UI 系统

AQRPlayerController 负责键鼠和手柄输入。工程在运行时创建 Enhanced Input 动作,并维护两套上下文:

  • Gameplay 上下文:移动、鼠标或右摇杆瞄准、按住射击、冲刺、冲击波和暂停。
  • UI 上下文:确认与返回,供菜单、暂停和升级三选一使用。

Controller 只把输入意图写入 AQRPlayerPawn,例如移动向量、世界瞄准点和射击按住状态。具体能不能冲刺、是否还在冷却、射击间隔到了没有,全部由 Pawn 自己判断。

AQRHUD 是局内界面的唯一拥有者,保证 HUD、升级、暂停和结算窗口各自最多只有一个实例。它通过玩家委托更新生命值、经验和冷却,通过 GameMode 更新波次、教程提示与 Boss 血条。UQRHUDWidget 只更新已有控件的数值,不在每帧重建界面树。

升级窗口打开时,GameMode 会释放当前移动和射击输入、切到 UI 上下文、显示三张升级卡并暂停世界;选择完成后再恢复世界、Gameplay 输入以及 WaveDirector 的推进。暂停菜单也走同一套权威切换,所以不会出现“界面暂停了但角色仍在开火”的分裂状态。

主菜单、设置、制作信息、HUD、升级、暂停、胜利和失败界面都由工程自己的 Widget 类构建。设置页把分辨率、窗口模式、画质、垂直同步以及三路音量写入保存对象;音量修改还会立即刷新正在播放的菜单音乐和分层 BGM。

3. 玩家系统:输入意图、战斗状态和成长落点

AQRPlayerPawn 集中管理玩家的运行时状态。它的球形碰撞体是根组件,网格、移动组件、弹簧臂相机、枪口和 UQRHealthComponent 都挂在这一实体上。

每帧更新被拆成移动、瞄准、冲刺、开火和无敌状态几个小步骤:

  • 移动输入经过加速与减速处理,并被限制在八边形竞技场内。
  • 鼠标世界点或手柄方向决定最后瞄准方向。
  • 按住射击时,只有射击冷却归零且角色不在冲刺,才生成下一枚投射物。
  • 冲刺会停止普通移动、设置方向、开启短暂无敌并启动冷却;相位尾迹升级可以在冲刺结束处追加范围伤害。
  • 冲击波使用球形重叠查询,对范围内目标统一送入伤害系统,并可打断敌人的攻击前摇。

升级不是在 UI 内直接改数值。UI 只提交 EQRUpgradeIdUQRUpgradeSubsystem 检查等级上限并调用玩家的 ApplyUpgrade。伤害、射速、穿透、分裂、暴击、冲刺冷却、冲击波冷却、最大生命、吸附范围、弹体尺寸和移动速度都在玩家的派生数值函数中生效。

4. 投射物与碰撞系统

AQRProjectile 同时服务玩家和敌人。它使用 Query Only 的球形碰撞体作为根节点,开启 CCD 连续碰撞,避免高速弹体跨帧穿过较小目标:

  • 对静态世界使用 Block,命中墙体后产生世界碰撞反馈并销毁。
  • 对 Pawn 和动态对象使用 Overlap,进入伤害分派流程。
  • 通过队伍标签拒绝友军伤害,并忽略 Owner 与 Instigator。
  • HitActors 集合保证同一枚弹体不会重复结算同一个目标。
  • 投射物尺寸升级同时放大可见网格与碰撞半径,保持“看到多大、命中多大”。

命中 Actor 后,投射物不会判断对方到底是 Crawler、Boss 还是其他特殊单位。它只查找 UQRHealthComponent,组装一份 FQRDamageSpec 并调用统一入口。返回的 FQRDamageResult 决定后续的反馈、穿透计数、分裂子弹和销毁。

范围技能走另一条碰撞入口:玩家使用球形 Overlap 收集 Pawn 与 WorldDynamic,去重后仍然调用同一个 HealthComponent。普通敌人受击后可以被击退,Boss 则通过标签被排除在击退之外。

5. 伤害与生命值系统

UQRHealthComponent 是玩家、普通敌人和 Boss 共用的生命值中心。所有一次伤害尝试都先被描述为 FQRDamageSpec:基础伤害、单次减伤、暴击概率、暴击倍率、来源队伍、来源 Actor 和无敌状态都包含在里面。

它按固定顺序处理:

  1. 拒绝死亡目标、组件无敌或本次攻击标记为无效的请求。
  2. 计算暴击。
  3. 将单次减伤与目标基础减伤按乘法关系合并。
  4. 把有效伤害钳制到当前生命值,防止过量伤害污染统计。
  5. 广播生命变化。
  6. 生命归零时只广播一次死亡事件。

返回的 FQRDamageResult 包含实际伤害、伤害前后生命、是否暴击、是否击杀和是否被格挡。战斗反馈系统只消费这个结果,不会再计算第二遍伤害,因此伤害数字、音效和真实生命值始终一致。

玩家受伤后额外开启 0.65 秒受伤无敌,记录本局承受伤害,并触发受伤音效、碎片与镜头震动。Bulwark 则在进入 HealthComponent 之前根据攻击来源方向加入正面减伤或背面倍率,但最终扣血仍然使用同一组件。

6. 敌人 AI 与死亡反向调度

三类普通敌人共享 AQREnemyBase。基类统一处理目标查找、分离力、场地边界、生命值、命中闪烁、死亡掉落和 WaveDirector 注册;派生类只关心自己的状态转换。

共同状态机是:Spawn → Seek → Telegraph → Attack → Recover → Dead

  • Crawler:接近玩家,进入清晰前摇,然后短距离突进并恢复。
  • Spitter:维持理想距离,过近撤退、过远靠近,同时横向走位并发射敌方投射物。
  • Bulwark:蓄力冲锋,正面拥有高减伤,背面核心承受额外伤害;冲击波可以中断其前摇。

敌人死亡不是简单 Destroy。它会按顺序停止计时器、关闭碰撞与 Tick、生成能量碎片、从 WaveDirector 的存活集合中注销、记录击杀、广播死亡事件,最后延迟销毁。正是“注销存活集合”这一步驱动 WaveDirector 判断当前批次是否清空以及是否进入下一阶段。

7. 波次与怪物生成系统

AQRWaveDirector 是一个独立、定时器驱动的四段流程器。它不直接操作 HUD、升级窗口或 BGM,而是广播状态:

  • 教学波:3 只 Crawler,清空后再生成 5 只。
  • 混合波:6 只近战起步,8 秒后追加 6 只近战和 2 只远程,最后再生成 4 只近战和 3 只远程。
  • 精英波:先制造高密度压力,存活数下降到阈值后补充下一批,最终加入 2 只 Bulwark。
  • Boss 段:播放短暂入场流程后向 GameMode 请求生成 Foundry Sentinel。

生成器维护待生成数量与存活敌人集合,场上普通敌人上限为 28。达到上限时,待生成项不会丢失,而是等敌人死亡注销后继续。出生点从竞技场八个扇区中选择,并避免连续使用同一扇区,降低怪物在同一方向堆叠的概率。

混合波和精英波结束后,WaveDirector 广播升级节点请求。GameMode 让 UQRUpgradeSubsystem 从未满级项目中抽取三个不重复选项;选择完成后,GameMode 再通知 WaveDirector 进入下一份计划。这样“出怪”和“弹升级 UI”不会互相调用形成循环依赖。

8. Boss 系统

AQRBossSentinel 由底座、旋臂和核心三块模型组成,并使用独立球形伤害碰撞体与 HealthComponent。Boss 的攻击节奏依靠 TimerManager,而 Tick 只负责旋臂和核心的视觉运动。

Boss 会从以下攻击中按阶段抽取,并避免连续重复同一种攻击:

  • Radial Shards:环形弹幕。
  • Core Slam:范围落地冲击。
  • Tracking Volley:预测玩家位置的追踪连射。
  • Sweep Beam:分段推进的扫线攻击。
  • Shard Mines:先显示预警,再在多个位置结算地雷。
  • 第三阶段加入环形弹幕与地雷、扫线与砸地的组合技。

阶段切换来自生命值比例,不依赖整场战斗计时。UE 工程当前原始配置为 1500 最大生命,70% 和 35% 两个阶段阈值;在线 Web 演示版为了延长体验,当前采用 4500 最大生命、50% 和 35% 阈值。两者的系统结构相同,只是平衡参数不同。

进入新阶段时,Boss 取消当前攻击、短暂无敌、清除玩家附近的敌方投射物、播放预警音并更新核心发光和旋臂速度。第二阶段召唤 4 只近战与 2 只远程;第三阶段每 12 秒检查普通随从数量,在不超过上限的情况下补充援军。

Boss 死亡后会立即停止攻击、关闭碰撞、清除预警、投射物和随从,触发碎片、命中停顿与胜利音乐,再延迟广播唯一的胜利请求。最终仍由 GameMode 收束流程,避免 Boss 自己直接操作结算 UI。

9. 动态 BGM 与音效系统

音频分成两个职责不同的模块。

UQRAudioSubsystem 保存声音 ID 到资源路径的映射,负责菜单音乐、2D/空间 SFX、三路音量以及并发限制。普通命中每帧最多保留 3 个声音,暴击保留更高优先级的第 4 个槽位,其他通用 SFX 每帧也有限额,避免高密度弹幕把混音推爆。

AQRBGMDirector 专门负责玩法音乐。Atmos、Drums、Pulse 和 BossLead 四条等长 Stem 在开局时一起启动;工程调用 Quartz 把它们锁在同一小节与 100 BPM 时钟上,运行过程中只改变各层音量,不重新开始音频,所以不会因为切换阶段而错拍。

调度关系是:

  • WaveDirector 根据流程广播 0.35、0.65、1.0 或 0.90 的战斗强度。
  • GameMode 把强度转给 BGMDirector,鼓组和脉冲层随压力渐入。
  • Boss 阶段变化同时进入 GameMode 与 BGMDirector;第二阶段引入 BossLead,第三阶段将其推到完整强度。
  • 胜利时鼓、脉冲和 BossLead 先淡出,环境层随后淡出,并播放胜利段落。

在线 Web 版使用 Web Audio 同时启动四个 AudioBufferSourceNode,再通过独立 GainNode 复现同样的层级逻辑。这里替换的是平台调用,不是音乐调度设计。

10. 战斗反馈系统

UQRCombatFeedbackSubsystem 把“命中后应该看见和听见什么”从伤害计算中独立出来。它收到 DamageResult 后生成伤害数字、网格碎片、命中或暴击音效、镜头震动;暴击还会请求极短的全局命中停顿。

敌人的攻击前摇与 Boss 地面技能通过 AQRWarningDecalActor 显示安全窗口。命中闪烁和阶段强化则修改动态材质的 EmissiveStrength,不需要替换模型。这样伤害逻辑即使被自动化测试直接调用,也不会依赖某个特效 Actor 是否存在。

11. 场景与资产生产

竞技场由 AQRLevelAssembler 装配。地板、墙、立柱、水晶、出生门和升级基座使用层级实例组件批量放置,形成八边形战斗区域并保存出生门位置。运行时敌人和 Boss 在这套稳定坐标上生成,碰撞边界与视觉墙体使用同一组场地尺寸。

美术资产由 Blender 5 管线生成并保留源文件,共 17 个模型:玩家、三类敌人、Boss 三件套、环境组件、掉落和 VFX 网格。工具脚本导出 FBX,Unreal Python 负责导入、创建材质、分配实例、设置碰撞、生成两张地图并输出检查报告。音频管线包含 6 个 BGM 与 12 个 SFX。

材质使用两个主材质承载顶点色分区、双色底色、自发光、脉冲与边缘表现,再通过材质实例区分玩家、敌人、Boss、环境、水晶、掉落和预警。玩法代码只在需要反馈时修改公开参数,不把具体颜色写进伤害系统。

12. Web 在线演示如何对应 UE 工程

Web 版保留了 17 个模型和 18 段音频内容,将 FBX 资产重新导出为 GLB;无损 WAV 继续作为工程母版,上线资源则编码为总量不超过 3 MB 的 MP3。运行时由 TypeScript 分成与 UE 类似的职责:

  • game.ts:运行状态、碰撞协调、伤害、升级队列与系统编排。
  • entities.ts:玩家、投射物、普通敌人和能量碎片。
  • boss.tswaves.ts:Boss 阶段及四段流程。
  • ui.ts:菜单、设置、HUD、暂停、升级和结算。
  • audio.ts:同步 Stem、动态混音与 SFX 并发限制。
  • rendering.ts:GLB 场景、材质、灯光、后处理与短寿命特效。

浏览器端用键盘、Pointer Events、Gamepad API、Web Audio、DOM 和本地存储替换 UE 对应调用,但系统边界、数据流和战斗节奏仍按 UE 版本重建。这也是在线版本最重要的定位:它是一份可立即游玩的技术演示,而不是对 UE 工程地位的替代。

如果你想先感受完整循环,可以直接打开文章开头的在线版本;如果希望继续查看实现细节,可以打开紧随其后的 UE5.8 工程仓库,对照本文架构图阅读 C++ 类、自动化测试、Blender 脚本与 Unreal Python 内容管线。