You could scan the level once and store height data in a 2D map so you can move unlimited projectiles around without having to line trace at all.
- Create a GameInstanceSubsystem manager class which holds a height map (TMap of 2D int vector (near absolute world coordinates) to a float height). GameInstanceSubsystem is nice cause you can access it from any projectile at any time, if it’s not accessible in BP use a normal subsystem or actor.
- On BeginPlay (or when you need to scan) let the manager class perform line traces from top to bottom over the bounds of the level, every X units (your preferred scan resolution). Let the trace report traces to the relevant landscape.
- Store each line trace as a height value in the manager class its height map.
- Now you can access a height by a 2D X Y coordinate from the manager whenever you want, so for example on tick on a projectile. See how it performs and if you can reduce this tick speed and perhaps interpolate between the height values you retrieve.
Question is if this is more performant than an actual line trace per projectile. It depends on how many values you store on the managing class. It’s a special case really it would work fine if you’d use it to for example pick a few landscape coordinates for birds to fly above and land on, but it would not be suitable for a sonic or racing game.
Something like
const int32 HeightMapResolution = 100;
const int32 LevelSize = 2000;
TMap<FIntVector2, float> HeightMap;
for (int32 x = 0; x <= LevelSize; x += HeightMapResolution) {
for (int32 y = 0; y <= LevelSize; y += HeightMapResolution) {
FHitResult OutHit;
const FVector StartTrace = FVector((float)x, (float)y, (float)(LevelSize + HeightMapResolution));
const FVector EndTrace = FVector((float)x, (float)y, 0.f);
TArray<AActor*> IgnoreTraceActors;
if (UKismetSystemLibrary::LineTraceSingle(this, StartTrace, EndTrace, UEngineTypes::ConvertToTraceType(ECC_Visibility), false, IgnoreTraceActors, EDrawDebugTrace::None, OutHit, true)) {
HeightMap.Add(FIntVector2(x, y), OutHit.ImpactPoint.Z);
}
else {
HeightMap.Add(FIntVector2(x, y), 0.f);
}
}
}
Whenever you need a height value for a projectile you get the absolute world position of the projectile and round its X,Y values to the nearest key in HeightMap.
HeightMap.FindChecked(FIntVector2(InX, InY));