Skip to main content

Sharpshooters

A solo-developed 3D third-person shooter built in Unity 6.

Status: In development — targeting itch.io release Q3 2026


Overview

Sharpshooters is a third-person shooter where players fight through waves of enemies across arena-style levels. The project's goal was to build a complete, playable game with production-quality systems — not a prototype.


Motivation

I wanted to learn:

  • State machines applied to a real player controller (not a tutorial example)
  • Enemy AI beyond simple "walk toward player" — patrol, flank, retreat
  • Inventory and weapons — a system flexible enough for multiple weapon types
  • Save data — persist progress between sessions

Technologies

ToolUse
Unity 6Engine
C#All gameplay code
CinemachineCamera system
NavMesh / NavMesh+Enemy pathfinding
ScriptableObjectsItem and weapon data
JSON serializationSave system
AsepriteUI icons
Git + GitHubVersion control

Screenshots

(Screenshots will go here — static/img/sharpshooters/)


Features

  • Third-person movement with sprint, crouch, and roll
  • Aim-down-sights with per-weapon camera FOV
  • Four weapon types: pistol, SMG, shotgun, sniper rifle
  • Wave-based enemy spawning with difficulty scaling
  • Three enemy archetypes: patrol guard, assault soldier, sniper
  • Pickup system — ammo, health, temporary buffs
  • Save/load — progress persists between play sessions
  • Full controller support

Architecture

How the major systems connect:

PlayerController

StateMachine (Idle → Move → Aim → Roll → Dead)

WeaponSystem ←── WeaponData (ScriptableObject)

BulletPool (object pooling for performance)

EnemyController

BehaviorTree (Patrol → Chase → Attack → Retreat)

NavMeshAgent

GameManager

WaveSpawner → EnemyPool

SaveSystem → JSON file

Each system communicates through C# events — no direct cross-system references except where unavoidable.


Interesting Problem: Enemy AI Flanking

Problem: Early enemy AI walked straight at the player, making the game trivial. I wanted enemies that would try to flank.

Investigation: I read about influence maps and realized I could give enemies a weighted target position: 40% toward the player, 60% toward the player's side. Using multiple NavMesh agents simultaneously, I tested 3–5 enemies flanking at once.

Solution: A TacticalPositioner component runs every 1.5 seconds and recalculates each enemy's approach vector based on:

  1. Current player position
  2. Other enemies' positions (avoid clustering)
  3. Cover objects in the scene (prefer moving along walls)

Outcome: Enemies feel noticeably smarter. Players have started adapting their own positioning in response — which was the goal.


Development Timeline

PhaseDurationWhat happened
Prototype3 weeksBasic movement, one weapon, placeholder enemies
Core systems6 weeksState machine, inventory, save/load
Enemy AI4 weeksBehavior trees, flanking logic, NavMesh
PolishOngoingUI, SFX, camera polish, difficulty tuning

Lessons Learned

  • Profile first. Rewrote the bullet system based on a wrong assumption — GC spikes were from string operations in the UI, not bullets at all.
  • ScriptableObjects are worth the setup cost. Adding a new weapon type is now 10 minutes: create asset, fill fields, add to spawn list.
  • State machine edge cases accumulate. The roll state alone has 7 transitions. Draw the full graph before coding.

Future Improvements

  • Boss enemy with phase-based behavior
  • Procedurally generated level layouts
  • Online leaderboard
  • More enemy archetypes (heavy, medic)
  • Controller vibration / haptic feedback

Play the Game

Play on itch.io (link active at release)
GitHub Repository (source code)