TArray AmbitWorldHelpers::GenerateRandomLocationsFromBox()

in Ambit/Source/Ambit/Utils/AmbitWorldHelpers.cpp [156:231]


TArray<FTransform> AmbitWorldHelpers::GenerateRandomLocationsFromBox(UBoxComponent* Box,
                                                                     const TArray<AActor*>& ActorsToHit,
                                                                     int32 RandomSeed, bool bSnapToSurfaceBelow,
                                                                     float DensityMin, float DensityMax,
                                                                     float RotationMin, float RotationMax)
{
    TArray<FTransform> Transforms;
    if (!IsValid(Box))
    {
        UE_LOG(LogAmbit, Warning, TEXT("Invalid box component."));
        return Transforms;
    }
    if (DensityMin > DensityMax)
    {
        UE_LOG(LogAmbit, Warning, TEXT("DensityMin is greater than DensityMax. No actors spawned."));
        return Transforms;
    }

    if (RotationMin > RotationMax)
    {
        UE_LOG(LogAmbit, Warning, TEXT("RotationMin is greater than RotationMax. No actors spawned."));
        return Transforms;
    }
    FRandomStream Random;
    Random.Initialize(RandomSeed);

    FTransform BoxTransform = Box->GetComponentTransform();

    FBox Bounds = Box->Bounds.GetBox();

    float BoxYaw = BoxTransform.Rotator().Yaw;
    float BoxYawMod = UKismetMathLibrary::GenericPercent_FloatFloat(BoxYaw, 90.f);
    if (!FMath::IsNearlyEqual(BoxYawMod, 0.f))
    {
        // If BoxComponent is a rotated box, create a rectangle
        // with the same area as a temporary bounding box
        Bounds = FBox(FVector(-Box->GetScaledBoxExtent().X, -Box->GetScaledBoxExtent().Y, 0.0),
                      FVector(Box->GetScaledBoxExtent().X, Box->GetScaledBoxExtent().Y, 0.0));
    }

    // Calculate the surface area of the bounding box to determine how many items to spawn.
    const FVector SizeMeters = Bounds.GetSize() / 100.f;
    const float AreaMeters = SizeMeters.X * SizeMeters.Y;
    int SpawnCount = AreaMeters * Random.FRandRange(DensityMin, DensityMax);

    FVector Location(0, 0, Bounds.Max.Z);

    while (SpawnCount > 0)
    {
        Location.X = Random.FRandRange(Bounds.Min.X, Bounds.Max.X);
        Location.Y = Random.FRandRange(Bounds.Min.Y, Bounds.Max.Y);

        FVector NewLocation = Location;
        if (!FMath::IsNearlyEqual(BoxYawMod, 0.f))
        {
            // If BoxComponent is a rotated box, iterate through transforms
            // and transform each location to match the rotated box area
            NewLocation = BoxTransform.TransformPosition(Location);
        }

        FTransform Transform(FRotator(0), NewLocation);
        if (CheckAndSnapToSurface(Transform, ActorsToHit, bSnapToSurfaceBelow))
        {
            FRotator Rotation(0, Random.FRandRange(RotationMin, RotationMax), 0);
            // Combine "local" Yaw rotation generated from user inputted restrictions
            // with the adjusted rotation axis
            Transform.SetRotation(Transform.GetRotation() * Rotation.Quaternion());
            Transforms.Push(Transform);
        }

        // No matter hit or not, reduce spawn count
        SpawnCount--;
    }

    return Transforms;
}