Initial Commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c5199b03c629b0438388962d478af20
|
||||
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Zenject;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public class DeathManager : IDeathManager, IInitializable, ITickable, IDisposable
|
||||
{
|
||||
private const float ShakeDuration = 0.2f;
|
||||
private const float ShakeMagnitude = 0.18f;
|
||||
|
||||
private readonly IGameManager _gameManager;
|
||||
|
||||
private Transform _cameraTransform;
|
||||
private Vector3 _cameraBaseLocalPosition;
|
||||
private float _shakeTimeRemaining;
|
||||
|
||||
[Inject]
|
||||
public DeathManager(IGameManager gameManager)
|
||||
{
|
||||
_gameManager = gameManager;
|
||||
}
|
||||
|
||||
public bool IsDead { get; private set; }
|
||||
|
||||
public event Action OnDied;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_gameManager.OnGameStateChanged += HandleGameStateChanged;
|
||||
CacheMainCamera();
|
||||
IsDead = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gameManager.OnGameStateChanged -= HandleGameStateChanged;
|
||||
ResetCameraPosition();
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (_shakeTimeRemaining <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CacheMainCamera())
|
||||
{
|
||||
_shakeTimeRemaining = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
_shakeTimeRemaining -= Time.deltaTime;
|
||||
if (_shakeTimeRemaining <= 0f)
|
||||
{
|
||||
ResetCameraPosition();
|
||||
return;
|
||||
}
|
||||
|
||||
float strength = ShakeMagnitude * (_shakeTimeRemaining / ShakeDuration);
|
||||
Vector2 offset = UnityEngine.Random.insideUnitCircle * strength;
|
||||
_cameraTransform.localPosition = _cameraBaseLocalPosition + new Vector3(offset.x, offset.y, 0f);
|
||||
}
|
||||
|
||||
public void Die()
|
||||
{
|
||||
if (IsDead || _gameManager.CurrentState != GameState.Playing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDead = true;
|
||||
StartCameraShake();
|
||||
OnDied?.Invoke();
|
||||
_gameManager.GameOver();
|
||||
}
|
||||
|
||||
private void HandleGameStateChanged(GameState state)
|
||||
{
|
||||
if (state == GameState.Menu)
|
||||
{
|
||||
IsDead = false;
|
||||
_shakeTimeRemaining = 0f;
|
||||
ResetCameraPosition();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartCameraShake()
|
||||
{
|
||||
if (!CacheMainCamera())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_shakeTimeRemaining = ShakeDuration;
|
||||
}
|
||||
|
||||
private bool CacheMainCamera()
|
||||
{
|
||||
if (_cameraTransform != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_cameraTransform = mainCamera.transform;
|
||||
_cameraBaseLocalPosition = _cameraTransform.localPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ResetCameraPosition()
|
||||
{
|
||||
if (_cameraTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cameraTransform.localPosition = _cameraBaseLocalPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ab5ec331cec5ae40bb491b4a5003c21
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Zenject;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public class GameManager : IGameManager, IInitializable
|
||||
{
|
||||
public GameState CurrentState { get; private set; } = GameState.Menu;
|
||||
|
||||
public event Action<GameState> OnGameStateChanged;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
CurrentState = GameState.Menu;
|
||||
Debug.Log($"Gameplay state: {CurrentState}");
|
||||
}
|
||||
|
||||
public void StartGame()
|
||||
{
|
||||
if (CurrentState != GameState.Menu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionTo(GameState.Playing);
|
||||
}
|
||||
|
||||
public void GameOver()
|
||||
{
|
||||
if (CurrentState != GameState.Playing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionTo(GameState.GameOver);
|
||||
}
|
||||
|
||||
public void ReturnToMenu()
|
||||
{
|
||||
if (CurrentState == GameState.Menu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionTo(GameState.Menu);
|
||||
}
|
||||
|
||||
private void TransitionTo(GameState state)
|
||||
{
|
||||
if (CurrentState == state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentState = state;
|
||||
Debug.Log($"Gameplay state: {CurrentState}");
|
||||
OnGameStateChanged?.Invoke(CurrentState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba4fbdb4d840a6c48b974ed2f0c910b4
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public class GameStartNotifier : IGameStartNotifier
|
||||
{
|
||||
public bool HasStarted { get; private set; }
|
||||
|
||||
public event Action OnGameStarted;
|
||||
|
||||
public void NotifyGameStarted()
|
||||
{
|
||||
if (HasStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HasStarted = true;
|
||||
OnGameStarted?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c03d9fbb1b6930498065f5321a0fa4b
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public enum GameState
|
||||
{
|
||||
Menu,
|
||||
Playing,
|
||||
GameOver
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2df155af08d5fdb45a3f4bd5124c26d2
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9db9961106beb4489ae7da8ae0957dc
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IDeathManager
|
||||
{
|
||||
bool IsDead { get; }
|
||||
event Action OnDied;
|
||||
void Die();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9db954e729c053b40844b0a6ee9187f2
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IGameManager
|
||||
{
|
||||
GameState CurrentState { get; }
|
||||
event Action<GameState> OnGameStateChanged;
|
||||
void StartGame();
|
||||
void GameOver();
|
||||
void ReturnToMenu();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1468ec5fa7ce8a4d84f1c5835bf8994
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IGameStartNotifier
|
||||
{
|
||||
bool HasStarted { get; }
|
||||
event Action OnGameStarted;
|
||||
void NotifyGameStarted();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afa8469042fc2df429c88b875f9a96fa
|
||||
@@ -0,0 +1,25 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IObjectPoolManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers a new prefab type with the pooling system.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The component type that implements IPoolable.</typeparam>
|
||||
/// <param name="prefab">The GameObject prefab to instantiate.</param>
|
||||
/// <param name="initialSize">How many instances to pre-warm in the pool.</param>
|
||||
void RegisterPool<T>(GameObject prefab, int initialSize = 0) where T : Component, IPoolable;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an instance from the pool, or creates a new one if the pool is empty.
|
||||
/// </summary>
|
||||
T Get<T>() where T : Component, IPoolable;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an instance to the pool and deactivates it.
|
||||
/// </summary>
|
||||
void Return<T>(T item) where T : Component, IPoolable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbfbc77fd3ee5cb40a099d0108484aaa
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IPipePassedNotifier
|
||||
{
|
||||
event Action OnPipePassed;
|
||||
void NotifyPipePassed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f4d49c65078b2e4e81f1887586c4c77
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IPlayerPrefsStorage
|
||||
{
|
||||
bool HasKey(string key);
|
||||
int GetInt(string key, int defaultValue = 0);
|
||||
void SetInt(string key, int value);
|
||||
void Save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 679bebf4f786fef409d9c40e69034315
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IPoolable
|
||||
{
|
||||
void OnSpawned();
|
||||
void OnDespawned();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 555e90e79e1b9d244bb25dc6bdf758e4
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public interface IScoreManager
|
||||
{
|
||||
int CurrentScore { get; }
|
||||
int BestScore { get; }
|
||||
event Action<int> OnScoreChanged;
|
||||
event Action<int> OnBestScoreChanged;
|
||||
void AddScore(int points);
|
||||
void ResetScore();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cc284d25b3acb240b6617c2fbe898e2
|
||||
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Zenject;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public class ObjectPoolManager : IObjectPoolManager, IInitializable
|
||||
{
|
||||
private readonly DiContainer _container;
|
||||
|
||||
private readonly Dictionary<Type, Queue<Component>> _pools = new Dictionary<Type, Queue<Component>>();
|
||||
private readonly Dictionary<Type, GameObject> _prefabs = new Dictionary<Type, GameObject>();
|
||||
private readonly Dictionary<Type, Transform> _poolRoots = new Dictionary<Type, Transform>();
|
||||
|
||||
private Transform _mainPoolRoot;
|
||||
|
||||
[Inject]
|
||||
public ObjectPoolManager(DiContainer container)
|
||||
{
|
||||
_container = container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the main root transform to keep the scene hierarchy clean.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
_mainPoolRoot = new GameObject("ObjectPools").transform;
|
||||
UnityEngine.Object.DontDestroyOnLoad(_mainPoolRoot);
|
||||
}
|
||||
|
||||
public void RegisterPool<T>(GameObject prefab, int initialSize = 0) where T : Component, IPoolable
|
||||
{
|
||||
Type type = typeof(T);
|
||||
|
||||
if (_pools.ContainsKey(type))
|
||||
{
|
||||
Debug.LogWarning($"[ObjectPoolManager] Pool for type {type.Name} is already registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
_prefabs[type] = prefab;
|
||||
_pools[type] = new Queue<Component>();
|
||||
|
||||
// Create a specific parent transform for this type
|
||||
GameObject rootGo = new GameObject($"{type.Name}_Pool");
|
||||
|
||||
// Check if prefab is a UI element (RectTransform)
|
||||
if (prefab.transform is RectTransform)
|
||||
{
|
||||
// UI elements need to stay under a Canvas even when pooled,
|
||||
// or be moved to a hidden Canvas to stay "initialized"
|
||||
rootGo.AddComponent<RectTransform>();
|
||||
}
|
||||
|
||||
Transform typeRoot = rootGo.transform;
|
||||
typeRoot.SetParent(_mainPoolRoot);
|
||||
_poolRoots[type] = typeRoot;
|
||||
|
||||
// Pre-warm the pool
|
||||
for (int i = 0; i < initialSize; i++)
|
||||
{
|
||||
T instance = CreateInstance<T>();
|
||||
instance.gameObject.SetActive(false);
|
||||
_pools[type].Enqueue(instance);
|
||||
}
|
||||
}
|
||||
|
||||
public T Get<T>() where T : Component, IPoolable
|
||||
{
|
||||
Type type = typeof(T);
|
||||
|
||||
if (!_pools.TryGetValue(type, out Queue<Component> pool))
|
||||
{
|
||||
throw new InvalidOperationException($"Pool for type {type.Name} is not registered.");
|
||||
}
|
||||
|
||||
T instance = (pool.Count == 0) ? CreateInstance<T>() : pool.Dequeue() as T;
|
||||
|
||||
instance.gameObject.SetActive(true);
|
||||
instance.OnSpawned();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an item to its respective pool.
|
||||
/// Automatically handles UI parenting back to the pool root.
|
||||
/// </summary>
|
||||
public void Return<T>(T item) where T : Component, IPoolable
|
||||
{
|
||||
if (item == null) return;
|
||||
|
||||
Type type = typeof(T);
|
||||
|
||||
if (!_pools.TryGetValue(type, out Queue<Component> pool))
|
||||
{
|
||||
// Fallback for cases where T might be an interface or base class
|
||||
type = item.GetType();
|
||||
if (!_pools.TryGetValue(type, out pool))
|
||||
{
|
||||
Debug.LogError($"[ObjectPoolManager] No pool registered for {type.Name}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item.OnDespawned();
|
||||
|
||||
// Re-parent to the specific pool root to keep Hierarchy clean
|
||||
item.transform.SetParent(_poolRoots[type], false);
|
||||
item.gameObject.SetActive(false);
|
||||
|
||||
pool.Enqueue(item);
|
||||
}
|
||||
|
||||
private T CreateInstance<T>() where T : Component, IPoolable
|
||||
{
|
||||
Type type = typeof(T);
|
||||
GameObject prefab = _prefabs[type];
|
||||
Transform root = _poolRoots[type];
|
||||
|
||||
// Zenject InstantiatePrefab ensures [Inject] works inside the prefab (needed for ScorePopupView)
|
||||
GameObject instance = _container.InstantiatePrefab(prefab, root);
|
||||
T component = instance.GetComponent<T>();
|
||||
|
||||
if (component == null)
|
||||
{
|
||||
component = instance.AddComponent<T>();
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7a6735ad66b59a43841c280b0f792a2
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public class PipePassedNotifier : IPipePassedNotifier
|
||||
{
|
||||
public event Action OnPipePassed;
|
||||
|
||||
public void NotifyPipePassed()
|
||||
{
|
||||
OnPipePassed?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e04163983505da74f89e304b1f594f52
|
||||
@@ -0,0 +1,27 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public class PlayerPrefsStorage : IPlayerPrefsStorage
|
||||
{
|
||||
public bool HasKey(string key)
|
||||
{
|
||||
return PlayerPrefs.HasKey(key);
|
||||
}
|
||||
|
||||
public int GetInt(string key, int defaultValue = 0)
|
||||
{
|
||||
return PlayerPrefs.GetInt(key, defaultValue);
|
||||
}
|
||||
|
||||
public void SetInt(string key, int value)
|
||||
{
|
||||
PlayerPrefs.SetInt(key, value);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66e3cd5bed2132b4a86b8df540a1dbbf
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Zenject;
|
||||
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
public class ScoreManager : IScoreManager, IInitializable, IDisposable
|
||||
{
|
||||
private const string BestScoreKey = "BestScore";
|
||||
|
||||
private readonly IGameManager _gameManager;
|
||||
private readonly IPlayerPrefsStorage _playerPrefsStorage;
|
||||
|
||||
[Inject]
|
||||
public ScoreManager(IGameManager gameManager, IPlayerPrefsStorage playerPrefsStorage)
|
||||
{
|
||||
_gameManager = gameManager;
|
||||
_playerPrefsStorage = playerPrefsStorage;
|
||||
}
|
||||
|
||||
public int CurrentScore { get; private set; }
|
||||
public int BestScore { get; private set; }
|
||||
|
||||
public event Action<int> OnScoreChanged;
|
||||
public event Action<int> OnBestScoreChanged;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
BestScore = _playerPrefsStorage.GetInt(BestScoreKey, 0);
|
||||
OnBestScoreChanged?.Invoke(BestScore);
|
||||
Debug.Log($"High score loaded: {BestScore}");
|
||||
_gameManager.OnGameStateChanged += HandleGameStateChanged;
|
||||
ResetScore();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gameManager.OnGameStateChanged -= HandleGameStateChanged;
|
||||
}
|
||||
|
||||
public void AddScore(int points)
|
||||
{
|
||||
if (points <= 0 || _gameManager.CurrentState != GameState.Playing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentScore += points;
|
||||
OnScoreChanged?.Invoke(CurrentScore);
|
||||
Debug.Log($"Current score: {CurrentScore}");
|
||||
UpdateBestScore(CurrentScore);
|
||||
}
|
||||
|
||||
public void ResetScore()
|
||||
{
|
||||
CurrentScore = 0;
|
||||
OnScoreChanged?.Invoke(CurrentScore);
|
||||
Debug.Log($"Current score reset: {CurrentScore}");
|
||||
}
|
||||
|
||||
private void HandleGameStateChanged(GameState state)
|
||||
{
|
||||
if (state == GameState.Menu)
|
||||
{
|
||||
ResetScore();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBestScore(int score)
|
||||
{
|
||||
if (score <= BestScore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BestScore = score;
|
||||
_playerPrefsStorage.SetInt(BestScoreKey, BestScore);
|
||||
_playerPrefsStorage.Save();
|
||||
OnBestScoreChanged?.Invoke(BestScore);
|
||||
Debug.Log($"High score updated: {BestScore}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d78b5c289c877040943bc5a1e563dfc
|
||||
Reference in New Issue
Block a user