Project Status : Complete Systems Showcase | Extensible Framework
Project Type : Modular Framework | Unreal Engine 5 (C++)
Core Focus : Systems Architecture | Data-Driven Design
First Person Shooter Framework
A production-ready FPS framework built in Unreal Engine 5, demonstrating professional systems architecture for weapon management, checkpoint persistence, and enemy AI. Designed as a modular, extensible foundation where new weapons and turret types require only data configuration, not code changes—enabling rapid iteration and team collaboration through component-based design patterns.
Main gameplay loop with:
- Checkpoint + Respawn mechanics
𓆩General𓆪
Quick Overview
☑︎ Quick Summary for Recruiters
- Component-based architecture for decoupled, testable systems
- Data-driven weapon system — add new weapons via config, no code changes
- Ammo & reload mechanics with automatic and manual reload options
- Checkpoint persistence — player state saved/restored on touch
- Turret AI & projectile pooling for efficient enemy management
- Score system with high-score serialization using UE5 Save Game API
- Health component & damage feedback with knockback and stun mechanics
- Professional UI systems (health bar, weapon display, score tracking)
Core Philosophy
✮ Core Philosophy
"Systems that scale are systems that are decoupled."
This framework demonstrates:
- ✅ Component-driven design (behavior lives in reusable components, not monolithic classes)
- ✅ Data-first architecture (configs define weapon properties, not hardcoded values)
- ✅ Separation of concerns (input, movement, combat, UI all live in separate systems)
- ✅ Performance-conscious (object pooling for projectiles, efficient raycasting)
- ✅ Team-ready (new designers can add content without touching gameplay code)
Every system is built to answer: "How do we make systems that multiple people can work on simultaneously?"
- - - --->Features<--- - - -
Highlight Features 𓆪
1. Weapon & Ammo System
✮ Data-Driven Weapon Framework
The Problem: Hardcoding weapon properties (damage, fire rate, ammo capacity) makes iteration impossible without programmer help. Every balance change requires code recompile.
The Solution: All weapon properties live in configurable data assets:
UWeaponDataAsset (example)
UCLASS()
class MYPROJECT_API UWeaponDataAsset : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Weapon")
FString WeaponName;
UPROPERTY(EditAnywhere, Category = "Weapon")
int32 MaxAmmo = 30;
UPROPERTY(EditAnywhere, Category = "Weapon")
float FireRate = 0.1f;
UPROPERTY(EditAnywhere, Category = "Weapon")
float Damage = 10.0f;
UPROPERTY(EditAnywhere, Category = "Weapon")
float ReloadTime = 2.5f;
};Result: Designers modify weapon properties in Unreal Editor—no code recompile needed. Add new weapons in minutes (duplicate asset, tweak values, done). Multiple weapons coexist without conflicts.
Ammo Management & Auto-Reload
Magazine-based system with reserve ammo + auto-reload at 0 ammo:
void UWeaponComponent::Fire()
{
if (!WeaponData || CurrentAmmo <= 0 || bIsReloading)
return;
// Spawn projectile and consume ammo
SpawnProjectile(GetActorForwardVector(), WeaponData->Damage);
CurrentAmmo--;
// Auto-reload at 0
if (CurrentAmmo == 0 && ReserveAmmo > 0)
Reload();
OnAmmoChanged.Broadcast(CurrentAmmo, ReserveAmmo);
}
void UWeaponComponent::Reload()
{
if (bIsReloading || ReserveAmmo <= 0) return;
bIsReloading = true;
GetWorld()->GetTimerManager().SetTimer(ReloadTimerHandle,
[this]() {
int32 AmmoToReload = FMath::Min(
WeaponData->MaxAmmo - CurrentAmmo, ReserveAmmo);
CurrentAmmo += AmmoToReload;
ReserveAmmo -= AmmoToReload;
bIsReloading = false;
OnAmmoChanged.Broadcast(CurrentAmmo, ReserveAmmo);
},
WeaponData->ReloadTime, false);
}2. Checkpoint & Persistence
✮ Checkpoint Respawn & Game State
Gameplay Flow:
- Player reaches checkpoint → touches actor, location saved
- Player takes damage → health reaches 0
- Death screen appears with respawn button
- Respawn teleports player to checkpoint, health restored
Result: Clean, reusable respawn pattern. Multiple checkpoints supported (last touched is "current").
Checkpoint Actor Implementation
```cpp
UCLASS()
class MYPROJECT_API AMyCheckpoint : public AActor, public IInteractionInterface
{
public:
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult);
};
void AMyCheckpoint::OnOverlapBegin(...)
{
if (APawn* Player = Cast<APawn>(OtherActor))
{
if (AGameManager* GM = AGameManager::Get())
{
GM->SetCheckpointLocation(GetActorLocation());
GM->AddScore(1); // Feedback hint
}
}
}
```
Death & Respawn Flow
void AHealthComponent::TakeDamage(float Damage)
{
CurrentHealth -= Damage;
OnHealthChanged.Broadcast();
if (CurrentHealth <= 0)
{
if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
{
PC->ShowDeathScreen();
PC->SetInputMode(FInputModeUIOnly());
}
}
}
void APlayerController::OnRespawnClicked()
{
if (AGameManager* GM = AGameManager::Get())
{
GetCharacter()->TeleportTo(GM->GetCheckpointLocation(), FRotator::ZeroRotator);
HealthComponent->SetHealth(HealthComponent->GetMaxHealth());
SetInputMode(FInputModeGameOnly());
}
}3. Turret AI & Performance
✮ Enemy AI & Projectile Pooling
Turret AI: Configurable detection range, fire rate, and targeting behavior.
Projectile Pooling: Pre-allocate pool → recycle objects instead of spawn/destroy every frame. Result: 50+ enemies firing simultaneously without frame rate dips.
Turret AI Behavior
UCLASS()
class MYPROJECT_API AEnemyTurret : public APawn
{
UPROPERTY(EditAnywhere) float DetectionRange = 1000.0f;
UPROPERTY(EditAnywhere) float FireRate = 0.5f;
private:
float LastFireTime = 0.0f;
APawn* CurrentTarget = nullptr;
};
void AEnemyTurret::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
SearchForTarget();
if (CurrentTarget && GetWorld()->GetTimeSeconds() - LastFireTime > FireRate)
{
FireAtTarget();
LastFireTime = GetWorld()->GetTimeSeconds();
}
}Object Pool Pattern
UCLASS()
class MYPROJECT_API AProjectilePool : public AActor
{
UPROPERTY(EditAnywhere) int32 PoolSize = 50;
AProjectileBase* GetProjectile();
void ReturnProjectile(AProjectileBase* Projectile);
private:
UPROPERTY() TArray<AProjectileBase*> AvailableProjectiles;
UPROPERTY() TArray<AProjectileBase*> ActiveProjectiles;
};
AProjectileBase* AProjectilePool::GetProjectile()
{
if (AvailableProjectiles.Num() > 0)
{
AProjectileBase* Proj = AvailableProjectiles.Pop();
ActiveProjectiles.Add(Proj);
return Proj;
}
return GetWorld()->SpawnActor<AProjectileBase>();
}Result: Zero allocations during gameplay. Memory efficient at scale.
4. Score & Persistence
✮ Score Tracking & High Score Persistence
Score awarded for:
- ✅ Turret destroyed: +1 score
- ✅ Checkpoint reached: +1 score (feedback hint)
High Score Persistence: Serialized to disk using UE5 SaveGame API. Score persists across play sessions—players are motivated to beat their high score.
Score System Implementation
UCLASS()
class MYPROJECT_API UHighScoreSaveGame : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY() int32 HighScore = 0;
UPROPERTY() FDateTime LastAchievedDate;
};
void AGameManager::SaveHighScore(int32 NewScore)
{
if (UHighScoreSaveGame* SaveData = Cast<UHighScoreSaveGame>(
UGameplayStatics::CreateSaveGameObject(UHighScoreSaveGame::StaticClass())))
{
SaveData->HighScore = NewScore;
UGameplayStatics::SaveGameToSlot(SaveData, TEXT("HighScore"), 0);
}
}
void AGameManager::AddScore(int32 Amount)
{
CurrentScore += Amount;
OnScoreChanged.Broadcast();
if (CurrentScore > HighScore)
{
HighScore = CurrentScore;
SaveHighScore(HighScore);
}
}5. Pickup & Buff System
✮ Pickup & Buff System
- Movement speed buff pickups
Uses the same PickupComponent + BuffBaseComponent pattern, adding new pickup types by creating data assets, no code changes needed.
Data-driven pickups with configurable buffs:
UPickupDataAsset (example)
UCLASS(BlueprintType)
class CPPPROJECTNEW_API UPickupDataAsset : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pickup")
FName PickupName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pickup")
UStaticMesh* PickupMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Buff")
float BuffLifespan = 10.f; // Buff duration in seconds
// List of buff component classes to apply on pickup
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Buff")
TArray<TSubclassOf<UBuffBaseComponent>> BuffComponentClasses;
};
void UPickupComponent::HandlePickup(APickupBase* PickupActor, UPickupDataAsset* PickupData)
{
if (!PickupData) return;
// Apply all buffs from the pickup data
for (TSubclassOf<UBuffBaseComponent> BuffClass : PickupData->BuffComponentClasses)
{
UBuffBaseComponent* NewBuff = NewObject<UBuffBaseComponent>(GetOwner(), BuffClass);
if (NewBuff)
{
NewBuff->BuffLifespan = PickupData->BuffLifespan;
NewBuff->RegisterComponent();
NewBuff->OnBuffApplied(GetOwner());
}
}
// Broadcast pickup event for UI feedback
OnPickupFeedback(PickupActor, PickupData);
}Result: Add new pickup types in minutes—create data asset, configure buffs, place in level. Same extensibility pattern as weapons: new designers can iterate without touching gameplay code.
- - - --->Technical<--- - - -
𓆩Technical Deep Dive 𓆪
Architecture Overview
✮ Architecture Overview & Design Patterns
Architecture Diagram
┌──────────────────────────────────────────────┐
│ FPSCharacter (Player Pawn) │
│ Handles input, movement, camera control │
└──────────────────────────────────────────────┘
↓ ↓ ↓
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Character│ │ Weapon │ │ Health │
│Movement │ │Component │ │Component │
│Component │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘
┌──────────────────────────────────────────────┐
│ GameManager (World Authority) │
│ Checkpoint location, Score, Game state │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ EnemyTurret (AI Pawn) │
│ Detects player, aims, fires │
└──────────────────────────────────────────────┘
↓
┌──────────────┐
│Projectile │
│Pool │
└──────────────┘Single Responsibility Principle:
FPSCharacter: Input → movement mappingWeaponComponent: Weapon state, firing logic, ammo trackingHealthComponent: Damage handling, death triggerGameManager: Checkpoint location, score authorityEnemyTurret: AI behavior, target detectionProjectilePool: Lifecycle management of projectiles
Why This Works:
- ✅ Add new weapon? Add new data asset, no code touch
- ✅ Add new enemy type? Extend turret class, same health system
- ✅ Change HUD? Update UIToolkit widget, gameplay unaffected
Health Component (Reusable)
Works on player AND enemies:
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class MYPROJECT_API UHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Health") float MaxHealth = 100.0f;
UFUNCTION(BlueprintCallable) void TakeDamage(float Damage, AActor* Instigator = nullptr);
UFUNCTION(BlueprintCallable) float GetHealth() const { return CurrentHealth; }
UFUNCTION(BlueprintCallable) float GetHealthPercent() const { return CurrentHealth / MaxHealth; }
FSimpleDelegate OnHealthChanged;
FSimpleDelegate OnDeath;
};
void UHealthComponent::TakeDamage(float Damage, AActor* DamageInstigator)
{
CurrentHealth = FMath::Max(0.0f, CurrentHealth - Damage);
OnHealthChanged.Broadcast();
// Knockback
if (DamageInstigator && ACharacter* Char = Cast<ACharacter>(GetOwner()))
{
FVector KnockbackDir = (GetOwner()->GetActorLocation() -
DamageInstigator->GetActorLocation()).GetSafeNormal();
Char->LaunchCharacter(KnockbackDir * 1000.0f, true, true);
}
if (CurrentHealth <= 0.0f)
{
OnDeath.Broadcast();
GetOwner()->Destroy();
}
}Design Patterns Used
- Observer Pattern: Delegates notify UI of state changes (no tight coupling)
- Object Pool Pattern: Projectile pooling for efficiency
- Singleton Pattern: Game manager is single source of truth
- State Pattern: Weapon reloading, health states
- Component Pattern: Composition over inheritance (why it scales)
Component-based pickup/buff system using data assets
- - - --->Extras<--- - - -
𓆩Extra Notes 𓆪
Core Competencies
✮ Core Competencies
Systems Architecture:
- ✅ Component-based design (decoupled, composable, reusable)
- ✅ Data-driven approaches (configs, not code changes)
- ✅ Separation of concerns (input, logic, state, UI are separate)
- ✅ Singleton patterns for game state authority
C++ Best Practices:
- ✅ Modern C++ (smart pointers, lambdas, delegates)
- ✅ Proper const-correctness and memory management
- ✅ Clear, documented code structure
- ✅ Efficient algorithms (object pooling, early returns)
UE5 Specifics:
- ✅ Component architecture mastery
- ✅ Delegate system for decoupled communication
- ✅ Save Game API for persistence
- ✅ Timer manager for async operations
- ✅ World spawning and lifecycle management
Use Cases & Extensibility
✮ Use Cases & Extensibility
Perfect For:
- ✅ Multiplayer games (state authority pattern scales well)
- ✅ Content-heavy projects (designers can iterate without code)
- ✅ Team development (clear ownership of systems)
- ✅ Games with multiple weapons/enemies (extensible architecture)
Extensibility:
- ✅ Add new weapon types (data asset only)
- ✅ Add new enemy behaviors (extend base turret class)
- ✅ Add upgrade systems (modify weapon data at runtime)
- ✅ Add difficulty levels (scale numbers in game manager)
- ✅ Add multiplayer (components are already networked-ready)
Technologies & Stack
✮ Technologies & Stack
- Engine: Unreal Engine 5.0+
- Language: C++ (Modern C++17 standards)
- Architecture: Component-based actors, composition over inheritance
- Data Management: DataAsset configs, SaveGame serialization
- UI: UIToolkit widget system for HUD
- Performance: Object pooling, efficient raycasting, zero allocations during gameplay
- Code Style: UE naming conventions, proper encapsulation, delegate-driven communication
Source Code:
FPSCharacter.h/cpp— Player input and movementWeaponComponent.h/cpp— Weapon firing and ammo managementHealthComponent.h/cpp— Generic damage systemEnemyTurret.h/cpp— AI enemy behaviorGameManager.h/cpp— Game state authorityProjectilePool.h/cpp— Efficient projectile management
Links & Resources
✮ Links & Resources
- GitHub: Link
- Source Code Documentation: [Not available yet]
- Play in Browser: [Not available yet]
