So this week I took another look at that 3D brawler I decided to do in the Unreal Engine. I wanted to create the arena where the battles will take place, unfortunately for me a skill that I completely lack is art. I can draw a semi ok cartoon duck head and that’s about it.
This, does not help with creating an arena. So, I asked myself (totally not the same as talking to yourself… just so you know) what will I need to create a basic arena.
Well I need an arena wall in the shape of a giant circle (at least for now). My blender skills aren’t that good and when I knew the basics was a long time ago, also my texture art skills are zero, so I went looking for modular dungeon crawler, thinking these will have some good wall type objects I could use.
I found an asset pack called Crumbling Ruins by Warren Marshall.
So now comes to actually creating the arena. To do this I created a new C++ class from within the UE project and chose its parent class as Actor.

The actor class has a virtual method called OnConstruction. It is here that I added the logic to create the arena walls.
The header file should look like this
UCLASS()
class BRAWL_API AArenaWallGenerator : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AArenaWallGenerator();
// Called every frame
virtual void Tick(float DeltaTime) override;
public:
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* WallSegment;
UPROPERTY(EditAnywhere, Category = "Construction")
float Radius;
protected:
// Called when the game starts or when spawned
//virtual void BeginPlay() override;
virtual void OnConstruction(const FTransform& Transform) override;
void ComputeDimensions();
void ClearWallArray();
void ConstructWalls();
protected:
float Diameter;
float Circumference;
int NumberOfSegments;
float WallSegmentSize;
// We need to keep a list of wall segments incase the parameters change
// this is so we can quickly delete the segments to create new ones.
TArray<UStaticMeshComponent*> WallArray;
};
As you can see there are some basic parameters that allow us to define the arena wall, we have a radius, diameter, circumference, the number of wall segments that make up the arena and a list of wall segments that we need to keep track of should anything change.
The radius is exposed to the editor, from this we compute the diameter, circumference and the number of wall segments that will be needed.
void AArenaWallGenerator::ComputeDimensions()
{
if (WallSegmentSize > .0f)
{
Diameter = Radius * 2.0f;
Circumference = PI * Diameter;
NumberOfSegments = static_cast<int>(Circumference / WallSegmentSize) * 0.5f;
}
}
The construction of the arena wall is just some basic maths. We need to know how many segments we need and the space and angle between each
const float DegInc = 360.0f / NumberOfSegments;
With this for each wall segment we move the next DegInc around the circumference of the circle.
For each segment position we use the following
const float segment_x = CenterPosition.X + cos(Rad) * Radius;
const float segment_y = CenterPosition.Y + sin(Rad) * Radius;
Note we have to convert our degrees here to radians. I use FMath::DegreesToRadians(Deg); to make this conversion.
The only thing to do now is to rotate the wall segment so it fits on the circumference of the arena.
NewWallSeg->SetWorldRotation(FRotator(.0f,Deg, .0f));
And that’s it. The full source is below.
// Sets default values
AArenaWallGenerator::AArenaWallGenerator()
: Diameter(.0f),
Circumference(.0f),
NumberOfSegments(0),
WallSegmentSize(.0f)
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
// Until i find a better way to do this for now set this wall segment as invisible.
WallSegment = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
SetRootComponent(WallSegment);
WallSegment->SetVisibility(false, false);
}
void AArenaWallGenerator::OnConstruction(const FTransform& Transform)
{
// Only want to set this first time in.
if (WallSegmentSize == .0f)
{
// Actor bounds comes from the root component that was
// set in the constructor
FVector origin;
FVector size;
GetActorBounds(false, origin, size);
WallSegmentSize = size.Y;
}
ClearWallArray();
ComputeDimensions();
RegisterAllComponents();
ConstructWalls();
}
void AArenaWallGenerator::ConstructWalls()
{
const FName WallName = FName("OuterWall");
const FVector CenterPosition = GetActorLocation();
const float DegInc = 360.0f / NumberOfSegments;
float Deg = .0f;
for (int SegmentIndex = 0; SegmentIndex != NumberOfSegments; ++SegmentIndex)
{
const float Rad = FMath::DegreesToRadians(Deg);
const float segment_x = CenterPosition.X + cos(Rad) * Radius;
const float segment_y = CenterPosition.Y + sin(Rad) * Radius;
// Construct the wall segment and position it along the circumference of the arena wall
const FString NewWallName = WallName.ToString() + FString::FromInt(SegmentIndex + 1);
UStaticMeshComponent* NewWallSeg = NewObject<UStaticMeshComponent>(this, *NewWallName);
if (NewWallSeg)
{
WallArray.Add(NewWallSeg);
NewWallSeg->SetStaticMesh(WallSegment->GetStaticMesh());
NewWallSeg->SetWorldLocation(FVector(segment_x, segment_y,CenterPosition.Z));
NewWallSeg->SetWorldRotation(FRotator(.0f,Deg, .0f));
NewWallSeg->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepWorldTransform);
NewWallSeg->RegisterComponent();
}
Deg += DegInc;
}
}
void AArenaWallGenerator::ComputeDimensions()
{
if (WallSegmentSize > .0f)
{
Diameter = Radius * 2.0f;
Circumference = PI * Diameter;
NumberOfSegments = static_cast<int>(Circumference / WallSegmentSize) * 0.5f;
}
}
void AArenaWallGenerator::ClearWallArray()
{
for (auto It = WallArray.CreateIterator(); It; It++)
{
(*It)->DestroyComponent();
}
WallArray.Empty();
}
///////////////////////////////////////////////////////////////////////////////
// Called every frame
void AArenaWallGenerator::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
This is a really good start from here we will be able to add in entrances, stands etc. and really flesh things out.
Next task for this game is some combat moves. Then we will take a look at doing some AI work.
