Add Hunter NPC and Teleporter features with associated prefabs and effects

This commit is contained in:
2025-12-13 00:01:29 +01:00
parent c0fb207768
commit d8b0583fac
32 changed files with 823 additions and 217 deletions

View File

@@ -10,7 +10,7 @@ namespace Infrastructure.Unity
{
public class GameBootstrap : MonoBehaviour
{
[Header("Infrastructure")]
[Header("Infrastructure")]
[SerializeField] private LevelGenerator levelGenerator;
[SerializeField] private OrbViewAdapter orbPrefab;
[SerializeField] private PlayerController playerPrefab;
@@ -18,23 +18,23 @@ namespace Infrastructure.Unity
[SerializeField] private SoundManager soundManager;
[SerializeField] private CameraController cameraController;
[SerializeField] private NpcController npcPrefab;
[SerializeField] private HunterNpcController hunterNpcPrefab;
[Header("Level Generation")]
[SerializeField] private int floorsCount = 3;
[SerializeField] private float floorHeightDistance = 15f;
[Header("Ui")]
[SerializeField] private TMP_Text scoreText;
[SerializeField] private TMP_Text highScoreText;
[SerializeField] private GameObject gameOverUi;
[SerializeField] private GameObject startScreenUi;
[Header("Settings")]
[SerializeField] private float restartTime = 3f;
[Header("Power Ups")]
[SerializeField] private PowerUpViewAdapter lightFootedPrefab;
[SerializeField] private PowerUpViewAdapter speedBoostPrefab;
[SerializeField] private PowerUpViewAdapter powerUpPrefab;
private readonly List<Tile> _allTiles = new();
private readonly Dictionary<string, TileViewAdapter> _tileViews = new();
@@ -59,17 +59,20 @@ namespace Infrastructure.Unity
private void Start()
{
if (levelGenerator) levelGenerator.Generate(soundManager, _allTiles, _tileViews);
SpawnDeathPlane();
SpawnPlayer();
if (gameOverUi) gameOverUi.SetActive(false);
if (startScreenUi) startScreenUi.SetActive(true);
_persistenceService = new PlayerPrefsPersistenceAdapter();
_gameSession = new GameSession(_allTiles, _persistenceService);
// Set Theme based on High Score
ThemeManager.CurrentTheme = ThemeManager.GetTheme(_gameSession.HighScore);
if (levelGenerator) levelGenerator.Generate(soundManager, _allTiles, _tileViews, cameraController);
SpawnDeathPlane();
SpawnPlayer();
if (gameOverUi) gameOverUi.SetActive(false);
if (startScreenUi) startScreenUi.SetActive(true);
WireEvents();
UpdateScoreUi(_gameSession.Score);
}
@@ -90,34 +93,39 @@ namespace Infrastructure.Unity
{
var playerY = _playerInstance.transform.position.y;
var currentFloor = Mathf.RoundToInt(-playerY / floorHeightDistance);
currentFloor = Mathf.Clamp(currentFloor, 0, floorsCount - 1);
_gameSession.SetPlayerFloor(currentFloor);
}
var dt = Time.deltaTime;
if (_isGameRunning) _gameSession.Tick(dt);
var dilation = _gameSession.TimeDilation;
// Hard Mode: Decay faster as score increases
var decayMultiplier = 1.0f + (_gameSession.Score / 500f);
for (var i = _allTiles.Count - 1; i >= 0; i--)
{
_allTiles[i].Tick(dt);
_allTiles[i].Tick(dt * dilation * decayMultiplier);
}
}
private void SpawnVisualOrb(string tileId)
{
if (_currentOrbInstance) Destroy(_currentOrbInstance);
if (!_tileViews.TryGetValue(tileId, out var tileView)) return;
if (!tileView) return;
var spawnPos = tileView.transform.position + Vector3.up;
var orb = Instantiate(orbPrefab, spawnPos, Quaternion.identity);
orb.OnCollected += () => _gameSession.OrbCollected();
_currentOrbInstance = orb.gameObject;
}
@@ -132,22 +140,25 @@ namespace Infrastructure.Unity
private void UpdateScoreUi(int newScore)
{
if (!scoreText) return;
scoreText.text = $"Data: {newScore}";
if (highScoreText) highScoreText.text = $"BEST: {_gameSession.HighScore}";
var combo = _gameSession?.ComboMultiplier ?? 1;
var comboText = combo > 1 ? $" (x{combo})" : "";
scoreText.text = $"Data: {newScore}{comboText}";
if (highScoreText && _gameSession != null) highScoreText.text = $"BEST: {_gameSession.HighScore}";
}
private void SpawnPlayer()
{
var spawnPos = new Vector3(0f, 5f, 0f);
_playerInstance = Instantiate(playerPrefab, spawnPos, Quaternion.identity);
_playerInstance.enabled = false;
_playerInstance.Rigidbody.isKinematic = true;
if (cameraController)
{
cameraController.SetTarget(_playerInstance.transform);
@@ -162,7 +173,7 @@ namespace Infrastructure.Unity
var pos = new Vector3(levelGenerator.GridSizeX / 2f, lowestY, levelGenerator.GridSizeY / 2f);
var plane = Instantiate(deathPlanePrefab, pos, Quaternion.identity);
plane.transform.localScale = new Vector3(levelGenerator.GridSizeX * 2f, 1f, levelGenerator.GridSizeY * 2f);
plane.OnPlayerFell += () => _gameSession.EndGame();
}
@@ -177,7 +188,7 @@ namespace Infrastructure.Unity
private IEnumerator RestartRoutine()
{
yield return new WaitForSeconds(restartTime);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
@@ -191,7 +202,7 @@ namespace Infrastructure.Unity
_gameSession.OnSpawnPowerUp += SpawnPowerUp;
if (!soundManager) return;
_gameSession.OnScoreChanged += _ => soundManager.PlayScore();
_gameSession.OnGameOver += () =>
{
@@ -202,18 +213,26 @@ namespace Infrastructure.Unity
private void SpawnNpc()
{
if (!npcPrefab) return;
var spawnPos = new Vector3(levelGenerator.GridSizeX / 2f, 7f, levelGenerator.GridSizeY / 2f);
Instantiate(npcPrefab, spawnPos, Quaternion.identity);
// 30% chance for Hunter if player available
if (_playerInstance && hunterNpcPrefab && Random.value < 0.3f)
{
var hunter = Instantiate(hunterNpcPrefab, spawnPos, Quaternion.identity);
hunter.Initialize(_playerInstance);
}
else if (npcPrefab)
{
Instantiate(npcPrefab, spawnPos, Quaternion.identity);
}
soundManager.PlayNpcSpawn();
}
private void StartGameSequence()
{
_isGameRunning = true;
if (startScreenUi) startScreenUi.SetActive(false);
if (soundManager)
@@ -227,7 +246,7 @@ namespace Infrastructure.Unity
_playerInstance.enabled = true;
_playerInstance.Rigidbody.isKinematic = false;
}
_gameSession.StartGame();
}
@@ -237,15 +256,17 @@ namespace Infrastructure.Unity
if (!tileView) return;
var spawnPos = tileView.transform.position + Vector3.up * 0.5f;
var prefabToSpawn = type == PowerUpType.LightFooted
? lightFootedPrefab
: speedBoostPrefab;
if (!prefabToSpawn) return;
var instance = Instantiate(powerUpPrefab, spawnPos, Quaternion.identity);
var instance = Instantiate(prefabToSpawn, spawnPos, Quaternion.identity);
instance.Configure(type);
instance.OnCollected += (t) =>
{
if (t == PowerUpType.TimeSlow)
{
_gameSession.ActivateTimeSlow(10f);
}
};
}
}
}