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
| Tool | Use |
|---|---|
| Unity 6 | Engine |
| C# | All gameplay code |
| Cinemachine | Camera system |
| NavMesh / NavMesh+ | Enemy pathfinding |
| ScriptableObjects | Item and weapon data |
| JSON serialization | Save system |
| Aseprite | UI icons |
| Git + GitHub | Version 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:
- Current player position
- Other enemies' positions (avoid clustering)
- 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
| Phase | Duration | What happened |
|---|---|---|
| Prototype | 3 weeks | Basic movement, one weapon, placeholder enemies |
| Core systems | 6 weeks | State machine, inventory, save/load |
| Enemy AI | 4 weeks | Behavior trees, flanking logic, NavMesh |
| Polish | Ongoing | UI, 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)