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