diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 806dc9f..f58b4ca 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -640,8 +640,6 @@ MonoBehaviour: npcPrefab: {fileID: 6083523108754401876, guid: 4b3d84858334857368bde30df360ae3e, type: 3} hunterNpcPrefab: {fileID: 4496988857626767934, guid: ab4e193839fef9a2189f27360914c044, type: 3} floorVisibilityManager: {fileID: 453022425} - floorsCount: 3 - floorHeightDistance: 15 scoreText: {fileID: 412275999} highScoreText: {fileID: 1626199842} gameOverUi: {fileID: 87831902} @@ -679,8 +677,8 @@ MonoBehaviour: tileBreakVfxPrefab: {fileID: 0} jumpPadPrefab: {fileID: 3258547662887829175, guid: e1d1bd44370c9986ebd4bb7730430a12, type: 3} teleporterPrefab: {fileID: 4601941687390792571, guid: 53f1de555c523511e9aaa1dee06fdf79, type: 3} - gridSizeX: 30 - gridSizeY: 30 + gridSizeX: 25 + gridSizeY: 25 floorsCount: 3 floorHeightDistance: 15 decayTime: 0.75 diff --git a/Assets/Scripts/Core/Domain/GameSession.cs b/Assets/Scripts/Core/Domain/GameSession.cs index d333740..7e5f68f 100644 --- a/Assets/Scripts/Core/Domain/GameSession.cs +++ b/Assets/Scripts/Core/Domain/GameSession.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using Core.Ports; +using UnityEngine; +using Random = System.Random; namespace Core.Domain { public class GameSession { private const string HighScoreKey = "HighScore"; - private const float NpcSpawnTime = 30f; private const float PowerUpSpawnInterval = 25f; public int Score { get; private set; } @@ -15,6 +16,7 @@ namespace Core.Domain public bool IsGameOver { get; private set; } public float TimeDilation { get; private set; } = 1.0f; + public float NpcSpawnTime { get; private set; } = 30f; public event Action OnScoreChanged; public event Action OnOrbSpawned; @@ -28,7 +30,7 @@ namespace Core.Domain private readonly Random _rng = new(); private int _playerFloorIndex = 0; private float _timeSinceStart; - private bool _npcSpawned; + private float _npcTimer; private float _powerUpTimer; private float _timeSlowTimer; @@ -53,7 +55,6 @@ namespace Core.Domain { _timeSinceStart = 0f; _powerUpTimer = 0f; - _npcSpawned = false; TimeDilation = 1.0f; ComboMultiplier = 1; @@ -67,10 +68,13 @@ namespace Core.Domain if (IsGameOver) return; _timeSinceStart += deltaTime; - if (!_npcSpawned && _timeSinceStart >= NpcSpawnTime) + _npcTimer += deltaTime; + + if (_npcTimer >= NpcSpawnTime) { - _npcSpawned = true; + _npcTimer = 0f; OnSpawnNpc?.Invoke(); + NpcSpawnTime = Mathf.Max(5f, NpcSpawnTime * 0.95f); } _powerUpTimer += deltaTime; @@ -189,7 +193,6 @@ namespace Core.Domain { case 0: type = PowerUpType.LightFooted; break; case 1: type = PowerUpType.SpeedBoost; break; - case 2: type = PowerUpType.Hover; break; case 3: type = PowerUpType.TimeSlow; break; } diff --git a/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs b/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs index 5384eeb..4b042a6 100644 --- a/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs +++ b/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs @@ -281,7 +281,8 @@ namespace Infrastructure.Unity } else if (npcPrefab) { - Instantiate(npcPrefab, spawnPos, Quaternion.identity); + var npc = Instantiate(npcPrefab, spawnPos, Quaternion.identity); + npc.Initialize(() => _gameSession.TimeDilation, () => _gameSession.EndGame()); } soundManager.PlayNpcSpawn(); diff --git a/Assets/Scripts/Infrastructure/Unity/NpcController.cs b/Assets/Scripts/Infrastructure/Unity/NpcController.cs index 7d8ec1f..2db2cee 100644 --- a/Assets/Scripts/Infrastructure/Unity/NpcController.cs +++ b/Assets/Scripts/Infrastructure/Unity/NpcController.cs @@ -18,9 +18,11 @@ namespace Infrastructure.Unity private Vector3 _currentDir; private float _timer; private Func _timeDilationProvider; + private Action _onPlayerHit; - public void Initialize(Func timeDilationProvider) + public void Initialize(Func timeDilationProvider, Action onPlayerHit) { + _onPlayerHit = onPlayerHit; _timeDilationProvider = timeDilationProvider; } @@ -70,5 +72,14 @@ namespace Infrastructure.Unity case 3: _currentDir = Vector3.right; break; } } + + private void OnCollisionEnter(Collision other) + { + if (other.gameObject.TryGetComponent(out var player)) + { + _onPlayerHit?.Invoke(); + Destroy(gameObject); + } + } } } \ No newline at end of file diff --git a/Assets/Scripts/Infrastructure/Unity/PowerUpViewAdapter.cs b/Assets/Scripts/Infrastructure/Unity/PowerUpViewAdapter.cs index a8cf793..6cbe8e2 100644 --- a/Assets/Scripts/Infrastructure/Unity/PowerUpViewAdapter.cs +++ b/Assets/Scripts/Infrastructure/Unity/PowerUpViewAdapter.cs @@ -35,9 +35,6 @@ namespace Infrastructure.Unity case PowerUpType.SpeedBoost: SetColor(EffectColors.SpeedBoostColor); break; - case PowerUpType.Hover: - SetColor(EffectColors.HoverColor); - break; case PowerUpType.TimeSlow: SetColor(EffectColors.TimeSlowColor); break; @@ -79,9 +76,6 @@ namespace Infrastructure.Unity case PowerUpType.SpeedBoost: player.Status.AddEffect(new SpeedBoostEffect(duration, 1.5f)); break; - case PowerUpType.Hover: - player.Status.AddEffect(new HoverEffect(duration)); - break; case PowerUpType.TimeSlow: // Handled globally break; diff --git a/README.md b/README.md new file mode 100644 index 0000000..a850021 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Decay Protocol + +**A fast-paced survival arcade game where the floor crumbles beneath your feet.** *Made with Unity 6 & C#* + +![Game Screenshot](link_to_screenshot_here.png) + +## đŸ•šī¸ High Concept +You are a rogue process in a dying mainframe. Every step you take corrupts the memory grid, causing the floor to disintegrate into the void. +* **Never stand still:** Idleness is death. +* **Watch your step:** You create the holes you must navigate. +* **Survive the purge:** Hunter processes and cleaning drones are trying to delete you. + +## 🎮 Controls +* **WASD / Arrow Keys:** Move +* **Space:** Jump / Air recovery (Includes Coyote Time) +* **R:** Restart (After death) + +## ✨ Key Features +* **Procedural Decay:** A "Swiss Cheese" map mechanic where every tile touched falls after 0.5s. +* **Dynamic Difficulty:** Tile decay speed and Enemy spawn rates increase as your score climbs. +* **Combo System:** Chain "Data Orb" collections within 2 seconds to multiply your score. +* **Power-Up System:** + * 🟡 **Speed Boost:** Move faster. + * đŸŸĸ **Hover:** Immunity to gravity/holes for a short time. + * âšĒ **Light-Footed:** Walk without breaking tiles. + * đŸ”ĩ **Time Slow:** Slows down world time (Matrix style). +* **Audio-Reactive Visuals:** The neon grid pulses in sync with the BPM of the soundtrack. + +## đŸ› ī¸ Technical Architecture +This project was built using a **Clean Architecture (Ports & Adapters)** approach to separate game logic from the Unity Engine API, ensuring testability and cleaner code. + +* **Core Domain:** `GameSession` manages the state, scoring, and rules purely in C# (no MonoBehaviours). +* **View Adapters:** `TileViewAdapter` and `PlayerController` handle the visual representation and physics. +* **Pooling System:** Custom `TilePool` to handle the high-frequency creation and destruction of grid tiles without GC spikes. +* **Input System:** Built on the new Unity Input System package. +* **Juice:** Heavy use of `LeanTween` for UI bounce, camera shake, and emission pulsing. + +## 🔧 Installation +1. Clone this repository. +2. Open with **Unity 6**. +3. Open scene: `Assets/Scenes/Game.unity`. +4. Press Play. + +## 📜 Credits +* **Code & Design:** [Your Name/Handle] +* **Assets:** Primitive shapes & Unity URP +* **Tweening:** LeanTween