









Project Status
Available Platforms
Project Type
Project Duration
Software Used
Languages Used
Primary Role(s)
Commercially Released
Android / iOS
Indie Game
3 years
Unreal Engine 4
C++/C#/Java/Swift/Blueprints
Solo Dev (All Roles)
A Maze 3D was my first commercially released title as an indie developer.
It was also my first Mobile project, as such I learnt a lot during each new stage of development and it’s safe to say, it added to the overall duration of the project.
I designed a suspension system similar to what vehicles uses to overcome the underlining issue of sphere on flat surfaces collision error that Nvidia PhysX contains that causes spheres to ‘bounce’ due to small collision values like 0.001 or less.
The central capsule trace combined with the 4 corner traces are used to cause partial to full loss of control over the pawn.
void ABallPawn::ApplySuspension(int32& NumSuspOnGround)
{
/* Hit Counter */
int32 LocalSuspHits = 0;
for (int32 Index = 0; Index < LocalSuspPoints.Num(); Index++)
{
FVector _PointWorldLocation = UKismetMathLibrary::TransformLocation(GetActorTransform(), LocalSuspPoints[Index]);
/* SuspensionTrace Results */
float LocalRatio = 0; FVector LocalForceLocation = FVector(); bool LocalHit = false;
PerformSuspensionTrace(_PointWorldLocation, LocalSuspPoints[Index], LocalRatio, LocalForceLocation, LocalHit);
/* Increment on Hit */
if (LocalHit) { LocalSuspHits++; }
/* Calculation how strong force to apply to suspension point, taking in last frame ratio relative to this frames ratio and Suspension Values & Gravity. */
float FloatForce = ((SuspensionStiffness * LocalRatio) + (SuspensionDamping * ((LocalRatio - LastFrameRatios[Index]) / MyDeltaTime))) * MassInKG * Gravity;
/* Apply Force at Calculated location with force relative to Suspension Values */
BallBase->AddForceAtLocation(GetActorUpVector() * FloatForce, LocalForceLocation);
/* Update Last Frame Ratio */
LastFrameRatios[Index] = LocalRatio;
}
NumSuspOnGround = LocalSuspHits;
} We also have full control over objective interactions with the large sphere and 4 corner spheres, making level objectives behave exactly as we want by simply giving them a few parameters.
Early prototype showcase off the objective ‘Pushing Wall’.
As all the physics are custom and no actual interaction between the visual ball appearance and the world is taking place we have to manually “fake” things like directional rotation and speed.
Side note: The shadow on the ground & ball is actually ‘faked’ too, all shader math driven.
void ALabyrinthPawn::UpdateBallRotation()
{
// Capture Horizontal Velocity
FVector HorizontalVelocity = GetVelocity() - GetVelocity().ProjectOnToNormal(GetActorUpVector());
if (!Abs<float>(HorizontalVelocity.Size(), 0.0f) <= 9.999999939e-09F)
{
float LocalAngle = (HorizontalVelocity.Size() / (3.14f * 0.5f)) * MyDeltaTime;
FVector VectorAxis = FRotationMatrix(FRotationMatrix::MakeFromX(HorizontalVelocity).Rotator()).GetScaledAxis(EAxis::Y);
FRotator DesiredRotator = FQuat(VectorAxis.GetSafeNormal(), FMath::DegreesToRadians(LocalAngle)).Rotator();
FRotator FinalRotation = UKismetMathLibrary::FRotator(FQuat(DesiredRotator)*FQuat(Ball->GetRelativeRotation()));
// Set mesh final Rotation
Ball->SetRelativeRotation(FinalRotation);
}
} Simple yet elegant player input manipulation, will either reduce player input or reverse input values.
I made the diamond multi-purpose for a wide range of objective uses, in this scenario it is used to trigger a hidden path reveal and creates a traversable path for the player.
This is the most basic of examples, the objective can come with an unlimited amount of keys required. The keys can be invisible at the start and be toggled visible through interacting with a Diamond.
There can also be multiple gates in one level, at which point the keys and gates are mapped by color.
Teleporters have 3 starting states, functional / broken / invisible, all which can be changed depending on current level objectives, And the teleporter connections are identifiable by color schemes.
I implemented this effect through a sphere mask, I went for a spotlight effect but all the lightning in the game is ‘faked’ and material driven so with my limited technical design knowledge this became the final effect.