Back to all projects

Gameplay Programmer

Dark Chamber

I owned gameplay programming for combat feel, AI encounter flow, and player feedback loops. The goal was to make every fight legible, tense, and responsive while still running comfortably on mid-tier hardware.

Unreal Engine 5C++BlueprintBehavior Trees

Duration

4 months

Team

3 developers

Engine

Unreal Engine 5

Platforms

PC

Gameplay Pillars

  • >Combat readability over raw visual noise
  • >Consistent input response under pressure
  • >Enemy behaviors that escalate tension without feeling unfair

My Responsibilities

  • >Implemented lock-on targeting, dodge invulnerability windows, and hit-stop tuning.
  • >Built a wave director that adapts enemy compositions based on player state.
  • >Integrated animation notifies with gameplay events for accurate telegraphs.
  • >Profiled combat scenes and reduced expensive overlap checks in hot paths.

Systems Breakdown

Adaptive Encounter Director

Spawns and retargets enemy archetypes based on player health, ammo, and current arena zone.

Impact: Reduced repetitive waves and improved average session retention during internal playtests.

Telegraph Timing Pipeline

Synchronized VFX, animation notifies, and damage windows in a single data-driven timing table.

Impact: Made incoming attacks easier to read, cutting avoidable damage spikes for new players.

Input Buffer + Recovery Windows

Added short command buffering for dodge and counter actions during recovery frames.

Impact: Greatly improved responsiveness perception without trivializing combat difficulty.

Media Gallery

Screenshots and clips from gameplay, debug tools, and iteration passes.

Dark Chamber combat arena

Final combat arena lighting and readability pass.

Debug overlay for targeting scores

Runtime debug overlay used to tune lock-on target scoring.

Short clip showing enemy telegraphs and counter timing windows.

Video Walkthroughs

Focused clips showing implementation outcomes, tuning passes, and polished gameplay moments.

Combat Feel Pass

Before/after comparison of hit-stop tuning, camera impulse, and recovery buffering.

Code Snippets

Practical samples from gameplay systems and runtime tools used in production.

Target Selection Scoring

CombatTargetingComponent.cpp | cpp

Weighted scoring that combines player facing and distance to choose a lock-on target.

AActor* UCombatTargetingComponent::ChooseTarget(
    const TArray<AActor*>& Candidates,
    const FVector& ForwardVector,
    const FVector& OwnerPosition
) const
{
    float BestScore = -FLT_MAX;
    AActor* BestTarget = nullptr;

    for (AActor* Candidate : Candidates)
    {
        if (!IsValid(Candidate))
        {
            continue;
        }

        const FVector ToTarget = (Candidate->GetActorLocation() - OwnerPosition).GetSafeNormal();
        const float FacingScore = FVector::DotProduct(ForwardVector, ToTarget);
        const float DistanceScore = 1.0f - FMath::Clamp(
            FVector::Distance(OwnerPosition, Candidate->GetActorLocation()) / MaxLockDistance,
            0.0f,
            1.0f
        );

        const float FinalScore = (FacingScore * 0.7f) + (DistanceScore * 0.3f);
        if (FinalScore > BestScore)
        {
            BestScore = FinalScore;
            BestTarget = Candidate;
        }
    }

    return BestTarget;
}

Input Buffer Window

PlayerCombatState.cpp | cpp

Buffers dodge input while attack animation recovers to keep controls responsive.

void UPlayerCombatState::HandleBufferedInputs(float DeltaSeconds)
{
    BufferedDodgeTime -= DeltaSeconds;

    if (BufferedDodgeTime > 0.0f && bCanDodge)
    {
        ExecuteDodge();
        BufferedDodgeTime = 0.0f;
        return;
    }

    if (bDodgePressedThisFrame)
    {
        BufferedDodgeTime = DodgeBufferDuration;
    }
}

Production Outcomes

  • >Encounter restart rate dropped after readability pass and telegraph retiming.
  • >Average combat duration became more stable across skill levels.
  • >Internal QA reports highlighted stronger perceived responsiveness.
Discuss this project