这周的学习记录有点特殊,不是那种“看了某某视频”的流水账,而是把主线从零散的教程切换到了UE肉鸽(Roguelike)这一个具体方向,同时用UE中文文档做基础支撑,配合UE培训教程里的案例去理解。累计15小时,按计划还剩6745小时。
说实话,这个数字放在眼前挺有压力的,但从这周的实际体验来看,15小时能出成果的关键不是我有多拼,而是终于找对了学习路径。这篇就把这周具体做了什么、怎么做的、踩了哪些坑,以及下一步打算怎么走,完整梳理一遍。
1. 内容整体设计与思路拆解
1.1 为什么把肉鸽当作UE学习的主线项目
**Roguelike(肉鸽)**这个品类在玩法上有个特别适合学习的底层逻辑:随机性 + 永久死亡 + 成长循环。这三个特性放到UE的项目结构里,几乎能把引擎的核心系统全部串起来。
我用15小时时间,重点拆解了肉鸽最核心下的三个模块:
| 模块 | 对应UE系统 | 学习价值 |
|---|---|---|
| 房间随机生成 | PCG(程序化内容生成)、DataTable、关卡流送 | 数据驱动的关卡设计 |
| 武器掉落与词缀 | GameplayTag、GAS(能力系统)、UDataAsset | 装备系统架构设计 |
| 永久死亡与局外成长 | SaveGame、子关卡加载、运行时数据持久化 | 存档系统与Run管理 |
这里想多说一句“为什么不用完整商业项目做教材”。商业项目里大量结构是为了多人、网络同步、热更新服务的,对于我这种要打基础的阶段,信息过载严重。肉鸽的优势在于单局闭环,代码里不需要管理跨局的状态同步,能让我把注意力集中在UE的核心机制上。
1.2 从“看教程”转向“查文档”的路径抉择
之前几个月的学习方式是典型的视频教程流——老师讲一步我暂停跟做一步。但到这周我明显感觉到瓶颈:教程里能覆盖的场景是有限的,一旦自己改动设计,报错和排查就开始依赖文档知识。
因此本周的战略是:以UE官方中文文档为底,以培训教程为引,以肉鸽项目为靶。
具体操作如下:
- 用一个具体需求(比如“随机生成一个房间”)作为问题起点。
- 先查中文文档中对应的系统说明(比如PCG、Level Streaming)。
- 再去培训教程里看官方推荐的标准做法。
- 手册没讲清楚或版本对不上的部分,用引擎源码或者实验验证。
这15小时里,至少有一半时间是在读文档和做笔记,真正打开编辑器“动手”的时间反而只有6小时左右。但这个比例是健康的,文档给出的系统性理解,是看几条零散视频给不了的。
1.3 6745小时的总盘:长期主义视角下的学习节奏
6745小时这个数字不是随手标的。它对应的是一条接近5年(假设平均每天4小时)的UE深耕路线。把肉鸽放在这个盘子里看,它其实只算第一个小里程碑。
我给自己定的学习节奏是:单周不超过20小时,避免疲劳导致吸收率下降;每月留一个完整周末做项目实验,专门把本周文档学的知识塞进可运行的小Demo里。本周的15小时属于“常规周”的高值,主要是因为补了不少文档阅读量。
2. 核心细节解析与实操要点
2.1 UE中文文档的阅读顺序和方法
UE中文文档好是好,但它的目录结构是面向完整知识体系的,不是面向“我就想做一个肉鸽”的。直接按目录从头读,两周也读不完。我的做法是先拆需求,再转译成文档目录的查询路径。
重点讲一下“房间随机生成”的需求怎么落到文档里:
- 需求:生成一间墙地面材质随机、敌人刷点随机、宝箱位置随机的战斗房。
- 文档关键词:PCG(程序化内容生成)、Sublevel、DataTable(用于配置随机权重)、Dynamic Material Instance(用于材质变化)。
- 查询路径:先看PCG总览,再看DataTable数据类型与用法,最后用Material Instance换肤。
实操中很关键的一点:中文文档的翻译质量整体不错,但系统名称和蓝图节点名我建议以英文原版为准。比如“关卡流送”对应Level Streaming、“程序化生成”对应Procedural Content Generation,中文版容易在社区交流时对不上号。
2.2 培训教程的正确打开方式:单向吸收不如双向校验
UE官方的培训教程(尤其是Lyra示例项目相关的)质量很高,但它的思路是“展示引擎的最佳实践”,不是“手把手教你做游戏”。如果你只是跟着点,做完就忘。我现在的用法是反过来的——带着自己项目里没解决的问题去看教程是怎么处理的。
比如这周我卡在“局外成长数据怎么写进存档”这个点,就在Lyra教程里翻了半天。Lyra用的是独立的SaveGame子系统和Login流程结合的方式,结构比我这颗树要复杂得多,但给了我一个很好的启发:存档不要只存键值对,而是要存版本号和Schema。后来我用DataTable做了一个配置驱动的存储结构,核心思路就是从这里迁移来的。
还有一个容易被忽视的点:培训教程里的示例工程版本号非常重要。UE 5.1和5.4之间的接口变动不小,有些节点名称变了,有些函数参数增加了。看教程前先确认它的引擎版本,可以省掉大量排查低级错误的时间。
2.3 围绕肉鸽方向重构学习地图
肉鸽这种品类在UE里有一个非常适合学习的地图:
| 子方向 | 对应资源 | 为什么要学 |
|---|---|---|
| 房间与关卡 | PCG、Level Streaming、World Partition | 理解UE的关卡体系与运行时加载 |
| 物品掉落 | UDataAsset、GameplayTag、GAS | 理解资产驱动设计和通用标签体系 |
| 战斗反馈 | Niagara、GameplayEffect、AnimMontage | 理解特效、战斗数值与动画协作 |
| 局外成长 | GameInstance、SaveGame、UMG | 理解跨关卡数据结构与UI联动 |
这15小时我完成了前两行的主攻,第三行开了个头,第四行只看了文档,没来得及实操。整体进度符合预期。
3. 实操过程与核心环节实现
3.1 用DataTable配置武器掉落池
肉鸽的掉落系统,本质上是一张带权重的配置表。在UE里最直观的实现方式就是DataTable。
我建了一个结构体FWeaponDropRow:
| 字段名 | 类型 | 说明 |
|---|---|---|
| WeaponID | FName | 唯一标识 |
| Weight | float | 掉落权重 |
| Rarity | ERarity | 品质(普通/精良/史诗) |
| BaseDamage | float | 基础伤害 |
| AffixCount | int | 可携带词缀数量 |
然后建了一张DataTable,先填入几把武器测试数据。掉落时用FMath::RandRange配合权重做随机抽取。这个做法不复杂,但DataTable相比硬编码的好处是可以外部导入CSV、热调整、版本对比。
关于权重的算法说明:
- 把每条记录的Weight累加得到
TotalWeight。 - 生成
0~TotalWeight之间的随机数。 - 遍历表,减去每条Weight,当随机数小于0时即选中该条。
这个算法本质是“按占比划分线段”,随机数落在哪个区间就选哪条。很好懂,也很好扩展。权重后期改成稀有度驱动也没问题。
我实际写了三把武器:单手剑(权重50)、法杖(权重30)、盾牌(权重20),跑了几十次掉落测试,频率分布和权重基本一致,稳定。
3.2 实现房间的简单循环生成
这周没有直接上PCG生成,原因是我发现PCG在静态网格体的合理摆放上强,但在做“房间形态”这种需要明确规则控制的场景里,前期DEBUG成本偏高。我换了一种更可控的方式:用预设Sublevel房间 + 随机入口出口匹配。
原理是这样的:
- 预制了A、B、C三种房间子关卡,每个房间都有一个入口锚点(EntranceMarker)和出口锚点(ExitMarker)。
- 在
GameMode里维护一个当前房间清单,每次进入新房间时根据当前出口方向,选择一组匹配的房间模板。 - 加载对应Sublevel,并把角色位置设置到入口锚点。
这样就实现了有限状态下的随机组合。虽然不算完全动态生成,但理解了子关卡加载机制和锚点匹配逻辑,之后替换成PCG方案时整个架构是兼容的。
OpenLevel和LoadStreamLevel我建议优先用后者。LoadStreamLevel不会中断游戏流程,能保持GameMode、PlayerController等对象的存活,肉鸽的局内数据可以持续累积;OpenLevel会整体切换,数据容易丢。
3.3 存档系统里最容易被忽略的版本兼容问题
局外成长(Meta Progression)在肉鸽里是核心,但它本质就是一个存档系统。文档里的SaveGame用法很简单,创建USaveGame子类,写变量,然后SaveGameToSlot、LoadGameFromSlot。
但在做“永久死亡后重新开始新Run”时,有个坑很容易踩:存档结构变更后的兼容。
我第一版存档就是直接存“金币数量”“已解锁武器ID”。但后来想加一个“累计击杀数”字段,旧存档加载就会报错或者静默丢失数据。
后来参考Lyra的思路,在存档里加了ArchiveVersion字段:
USTRUCT() struct FPlayerSaveData { GENERATED_BODY() UPROPERTY() int32 SaveVersion = 1; UPROPERTY() int32 Gold = 0; UPROPERTY() TArray<FName> UnlockedWeapons; };加载时先读版本号,再做字段迁移:
if (SaveData.SaveVersion < 2) { // 旧版本存档:补充新字段的默认值 SaveData.TotalKills = 0; SaveData.SaveVersion = 2; }这个习惯越早养成越好。因为你永远不知道自己的项目几个月后会加什么字段,早做版本兼容,后面省的是大把“为什么我改了代码存档就废了”的排查时间。
3.4 UMG界面显示与存档数据的联动
这周日最后还抽时间做了局外成长界面的UI显示。核心是把GameInstance作为数据持有者,它不随关卡切换销毁。
UCLASS() class UMyGameInstance : public UGameInstance { GENERATED_BODY() public: UPROPERTY() FPlayerSaveData MetaData; };在每次开始新Run时,从存档写入GameInstance,然后子关卡内的UI(比如左上角的金币显示)直接由GameInstance广播变更事件。
这里有个小技巧:如果只是显示数字,用绑定(Binding)最省事,但它只能被动显示;如果是掉落加金币这种需要主动刷新的场景,用事件分发器(DECLARE_DYNAMIC_MULTICAST_DELEGATE)把数据变更广播出去,蓝图再监听更新。
绑定适合静态展示,事件分发器适合双向联动。思路变清晰以后,UI这块花费的时间几乎可以忽略。
4. 常见问题与排查技巧实录
4.1 中文文档与英文版本的术语偏差
这一条几乎算是所有用中文文档学UE的人必踩的坑。文档翻译时,部分术语采用了大陆行业习惯,比如“流送”对应Streaming,“动态多播委托”对应Dynamic Multicast Delegate。你记中文关键词搜社区内容包括博客、论坛的结果往往不如英文关键词准确。
我的应对策略:
- 看中文文档理解原理。
- 记英文关键词用于搜索。
- 做笔记时中英对照。
比如PCG里有个常用节点叫Spawner,中文文档译作“生成器”,社区通常叫“Spawner”或者“生成节点”。三个关键词记在一起,遇到报错搜英文,看文档查中文,两不误。
4.2 数据结构引发的垃圾回收卡顿
在做掉落物拾取的时候,遇到一个白屏卡顿几秒的问题。一开始怀疑是关卡加载,后来定位到是掉落物Actor的生成与销毁频率过高,导致GC频繁。
排查流程:
- 打开
stat garbageCollection,观察到GC开销异常高。 - 再看
stat memory,发现Actor数量在拾取后没有下降。 - 检查代码后发现掉落物
Destroy()了,但绑定的某个DynamicMaterialInstance没有释放引用。
修复方式:在EndPlay或Destroy前显式清理动态材质实例,再取消所有定时器和委托绑定。这类垃圾回收问题在数据量大的肉鸽项目里特别明显,尤其是敌人波次、子弹、掉落物混在一起时。
4.3 随机手感差:没有种子可复现
肉鸽最重要的问题是“随机”。我用FRandomStream替代了裸的FMath::RandRange:
FRandomStream Stream; Stream.Initialize(Seed); int32 Index = Stream.RandRange(0, TotalWeight - 1);播报如下:
- 用种子固定序列后,同一局内可复现,调试特定异常掉落变得可操作。
- 每局开始生成一个随机种子,保存到存档里。
如果你正在做肉鸽,强烈建议从第一天就用FRandomStream,不然等你做到“敌人的攻击模式也要随机”时,再回头改造会吐血的。
4.4 版本差异:教程和文档对应不上
我用的引擎是UE 5.4,但不少优秀的培训教程基于5.1或者5.3。具体差异点有:
Level Streaming面板里部分选项改名。- PCG节点的输入输出名称调整。
- GAS相关接口从Blueprint调用方式发生了变化。
排查建议:遇到教程与引擎对不上时,先看对应节点或函数的官方文档页,页面顶部通常会标注“introduced in UE 5.x”。没有标注的,就切到官方示例工程对照源码。不要硬猜,硬猜一次可能花掉两小时。
5. 下一步计划:从“能跑”到“好玩”
5.1 用Lyra示例强化GAS与武器系统
接触过UE官方训练后就知道,Lyra是现在UE中比较完整的多人射击模板,GAS部分尤其值得参考。肉鸽虽然是单机为主,但GAS做Buff、词缀、负面状态,比自建一套节点图要规范很多倍。
我的计划是花一个完整周末,把Lyra的GAS部分拆掉外壳,缩成一个“只带技能和词缀”的独立模块,搬到肉鸽项目里。
为什么这是一笔划算的投资:GAS本质上是把Effect、Attribute、Ability解耦。肉鸽的武器词缀,比如“攻击附带流血”“暴击时减缓敌人移动速度”,用GAS的GameplayEffect描述特别自然,完全不用自己写一堆条件判断。
5.2 Cesium文档与地理空间扩展
这周查阅热搜的时候注意到Cesium中文文档也在被高频查阅。Cesium在UE里主要是做真实地理空间数据的,比如城市级场景、卫星影像加载。和肉鸽看起来关系不大,但我考虑后面做一个“现实地点生成地牢”的实验:从Cesium拉一个真实城市轮廓,把地牢入口布置在著名建筑附近,地图种子由真实世界坐标驱动。
这种跨界玩法目前还比较少,但作为作品集展示时的记忆点非常强。文档我会继续跟进,毕竟Cesium和UE的版本兼容也是个容易踩坑的地方。
5.3 关注VR IK方向的长期储备
热搜里还有ue bodysync - full body vr ik solver。VR全身IK解算和肉鸽组合,其实是一个很自然的进化:肉鸽的躲避、翻滚、近战挥砍,在VR里会有完全不同的体感设计需求。目前我不打算立刻上手,但会在文档阅读计划里加入IK与Animation Blueprint的一部分基础内容,等肉鸽项目单局闭环做完,再考虑VR化。
5.4 关于6745小时的分配
大数的学习规划,说句实话,不能让每天被“还剩6745小时”压住。这一周我最大的体会是:数字是用来校准方向的,不是用来制造焦虑的。
如果非要给后续一个分配建议:
| 时间段 | 目标 | 分配比例 |
|---|---|---|
| 近期(约200小时) | 肉鸽Demo从原型到可玩 | 文档40%,实操50%,艺术资源10% |
| 中期(约800小时) | 精通GAS、PCG、网络同步 | 文档30%,实操60%,逆向分析10% |
| 远期(剩余) | 多方向实验(VR、地理空间、动画系统) | 按具体项目动态调整 |
重点是保持每周稳定投入,定期拿Demo出来检验所学。堆时长不是目的,让每一小时都落在具体的项目篮子里才是。
6. 一些经验
这15小时里最有价值的一个改变,是从“跟着教程点鼠标”变成了“对着文档做设计”。文档不会告诉你每一个按钮在哪,但它会把系统的骨架、数据流向、适用边界讲清楚。肉鸽这种玩法高度依赖数据驱动和系统联动,恰好是文档式学习最能发挥作用的场景。
最后还有一个很实际的技巧:做笔记时把遇到的每个报错、当时的引擎版本、中文文档的章节路径、以及最终的解决方式记在同一个表格里。这样以后遇到类似问题,搜索成本会低到你不敢相信。我已经用这个方法在本周内解决了三次“似曾相识”的问题,平均每次的排查时间不到20分钟。
下一周的学习目标是继续推进房间生成和武器词缀的GAS化改造,希望到时候能拿出更具体的成果再来记录。