A Virtual Reality tower defense game built with Unity, featuring immersive spatial gameplay where players defend their headquarters using strategic tower placement. Project uses Passthrough feature
- Tested only on Meta Quest III
Players must defend their headquarters from enemy attacks by strategically placing and managing different types of towers. The VR implementation allows for intuitive hand-based interactions and immersive spatial awareness that traditional tower defense games can't provide.
- Rocket Launcher - High damage explosive projectiles
- Machine Gun - Rapid-fire ballistic attacks
- Laser Tower - Continuous beam damage with visual effects
This project emphasizes clean code architecture and scalable design patterns from the ground up:
- Factory Pattern - Modular tower and projectile creation system
- State Pattern - Complex tower and enemy behavior management
- Command Pattern - Action management with undo capability
- Singleton Pattern - Core game managers and systems
- Object Pooling - Efficient memory management for projectiles and effects
- Component-based Architecture - Modular, reusable game systems
- Health System - Modular damage and health management
- Tower Behavior State Machine - Complex tower AI with multiple states:
- Auto-placement and ground alignment
- Target acquisition and prioritization
- Attack preparation and execution
- Idle animations
- Shooting Mechanics - Projectile-based and raycast-based attacks
- Particle Effects - Visual feedback for combat and interactions
- Object Pooling - Performance-optimized projectile management
- Hand Tower Menu - Wrist-mounted tower selection interface
- Hand Pitch Interactions - Natural grabbing and throwing mechanics
- Spatial UI - 3D interactive elements with hand tracking
- Auto-placement System - Smart tower positioning on surfaces
- Health Bar Visualization - Real-time health display for all units
- Hand Hover Detection - Precise hand tracking for UI interactions
- Pinch Gestures - Natural selection and activation methods
- Visual Feedback - Emission-based material effects for interactive elements
Assets/Scripts/
├── Core/
│ ├── Commands/ # Command pattern implementation
│ ├── Factories/ # Factory pattern for unit creation
│ ├── HealthSystem/ # Modular health management
│ ├── Pooling/ # Object pooling system
│ ├── StateMachine/ # State pattern for unit behaviors
│ └── TowerUnits/ # Specialized tower implementations
├── Data/
│ ├── UI/ # UI data structures
│ └── Units/ # Unit configuration ScriptableObjects
├── Hands/ # Hand tracking and interaction
└── UI/
└── Inventory/ # VR menu and interaction systems
Behavior management using the State Pattern:
// Tower states: AutoPlacement → Idle → PrepareToAttack → Attack
// Enemy states: FlyTowardsTarget → ProjectileAttack → SelfExplodeFlexible unit creation with concrete factories:
TowerFactory- Tower unit instantiation with poolingEnemyFactory- Enemy unit creationBulletFactory/RocketFactory- Projectile creation
Action management with undo capability:
// Spawn towers with command pattern for potential undo functionality
var command = new SpawnTowerCommand(unitFactory, position, rotation);
CommandManager.Instance.ExecuteCommand(command);- XR Hand Subsystem integration for precise hand detection
- Pinch Gestures for natural selection mechanics
- Wrist Menu Controller - Palm orientation-based menu activation
- Spatial Button Interactions - 3D buttons with hover and selection states
- Object Pooling prevents garbage collection spikes during intense gameplay
- Efficient State Management reduces computational overhead
- Optimized Particle Systems maintain frame rate stability
- Smart Target Acquisition with layered priority system
- Unity 2022.3 LTS or newer
- XR Interaction Toolkit
- XR Hands package
- VR headset with hand tracking support
- Activate Wrist Menu: Orient your palm towards your face
- Select Tower: Use pinch gestures on 3D tower models
- Place Towers: Grab and position/throw towers strategically
- Auto-placement: Towers automatically align to surfaces
- Defend: Towers automatically engage enemies within range
Tower behavior management using clean state transitions:
public class ProjectileTowerUnit : Unit
{
private TowerAutoPlacement _autoPlacementState;
private TowerProjectileAttack _attackState;
private TowerIdle _idleState;
private TowerPrepareToAttack _towerPrepareState;
protected override void Initialize()
{
// Get state components and set up event handlers
_autoPlacementState = statesLayer.GetComponent<TowerAutoPlacement>();
_attackState = statesLayer.GetComponent<TowerProjectileAttack>();
base.ChangeState(_autoPlacementState);
// Event-driven state transitions
_autoPlacementState.OnStateFinished += OnAutoPlacementStateFinished;
_towerPrepareState.OnStateFinished += OnTowerPrepareStateFinished;
}
protected override void Tick()
{
// Smart state management based on target availability
if (currentTarget == null && !_autoPlacementState.IsStateActive && !_idleState.IsStateActive)
{
base.ChangeState(_idleState);
}
if (currentTarget != null && !_towerPrepareState.IsStateActive && !_attackState.IsStateActive)
{
base.ChangeState(_towerPrepareState, base.currentTarget);
}
}
}High-performance memory management with generic pool implementation:
public static T SpawnObject<T>(T typePrefab, Vector3 spawnPos, Quaternion spawnRotation) where T : Component
{
if (!_objectPool.ContainsKey(typePrefab.gameObject))
{
CreatePool(typePrefab.gameObject, spawnPos, spawnRotation);
}
GameObject obj = _objectPool[typePrefab.gameObject].Get();
if (!_cloneToPrefabMap.ContainsKey(obj))
{
_cloneToPrefabMap.Add(obj, typePrefab.gameObject);
}
obj.transform.position = spawnPos;
obj.transform.rotation = spawnRotation;
obj.SetActive(true);
return obj.GetComponent<T>();
}Precise hand tracking with pinch gesture recognition:
private void CheckPinchGesture()
{
XRHand hand = useLeftHand ? handSubsystem.leftHand : handSubsystem.rightHand;
if (hand.GetJoint(XRHandJointID.ThumbTip).TryGetPose(out Pose thumbTipPose) &&
hand.GetJoint(XRHandJointID.IndexTip).TryGetPose(out Pose indexTipPose))
{
float pinchDistance = Vector3.Distance(thumbTipPose.position, indexTipPose.position);
if (!isPinching && pinchDistance <= pinchThreshold &&
!PieMenuHoverManager.Instance.HasAnyButtonTriggeredPinchAction())
{
bool canStartPinch = requireHoverToPinch ? isHovering :
IsClosestButtonToPinch(thumbTipPose.position, indexTipPose.position);
if (canStartPinch)
{
isPinching = true;
OnPinchSelectAction(); // Trigger tower spawning
}
}
}
}Clean action management with undo capability:
[Serializable]
public class SpawnTowerCommand : ICommand
{
private UnitFactory _unitFactory;
private Vector3 _spawnPosition;
private Quaternion _spawnRotation;
public SpawnTowerCommand(UnitFactory unitFactory, Vector3 position, Quaternion rotation)
{
_unitFactory = unitFactory;
_spawnPosition = position;
_spawnRotation = rotation;
}
public void Execute()
{
_unitFactory.CreateTower(_spawnPosition, _spawnRotation);
}
public void Undo()
{
// Implementation for tower removal
}
}
// Usage in VR interaction
public void OnButtonClick()
{
Vector3 spawnPosition = GetSpawnPosition();
Quaternion spawnRotation = GetSpawnRotation();
var command = new SpawnTowerCommand(unitFactory, spawnPosition, spawnRotation);
CommandManager.Instance.ExecuteCommand(command);
}Modular damage system with clean separation of concerns:
public class HealthController : MonoBehaviour
{
[SerializeField] private HealthBarView view;
private HealthModel _model;
private void OnEnable()
{
_model = new HealthModel(baseUnitSettings, baseUnitSettings.MaxHealth);
_model.OnDeath += OnDeath;
view.Initialize(baseUnitSettings.MaxHealth);
}
public void TakeDamage(float damage)
{
_model.TakeDamage(damage);
view.UpdateHealthBar(_model.HealthPercentage);
}
private void OnDeath()
{
// Particle effects, cleanup, and pooling
ParticleSystem deathEffect = Instantiate(baseUnitSettings.DeathParticleSystem,
transform.position, Quaternion.identity);
ObjectPoolManager.ReturnObjectToPool(gameObject);
}
}Priority-based enemy targeting with layered detection:
[System.Serializable]
public class LayerGroup
{
public string name;
public LayerMask layerMask;
public int priority; // Lower number = higher priority
}
private TargetInfo FindNewTargetWithPriority()
{
foreach (var layerGroup in layerGroups)
{
Transform target = FindClosestTarget(layerGroup.layerMask);
if (target != null)
{
return new TargetInfo(target, layerGroup.priority);
}
}
return new TargetInfo(null, int.MaxValue);
}
private bool ShouldSwitchTarget(TargetInfo newTargetInfo)
{
if (newTargetInfo.priority < currentTargetPriority)
{
Debug.Log($"Switching to higher priority target. Old: {currentTargetPriority}, New: {newTargetInfo.priority}");
return true;
}
return false;
}This project is in active development with a focus on:
- Clean, maintainable code architecture
- Performance optimization for VR
- Immersive spatial interactions
- Scalable design patterns
This project demonstrates:
- SOLID Principles applied throughout the codebase
- Design Pattern Implementation for maintainable architecture
- Performance-First Approach with object pooling and optimized rendering
- Modular Component Design for easy feature extension