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(大类区分)。
