When I started working on coffee tables building columns, outdoor planters with benches… i noticed that they all had something in common. The placement of objects around a center object or point. So I decided to create a centerpiece class actor to make the layout of these types of objects easier. The main functions that I created for this are
// Gets the the bounding box of the center piece item
UCageUtilities::GetBoxCorners
These functions made it a breeze to place objects around a centerpiece. Now from here I can further define each blueprint/prefab based on the objects. A simple array of actors/meshes can be used, in conjunction with hierarchal instance static mesh components. For my use, I have specialized actors like light bulb lamp actors, chair actors and other specialized actors that I will be using accompanied by a data table to select the specific item type. I will not cover that here. But there may be a Part 2 in the near future.
It’s rather simple to roll your own placement of these objects, but Unreal’s PCG framework makes it pretty simple as well to lay out a ballroom for instance or dining hall.
TArrayUCageUtilities::GetBoxCorners(UPrimitiveComponent* InComp, float RadiusOffset) { if (InComp == nullptr) { return TArray (); } else { TArray BoxCorners; FVector Origin, BoxExtent; float SphereRadius; UKismetSystemLibrary::GetComponentBounds(InComp, Origin, BoxExtent, SphereRadius); FVector BoxVector = Origin - InComp->GetComponentLocation(); float BoxZ = BoxVector.Z; FBoxSphereBounds ComponentBounds = InComp->CalcBounds(InComp->GetComponentTransform()); // Apply inset using RadiusOffset to X and Y axes FVector Inset(BoxExtent.X - RadiusOffset, BoxExtent.Y - RadiusOffset, 0.0f); FVector Corner1 = FVector(-Inset.X, -Inset.Y, BoxZ); FVector Corner2 = FVector(Inset.X, -Inset.Y, BoxZ); FVector Corner3 = FVector(Inset.X, Inset.Y, BoxZ); FVector Corner4 = FVector(-Inset.X, Inset.Y, BoxZ); BoxCorners.Add(Corner1); BoxCorners.Add(Corner2); BoxCorners.Add(Corner3); BoxCorners.Add(Corner4); return BoxCorners; } }
UCageUtilities::GetMeshDimensions
FVector UCageUtilities::GetMeshDimensions(class UStaticMesh* InMesh, bool UseLocalBounds) { if (InMesh == nullptr) { return FVector::ZeroVector; } FVector Dimensions; if (UseLocalBounds) { // DEPR - requires the component input /* FBoxSphereBounds Bounds = InMesh->GetLocalBounds(); FVector Scale = InMesh->GetComponentScale(); Dimensions = Bounds.BoxExtent * 2 * Scale;*/ } else { FVector BoxMin = InMesh->GetBoundingBox().Min; FVector BoxMax = InMesh->GetBoundingBox().Max; Dimensions = FVector(BoxMax.X - BoxMin.X, BoxMax.Y - BoxMin.Y, BoxMax.Z - BoxMin.Z); } return Dimensions; }
UCageUtilities::CircleObjectsAroundComponent
/* Version that takes in a Scene Component */ void UCageUtilities::CircleObjectsAroundComponent(USceneComponent* Target, int32 Amount, float Radius, EAxis::Type Axis, TArray& ObjectTransforms) { TArray LocalTransforms; if (Target != nullptr) { for (int32 Index = 0; Index < Amount; ++Index) { float Angle = 2 * PI * Index / Amount; FVector CircleCalculation; CircleCalculation = FVector(cos(Angle) * Radius, sin(Angle) * Radius, 0); FTransform NewTransform; NewTransform.SetLocation(Target->GetRelativeLocation() + CircleCalculation * -1.0f); FRotator NewRotation; switch(Axis) { case EAxis::Type::X: NewRotation = FRotator(0, 0, FMath::RadiansToDegrees(Angle)); break; case EAxis::Type::Y: NewRotation = FRotator(FMath::RadiansToDegrees(Angle), 0, 0); break; case EAxis::Type::Z: NewRotation = FRotator(0, FMath::RadiansToDegrees(Angle), 0); break; } FQuat QuatRotation = FQuat(NewRotation); NewTransform.SetRotation(QuatRotation); NewTransform.SetScale3D(FVector(1, 1, 1)); LocalTransforms.Add(NewTransform); } } ObjectTransforms = LocalTransforms; }
UCageUtilities::CircleObjectsAroundVector
/* Version that takes in a vector */ void UCageUtilities::CircleObjectsAroundVector(FVector InVector, int32 Amount, float Radius, EAxis::Type Axis, TArray& ObjectTransforms) { TArray LocalTransforms; for (int32 Index = 0; Index < Amount; ++Index) { float Angle = 2 * PI * Index / Amount; FVector CircleCalculation; switch(Axis) { case EAxis::Type::X: CircleCalculation = FVector(0, cos(Angle) * Radius, sin(Angle) * Radius); break; case EAxis::Type::Y: CircleCalculation = FVector(cos(Angle) * Radius, 0, sin(Angle) * Radius); break; case EAxis::Type::Z: CircleCalculation = FVector(cos(Angle) * Radius, sin(Angle) * Radius, 0); break; } FTransform NewTransform; FVector NewPoint; NewPoint = InVector + CircleCalculation * -1.0f; NewTransform.SetLocation(NewPoint); FRotator NewRotation; NewRotation = FRotator(0, FMath::RadiansToDegrees(Angle), 0); FQuat QuatRotation = FQuat(NewRotation); NewTransform.SetRotation(QuatRotation); NewTransform.SetScale3D(FVector(1, 1, 1)); LocalTransforms.Add(NewTransform); } ObjectTransforms = LocalTransforms; }