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