UE学习资料整理:索引表、DDC缓存与高频问题排查 UE学习资料整理这件事我前后推倒重来过三次。第一次是老老实实往浏览器书签里塞链接塞到三百多条之后再也没打开过第二次是按教程/文档/示例工程分了三个文件夹结果找半透明怎么吃景深这种具体问题时还是得重新搜一遍第三次我才想明白资料整理的核心不是收藏而是能在需要的时候三分钟内定位到答案最好还能附上我自己验证过的结论。这篇就把我现在在用的这套方法摊开讲顺带把几个高频卡点——安装包与缓存目录、Size to Content、投掷物抛物线、平面反射的倒影渐变、半透明景深、FString/FText/FName 的区别和换行符、帧率排查、策略游戏方向——按我自己的笔记原样整理进来。不管你是刚装完引擎的新手还是已经做过一两个项目的从业者这里面的目录结构和排查顺序都能直接抄。1. 我的UE资料库为什么从收藏夹改成索引表1.1 按引擎版本和系统模块做双轴归档UE 的资料有个很讨厌的特点同一个节点、同一个参数在 4.27、5.0、5.3、5.5 里的行为可能完全不一样。我在 4.26 时代记的Translucency 里没有 Output Depth这条笔记到 5.x 就变成错的如果不标版本过半年自己都会把错误结论当成常识用。所以我的归档轴是两条版本轴和模块轴。版本轴很简单我现在维护5.3、5.4、5.5三个目录跨版本通用的结论单独放在common里。模块轴我固定成八类渲染与材质、UMG与UI、Gameplay框架、AI与导航、物理与碰撞、网络同步、性能分析、打包与部署。为什么不按教程来源分因为来源只解决我从哪学的不解决我要解决什么问题而后者才是你真正会去检索的东西。每条笔记我强制写成同一个格式一共五行问题描述 → 最小复现步骤 → 结论 → 适用版本 → 工程路径。举个例子问题描述半透明水面不吃景深最小复现步骤新建空场景放一个半透明平面加 Post Process Volume 开 DOF把焦距拉到很近结论看似无效实际是 Separate Translucency 在 DOF 之后合成适用版本5.0 以上工程路径D:\UE_Notes\Proj_TranslucencyDOF。这个格式的关键是最后一项它是把笔记从死文字变成活证据的唯一手段。提示写笔记的时候凡是没亲手复现过的结论一律加一个[未验证]标签。我踩过太多次从论坛搬来的参数直接用在项目里、结果发现是旧版本的默认值。1.2 三类清单必读、备查、待验证我把所有资料分成三个清单这个分法帮我砍掉了至少一半的无效阅读。必读清单只放那些不读就做不下去的东西官方文档里对应模块的概览页、引擎自带的 Content Examples 对应关卡、以及我自己写的模块流程图。这个清单常年保持在 15 条以内超过就说明我在囤积。备查清单是工具型资料比如各个stat命令的含义、材质 Blend Mode 的对照表、打包常见报错的解决方案。这类东西不该读完只该能搜到所以我会把它们整理成 Markdown 表格用关键词命名文件比如stat命令速查.md、材质BlendMode对照.md。待验证清单最有意思专门放那些听起来很有道理但我还没试过的说法。比如用 MassEntity 能把一万个单位跑到 120 帧这话我信但不在我的项目场景里跑一遍我不会写进结论。这个清单我会定期清空把它变成必读或直接删掉。还有一条经验官方文档一定要先切版本再读。文档站右上角有版本切换默认往往不是你在用的版本很多人照着默认版本读完回去发现细节面板里根本没那个选项白白浪费半小时。2. 安装包、缓存目录与多版本共存先把地基打平2.1 一个UE版本到底占多少空间装机前怎么规划很多人搜UE安装包其实混了两件事一是引擎安装包Epic 启动器下载的那个几十 GB 的东西二是项目打包产出的安装包打包成 exe。这两个完全不同先说前者。一个 UE5 版本只勾选 Windows 平台、不含调试符号的情况下装完大约 30 到 50 GB。如果你在安装时顺手勾了 Android、iOS、Linux 这些目标平台体积很容易翻倍。我的做法是装机时只勾当前要用的平台需要的时候再回来补勾补勾的代价远小于长期占着一堆不用的 SDK。另外引擎安装时强烈建议用自定义安装路径放在独立的大容量 SSD 上例如D:\Program Files\Epic Games\UE_5.4别放在 C 盘——后面缓存一涨C 盘直接报警。打包那一侧关键点在Project Settings → Packaging。Development 配置带调试信息包体大、性能也不是真实表现Shipping 才是线上该看的。测性能我一般直接打 Shipping或者至少在编辑器里用独立进程模式跑-game因为编辑器本体有一大堆开销在编辑器里测出来的帧率对上线没什么参考价值。2.2 改DDC缓存目录的三种做法与选型理由这是ue改缓存目录最常指的东西DDCDerived Data Cache派生数据缓存。它的作用是缓存着色器编译结果、贴图中间格式这些算出来就不想再算一遍的数据。默认位置在C:\Users\用户名\AppData\Local\UnrealEngine\Common\DerivedDataCache一个项目反复迭代下来这里涨到几十 GB 是常态而且是全项目共享的一个坑。改法有三种我把它们放在一起对比做法操作位置优点缺点环境变量UE-LocalDataCachePath系统环境变量不动引擎文件升级引擎不丢配置每台机器要单独设一次改BaseEngine.ini的[DerivedDataBackendGraph]Engine/Config/BaseEngine.ini全局生效一目了然引擎升级/校验文件时可能被覆盖启动参数-ddc快捷方式或命令行临时、灵活、可做多套环境每次启动都要带参数我选的是第一种环境变量UE-LocalDataCachePath指向D:\UE_DDC。理由很实在引擎版本我经常换改 ini 会在升级时被冲掉而环境变量一次设好所有版本都吃这个配置。引擎的BaseEngine.ini里其实已经把EnvPathOverrideUE-LocalDataCachePath写好了也就是说它天生就支持你用环境变量覆盖这是最正规的入口。还有几个坑必须说清楚。第一路径里千万不要有中文和空格着色器编译偶尔会在这种路径上报莫名其妙的错。第二绝对不要放在网络盘或者机械盘上DDC 是高频随机读写放网络盘会让第一次打开项目慢到你以为死机。第三删 DDC 不是清理垃圾删完之后所有项目要重新编译一次着色器你可能等上半小时到一小时所以只在确认缓存损坏时才删。注意团队协作会用到共享 DDC对应环境变量是UE-SharedDataCachePath通常是局域网内一台机器的共享目录。它和本地 DDC 是叠加关系本地命中优先没命中才去共享里找共享里再没有才自己编译。2.3 多版本共存时的目录命名与切换习惯多版本共存本身不是问题混乱的是命名。我的习惯是严格跟着官方命名走UE_5.3、UE_5.4、UE_5.5不加任何自定义后缀这样.uproject里的EngineAssociation和目录名能对上右键项目切换版本的时候不容易选错。.uproject右键菜单里的Switch Unreal Engine Version只改EngineAssociation字段不动任何资产。但要注意从高版本切回低版本如果项目里用了新版本的资产特性会直接报错甚至资产损坏。所以我所有项目的切换原则是只向上、不向下向下切换一律从版本控制里拉一份干净副本。3. UMG里那些看着会、用着错的节点Size to Content3.1 Size to Content到底改的是什么size to content 在 ue 里这个搜索我遇到过太多次多数人是这么踩的在 Canvas Panel 上勾了 Size to Content然后发现画面里的东西一点没变只是外面套着的那个容器突然塌了或者撑开了。要理解这个得先知道 Canvas Panel 是个绝对定位面板。它里面的子控件靠锚点Anchor加偏移Offset决定位置和大小Canvas Panel 自己则默认占满父级。勾上 Size to Content 之后Canvas Panel 的期望尺寸会变成所有子控件的期望尺寸之和而子控件的绝对坐标不会因此改变。所以你会看到两种典型现象一是子控件本来就在面板范围内勾了之后视觉上什么都没发生只是外层面板变小了二是子控件的坐标超出面板范围面板变大但内容位置纹丝不动因为锚点和偏移没变。它真正好用的场景是内容驱动的容器比如一个根据文字长度自适应的提示气泡、一个列表长度不定的设置面板。这时候的正确组合是Size Box或者Vertical Box在外Canvas Panel 在内并勾 Size to Content让面板包住内容。3.2 和DPI缩放、文本换行一起用时容易出问题的点和 Size to Content 关系最紧密的是文本换行。UE 的 Text Block 有一个Auto Wrap Text很多人勾了发现还是不换行原因不是设置没生效而是它的父级没有给出宽度约束。放在 Horizontal Box 或者勾了 Size to Content 的 Canvas Panel 里宽度是无限的文本自然永远不换行。解决办法很土但很有效在 Text Block 外面套一个 Size Box把 Width Override 定死或者把它塞进一个 Fill 的 Vertical Box 槽位里。DPI 缩放是第二个坑。Project Settings → User Interface → DPI Scaling里有个 DPI Scale Rule常见选项是 Shortest Side、Longest Side、Horizontal、Vertical。如果你的 UI 在 16:9 显示器上调好了拿到 21:9 或者竖屏上就错位八成是缩放参考轴选错了。我一般做跨平台项目就选 Shortest Side做宽屏为主的桌面项目选 Longest Side然后在 Canvas Panel 上勾Size to Content配合 DPI Curve 的断点让不同分辨率下走不同的分辨率档位。Safe Zone是第三个。做移动端或者带刘海屏设备适配时用 Safe Zone 控件包住主要内容但 Safe Zone 的 Padding 是跟着 DPI 走的如果你同时用了 Size to Content会出现内边距把内容挤出去了但面板没变大的情况。我的处理方式是 Safe Zone 在最外层且不勾任何尺寸自适应内部再按需使用 Size to Content。提示同名选项在不同控件里的含义是不一样的点之前先看清你选中的是哪个控件。在细节面板里点一下控件名确认层级关系比事后调半天布局划算得多。4. 投掷物抛物线从Projectile Movement到轨迹预览4.1 ProjectileMovementComponent的关键参数与重力常数做ue投掷物抛物线起点一定是ProjectileMovementComponent。核心参数就几个但每一个都值得说明白参数作用我的常用值Initial Speed初始速度大小配合 Velocity 方向按射程反推见下文Projectile Gravity Scale重力倍数1 表示吃世界重力手雷 0.9-1.0箭矢 0.6 左右Max Speed速度上限一般设为 0不限速bRotationFollowsVelocity朝向跟随速度方向子弹开投掷物看需求bShouldBounce / Bounciness反弹开关与弹性手雷开弹性 0.4 左右bForceSubStepping强制子步进高速物体必须开世界重力的默认值是-980 cm/s²在Project Settings → Physics → Default Gravity Z里能看到。有了这个常数抛物线就能反推参数。举个我在项目里实际算过的例子想让投掷物以 45 度仰角打出 60 米6000 厘米的水平射程用公式R v² × sin(2θ) / gθ45° 时 sin90°1于是6000 v² / 980得出v √(6000 × 980) ≈ 2425 cm/s。这个数直接填进 Initial Speed 再给一个 45 度方向的 Velocity一次就能打到目标附近不用靠手感瞎调。如果你要的是最大高度用H v² × sin²θ / (2g)同样的 2425 cm/s 和 45 度高度大约是 1500 厘米也就是 15 米。把这两个公式记住调投掷物的效率会高一个数量级。4.2 PredictProjectilePath瞄准弧线预览的正确做法策略游戏和射击游戏里都需要瞄准时显示一条虚线弧标准做法是PredictProjectilePath。蓝图里叫Predict Projectile Path By Trace ChannelC 里是UKismetSystemLibrary::PredictProjectilePath参数结构是FPredictProjectilePathParams返回FPredictProjectilePathResult。几个必须配的参数StartLocation用枪口或手部位置LaunchVelocity用和实际发射一致的初速度ProjectileRadius填碰撞半径MaxSimTime限制预测时长一般填 3 到 5 秒就够SimFrequency决定采样密度默认 15 在近距看起来会有点折线感我一般调到 30。返回结果里的PathData.PathPoints就是一条折线上的采样点可以直接画DrawDebugLine调试也可以塞进 Spline Component 用样条网格渲染成实体弧线。这里有个很容易忽略的坑预测出来的路径和实际飞行的路径不一定完全重合。原因是ProjectileMovementComponent的积分方式、子步进设置、碰撞响应和预测用的简化模型之间会有偏差尤其是开了反弹、或者物体半径比较大的时候。我的处理办法是把预测的采样点数量、半径、重力都跟实际投射物对齐然后在实测中对比落点如果偏差稳定存在就在预测的OverrideGravityZ上做一点微调补偿。4.3 反弹、命中与同步的注意点高速投掷物必须开子步进否则会直接穿墙。相关参数是bForceSubStepping、MaxSimulationTimeStep、MaxSimulationIterations思路是把一帧的大位移切成若干小步做碰撞检测。代价是 CPU 开销上升所以别全局无脑开只给高速物体开。命中判定建议一律走服务端权威。OnComponentHit里直接扣血、生成特效这种写法在单机没问题联机就会出现两边表现不一致。正确做法是客户端只做预测表现Tracer、音效真正的伤害结算放在服务端客户端收到确认后再播最终特效。还有一种情况值得单独拎出来你其实并不需要物理。如果只是做一段固定的抛物线动画比如掉落拾取物的表现用 Timeline 驱动一个曲线位移开销远小于开一个物理组件。我在一个策略游戏里就把所有金币掉落的表现换成了 Timeline 曲线同屏两百个的情况下省了不少 CPU。判断标准很简单需要和场景碰撞交互就用物理组件不需要就用曲线。5. 画面类效果平面反射倒影渐变与半透明景深5.1 平面反射的倒影渐变思路与材质节点ue平面反射倒影渐变这个需求本质上是做水面的反射衰减——靠近岸边的水面反射弱越往远处反射越清晰或者反过来。实现链条是这样的场景里放一个Planar Reflection组件材质里必须使用PlanarReflection 材质函数节点去取反射纹理而不是普通的 Scene Capture。拿到反射结果之后渐变就交给遮罩了。我常用三种遮罩来源各有适用场景第一种是距离遮罩。用像素的 World Position 到反射中心点的距离做归一化再送进一条曲线越远越清晰或越远越淡都可以。优点是全自动缺点是弯曲河道会算不准。第二种是 Fresnel。视角越平越强适合做远处水面像镜子、脚下水面能看到底的经典效果。这个几乎零成本是最通用的方案。第三种是美术手绘遮罩。用一张贴图或者顶点色让美术直接在岸边画一条淡出带。这是效果最好、也最常见的做法尤其是风格化项目。代价是要额外维护一张遮罩图河道改形状就得重画。然后就是把这些遮罩跟基础水面颜色做 Lerp也可以用遮罩去控制反射的模糊程度让远处更锐利、近处更糊视觉上会更自然。性能上要说清楚平面反射是额外渲染一遍场景代价不小。策略游戏里如果有一大片水域直接开全分辨率平面反射基本是把帧率砍半。我的做法是反射分辨率降到一半甚至更低、配合距离剔除只在近处启用或者干脆用 SceneCapture2D 低频更新比如每三帧更新一次来伪装。真要极致省就上屏幕空间反射加噪声扰动水面动起来的时候破绽不明显。注意平面反射渲染的是镜像相机它默认不包含半透明物体也不会显示水面自身。调试的时候如果发现倒影里少了一棵树先去检查那个物体的 ShowFlags 和材质是否被反射排除掉了。5.2 半透明物体为什么不吃景深Separate Translucency那点事ue半透明景深这个组合让很多人困惑明明开了景深远处的半透明玻璃、水雾、粒子却还是清清楚楚。根因是Separate Translucency分离半透明。这个功能默认是开的它的工作方式是不透明几何先渲染并接受后处理包括景深半透明物体单独走一趟在景深之后才合成到画面上。也就是说半透明物体根本没参与景深那道工序自然不会被模糊。你可以用控制台命令r.SeparateTranslucency 0关掉它验证一下关掉之后半透明就会和场景一起被模糊——但同时会带来一堆副作用比如半透明和粒子的排序、光照表现都会变所以这是个全局开关不建议在正式项目里长期关。5.3 让半透明参与景深的代码级做法与代价正确的做法是按材质逐个打开。在材质的细节面板里找到 Translucency 分组里面有Output Depth和Output Velocity这两个开关不同版本位置略有差异5.x 基本都在这一组。打开 Output Depth 之后这个半透明材质会把自己的深度写出去后处理里的景深就能拿到正确深度去模糊它Output Velocity 则是给运动模糊用的如果你的项目开了运动模糊而且半透明物体在动这个也得开。代价是这类材质不能再依赖默认的半透明排序而且会有一点点额外的渲染开销。所以我的策略是只给真正需要的材质开——水雾、玻璃、需要被景深模糊的魔法特效普通发光贴片就不开。顺带把三种方案的取舍整理成表方案改动范围效果风险材质开 Output Depth单个材质精准只影响目标物体需检查排序开销略增全局关 Separate Translucency整个项目所有半透明都吃景深排序、粒子、光照表现变化大用不透明材质伪装单个材质零风险最省失去半透明边缘过渡还有一个常见误判改了材质发现没生效先确认三点——材质有没有编译保存、场景里是不是有 Post Process Volume 覆盖了景深参数、以及是不是在移动端渲染路径下移动端和桌面端的后处理能力差异很大很多桌面能做的在移动端根本没有。另外景深强度本身受焦距和光圈影响把焦距拉得离物体很近再调比来回改代码有效得多。6. 文本与字符串FString、FText、FName的分工和换行符陷阱6.1 三者的定位与选择标准ue中的字符串和文本的区别是个问得非常对的问题因为这三个类型混用是新手代码里最常见的坏味道。先把定位摆清楚类型可变性大小写本地化典型用途FString可变敏感不支持拼接、解析、日志输出、文件路径FName不可变不敏感不支持资源名、行名、标签、字典键FText不可变敏感支持所有面向玩家的显示文本FString是干活用的。日志用UE_LOG(LogTemp, Warning, TEXT(%s), *MyString)注意那个星号它是把 FString 转成宽字符指针。文件读取、字符串切割、拼 URL 这类都用它。FName是为查找优化的。它在全局名字表里存一份比较两个 FName 实际上是比较索引所以极快。代价是创建 FName 有开销而且不区分大小写——Fire和fire会被认为是同一个。所以用它当键非常合适当显示文本就完全不合适。FText才是给玩家看的。它支持本地化写法是LOCTEXT(Key, 文本)或者带命名空间的NSLOCTEXT(Namespace, Key, 文本)格式化的标准写法是FText::Format(LOCTEXT(KillMsg, {0} 击杀了 {1}), KillerName, VictimName)。有个必须注意的点从 FString 转 FText 用FText::FromString得到的文本是无法被本地化收集的拿来做临时调试可以做正式 UI 文案绝对不行。同理C 里要让一个 FText 进入本地化流程它必须是被UPROPERTY标记的成员或者出现在LOCTEXT宏里。另外 FText 不能用直接比较要用FText::EqualTo这也是容易翻车的地方。6.2 换行符\n、\r\n与编辑器里的ShiftEnterue的换行符看着是小问题实际坑人不少。分三层来说。C 层。UE 提供了宏LINE_TERMINATOR在 Windows 上它是\r\n在 Linux 上是\n。如果你在做跨平台的文件写出用这个宏比自己敲\n稳妥。反过来读取文件的时候要小心\rWindows 生成的 CSV、TXT 行尾是\r\n按\n切割之后每行末尾会多一个看不见的\r导致字符串比较失败、或者数值解析报错。这种 bug 最气人的地方是肉眼看不出区别我第一次遇到的时候对着两个一模一样的字符串查了一下午。解决办法是用FString::TrimStartAndEnd()清一遍或者显式替换掉\r。数据表层。CSV 导入 DataTable 时最后一行如果没有换行符某些版本会少读一行字段里的引号和逗号也要按 CSV 规范转义。我现在养成的习惯是DataTable 的源文件一律用工具生成而不是手写从源头避免这类问题。编辑器层。Text Block 里输入多行文本时直接敲 Enter 会产生真正的换行符ShiftEnter 是软换行——在编辑器里看起来都是换行但行为不同。富文本控件里换行要用br标签。还有一点在细节面板里输入\n这两个字符某些属性框会把它当字面量处理需要展开成多行编辑框才能真正换行遇到文本里出现了反斜杠 n就是这个原因。最后一个容易被忽略的点做本地化的时候尽量别用硬回车断行。不同语言的句子长度差异很大你按中文长度切好的换行到了另一种语言里可能把单词劈成两半。让控件自动换行比手动敲回车靠谱得多。7. 帧率低了从哪查我的固定排查顺序7.1 第一步永远是stat unit先分清CPU还是GPUue排查帧率低的原因这件事最大的忌讳是凭感觉猜。是不是特效太多了是不是模型面数太高这类猜测十有八九是错的。我的第一步永远是stat unit看那四个数Frame、Game、Draw、GPU。判读逻辑很朴素Frame 大致等于 Game、Draw、GPU 三者里最大的那个。所以哪个数最接近 Frame瓶颈就在哪边。Game 高说明游戏线程忙通常是蓝图逻辑、物理、AI、动画计算Draw 高说明渲染线程忙常见于绘制调用太多、UI 太复杂GPU 高说明显卡扛不住通常是像素着色、后处理、阴影、半透明 Overdraw。这里有个我踩过的坑Game 和 Draw 会互相掩盖。游戏线程卡的时候Draw 看起来很低你就以为渲染没问题其实只是渲染线程在等游戏线程。所以一定要看完整的三个数别看单个。另外一个前提条件必须在固定场景、固定视角、固定位置测。边走边看数字跳来跳去什么结论都得不出来。我一般是加载一个测试关卡把相机对准最重的那个视角站定等五秒让数据稳定再截图记录。7.2 CPU侧常见元凶与Blueprint层面的自查清单确定是 Game 高之后我会按这个清单过一遍Actor 的 Tick 数量。stat game配合 Unreal Insights 能看出 Tick 占比几百个 Actor 每帧 Tick 是常见死因。能改 Timer 就改 Timer能用事件驱动就别轮询实在要 Tick 的就把Tick Interval拉长。每帧的全场景查询。GetAllActorsOfClass、GetAllActorsWithTag这类调用如果出现在 Tick 里是典型的性能炸弹。正确做法是在 BeginPlay 里查一次存起来或者用事件通知维护列表。蓝图里的循环加 Cast。ForEachLoop里套Cast数量一上去开销非常可观。能用接口就用接口能把 Cast 提前到循环外就提前。物理模拟物体的数量。stat physics看物理耗时策略游戏里满地掉落的物品如果都开了模拟很容易成为瓶颈。解决办法是掉落后进入休眠、或者用非物理的表现替代。垃圾回收停顿。频繁创建销毁 UObject 会导致 GC 周期性卡顿表现是帧率突然掉一下然后恢复而不是持续低。stat gc可以看 GC 时间减少对象创建、用对象池是主要手段。动画。stat anim看动画耗时大量 Skeletal Mesh 同时更新骨骼是很贵的远景单位该降频就降频。这里补一个方法论上的细节stat unit只能告诉你哪条线程忙具体是谁忙要靠 Unreal Insights。操作是控制台输入stat startfile跑十秒左右再stat stopfile然后打开 Insights 加载那个.utrace文件直接看 Game Thread 里耗时最高的函数排行。这个流程我建议每个人都跑一次跑完你会发现很多你以为是渲染问题的东西其实是某一段蓝图逻辑。7.3 GPU侧与几个常用命令GPU 高的话先看stat gpu它会按渲染阶段列出耗时Base Pass、Lighting、Shadow、PostProcess、Translucency 等。哪个阶段最贵方向就清楚了。阴影是最常见的 GPU 大头。虚拟阴影贴图、级联阴影的距离设置、动态光源数量都会影响。半透明 Overdraw也很常见尤其是有大量粒子叠在一起的时候这时候要去看stat scenerendering里的绘制调用和三角面数再配合 GPU Visualizer快捷键 CtrlShift或者控制台ProfileGPU看具体是哪个 Pass 在烧时间。分辨率是最粗暴也最有效的杠杆。r.ScreenPercentage降到 80 甚至 70视觉上很多人看不出来帧率立刻上去了。此外r.VolumetricFog、r.Lumen相关的开关都可以临时关掉做对照测试用来确认某个效果是不是主要开销。我总结的对照表大致是这样现象先看什么常见原因Game 明显最高stat game / Unreal InsightsTick 过多、全场景查询、蓝图循环Draw 明显最高stat scenerendering绘制调用过多、UI 复杂、材质槽太多GPU 明显最高stat gpu / GPU Visualizer阴影、后处理、半透明 Overdraw帧率周期性抖动stat gc频繁对象创建导致的 GC 停顿只有特定视角掉帧固定视角对比该视角下物体集中、遮挡剔除失效最后说一句最重要的话永远在 Shipping 或者独立进程里做最终确认。编辑器里有大量调试开销你在编辑器里优化到 60 帧打包后可能是 90也可能因为别的因素变成 45两边都不可互相替代。8. 策略游戏方向资料该怎么挑8.1 大规模单位的渲染与逻辑ISM/HISM、MassEntityue策略游戏这个方向最大的技术分水岭就是单位数量。几十个单位怎么实现都行几百上千个架构选择就直接决定项目能不能做下去。渲染侧的结论很明确不要给每个单位一个 Actor Skeletal Mesh。正确路径是InstancedStaticMeshComponentISM或者它的层级版本HISM把同一种单位合并成一次绘制调用。需要动画的话用顶点动画贴图Vertex Animation Texture把骨骼动画烘进贴图在材质里按时间采样更远距离的单位可以用粒子或者带动画的贴片顶替——City Sample 里的大规模人群就是这么分层的近处是完整骨骼动画中距离切简化动画远处直接变粒子。逻辑侧的答案是MassEntity。它是一套面向数据的设计ECS 思路把单位拆成一组稀疏的 Fragment 数据由 Processor 批量处理不再需要每个单位一个 Actor 一个 Tick。配套的还有 MassAI 做行为、MassCrowd 做群体导航。这套东西上手曲线陡但它是目前 UE 里做几千个单位同时跑逻辑最正经的方案。我的建议是先在 City Sample 里跑一遍看效果再决定要不要在自己的项目里上——因为它会改变你整个 Gameplay 层的写法中期切换代价极大。8.2 值得拆的官方示例与拆解顺序策略游戏能参考的官方资源不少但很多人的问题是下载了、打开了、不知道该看哪。我按用途列一下我实际拆过的几个示例主要参考价值适合解决的问题Content Examples各系统的最小示例想搞懂某个功能怎么用LyraGAS、输入、UI、网络技能系统、角色框架怎么搭City SampleMassEntity、MassCrowd大规模单位与人群Cropout小体量策略玩法、破坏单位选择、资源循环、小团队做法Action RPGGAS 实战、AI技能与战斗数值落地拆解顺序我有一套固定流程四步先跑通再找入口再看数据流最后做最小复现。第二步找入口是最容易卡住的地方因为官方示例动辄几百个类你不知道从哪开始。我的做法是打开 GameMode 和 PlayerController顺着 BeginPlay 往下追把关键系统列成一张入口类清单写在笔记里。这张清单比任何教程都值钱因为它是我自己在那个项目里走通的路线。顺带说一个策略游戏专属的常见需求框选。思路是在鼠标按下和抬起之间画一个矩形把这个矩形投影到世界空间用GetActorsInSelectionRectangle之类的接口筛选范围内的单位。地面点选则用 LineTrace 打到地面再通过导航系统把落点校正到可行走区域。这两个功能都不难但要在几百个单位下保持流畅就需要配合前面说的实例化渲染和批量逻辑处理否则光选中高亮就能把帧率吃掉一半。这套资料库我维护了差不多两年最大的体会是整理的价值不在于存了多少而在于下次遇到同类问题时我能多快拿到一个自己验证过的结论。我现在查一个半透明景深的问题从打开笔记到定位到那个最小工程大概两分钟换成两年前翻收藏夹可能已经跑去搜第三遍了。另外有个小技巧很管用就是每次解决完一个坑顺手在笔记里加一行下次先看什么——这行字通常是整篇笔记里最省时间的部分因为它记录的是我当时的思路顺序而不是结论本身。