UE5.8 项目模板与目录结构:从新建工程到团队踩坑实录

📑 目录

UE5.8 项目模板与目录结构:从新建工程到团队踩坑实录

去年冬天,我帮一个做开放世界手游的团队排查打包问题。他们的工程开了三个月,某天 CI 突然挂掉,报错信息是 Module xxx referenced by yyy is not available。我远程连过去,第一眼就被项目根目录惊呆了:Binaries 里面躺着三年前的旧 DLL,Intermediate 目录超过 120 GB,Content 根目录下散落着三十多个未命名的测试地图。策划找不到正式关卡,美术分不清哪些材质还在用,程序则在为循环依赖掉头发。

说白了,这个工程从新建项目的第一步就埋下了祸根。模板选错了,目录管乱了,临时文件没人敢删。UE5.8 的目录结构看着复杂,其实每一块都有明确的职责。今天我把这些年踩过的坑整理出来,聊清楚项目模板怎么选、各个目录该放什么、哪些文件能删、哪些碰都不能碰。

新建项目时,模板到底在决定什么

打开 Epic Games Launcher,点进 UE5.8 的创建项目界面,左侧会列出几大类模板:Games、Film / Television / Live Events、Architecture / Engineering / Construction,还有 Automotive、Simulation 等。很多人习惯直接点 Games,觉得反正以后都能改。这个想法对了一半,模板确实可以随时替换内容,但不同模板在创建瞬间就决定了几件事:默认启用的插件、渲染设置、输入映射、时间轴配置、以及初始 Content 的组织方式。

我习惯把模板理解成「开局配置包」。选一个贴近目标的模板,能少改很多东西。选一个差太远的模板,等于开局就多背一包袱不需要的配置。

Games 模板:客户端团队的主战场

Games 下面最常用的有 Blank、First Person、Third Person、Top Down、Handheld AR、Virtual Reality、Vehicle 等。UE5.8 在这些模板里加入了 Variants 机制,同一个 Third Person 模板可以选 Combat、Side Scroller、Platforming 等变体。我看过 Epic 在 5.6 之后的更新说明,Variants 的目标很明确:让原型启动更快,减少你删掉默认资产再重做的步骤。

以我带过的动作游戏团队为例,他们一开始选了 Blank,然后从 Marketplace 导了角色、武器、动画,结果输入系统、GameMode、PlayerState 全部要自己搭。两个月后换成 Third Person Combat Variant,策划直接就能拿到一个能跑能打的角色,摄像机、受击反馈、血条 UI 都现成的。这个切换让他们原型阶段省了两周,代价只是把之前自研的那套半成品 GameMode 删掉。

Games 模板的核心特点:

  • 默认启用 Enhanced Input、Chaos Physics、Niagara。
  • 渲染偏向实时性能,默认关闭光线追踪,Lumen 根据平台可选。
  • 默认包含 PlayerController、GameMode、Character 的蓝图骨架。
  • 适合需要 Tick、Replication、Gameplay Ability System 的项目。

如果你的目标是客户端游戏、独立游戏、联网对战,Games 模板是最直接的起点。

Film / Television / Live Events 模板:虚拟制片的那套逻辑

影视和直播类模板的核心是 nDisplay、DMX、Live Link、Switchboard、In-Camera VFX。UE5.8 的 Film 模板默认会启用 Virtual Production Utilities、DMX、nDisplay、Movie Render Queue 这些插件。工程创建后,Content 里会有 LED Wall、Camera Rig、Light Card 等预设。

去年我接触过一个做 XR 舞台的项目,他们直接用 Games 模板起步,结果三个月后发现需要 nDisplay,手动启用插件时发现大量渲染设置冲突。特别是默认的 Forward Shading 和影视常用的 Deferred + Ray Tracing 混在一起,改起来很痛苦。如果他们当时直接选 nDisplay Template,初始就会拿到 NDC_Basic、NDC_Cave、NDC_WallCurved3x2 等示例配置,省掉大量从零搭建集群显示的工作。

影视模板的关键差异:

  • 默认启用 nDisplay、DMX、Live Link、Switchboard。
  • 渲染设置偏向高质量、稳定帧率,常用 Movie Render Queue 输出序列帧。
  • 时间轴和 Sequencer 的使用权重远高于 gameplay。
  • 多机位、多视口、跟踪系统整合是常态。

做游戏的同学如果临时接影视项目,最容易忽略的一点就是:Film 模板不会默认给你 GameMode 那一套东西,它的世界是以镜头和序列为中心的。

Architecture / Engineering / Construction 模板:建筑可视化的起点

AEC 模板在 UE5.8 里主要面向建筑可视化、数字孪生、BIM 数据展示。它默认启用 Datasmith、Twinmotion Content、Sun Position、Georeferencing 等插件。Content 里通常会有 ArchViz 材质库、家具、植被、灯光预设。

AEC 模板和 Games 模板最大的区别在数据流。游戏项目的内容大多直接在 Content Browser 里制作,而 AEC 项目的内容常常来自 Revit、Rhino、SketchUp、3ds Max,通过 Datasmith 或 Twinmotion 导入。模板的选择会影响默认单位、光照单位、LOD 策略、以及是否启用 GeoReferencing。

我有个朋友做售楼部数字孪生,一开始用 Games 模板,导入 Revit 模型后单位对不上,一栋楼的尺寸差了 100 倍。后来换成 AEC ArchViz 模板,默认单位是厘米但支持米级场景坐标,配合 Datasmith 的层级保留,模型导入后结构基本不用重建。

AEC 模板的关键差异:

  • 默认启用 Datasmith、SunSky、Volumetric Cloud、Twinmotion Content。
  • 单位与坐标系设置偏向建筑尺度。
  • 材质和光照预设偏向静态展示和漫游。
  • 弱化 gameplay,强化导入、渲染、导出管线。

下面这张图展示了三类模板在创建时的核心差异:

architecture-beta
    group games[Games 模板]
        service enhanced_input(Enhanced Input)
        service chaos(Chaos Physics)
        service gas(Gameplay Ability System)
    group film[Film / Television / Live Events 模板]
        service ndisplay(nDisplay)
        service dmx(DMX)
        service live_link(Live Link)
        service sequencer(Sequencer)
    group aec[Architecture / Engineering / Construction 模板]
        service datasmith(Datasmith)
        service sunsky(SunSky)
        service twinmotion(Twinmotion Content)
        service georef(GeoReferencing)

    games:T --> film:T
    film:T --> aec:T

目录结构:每个文件夹都有脾气

模板选完,工程真正写到磁盘上,考验才刚开始。UE5.8 的项目根目录通常长这样:

MyProject/
├── Binaries/
├── Config/
├── Content/
├── DerivedDataCache/
├── Intermediate/
├── Plugins/
├── Saved/
├── Source/
├── MyProject.uproject
└── MyProject.sln

这些目录里,Source、Config、Content、Plugins、.uproject 是项目的「本体」,丢了就真没了。Binaries、Intermediate、DerivedDataCache、Saved 是生成文件,删了能重建,但删的时候要讲究方法。

Content:资产的战场,也是最容易失控的地方

Content 目录放的是 .uasset、.umap、材质、模型、纹理、蓝图、声音、动画等所有编辑器资产。UE 编辑器打开项目时,读取的就是这里的内容。

我定过一条团队规矩:Content 根目录下只允许存在逻辑明确的顶层文件夹,禁止直接放资产文件。一个常见且可用的划分是:

Content/
├── Core/
│   └── 全局材质、基础蓝图、输入映射
├── Characters/
│   └── 玩家角色、NPC、动画
├── Environments/
│   └── 关卡、地编资产、植被
├── UI/
│   └── 控件蓝图、字体、图标
├── Weapons/
│   └── 武器、道具
└── Maps/
    └── 正式关卡、测试关卡

这个规矩执行起来 hardest 的部分是阻止策划在根目录建测试地图。我见过的灾难性项目里,Content 根目录经常有 TestMapTestMap_NewTestMap_New_FinalTestMap_New_Final_Really 这种文件。最后谁也不知道哪个是正式的。

Content 还有一个隐藏雷区:重定向器(Redirector)。移动或重命名资产后,UE 会生成一个重定向器文件,让旧引用继续工作。如果长时间不修复,Content 里会积累大量 .uasset 重定向器,拖慢加载。我们团队每周五下午会花半小时右键 Fix Up Redirectors,这个习惯救过好几次命。

Source:C++ 模块的家

Source 目录按模块组织。一个最简单的 C++ 项目可能只有一个模块,结构如下:

Source/
└── MyProject/
    ├── MyProject.Build.cs
    ├── MyProject.Target.cs
    ├── Private/
    │   ├── MyProject.cpp
    │   ├── MyGameMode.cpp
    │   └── MyCharacter.cpp
    └── Public/
        ├── MyGameMode.h
        └── MyCharacter.h

Private 放 .cpp 和不愿暴露的头文件,Public 放对外暴露的头文件。这个划分不是摆设,它直接决定 UBT 编译时的 include 可见性。Public 目录里的头文件可以被其他模块 include,Private 里的不行。

模块变多之后,Source 会变成这样:

Source/
├── MyProject/
│   ├── MyProject.Build.cs
│   ├── Private/
│   └── Public/
├── MyProjectEditor/
│   ├── MyProjectEditor.Build.cs
│   ├── Private/
│   └── Public/
└── MyProjectGameplay/
    ├── MyProjectGameplay.Build.cs
    ├── Private/
    └── Public/

拆模块的核心目的是控制编译依赖。把所有代码塞一个模块,初期很快,后期随便改一行 .h 都可能触发全量编译。我一般在项目人数超过 8 人、模块数量超过 3 个时开始认真拆模块。拆得太早也会浪费,毕竟模块间通信需要接口设计,小公司折腾不起。

下面是一个简化版的 .Build.cs 示例:

using UnrealBuildTool;

public class MyProject : ModuleRules
{
    public MyProject(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] {
            "Core",
            "CoreUObject",
            "Engine",
            "InputCore",
            "EnhancedInput"
        });

        PrivateDependencyModuleNames.AddRange(new string[] {
            "Projects",
            "GameplayTags"
        });
    }
}

PublicDependencyModuleNames 里的依赖会传递给引用本模块的其他模块;PrivateDependencyModuleNames 只在本模块内部生效。这个边界一定要守住,否则很容易出现「我在 A 模块里 include 了 B 模块的私有头文件,本机能编过,CI 却报错」的情况。

Config:控制引擎行为的配置文件

Config 目录放的是 .ini 文件,DefaultEngine.ini、DefaultGame.ini、DefaultInput.ini 是最常见的几个。这些文件在打包和运行时会被加载,决定渲染、输入、网络、日志等行为。

我总结过一个小经验:DefaultEngine.ini 里的设置要慎重改,因为它会被继承到所有平台。平台相关的覆盖应该放在 Config/Windows/、Config/Android/ 等子目录下。我们曾经在一个项目里把 r.DefaultFeature.MotionBlur 直接在 DefaultEngine.ini 里设为 false,结果 PS5 版本需要动态模糊,不得不又加了一层平台配置覆盖回去。

Config 里还有一个容易忽略的文件:DefaultEditor.ini。编辑器偏好、内容浏览器过滤器、关卡编辑器视口设置都可以放在这里。团队规模变大后,统一编辑器配置能减少很多「我这儿看着正常,你那儿怎么偏色」的扯皮。

Plugins:项目级插件和第三方 SDK

Plugins 目录放的是项目专用的插件。这些插件可以是 Marketplace 下载的,也可以是自己写的。每个插件有自己的 Content、Source、Config、Resources,以及一个 .uplugin 描述文件。

一个插件的典型结构:

Plugins/
└── MyCustomPlugin/
    ├── MyCustomPlugin.uplugin
    ├── Content/
    ├── Resources/
    └── Source/
        └── MyCustomPlugin/
            ├── MyCustomPlugin.Build.cs
            ├── Private/
            └── Public/

.uplugin 文件长这样:

{
    "FileVersion": 3,
    "Version": 1,
    "VersionName": "1.0",
    "FriendlyName": "My Custom Plugin",
    "Description": "项目级自定义插件",
    "Category": "Gameplay",
    "CreatedBy": "My Studio",
    "CanContainContent": true,
    "IsBetaVersion": false,
    "Installed": false,
    "Modules": [
        {
            "Name": "MyCustomPlugin",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        }
    ],
    "Plugins": [
        {
            "Name": "GameplayTags",
            "Enabled": true
        }
    ]
}

把功能拆成插件有几个好处:

  • 插件可以单独编译、单独开关。
  • 多个项目之间可以复用同一个插件。
  • Marketplace 购买的插件放进 Plugins,升级时替换整个文件夹即可。

不过插件也有坑。插件之间如果出现循环依赖,编译报错会非常难看。我习惯在插件里再拆模块,让核心逻辑和编辑器工具分开,避免把 Runtime 和 Editor 代码混在一起。

生成目录:能删,但要懂怎么删

Binaries、Intermediate、Saved、DerivedDataCache 都是运行和编译过程中生成的。很多新手不敢删,怕项目坏掉。其实真正不能删的是 Content、Source、Config、Plugins、.uproject。只要这些在,其他都能重建。

Binaries:编译产物,删之前先关编辑器

Binaries 放的是编译出来的 DLL、可执行文件、符号文件。Windows 上主要是 .dll 和 .pdb,主机平台会有对应格式。这个目录可以整个删掉,下次 Generate Visual Studio Project Files 或编译时会重建。

有个细节要注意:Binaries 里的 DLL 在编辑器运行时被占用,直接删会报文件占用。正确做法是关闭编辑器、关闭 Visual Studio、甚至杀掉残留的 UE 进程,再删。我在 CI 脚本里会先运行 taskkill /F /IM UE4Editor.exe 再清理 Binaries。

Intermediate:临时文件的集散地

Intermediate 是 UBT 和编辑器生成临时文件的地方,包括:

  • Visual Studio / Xcode 项目文件
  • 反射生成的 .generated.h / .gen.cpp
  • Shader 编译缓存
  • 光照贴图构建中间数据
  • 打包时的 staging 文件

Intermediate 通常占空间最大,删了最解压。但删完之后第一次打开工程会重新生成项目文件、重新准备 shader,可能要等十几分钟。大项目我一般在周五晚上删,让机器周末自己热身。

Saved:日志、自动保存、崩溃 dump

Saved 目录放的是运行时生成的数据:

  • Saved/Logs:编辑器日志
  • Saved/Autosaves:自动保存的关卡
  • Saved/Crashes:崩溃 dump
  • Saved/Config:本地覆盖的配置
  • Saved/StagedBuilds:打包输出

Saved/Autosaves 在关键时候能救命。我曾经遇到美术在蓝图中连错线导致编辑器崩溃,靠 Autosaves 里五分钟前的版本找回了半小时的工作。但 Autosaves 也会越积越多,建议定期清理,或者只保留最近一周的。

DerivedDataCache:被误解最深的目录

DerivedDataCache,简称 DDC,放的是派生数据缓存。它的作用是把资产加载时生成的中间结果存起来,下次加载直接读缓存,避免重复计算。典型内容包括:

  • 纹理压缩后的平台格式
  • 静态网格的 LOD 和碰撞数据
  • 材质编译后的 shader
  • Niagara 模拟缓存

没有 DDC,项目第一次加载会很慢,但不会坏。DDC 分为本地 DDC 和共享 DDC。团队规模大了之后,建议搭一个共享 DDC 服务器,这样美术导一张 8K 纹理,全团队都不用再压缩一次。

本地 DDC 默认在 %LOCALAPPDATA%\UnrealEngine\Common\DerivedDataCache,项目级 DDC 在工程目录的 DerivedDataCache 文件夹里。清理 DDC 是排查材质、纹理、shader 异常的经典手段。清完后第一次加载会重新生成,耐心等待即可。

下面这张图展示了项目启动时各目录的协作流程:

sequenceDiagram
    participant U as 用户双击 .uproject
    participant UB as Unreal Build Tool
    participant S as Source / Config
    participant P as Plugins
    participant I as Intermediate
    participant B as Binaries
    participant D as DerivedDataCache
    participant C as Content
    participant E as 编辑器启动

    U->>UB: 解析 .uproject 与模块
    UB->>S: 读取 .Build.cs / .Target.cs
    UB->>P: 加载插件依赖
    UB->>I: 生成项目文件与反射代码
    UB->>B: 编译 DLL / 可执行文件
    UB->>D: 写入派生数据缓存
    E->>C: 加载 .uasset / .umap
    E->>D: 读取已缓存的派生数据
    E->>E: 完成初始化

配置文件之间的层级关系

.uproject、.uplugin、.Build.cs、.Target.cs 这四个文件经常被混淆。它们的关系可以用一张图说清楚:

graph TD
    A[MyProject.uproject] --> B[插件列表与模块声明]
    A --> C[引擎版本 EngineAssociation]
    B --> D[PluginA.uplugin]
    B --> E[PluginB.uplugin]
    D --> F[PluginA.Build.cs]
    E --> G[PluginB.Build.cs]
    A --> H[Source/MyProject/MyProject.Build.cs]
    H --> I[Public 头文件]
    H --> J[Private 源码]
    K[Source/MyProject/MyProject.Target.cs] --> L[目标平台与编译配置]
    K --> A
  • .uproject:项目的入口描述文件,告诉引擎项目名称、依赖的引擎版本、启用的模块和插件。
  • .uplugin:插件的描述文件,描述插件版本、模块、依赖的其他插件。
  • .Build.cs:模块构建规则,声明依赖、源码路径、编译宏、平台特化逻辑。
  • .Target.cs:目标构建规则,声明编译目标类型(Editor、Game、Client、Server)、平台、配置。

一个 .Target.cs 的简化示例:

using UnrealBuildTool;
using System.Collections.Generic;

public class MyProjectTarget : TargetRules
{
    public MyProjectTarget(TargetInfo Target) : base(Target)
    {
        Type = TargetType.Game;
        DefaultBuildSettings = BuildSettingsVersion.V5;
        IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_4;
        ExtraModuleNames.Add("MyProject");
    }
}

Editor 目标会有单独的 MyProjectEditor.Target.cs,类型是 TargetType.Editor。Dedicated Server 则用 TargetType.Server。很多做联网游戏的团队会同时拥有 Game、Editor、Server 三个 Target 文件。

一个真实团队的修复故事

回到开头那个开放世界手游团队。我们花了两天时间把工程从混乱状态拉回来,步骤大致如下:

第一天,先备份整个工程,然后关掉所有编辑器进程,删除 Binaries、Intermediate、Saved/Logs、Saved/Crashes、DerivedDataCache。磁盘立刻腾出 140 GB。第二天,重新 Generate Visual Studio Project Files,全量编译通过。

接着整理 Content。我们把所有测试地图移到 Content/_Deprecated/,让策划认领。一周内无人认领的地图直接删除。根目录只保留 Core、Characters、Environments、UI、Weapons、Maps 六个顶层文件夹。然后全工程 Fix Up Redirectors,重定向器从三百多个降到十二个。

Source 这边,原本所有代码塞在 MyProject 一个模块里。我们拆出了三个模块:MyProjectCore、MyProjectGameplay、MyProjectEditor。循环依赖通过接口类和事件系统解耦。拆完之后,日常增量编译时间从七分半降到两分钟出头。

最后把第三方 SDK 和工具脚本放进 Plugins,每个插件配好 .uplugin,CI 里用 -Plugin 参数控制哪些插件参与打包。

整个过程里,最大的阻力不是技术,是人的习惯。策划舍不得删测试关卡,程序舍不得拆模块,美术舍不得清 DDC。但磁盘空间、编译时间、加载时间这些数据摆出来,大家也就服了。

目录占用与重建成本估算

我们用下面这个简单的估算式来理解各目录的空间价值:

\text{工程总占用} \approx C_{content} + C_{source} + C_{config} + C_{plugins} + C_{binaries} + C_{intermediate} + C_{ddc} + C_{saved}

其中 Content、Source、Config、Plugins 是「真实资产」,应该进版本控制。Binaries、Intermediate、Saved、DDC 是「派生资产」,不应该进 Git。我见过的项目里,这四类生成目录加起来经常是真实资产的 5 到 20 倍。把它们加进 .gitignore 是基本操作:

Binaries/
DerivedDataCache/
Intermediate/
Saved/
.vs/
*.sln
*.VC.db

注意 .sln 文件是生成的,不该进版本控制。只有 .uproject、Source、Content、Config、Plugins 需要保留。

模板选择决策流程

如果你还是不确定选哪个模板,可以参考下面的决策流:

flowchart TD
    A[项目目标是什么] --> B{需要玩家操控角色吗}
    B -->|是| C[选 Games 模板]
    B -->|否| D{需要 LED 墙 / 多机位 / 实时合成吗}
    D -->|是| E[选 Film / Television / Live Events 模板]
    D -->|否| F{需要导入 BIM / CAD / 建筑模型吗}
    F -->|是| G[选 Architecture / Engineering / Construction 模板]
    F -->|否| H[选 Blank 模板,按需添加插件]
    C --> I[根据视角选 First Person / Third Person / Top Down 等]
    E --> J[优先选 nDisplay Template]
    G --> K[优先选 ArchViz 模板]

Blank 模板不是不能用,而是需要你清楚自己要什么。新手用 Blank 容易迷失,老手用 Blank 反而最干净。

结尾:结构是底线,习惯决定上限

UE5.8 的目录结构和项目模板不是新知识,但每年都有项目在这些基础环节翻车。模板选错可以重构,目录乱套可以整理,真正难改的是团队的文件管理习惯。我见过太多团队在冲刺阶段为了省一天时间,把资产随意堆放,结果半年后花一个月还债。

我的建议很简单:

  • 新建项目前先想清楚目标,别为了省事直接 Blank。
  • Content 根目录定下规则,测试内容定期清理。
  • Source 按模块拆分,守住 Public/Private 边界。
  • 生成目录大胆删,但记得先备份、关进程。
  • 把 .uproject、.uplugin、.Build.cs、.Target.cs 的关系吃透, troubleshooting 时能省大量时间。

项目结构这件事,说到底就是一句话:让该放的东西待在该待的地方。你尊重它,它就会在打包、协作、排错的时候回报你。