Cast:被误解的性能节点

先给结论:Cast 并不是性能毒药,用错地方才是。

Cast<ToType>(X) 做了两件事:① 检查 X 是否是 ToType(类型确认,有开销);② 若是,返回类型转换后的指针(几乎零成本)。所以 Cast 的开销几乎全在"类型确认",而 UE 的 Cast 对 UObject 已经走 IsA 的快速路径——单次 Cast 大约几十纳秒。

什么情况下 Cast 会变成问题

不是单次开销,而是乘数效应:

// 每帧、每个投射物、每次命中都 Cast 一次
for (AActor* Hit : HitActors)
{
    if (AEnemy* E = Cast<AEnemy>(Hit))   // ×N 个敌人 × 每帧
        E->ApplyDamage();
}

场景里有 200 个可命中物体、单次攻击命中 30 个 → 30 次 Cast,不是问题;但如果你在 Tick 里 Cast 上百个对象,或者 蓝图里每帧做大量 Cast,VM 开销就叠加起来了。

更要紧的问题是"设计气味"

Cast 到具体类型意味着你的代码认识了具体类型。第 3 章讲过:该用接口的地方用 Cast,加一个新敌人类型就要回去改所有调用方。这是维护性问题,比性能问题更严重。

替代方案对照

场景 替代 Cast 的方式
只想调用对方共有行为 蓝图接口 / IInterface
只想知道对方某个组件 FindComponentByClass(一次缓存)或组件引用
只想区分大类(角色 vs 投射物) IsA(TargetClass) 或 Cast 提前到"每进入状态一次"
每帧都要访问的组件 在 BeginPlay 就缓存一次,之后直接用

缓存:最简单有效的优化

void AMyCharacter::BeginPlay()
{
    CachedHealthComp = FindComponentByClass<UHealthComponent>();   // 只查一次
}

void AMyCharacter::Tick(float dt)
{
    if (CachedHealthComp) CachedHealthComp->Regen(dt);             // Tick 里直接用
}

蓝图同理:BeginPlay 里 Get 一次存到变量,Tick 里用变量,而不是每次都 GetComponentByClass 或 Cast。

本章小结

  • 单次 Cast 成本极低(几十纳秒),问题在"每帧 × N 次"的乘数效应与 VM 叠加。
  • 更严重的是设计气味:Cast 到具体类型 = 认识具体类型,新类型加入要改调用方。
  • 替代:接口(行为)、缓存组件引用(每帧访问)、IsA(大类区分)。