Unreal Engine
UE4.27迁移UE5.8
本文总结 project09 从 UE4.27 迁移到 UE5.8 后完成的主要修复工作,覆盖场景光照、模型与武器资产、Windows Dedicated Server、武器物理、多人同步、晚加入恢复、非房主拾取/丢弃以及 HUD 更新。
project09 UE5.8 迁移与多人游戏修复总结
1. 文档目的
本文总结 project09 从 UE4.27 迁移到 UE5.8 后完成的主要修复工作,覆盖场景光照、模型与武器资产、Windows Dedicated Server、武器物理、多人同步、晚加入恢复、非房主拾取/丢弃以及 HUD 更新。
本文用于保存当前阶段的技术结论、问题根因和验证结果,方便后续回归测试、Dedicated Server 验证以及服务端权威化改造。它不是最终验收报告;未完成的压力测试和 C++ 权威化工作会在文末单独列出。
2. 项目与环境基线
| 项目 | 当前信息 |
|---|---|
| 工作目录 | C:\personal\UE_Project\project10 |
| Unreal 项目 | project09.uproject |
| 当前目标引擎 | UE5.8 |
| UE5 Launcher 路径 | C:\Program Files\Epic Games\UE_5.8 |
| UE5 源码引擎路径 | C:\UESource\UnrealEngine5.8 |
| UE5 迁移基线提交 | 9b508bf |
| 默认地图 | /Game/Season7/Main |
| 多人测试地图 | /Game/Maps/csgo |
| 默认 GameMode | /Game/All/BP_MyGamemode.BP_MyGamemode_C |
| GameInstance | /Game/All/BP_MyGameInstance.BP_MyGameInstance_C |
当前项目仍以蓝图玩法为主。FppShooter 负责输入与 Server/Multicast RPC 转发,Shooter 负责角色和武器库存状态,Weapon 负责武器基础行为、表现和掉落物理。
3. UE4.27 到 UE5.8 的迁移工作
3.1 项目升级
项目已完成从 UE4.27 到 UE5.8 的基础迁移,并建立迁移基线提交 9b508bf。迁移涉及:
- 更新项目与 UE5.8 的引擎关联。
- 重新编译项目 C++ 模块和 Target。
- 更新客户端与服务器构建脚本中的引擎路径。
- 适配 UE5.8 的 Windows 客户端 Stage/Archive 目录结构。
- 重新保存受版本升级影响的蓝图、地图和资源。
- 保留旧 UE4 源码引擎作为迁移对照,待问题完全稳定后再决定是否清理。
3.2 构建脚本更新
以下脚本已经切换到 UE5.8 源码引擎:
build_client.batbuild_server.batstart_packaged_server.bat
客户端归档目录由旧的 WindowsNoEditor 调整为 UE5.8 使用的 Windows,并增加了构建失败、Cook 缺失和最终可执行文件缺失检查。
服务器脚本可以构建并归档 Windows Dedicated Server,服务器测试地图显式使用 /Game/Maps/csgo,避免默认地图 /Game/Season7/Main 与测试地图不一致。
4. 场景光照与地图修复
迁移后对场景显示和光照进行了调整,并重新保存相关地图与光照构建数据。当前仓库中可确认涉及的资产包括:
/Game/Maps/csgo/Game/Maps/csgo_BuiltData/Game/Season7/Main/Game/Season7/Main_BuiltData
本阶段处理的目标是恢复 UE5.8 下可用、稳定的场景表现,避免迁移后出现光照缺失、亮度异常、构建数据失配或地图使用旧版本 BuiltData 的问题。
当前结果:
csgo多人测试地图可以在 PIE、Listen Server 和打包 Dedicated Server 流程中加载。Main默认地图仍保留为项目入口地图。- 地图和 BuiltData 已随 UE5.8 迁移重新保存。
- 场景光照问题已经完成当前阶段的人工验证。
由于光源的逐项参数修改没有单独形成变更记录,本文不虚构具体亮度、曝光、阴影或烘焙参数。后续若继续调整光照,建议记录光源 Actor、修改前后参数、目标平台和截图,避免 BuiltData 变化无法追溯。
5. 模型与武器资产修复
UE5.8 迁移后重新检查并调整了角色/武器相关模型表现。当前工作区中可确认修改过的武器资产包括:
/Game/All/Weapon/AK47/Game/All/Weapon/AUG/Game/All/Weapon/AWP/Game/All/Weapon/Deagle/Game/All/Weapon/Knife/Game/All/Weapon/Shotgun/Game/All/Weapon/Weapon
修复重点包括:
- UE5.8 下武器模型和基础蓝图的兼容性。
- 第一人称与第三人称武器显示。
- 武器在角色手部 Socket 上的挂接表现。
- 武器掉落后的物理根组件状态。
- 不同客户端看到的武器模型、可见性和位置一致性。
角色选择武器挂接组件的现有规则保持不变:
本地玩家 -> 第一人称 Mesh
其他玩家 -> 第三人称 TPP
挂接 Socket -> b_RightWeaponSocket
变换规则 -> SnapToTarget
排查证明,晚加入时武器位置错误不是 getMesh 或 Socket 选择错误,而是武器在复制初始化期间只 Attach 一次,随后被网络 Transform 或物理状态覆盖。
6. Dedicated Server 支持
6.1 已完成内容
- 创建并使用
project09Server.Target.cs。 - 完成 UE5.8 Windows 客户端构建、Cook、Stage 和 Archive。
- 完成 UE5.8 Windows Dedicated Server 构建、Stage 和 Archive。
- 增加打包服务器启动脚本。
- 使用
/Game/Maps/csgo进行独立服务器测试。 - 验证两个本地客户端可以加入服务器。
6.2 已完成的多人流程验证
当前阶段已经人工验证:
- 客户端加入 Dedicated Server。
- 两客户端并发进入游戏。
- 玩家死亡与复活。
- 比赛结束。
- 空房间重置。
- 服务器地图跳转。
- 服务器进程可以正常退出。
已检查的 Dedicated Server 日志中没有发现 Fatal、Assertion、Blueprint Runtime Error、崩溃或异常网络失败。服务器曾连续运行超过 13 分钟并正常退出。
需要注意:已有两个客户端的会话不是一条完整连续十分钟记录,因此严格的 M1 十分钟双客户端验收证据仍应重新采集。
6.3 性能观察
本机同时运行服务器和多个客户端时,画面流畅度差异部分来自本地 CPU、GPU 和内存资源竞争。服务器日志中曾观察到最大 Tick Rate 为 30。该现象不能直接视为网络同步错误,后续应在独立机器或受控环境中区分服务器帧率、客户端渲染性能与网络复制成本。
7. 武器穿墙和消失问题
7.1 现象
武器被丢出后可能高速穿过墙体或地面,看起来像武器被销毁或消失。
7.2 根因
排查确认:
- 武器没有调用
DestroyActor。 - 武器
InitialLifeSpan = 0,不存在自动销毁。 - 武器物理 Root 使用
/Game/All/smallcube。 smallcube边界约为-1..1 cm,物理代理很小。- 投掷/冲量速度较高,小型物理体容易在离散碰撞中发生 tunneling。
- 原武器 Root 的 CCD 未启用。
7.3 修复
在保留现有 Root 和组件层级的前提下,为武器物理 Root 启用 Use CCD。
该修改避免了不必要的组件替换和资产层级重构,用户测试确认武器穿墙消失问题已修复。
8. 晚加入客户端武器同步
8.1 原始问题
比赛进行中加入的客户端无法得到此前发生的 Multicast 历史。表现包括:
- 对方当前武器缺失或位置错误。
- 备用武器仍显示在地面。
- 切一次武器后显示恢复。
- 备用武器的
WeaponOwner为空。 - 后续切换时触发
onEquip、PlayMontage空引用错误。
根因是原逻辑主要依赖 all-pickWeapon 和 all_attachWeapon Multicast。Multicast 只发送给事件发生时已经连接的客户端,不会为晚加入客户端重放历史。
8.2 状态模型
最终采用以下状态分工:
| 状态 | 用途 |
|---|---|
CreatedWeapon | 初始创建武器,不作为长期当前武器真相 |
WeaponArray | RepNotify,复制完整库存快照 |
EquippedWeapon | RepNotify,复制当前装备武器引用 |
CurrentWeaponindex | 客户端表现和现有切枪逻辑使用的索引 |
没有复用 CreatedWeapon 作为当前武器,因为它的原始语义是“新创建的武器”,且 CreateWeapon、OnRep_CreatedWeapon 和现有初始化流程都依赖该语义。
8.3 WeaponArray 重建
OnRep_WeaponArray 在远端客户端遍历库存:
ForEach WeaponArray
-> IsValid Weapon
-> Weapon.onPick(Self)
-> 恢复 WeaponOwner
-> 关闭碰撞
-> 关闭物理模拟
-> 非当前武器 SetVisibility(false)
这解决了晚加入客户端上备用武器仍留在地面、备用武器没有 Owner、切换备用武器后空引用等问题。
8.4 EquippedWeapon 重建
由于 RepNotify 自动函数不能被普通蓝图直接调用,新增普通函数:
ApplyReplicatedEquippedWeapon
OnRep_EquippedWeapon 和 OnRep_WeaponArray 完成后都调用该普通函数,统一应用当前装备状态。
应用状态前增加:
IsValid(EquippedWeapon)
WeaponArray Contains EquippedWeapon
只有当前武器仍然属于库存时才执行:
onPick(Self)
-> all_attachWeapon(EquippedWeapon)
-> SetVisibility(true)
-> CurrentWeaponindex = EquippedWeapon.getType
-> onEquip
如果武器无效或已不在数组中,则设置:
CurrentWeaponindex = 3
Contains 检查避免了丢枪时 WeaponArray 与 EquippedWeapon 到达顺序不同,导致客户端把刚丢出的武器重新捡回。
8.5 Attach 初始化时序
正常换枪路径中的 all_attachWeapon 在 Remote 端使用 0.2 秒间隔进行有限次数重试。晚加入状态恢复改为复用这一挂接路径,而不是只执行一次直接 Attach。
同时增加终止条件:只有武器自定义 WeaponOwner 仍等于当前 Shooter 时才继续 Attach。不能使用 Actor 自带的 GetOwner,因为现有 onPick 设置的是武器蓝图中的 WeaponOwner 变量。
该检查解决了枪已经 onDrop 后,残留 Attach 重试又把枪拉回手中的问题。
9. 非房主拾取与丢弃修复
9.1 原始问题
Listen Server 房主丢枪基本正常,但非房主拾枪后丢弃可能出现:
- 枪没有正确抛出。
- 枪短暂落地后又回到手上。
- 客户端找不到要丢弃的当前武器。
- Dedicated Server 风险高于 Listen Server。
9.2 根因
原流程为:
客户端按 G
-> Server-dropWeapon
-> all-dropWeapon
-> Delay 0.2
-> getCurrentWeapon
-> DropWeapon
延迟期间,服务器复制的 WeaponArray、EquippedWeapon 或 CurrentWeaponindex 可能已经变化。客户端延迟结束后重新查询“当前武器”时可能得到 None 或索引 3,从而跳过丢枪。
此外,Attach 重试在非房主客户端仍可能继续执行,与 onDrop 的物理状态冲突。
9.3 修复后的流程
服务器在状态变化前捕获明确武器引用:
Server-dropWeapon
-> getCurrentWeapon
-> IsValid
-> all-dropWeapon(WeaponToDrop)
all-dropWeapon 将同一个 WeaponToDrop 传递给后续流程:
WeaponToDrop.OnShootButtonUp
-> Delay 0.2
-> WeaponToDrop.middlestep_AUG_AWP
-> DropWeapon(WeaponToDrop)
DropWeapon 增加 WeaponToDrop 输入,并立即缓存为局部变量。函数不再依赖延迟后的 getCurrentWeapon 或 CurrentWeaponindex != 3 判断。
关键处理包括:
- 使用
WeaponToDrop执行 Detach、SetActorLocation 和onDrop。 - 使用
WeaponToDrop.getType清空正确库存槽位。 getNearByWeapon.exceptthis明确排除刚丢出的武器。- 只有 Authority 且
EquippedWeapon == WeaponToDrop时才清空EquippedWeapon。 onDrop清空WeaponOwner后,远端 Attach 重试停止。
用户已在 Listen Server 下验证非房主拾取、丢弃和武器切换恢复正常。
10. WeaponOwner 空引用防护
新增复制恢复后,原先假设 WeaponOwner 永远有效的函数可能在初始化、切换、丢弃或网络状态过渡时被调用。
已经在以下函数读取 WeaponOwner 前增加执行式 IsValid:
Weapon.onEquipWeapon.PlayMontageWeapon.StopMontage
无效分支直接结束,不再调用:
WeaponOwner.isLocalPlayerPlayAnimMontageStopAnimMontage
日志曾出现的 WeaponOwner Accessed None 错误已经通过状态重建和判空保护处理。
11. HUD 重复与残留修复
11.1 现象
- 丢弃枪并切到刀后,枪的 HUD 仍留在视口。
- 捡起枪后,刀的 HUD 仍留在视口。
- 主武器、副武器和刀之间切换时可能出现多个 HUD 重叠。
11.2 根因
一次装备现在可能由以下路径多次调用 onEquip:
- 正常
Equip Weapon。 OnRep_EquippedWeapon。OnRep_WeaponArray完成后的统一状态应用。
旧 onEquip 只检查 HUD Class 是否存在,每次调用都可能重新 CreateWidgetAndAddToViewport。CrossHairUI 变量只保存最后创建的实例,onUnEquip 因此只能移除最后一个 Widget,之前创建的实例会成为视口中的残留 HUD。
11.3 修复
将 onEquip 改为幂等逻辑:
IsDedicatedServer
-> IsValid WeaponOwner
-> WeaponOwner.isLocalPlayer
-> hideshadow
-> IsValid CrossHairUI
Valid -> 结束,不重复创建
Not Valid -> 获取 HUD Class
-> CreateWidgetAndAddToViewport
-> Set CrossHairUI
onUnEquip 保持:
IsValid CrossHairUI
-> RemoveFromParent
-> CrossHairUI = None
由于旧 PIE 会话中已经失去引用的 Widget 无法再通过 CrossHairUI 删除,修改后必须完全停止并重新启动 PIE 才能验证。
用户测试确认 HUD 残留问题已经修复。
12. 当前验证结果汇总
| 项目 | 当前结果 |
|---|---|
| UE5.8 项目迁移 | 已完成基础迁移 |
| UE5.8 C++ 编译 | 已完成 |
| Windows 客户端打包 | 已完成 |
| Windows Dedicated Server 打包 | 已完成 |
csgo 地图服务器启动 | 已验证 |
| 两客户端加入 | 已验证 |
| 死亡与复活 | 已验证 |
| 比赛结束/空房间重置/跳图 | 已验证 |
| 场景光照当前表现 | 已人工确认修复 |
| 武器模型与挂接 | 已人工确认修复 |
| 高速掉落武器穿墙 | CCD 修复后通过 |
| 晚加入当前武器 | 已修复并验证 |
| 晚加入备用武器地面残留 | 已修复并验证 |
| 晚加入后换枪 | 已修复并验证 |
| 非房主拾取/丢弃 | Listen Server 下已验证 |
| WeaponOwner 空引用 | 已增加状态恢复与判空 |
| 武器 HUD 重复/残留 | 已修复并验证 |
13. 主要设计结论
- Multicast 不能代替持久复制状态,因为晚加入客户端不会收到历史事件。
WeaponArray应表达库存快照,EquippedWeapon应表达当前装备快照,二者不能继续由CreatedWeapon混合承担。- RepNotify 到达顺序不能假定固定;状态应用必须验证武器仍属于库存。
- 延迟 RPC 或 Multicast 流程不能在延迟结束后重新查询易变的“当前对象”,应在 Authority 端提前捕获明确引用。
- 重试 Attach 必须有终止条件,不能只判断 Actor 是否有效。
- UI 和表现函数可能被正常流程与 RepNotify 重复调用,因此必须具备幂等性。
WeaponOwner是当前武器蓝图自定义状态,不等同于 Unreal Actor 内置 Owner。- 小型高速物理体需要考虑 CCD,不能把穿墙误判成 Actor 被销毁。
14. 当前资产和工作区状态
本次总结生成时,Git 工作区中可见以下未提交资产变化:
Content/All/Shooter.uassetContent/All/Weapon/Weapon.uassetContent/All/Weapon/AK47.uassetContent/All/Weapon/AUG.uassetContent/All/Weapon/AWP.uassetContent/All/Weapon/Deagle.uassetContent/All/Weapon/Knife.uassetContent/All/Weapon/Shotgun.uassetContent/Maps/csgo.umapContent/Maps/csgo_BuiltData.uassetContent/Season7/Main.umapbuild_client.batbuild_server.bat
此外,以下网络文档尚未提交:
Docs/MultiplayerArchitecture.mdDocs/NetworkTestMatrix.mdDocs/ReplicationProfiling.md
这些是状态记录,不代表应立即提交。提交前仍需在编辑器中 Compile、Save All,并检查实际 Diff 范围是否全部属于本阶段修改。
15. 后续建议
15.1 必做回归
在 Dedicated Server 下重新执行:
- 主武器、副武器、刀的拾取、切换和丢弃。
- 非房主在拾枪后两秒内立即丢弃。
- 多次连续捡起、丢弃同一武器。
- 玩家持有三类武器后让新客户端晚加入。
- 晚加入后继续换枪、丢枪和再拾取。
- 死亡时持枪、复活后默认武器恢复。
- HUD 在所有上述流程中始终只显示当前武器的一份实例。
15.2 日志验收
每轮测试结束后检查:
Fatal
Ensure
Blueprint Runtime Error
Accessed None
WeaponOwner
Infinite Loop
Assertion
15.3 网络环境
当前主要结果来自 LAN、PIE、Listen Server 和本机 Dedicated Server。后续仍需执行:
- 完整十分钟双客户端 Dedicated Server 会话。
- WAN 延迟测试。
- 2% 丢包测试。
- 高延迟和 5% 丢包 Stress 测试。
- 三十分钟服务器帧时间、内存和网络复制稳定性采集。
15.4 架构演进
当前修复优先恢复 UE5.8 蓝图项目的现有行为,没有完成全面的 C++ 服务端权威战斗改造。后续仍应逐步确保:
- 服务器决定武器归属、库存和当前装备状态。
- 服务器决定弹药、射速、命中、伤害、死亡和得分。
- Multicast 只承担动画、声音、特效和必要的表现刷新。
- HUD 只读取本地玩家的复制状态,不参与玩法真相。
- 掉落武器的物理和 Transform 由服务器保持最终一致。
16. 相关文档
项目内已有以下配套文档:
Docs/MultiplayerArchitecture.mdDocs/NetworkTestMatrix.mdDocs/ReplicationProfiling.mdDocs/Week5_网络职责表.mdDocs/Week5_回归测试记录.md
本文是当前阶段的总结快照;后续修改网络状态模型、RPC 边界或 HUD 生命周期时,应同步更新相关架构与测试文档。