Valentine Notes Dlivery game flow changes and improvements.
This commit is contained in:
@@ -49,6 +49,10 @@ namespace Common.Camera
|
||||
[Tooltip("Priority for the active camera")]
|
||||
[SerializeField] protected int activePriority = 20;
|
||||
|
||||
[Header("Cinemachine Brain")]
|
||||
[Tooltip("CinemachineBrain for blend detection (auto-finds if null)")]
|
||||
[SerializeField] protected CinemachineBrain cinemachineBrain;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] protected bool showDebugLogs = false;
|
||||
|
||||
@@ -60,6 +64,11 @@ namespace Common.Camera
|
||||
private TState _currentState;
|
||||
private bool _isInitialized = false;
|
||||
|
||||
// Event-driven blend tracking
|
||||
private CinemachineCamera _pendingBlendTarget;
|
||||
private bool _isBlendComplete;
|
||||
private Action _pendingBlendCallback;
|
||||
|
||||
public TState CurrentState => _currentState;
|
||||
|
||||
#endregion
|
||||
@@ -71,6 +80,11 @@ namespace Common.Camera
|
||||
/// </summary>
|
||||
public event Action<TState, TState> OnStateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when camera blend completes after state switch
|
||||
/// </summary>
|
||||
public event Action OnBlendComplete;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifecycle
|
||||
@@ -84,6 +98,17 @@ namespace Common.Camera
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Auto-find CinemachineBrain if not assigned
|
||||
if (cinemachineBrain == null)
|
||||
{
|
||||
cinemachineBrain = UnityEngine.Camera.main?.GetComponent<CinemachineBrain>();
|
||||
|
||||
if (cinemachineBrain == null && showDebugLogs)
|
||||
{
|
||||
Logging.Warning($"[{GetType().Name}] CinemachineBrain not found. Blend tracking will be unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize cameras from Inspector mappings
|
||||
InitializeCameraMap();
|
||||
|
||||
@@ -91,6 +116,35 @@ namespace Common.Camera
|
||||
ValidateCameras();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to Cinemachine events on enable
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
// Subscribe to Cinemachine global events
|
||||
CinemachineCore.BlendFinishedEvent.AddListener(OnBlendFinished);
|
||||
CinemachineCore.CameraActivatedEvent.AddListener(OnCameraActivated);
|
||||
|
||||
if (showDebugLogs)
|
||||
Logging.Debug($"[{GetType().Name}] Subscribed to Cinemachine events");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from Cinemachine events on disable
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
// Unsubscribe from Cinemachine events to prevent memory leaks
|
||||
CinemachineCore.BlendFinishedEvent.RemoveListener(OnBlendFinished);
|
||||
CinemachineCore.CameraActivatedEvent.RemoveListener(OnCameraActivated);
|
||||
|
||||
// Clear any pending callbacks
|
||||
_pendingBlendCallback = null;
|
||||
|
||||
if (showDebugLogs)
|
||||
Logging.Debug($"[{GetType().Name}] Unsubscribed from Cinemachine events");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
@@ -229,6 +283,115 @@ namespace Common.Camera
|
||||
return _cameraMap.ContainsKey(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend to a state and wait asynchronously (coroutine).
|
||||
/// Yields until Cinemachine blend event fires. Event-driven, no polling.
|
||||
/// Use this when you need to wait for the blend to complete before continuing.
|
||||
/// </summary>
|
||||
public System.Collections.IEnumerator BlendToStateAsync(TState newState)
|
||||
{
|
||||
// Reset completion flag
|
||||
_isBlendComplete = false;
|
||||
|
||||
// Set pending target camera
|
||||
if (!_cameraMap.TryGetValue(newState, out _pendingBlendTarget))
|
||||
{
|
||||
Logging.Error($"[{GetType().Name}] No camera for state {newState}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Switch camera state (triggers blend)
|
||||
SwitchToState(newState);
|
||||
|
||||
// Fallback: if no brain, complete immediately
|
||||
if (cinemachineBrain == null)
|
||||
{
|
||||
if (showDebugLogs)
|
||||
Logging.Warning($"[{GetType().Name}] No brain, completing blend immediately");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Wait for event to fire (event handlers set _isBlendComplete = true)
|
||||
yield return new WaitUntil(() => _isBlendComplete);
|
||||
|
||||
if (showDebugLogs)
|
||||
Logging.Debug($"[{GetType().Name}] Blend to {newState} completed (event-driven)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend to a state with callback invoked on completion.
|
||||
/// Callback fires when Cinemachine blend event fires. Event-driven, no polling.
|
||||
/// Use this when you want to perform an action after the blend completes.
|
||||
/// </summary>
|
||||
public void BlendToState(TState newState, Action onComplete)
|
||||
{
|
||||
// Set pending target camera
|
||||
if (!_cameraMap.TryGetValue(newState, out _pendingBlendTarget))
|
||||
{
|
||||
Logging.Error($"[{GetType().Name}] No camera for state {newState}");
|
||||
onComplete?.Invoke(); // Still invoke to prevent hanging
|
||||
return;
|
||||
}
|
||||
|
||||
// Store callback
|
||||
_pendingBlendCallback = onComplete;
|
||||
|
||||
// Switch camera state (triggers blend)
|
||||
SwitchToState(newState);
|
||||
|
||||
// Fallback: if no brain, invoke callback immediately
|
||||
if (cinemachineBrain == null)
|
||||
{
|
||||
if (showDebugLogs)
|
||||
Logging.Warning($"[{GetType().Name}] No brain, invoking callback immediately");
|
||||
CompleteBlend();
|
||||
}
|
||||
|
||||
// Event handlers will invoke callback when blend finishes
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Called when Cinemachine finishes a blend (non-zero length blends)
|
||||
/// </summary>
|
||||
private void OnBlendFinished(ICinemachineMixer mixer, ICinemachineCamera cam)
|
||||
{
|
||||
// Filter: only respond to blends from our brain to our expected camera
|
||||
if (mixer == cinemachineBrain && cam == _pendingBlendTarget)
|
||||
{
|
||||
CompleteBlend();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when Cinemachine activates a camera (handles instant cuts)
|
||||
/// </summary>
|
||||
private void OnCameraActivated(ICinemachineCamera.ActivationEventParams evt)
|
||||
{
|
||||
// Filter: only respond to cuts from our brain to our expected camera
|
||||
if (evt.Origin == cinemachineBrain && evt.IncomingCamera == _pendingBlendTarget && evt.IsCut)
|
||||
{
|
||||
CompleteBlend();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark blend as complete and fire callbacks
|
||||
/// </summary>
|
||||
private void CompleteBlend()
|
||||
{
|
||||
_isBlendComplete = true;
|
||||
_pendingBlendCallback?.Invoke();
|
||||
_pendingBlendCallback = null;
|
||||
OnBlendComplete?.Invoke();
|
||||
|
||||
if (showDebugLogs)
|
||||
Logging.Debug($"[{GetType().Name}] Blend completed, callbacks invoked");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Validation
|
||||
|
||||
Reference in New Issue
Block a user