Refactor NPC spawning logic and adjust grid size; remove unused power-up type

This commit is contained in:
2025-12-13 02:59:59 +01:00
parent 0e86d2f4f9
commit 1ae7190fd3
6 changed files with 72 additions and 18 deletions

View File

@@ -640,8 +640,6 @@ MonoBehaviour:
npcPrefab: {fileID: 6083523108754401876, guid: 4b3d84858334857368bde30df360ae3e, type: 3} npcPrefab: {fileID: 6083523108754401876, guid: 4b3d84858334857368bde30df360ae3e, type: 3}
hunterNpcPrefab: {fileID: 4496988857626767934, guid: ab4e193839fef9a2189f27360914c044, type: 3} hunterNpcPrefab: {fileID: 4496988857626767934, guid: ab4e193839fef9a2189f27360914c044, type: 3}
floorVisibilityManager: {fileID: 453022425} floorVisibilityManager: {fileID: 453022425}
floorsCount: 3
floorHeightDistance: 15
scoreText: {fileID: 412275999} scoreText: {fileID: 412275999}
highScoreText: {fileID: 1626199842} highScoreText: {fileID: 1626199842}
gameOverUi: {fileID: 87831902} gameOverUi: {fileID: 87831902}
@@ -679,8 +677,8 @@ MonoBehaviour:
tileBreakVfxPrefab: {fileID: 0} tileBreakVfxPrefab: {fileID: 0}
jumpPadPrefab: {fileID: 3258547662887829175, guid: e1d1bd44370c9986ebd4bb7730430a12, type: 3} jumpPadPrefab: {fileID: 3258547662887829175, guid: e1d1bd44370c9986ebd4bb7730430a12, type: 3}
teleporterPrefab: {fileID: 4601941687390792571, guid: 53f1de555c523511e9aaa1dee06fdf79, type: 3} teleporterPrefab: {fileID: 4601941687390792571, guid: 53f1de555c523511e9aaa1dee06fdf79, type: 3}
gridSizeX: 30 gridSizeX: 25
gridSizeY: 30 gridSizeY: 25
floorsCount: 3 floorsCount: 3
floorHeightDistance: 15 floorHeightDistance: 15
decayTime: 0.75 decayTime: 0.75

View File

@@ -1,13 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Core.Ports; using Core.Ports;
using UnityEngine;
using Random = System.Random;
namespace Core.Domain namespace Core.Domain
{ {
public class GameSession public class GameSession
{ {
private const string HighScoreKey = "HighScore"; private const string HighScoreKey = "HighScore";
private const float NpcSpawnTime = 30f;
private const float PowerUpSpawnInterval = 25f; private const float PowerUpSpawnInterval = 25f;
public int Score { get; private set; } public int Score { get; private set; }
@@ -15,6 +16,7 @@ namespace Core.Domain
public bool IsGameOver { get; private set; } public bool IsGameOver { get; private set; }
public float TimeDilation { get; private set; } = 1.0f; public float TimeDilation { get; private set; } = 1.0f;
public float NpcSpawnTime { get; private set; } = 30f;
public event Action<int> OnScoreChanged; public event Action<int> OnScoreChanged;
public event Action<string> OnOrbSpawned; public event Action<string> OnOrbSpawned;
@@ -28,7 +30,7 @@ namespace Core.Domain
private readonly Random _rng = new(); private readonly Random _rng = new();
private int _playerFloorIndex = 0; private int _playerFloorIndex = 0;
private float _timeSinceStart; private float _timeSinceStart;
private bool _npcSpawned; private float _npcTimer;
private float _powerUpTimer; private float _powerUpTimer;
private float _timeSlowTimer; private float _timeSlowTimer;
@@ -53,7 +55,6 @@ namespace Core.Domain
{ {
_timeSinceStart = 0f; _timeSinceStart = 0f;
_powerUpTimer = 0f; _powerUpTimer = 0f;
_npcSpawned = false;
TimeDilation = 1.0f; TimeDilation = 1.0f;
ComboMultiplier = 1; ComboMultiplier = 1;
@@ -67,10 +68,13 @@ namespace Core.Domain
if (IsGameOver) return; if (IsGameOver) return;
_timeSinceStart += deltaTime; _timeSinceStart += deltaTime;
if (!_npcSpawned && _timeSinceStart >= NpcSpawnTime) _npcTimer += deltaTime;
if (_npcTimer >= NpcSpawnTime)
{ {
_npcSpawned = true; _npcTimer = 0f;
OnSpawnNpc?.Invoke(); OnSpawnNpc?.Invoke();
NpcSpawnTime = Mathf.Max(5f, NpcSpawnTime * 0.95f);
} }
_powerUpTimer += deltaTime; _powerUpTimer += deltaTime;
@@ -189,7 +193,6 @@ namespace Core.Domain
{ {
case 0: type = PowerUpType.LightFooted; break; case 0: type = PowerUpType.LightFooted; break;
case 1: type = PowerUpType.SpeedBoost; break; case 1: type = PowerUpType.SpeedBoost; break;
case 2: type = PowerUpType.Hover; break;
case 3: type = PowerUpType.TimeSlow; break; case 3: type = PowerUpType.TimeSlow; break;
} }

View File

@@ -281,7 +281,8 @@ namespace Infrastructure.Unity
} }
else if (npcPrefab) else if (npcPrefab)
{ {
Instantiate(npcPrefab, spawnPos, Quaternion.identity); var npc = Instantiate(npcPrefab, spawnPos, Quaternion.identity);
npc.Initialize(() => _gameSession.TimeDilation, () => _gameSession.EndGame());
} }
soundManager.PlayNpcSpawn(); soundManager.PlayNpcSpawn();

View File

@@ -18,9 +18,11 @@ namespace Infrastructure.Unity
private Vector3 _currentDir; private Vector3 _currentDir;
private float _timer; private float _timer;
private Func<float> _timeDilationProvider; private Func<float> _timeDilationProvider;
private Action _onPlayerHit;
public void Initialize(Func<float> timeDilationProvider) public void Initialize(Func<float> timeDilationProvider, Action onPlayerHit)
{ {
_onPlayerHit = onPlayerHit;
_timeDilationProvider = timeDilationProvider; _timeDilationProvider = timeDilationProvider;
} }
@@ -70,5 +72,14 @@ namespace Infrastructure.Unity
case 3: _currentDir = Vector3.right; break; case 3: _currentDir = Vector3.right; break;
} }
} }
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.TryGetComponent<PlayerController>(out var player))
{
_onPlayerHit?.Invoke();
Destroy(gameObject);
}
}
} }
} }

View File

@@ -35,9 +35,6 @@ namespace Infrastructure.Unity
case PowerUpType.SpeedBoost: case PowerUpType.SpeedBoost:
SetColor(EffectColors.SpeedBoostColor); SetColor(EffectColors.SpeedBoostColor);
break; break;
case PowerUpType.Hover:
SetColor(EffectColors.HoverColor);
break;
case PowerUpType.TimeSlow: case PowerUpType.TimeSlow:
SetColor(EffectColors.TimeSlowColor); SetColor(EffectColors.TimeSlowColor);
break; break;
@@ -79,9 +76,6 @@ namespace Infrastructure.Unity
case PowerUpType.SpeedBoost: case PowerUpType.SpeedBoost:
player.Status.AddEffect(new SpeedBoostEffect(duration, 1.5f)); player.Status.AddEffect(new SpeedBoostEffect(duration, 1.5f));
break; break;
case PowerUpType.Hover:
player.Status.AddEffect(new HoverEffect(duration));
break;
case PowerUpType.TimeSlow: case PowerUpType.TimeSlow:
// Handled globally // Handled globally
break; break;

47
README.md Normal file
View File

@@ -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