using System; using System.Collections.Generic; using Core; using Core.Lifecycle; using Unity.Cinemachine; using UnityEngine; namespace Common.Camera { /// /// Generic state-based camera controller using Cinemachine. /// Manages camera transitions by setting priorities on virtual cameras. /// Type parameter TState must be an enum representing camera states. /// public abstract class CameraStateManager : ManagedBehaviour where TState : Enum { #region Configuration [Header("Camera Priority Settings")] [Tooltip("Priority for inactive cameras")] [SerializeField] protected int inactivePriority = 10; [Tooltip("Priority for the active camera")] [SerializeField] protected int activePriority = 20; [Header("Debug")] [SerializeField] protected bool showDebugLogs = false; #endregion #region State private Dictionary _cameraMap = new Dictionary(); private TState _currentState; private bool _isInitialized = false; public TState CurrentState => _currentState; #endregion #region Events /// /// Fired when camera state changes. Parameters: (TState oldState, TState newState) /// public event Action OnStateChanged; #endregion #region Initialization /// /// Register a camera for a specific state. /// Call this in subclass OnManagedAwake to set up the camera map. /// protected void RegisterCamera(TState state, CinemachineCamera pCamera) { if (pCamera == null) { Logging.Warning($"[{GetType().Name}] Attempted to register null camera for state {state}"); return; } if (_cameraMap.ContainsKey(state)) { Logging.Warning($"[{GetType().Name}] Camera for state {state} already registered, overwriting"); } _cameraMap[state] = pCamera; // Set all cameras to inactive priority initially pCamera.Priority.Value = inactivePriority; if (showDebugLogs) Logging.Debug($"[{GetType().Name}] Registered camera '{pCamera.gameObject.name}' for state {state}"); } /// /// Finalize initialization after all cameras are registered. /// Call this at the end of subclass OnManagedAwake. /// protected void FinalizeInitialization() { _isInitialized = true; if (_cameraMap.Count == 0) { Logging.Warning($"[{GetType().Name}] No cameras registered!"); } if (showDebugLogs) Logging.Debug($"[{GetType().Name}] Initialized with {_cameraMap.Count} cameras"); } #endregion #region State Management /// /// Switch to a specific camera state /// public virtual void SwitchToState(TState newState) { if (!_isInitialized) { Logging.Error($"[{GetType().Name}] Cannot switch state - not initialized!"); return; } if (!_cameraMap.ContainsKey(newState)) { Logging.Error($"[{GetType().Name}] No camera registered for state {newState}!"); return; } TState oldState = _currentState; _currentState = newState; // Set all cameras to inactive priority foreach (var kvp in _cameraMap) { kvp.Value.Priority.Value = inactivePriority; } // Set target camera to active priority _cameraMap[newState].Priority.Value = activePriority; if (showDebugLogs) Logging.Debug($"[{GetType().Name}] Switched from {oldState} to {newState} (camera: {_cameraMap[newState].gameObject.name})"); OnStateChanged?.Invoke(oldState, newState); } /// /// Get the camera for a specific state /// public CinemachineCamera GetCamera(TState state) { if (_cameraMap.TryGetValue(state, out CinemachineCamera pCamera)) { return pCamera; } Logging.Warning($"[{GetType().Name}] No camera found for state {state}"); return null; } /// /// Check if a camera is registered for a state /// public bool HasCamera(TState state) { return _cameraMap.ContainsKey(state); } #endregion #region Validation /// /// Validate that all required states have cameras registered. /// Subclasses can override to add custom validation. /// protected virtual void ValidateCameras() { // Subclasses should implement specific validation } #endregion } }