第十七章:程序化刷新区
1. 本章核心目标
本章节的目标分为显性与隐性两个层面,它们共同服务于构建一个动态、高性能的开放世界。
-
显性目标 (玩家体验层面):
-
动态AI生成: 实现一个区域系统(Zone System),当玩家进入特定区域时,动态生成AI(如鹿、狼),离开时则销毁它们,使世界显得生动且不可预测。
-
全场景植被采伐: 使地图上所有由程序化内容生成(PCG)的静态植被(Instanced Static Meshes)变为可采伐资源。玩家可以使用斧头砍树,或直接用手采集灌木。
-
资源再生: 为被采伐的植被(树木、岩石、灌木)添加重生(Respawn)逻辑,使其在一段时间后能够重新出现在世界中。
-
-
隐性/战略目标 (技术与项目管理层面):
-
性能优化:
-
AI生命周期管理: 通过区域化加载和卸载AI,避免在整个大型地图上持续运行所有AI的逻辑,极大地节省了CPU资源。
-
动态导航网格 (Dynamic NavMesh): 采用
Navigation Invokers
技术,仅在AI周围动态生成导航网格,而不是预烘焙覆盖整个大地图的导航数据,显著降低了内存占用和加载时间。 -
异步资源加载: 在采伐系统中,使用软引用(Soft References)和异步加载(Async Load)来加载被转换的蓝图Actor,避免了因同步加载大量资源而可能导致的瞬间卡顿。
-
-
系统解耦与可扩展性:
-
通过数据驱动(Data-Driven)的方式设计采伐系统,使用数据表(Data Table)将静态网格体(Static Mesh)与其对应的可采伐蓝图(Blueprint)和资源信息关联起来,使添加新的可采伐物种无需修改核心代码,提高了系统的可扩展性。
-
广泛使用接口(Interfaces)来降低系统间的耦合度,例如,采伐工具通过区域接口(
BPI_Zone
)来请求移除植被,而无需了解BP_Zone
内部的具体实现。
-
-
多人游戏同步的鲁棒性:
- 解决实例静态网格(ISM)在多人环境下的移除同步问题,特别是要确保后加入游戏的玩家能看到已经被其他玩家采伐的植被是消失状态,这是实现持久化世界体验的关键一步。
-
2. 系统与功能实现
本章主要实现和修改了以下几个核心系统:
系统/功能 (System/Function) | 详细描述 | 与前期系统的交互/依赖 |
---|---|---|
程序化区域 (BP_Zone ) |
一个基于Actor的蓝图,包含一个大型的碰撞盒。它负责管理其范围内的AI和植被的生命周期。 | 依赖: 玩家角色(用于触发重叠事件)。 交互: 主动生成和销毁AI Actor,管理植被重生计时器。 |
AI生成与 despawn 逻辑 | 玩家进入区域时,服务器在该区域内通过Line Trace寻找有效地面位置,随机生成1-3个被动AI和1-2个攻击性AI。 玩家离开后,启动一个300秒的计时器,若区域内无其他玩家,则销毁所有AI。 | 依赖: AI Master蓝图、玩家Pawn的Tag(“Player”)。 冲突: 初始版本AI会生成在水中,后通过 BPI_Water 接口或Tag进行规避。 |
动态采伐系统 (ISM -> Actor) | 修改了Hatchet_Master 和FirstPersonCharacter 的交互逻辑。当与一个实例静态网格(ISM)交互时,系统会:1. 获取ISM的显示名称。 2. 在数据表 ( DT_LargeHarvestable 或DT_GroundHarvestables ) 中查找对应的可采伐蓝图类(软引用)。3. 异步加载并生成该蓝图Actor。 4. 通过区域接口调用,移除原始的ISM实例,并确保此操作在所有客户端同步。 |
依赖: 前期的采伐工具、角色交互系统、数据表结构。 交互: 与 BP_Zone 通过BPI_Zone 接口通信,以处理ISM的移除和后续的重生逻辑。 |
植被重生系统 | 在植被对应的Destructible_Mesh 蓝图被销毁前,它会调用BP_Zone 的接口,将自身的网格体、变换信息记录到BP_Zone 的一个数组中。 BP_Zone 启动一个长计时器(1800秒),计时结束后,若区域内无玩家,则遍历数组,重新生成这些植被。 |
依赖: BP_Zone 系统、Destructible Mesh系统。交互: 通过 BP_FoliageRemove 这个辅助Actor的RepNotify 机制,确保被移除的植被对后加入的玩家也可见,并在重生时被正确销毁。 |
动态导航系统 | 采用引擎的Navigation Invokers 功能。在AI Master蓝图上添加该组件,并启用项目设置中的"Generate Navigation Only Around Invokers"。 |
依赖: AI系统。 交互: 引擎的导航系统会围绕带有Invoker的AI动态生成导航网格,而不是一次性为整个地图生成,极大优化了性能。 |
3. 关键设计思想
本章的实现体现了多种优秀的设计模式与原则,确保了系统的健壮性和可维护性。
- 设计模式 (Design Patterns):
设计模式 | 应用实例 | 目的与优势 |
---|---|---|
数据驱动 (Data-Driven) | 使用DT_LargeHarvestable 和DT_GroundHarvestables 数据表,将静态网格体名称映射到其可采伐的蓝图类。 |
解耦与扩展性: 无需修改代码即可添加新的可采伐物种。策划和美术可直接通过编辑数据表来配置游戏内容。 |
接口模式 (Interface) | BPI_Zone 、BPI_Water 、BPI_Controller 等接口被广泛使用。例如,任何工具或角色都可以通过BPI_Zone 的RemoveFoliage 函数来移除植被,而无需知道BP_Zone 的具体实现。 |
降低耦合度: 使得系统模块之间可以独立修改和演进,提高了代码的灵活性和可重用性。 |
观察者模式 (Observer) | 通过RepNotify (Replicated Using Notify) 机制实现。BP_FoliageRemove Actor中的一个复制变量被修改时,所有客户端都会自动执行OnRep 函数,从而触发植被实例的移除。 |
状态同步: 提供了一种高效、自动化的方式来同步服务器上的状态变更到所有客户端,尤其对处理后加入玩家的状态同步至关重要。 |
异步加载 (Async Loading) | 在采伐系统中,从数据表获取到蓝图类的软引用后,使用AsyncLoad 节点来加载该类,加载完成后再生成Actor。 |
性能优化: 避免了在游戏主线程中同步加载资源导致的卡顿,保证了流畅的游戏体验。 |
- 设计原则 (Design Principles):
设计原则 | 体现 |
---|---|
单一职责原则 (SRP) | BP_Zone 负责区域内的生命周期管理。Hatchet_Master 负责斧头的采伐逻辑。BP_FoliageRemove 专门负责处理复制移除的逻辑。每个类的职责都非常清晰。 |
开闭原则 (OCP) | 采伐系统对扩展是开放的(可以通过数据表添加新内容),对修改是封闭的(核心的采伐和移除逻辑不需要改动)。 |
依赖倒置原则 (DIP) | 高层模块(如采伐工具)不直接依赖底层模块(如BP_Zone ),而是两者都依赖于抽象(如BPI_Zone 接口)。 |
4. 核心技术点与难点
技术点/难点 | 描述与解决方案 |
---|---|
实例静态网格的交互与移除 | 难点: 实例静态网格(ISM)本身不是Actor,无法直接绑定交互逻辑和进行网络复制。 解决方案: 采用“替换法”。当检测到对ISM的交互时,获取该实例的变换(Transform),在相同位置生成一个带有完整交互和销毁逻辑的蓝图Actor,然后服务器调用 RemoveInstance 移除原始的ISM实例。 |
多人同步中的“幽灵”问题 | 难点: 初期使用简单的多播(Multicast)RPC移除ISM实例,在网络不佳时可能导致客户端移除失败,留下一个无法交互的“幽灵”植被。 解决方案(重构): 采用了更可靠的同步策略。服务器获取所有在线玩家的控制器,然后为每个控制器调用一个 RunOnClient RPC,确保RemoveInstance 指令在每个客户端本地执行,从而杜绝了幽灵问题。 |
后加入玩家的状态同步 | 难点: 如何让一个新加入服务器的玩家看到在他加入之前就已经被砍掉的树? 解决方案: 设计了 BP_FoliageRemove Actor。每当一个植被被移除,就在其位置生成一个该Actor。此Actor被设置为网络复制,其内部一个关键变量使用RepNotify 。当新玩家加入时,服务器会向他同步这个Actor的存在,OnRep 函数被自动触发,执行移除相应ISM实例的逻辑,保证了世界的持久性和一致性。 |
开放世界性能优化 | 难点: 在巨大的地图上维持高性能。 解决方案: 综合运用了多种技术:1. 区域化加载( BP_Zone )管理AI和逻辑。 2. 动态导航网格(Navigation Invokers )避免巨大的静态导航数据。 3. 异步加载(AsyncLoad )防止资源加载卡顿。 |
5. 自我批判与重构
-
遇到的“坑”或关键问题:
-
“幽灵”实例问题: 如上所述,初期的多播同步方案不可靠,是本章遇到的最严重的技术问题。
-
资源命名不一致: 数据表依赖精确的静态网格体显示名称进行查找。开发过程中因命名不规范(如
Tree_01
vsTree01
)导致部分植被无法被正确识别和采伐。 -
几何集合体(Geometry Collection)的缩放错误: 在为岩石创建可破碎模型时,直接对场景中已经缩放过的岩石创建几何集合体,导致生成的破碎蓝图默认尺寸巨大,与原始岩石不匹配。
-
-
对前期设计的反思与修正:
- ISM移除逻辑的重构: 核心修正是放弃了简单的多播方案,转而采用了更健壮的“服务器遍历所有客户端并逐个发送RPC”的模式,彻底解决了同步问题。这是一个关键的迭代优化。
-
如果重来一次,如何优化:
-
建立严格的命名规范: 在项目早期就应强制推行统一的资源命名规则,并使用工具进行校验,以避免因手动输入错误导致的数据关联失败。
-
抽象采伐逻辑为组件: 可以将“检测ISM -> 查数据表 -> 异步加载 -> 生成Actor -> 请求移除ISM”这一整套逻辑封装到一个可复用的Actor组件中。这样,无论是斧头(通过重叠)还是徒手交互(通过射线检测),都可以直接调用这个组件的功能,减少在
Hatchet_Master
和FirstPersonCharacter
中的代码重复。 -
规范化基础资产制作流程: 在制作可破碎模型等衍生资产时,应规定必须基于未缩放、未旋转的原始模型(T-Pose/A-Pose)进行制作,以确保衍生资产的默认状态是标准的,避免在蓝图中进行不必要的“魔法数字”式的缩放调整。
-