优化提普斯
December 22, 2024About 3 min
优化提普斯
已有文档:
SignificanceBudget 扩展——低重要性 NPC 关闭阴影投射
void AMyCharacter::ApplyShadowSignificance()
{
if (!bShadowStatesCached)
{
bShadowStatesCached = true;
TInlineComponentArray<UPrimitiveComponent*> PrimitiveComps;
GetComponents<UPrimitiveComponent>(PrimitiveComps);
OriginalShadowStates.Reserve(PrimitiveComps.Num());
for (UPrimitiveComponent* Comp : PrimitiveComps)
{
if (Comp && Comp->CastShadow)
{
OriginalShadowStates.Emplace(Comp, true);
}
}
}
const bool bDisableShadow = (static_cast<int32>(ESignificanceBucket)
>= static_cast<int32>(ESignificanceBuckets::Low));
for (auto& Pair : OriginalShadowStates)
{
if (UPrimitiveComponent* Comp = Pair.Key.Get())
{
Comp->SetCastShadow(!bDisableShadow);
}
}
}
SignificanceBudget 扩展——低重要性 NPC 降低移动组件Tick频率
void AMyCharacter::ApplyMovementSignificance()
{
UCharacterMovementComponent* MoveComp = GetCharacterMovement();
if (!MoveComp)
{
return;
}
switch (ESignificanceBucket)
{
case ESignificanceBuckets::High:
MoveComp->SetComponentTickInterval(0.f);
break;
case ESignificanceBuckets::Medium:
MoveComp->SetComponentTickInterval(0.033f);
break;
case ESignificanceBuckets::Low:
MoveComp->SetComponentTickInterval(0.066f);
break;
case ESignificanceBuckets::Lowest:
MoveComp->SetComponentTickInterval(0.133f);
break;
default:
break;
}
}
NPC 启用 Update Rate Optimization (URO)
如果用OnlyTickPoseWhenRendered,一个在屏幕外攻击玩家的 NPC 的 Montage 不会 Tick,Notify 不会触发,打不出伤害。
// Performance: Enable URO for NPC skeletal meshes
// Reduces animation update frequency for distant/off-screen characters
if (!IsPlayer)
{
if (USkeletalMeshComponent* MeshComp = GetMesh())
{
MeshComp->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickMontagesWhenNotRendered;
MeshComp->bEnableUpdateRateOptimizations = true;
}
}
跳过空广播
// Performance: only dispatch BP event if the BP subclass actually overrides it
if (OnTraceInstanceHitEvent.IsBound())
{
OnTraceInstanceHitEvent.Broadcast(TraceInstance, HitResult);
}
SignificanceBudget 与物理模拟
使用SignificanceBudget跳过tick或者是关闭PhysicsControl会出现角色闪现的问题,控制代码如下:
if (Data.bPhysicsControlTick) {
SkeletalMesh->bUpdateMeshWhenKinematic = true;
SkeletalMesh->SetAllBodiesPhysicsBlendWeight(1.0f); // 布娃娃姿态
} else {
SkeletalMesh->bUpdateMeshWhenKinematic = false;
SkeletalMesh->SetAllBodiesPhysicsBlendWeight(0.0f); // 动画姿态 / T-Pose
}
与此同时,还会出现收PhysicsControl控制的角色会有问题,如不受控的移动等。只需要在是否跳过Tick检查的地方判断一下当前角色是否在物理模拟状态下,如果是就不跳过tick和关闭物理控制就可以。
bTickPhysicsAsync 异步物理
MaxSubsteps: 6 (核心) 调大一些。这是开 async 后必须做的伴生改动。
- async 物理走 AsyncFixedTimeStepSize=0.01667 (60Hz) 的固定步长。
- 每个游戏帧最多推进 MaxSubsteps 个固定步,即 3 × 16.67ms = 50ms 的物理时间。
- 当帧时间 > 50ms (即 < 20fps,见 TraceAnalysis_20260421.md 基线) 时,物理 轴追不上墙钟,表现为"瓶子下落 5 秒""ragdoll 飘"。
- 提到 6 后,单帧能覆盖 6 × 16.67 = 100ms,把"慢动作阈值"从 20fps 拉到 ~10fps,正好兜住目前 PS5 worst-case 帧率。
MaxPhysicsDeltaTime: 0.0333 → 0.1 (与上一项配套)
- 这是引擎吃掉 dt 的总封顶。如果只把 MaxSubsteps 抬到 6 但 MaxPhysicsDeltaTime 还卡在 33ms,async 路径每帧最多还是只推 33ms。
- 改到 100ms 才能让 6 个子步真正发挥作用,MaxSubsteps × MaxSubstepDeltaTime = 6 × 16.67 = 100ms 与之对齐。
- 兜底 100ms 也避免 hitch 后一次性补一个超大 dt 把 ragdoll 打飞。
bEnableStabilization: False → True
- async + 子步推进后,ragdoll 在地面堆叠时多个刚体之间的接触会在不同子步里反复求解,容易抖。
- PCM 已经开了 (bEnablePCM=True),再加上 stabilization 让 solver 在多次接触迭代之间保留缓冲,降低 ragdoll 触地颤动
MaxDepenetrationVelocity: 0 → 500
- 0 = 不限制反推速度。开 async 后子步更多,瞬间穿透解算如果不封顶,会把刚体弹飞(典型表现:ragdoll 一接触就"嗖"飞出场景)。
- 500 cm/s 是 UE 推荐的稳妥上限,既能解出穿透又不会爆冲。
- 这条与 async 无强绑定,但属于"async 高频求解暴露出来的老坑",顺手补上。