CharacterMovement
CharacterMovement
性能优化
simulate step
所谓的step,就是当一帧时间过长时候(帧率低或者卡顿的时候),移动组件会把这个时间拆分成小的step,每一个step完整跑一段移动模拟,每一段移动模拟至少产生三次物理碰撞检测(sweep),step up,step forward, step down。
优化移动模拟次数。这次有两个值控制:
- MaxSimulationIterations,决定一次移动组件update最多少产生多少次模拟,定义最大子步数
- MaxSimulationTimeStep,每一次模拟最大时长,限制每个子步的时间间隔
按角色重要性动态调整参数
我们把这两个值接入到逻辑LOD管理器,根据是否屏幕可视和距离决定跌代次数,不见时把MaxSimulationIterations改为1,太远的时候MaxSimulationIterations改为2,目前看来,没发现有什么问题。
利用UE的SignificanceManager系统,可根据角色与玩家的距离或重要性分级设置参数:
- 近处/关键角色(如玩家自身、Boss):保留默认值(
MaxSimulationIterations=8,MaxSimulationTimeStep=0.0167s),确保移动精度和碰撞响应的及时性。例如,玩家角色在爬墙或跳跃时,足够的迭代次数能避免穿模或卡顿。 - 远处/次要角色(如背景NPC):降低迭代次数(如
MaxSimulationIterations=4)并增大时间步长(如MaxSimulationTimeStep=0.033s)。这会减少CPU占用,但可能导致移动细节简化(如布料摆动幅度减小)。
性能与精度的权衡技巧
限制最大迭代次数的边际效益:
超过16次迭代后,精度提升有限但性能成本显著增加。建议通过UE Profiler监控CharacterMovement模块的耗时,找到平衡点。例如,在移动端将迭代次数控制在4-6次,避免帧率波动。时间步长与帧率的匹配:
MaxSimulationTimeStep应与目标帧率对应(如30FPS对应0.033s,60FPS对应0.0167s)。若设置过大(如>0.05s),可能导致角色移动“跳跃”;过小(如<0.01s)则会增加子步计算次数。例如,在低帧率场景(如20FPS)下,可将时间步长设为0.05s,减少CPU压力。网络同步场景的特殊处理:
在多人游戏中,服务器需对所有角色进行物理模拟,建议统一降低MaxSimulationIterations(如设为6),并通过ClientAdjustPosition函数修正客户端位置偏差,避免因参数不一致导致的同步问题。
常见问题
| 问题 | 原因 | 优化方案 |
|---|---|---|
| 角色穿模或卡在地形 | 迭代次数不足,碰撞检测不充分 | 提高MaxSimulationIterations至8-10,或减小MaxSimulationTimeStep至0.01s |
| 移动端帧率过低 | 物理计算占用CPU过高 | 降低迭代次数至4-5,增大时间步长至0.033s,关闭次要角色的物理模拟 |
| 跳跃顶点动画卡顿 | 时间步长过大,速度计算精度不足 | 启用CharacterMovementCVars::ForceJumpPeakSubstep,细分顶点时间步 |
update骨骼和子transform
当移动组件移动时,更新当前actor下所有的transform,比如骨骼网格体的物理资产,骨骼socket挂接的特效等。这里消耗不低,物理是特效,如果量大的话,会带来一系列的性能,比如发送transform信息到render thread,更新gpu scene.
overlap事件
当移动组件完成后,会在最后时机,把当前actor下所有会产生overlap事件的component,一个一个递归update,减少没必要的overlap事件,我们优化的时候,大部分的overlap事件都是无用的,只保留capsule就够了。
check floor
引擎默认是每帧都检测地面的,可以关闭,只有移动的时候才检测地面,这里可以减少大量怪物在待机状态下每帧都进行地面的检测消耗,但是会带来一个负作用就是,如果角色站在一个破坏物上,破坏物被打碎,角色仍在空中的情况,因为角色没有动。我们把check floor这个字段接入到逻辑LOD管理器,把远处的check floor全去掉,移动组件干净了好多。