Compare commits
2 Commits
98883bd382
...
hud_unific
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f515e954d | ||
|
|
e9855faede |
@@ -1185,7 +1185,6 @@ MonoBehaviour:
|
||||
CinematicBackground: {fileID: 1256355336041814197}
|
||||
eagleEye: {fileID: 8093509920149135307}
|
||||
ramaSjangButton: {fileID: 4599222264323240281}
|
||||
scrabBookButton: {fileID: 2880351836456325619}
|
||||
cinematicSprites: {fileID: 0}
|
||||
cinematicBackgroundSprites: {fileID: 0}
|
||||
currentCinematicPlayer: {fileID: 0}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -453761,10 +453761,6 @@ PrefabInstance:
|
||||
propertyPath: m_PlayOnAwake
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8545106365577783398, guid: ead4e790fa3a1924ebd1586c93cd5479, type: 3}
|
||||
propertyPath: isOneTime
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -469,34 +469,6 @@ namespace Core.Lifecycle
|
||||
LogDebug($"Restored scene data to {restoredCount} components");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts scene restore completed event to all registered components.
|
||||
/// Called AFTER all OnSceneRestoreRequested calls complete.
|
||||
/// </summary>
|
||||
public void BroadcastSceneRestoreCompleted()
|
||||
{
|
||||
LogDebug("Broadcasting SceneRestoreCompleted");
|
||||
|
||||
// Create a copy to avoid collection modification during iteration
|
||||
var componentsCopy = new List<ManagedBehaviour>(managedAwakeList);
|
||||
|
||||
foreach (var component in componentsCopy)
|
||||
{
|
||||
if (component == null) continue;
|
||||
|
||||
try
|
||||
{
|
||||
component.InvokeSceneRestoreCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[LifecycleManager] Exception during scene restore completed for {component.SaveId}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
LogDebug("SceneRestoreCompleted broadcast complete");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts global restore request to all registered components that opt-in.
|
||||
/// Distributes serialized data to matching components by SaveId.
|
||||
|
||||
@@ -89,7 +89,6 @@ namespace Core.Lifecycle
|
||||
public void InvokeSceneReady() => OnSceneReady();
|
||||
public string InvokeSceneSaveRequested() => OnSceneSaveRequested();
|
||||
public void InvokeSceneRestoreRequested(string data) => OnSceneRestoreRequested(data);
|
||||
public void InvokeSceneRestoreCompleted() => OnSceneRestoreCompleted();
|
||||
public string InvokeGlobalSaveRequested() => OnGlobalSaveRequested();
|
||||
public void InvokeGlobalRestoreRequested(string data) => OnGlobalRestoreRequested(data);
|
||||
public void InvokeManagedDestroy() => OnManagedDestroy();
|
||||
@@ -203,10 +202,6 @@ namespace Core.Lifecycle
|
||||
/// Called during scene transitions to restore scene-specific state.
|
||||
/// Receives previously serialized data (from OnSceneSaveRequested).
|
||||
///
|
||||
/// IMPORTANT: This method MUST be synchronous. Do not use coroutines or async/await.
|
||||
/// OnSceneRestoreCompleted is called immediately after all restore calls complete,
|
||||
/// so any async operations would still be running when it fires.
|
||||
///
|
||||
/// TIMING:
|
||||
/// - Called AFTER scene load, during OnSceneReady phase
|
||||
/// - Frequency: Every scene transition
|
||||
@@ -217,29 +212,6 @@ namespace Core.Lifecycle
|
||||
// Default: no-op
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after all scene restore operations complete.
|
||||
/// Does NOT receive data - use OnSceneRestoreRequested for that.
|
||||
///
|
||||
/// GUARANTEE:
|
||||
/// - ALWAYS called after scene load, whether there's save data or not
|
||||
/// - All OnSceneRestoreRequested() calls have RETURNED (but async operations may still be running)
|
||||
/// - Safe for synchronous restore operations (JSON deserialization, setting fields, etc.)
|
||||
///
|
||||
/// TIMING:
|
||||
/// - Called AFTER all OnSceneRestoreRequested calls complete (or immediately if no save data exists)
|
||||
/// - Frequency: Every scene transition
|
||||
/// - Use for: Post-restore initialization, first-time initialization, triggering events after state is restored
|
||||
///
|
||||
/// COMMON PATTERN:
|
||||
/// Use this to perform actions that depend on whether data was restored or not.
|
||||
/// Example: Play one-time audio only if it hasn't been played before (_hasPlayed == false).
|
||||
/// </summary>
|
||||
protected virtual void OnSceneRestoreCompleted()
|
||||
{
|
||||
// Default: no-op
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once on game boot to restore global persistent state.
|
||||
/// Receives data that was saved via OnGlobalSaveRequested.
|
||||
|
||||
@@ -504,19 +504,9 @@ namespace Core.SaveLoad
|
||||
/// </summary>
|
||||
public void RestoreSceneData()
|
||||
{
|
||||
if (Lifecycle.LifecycleManager.Instance == null)
|
||||
{
|
||||
Logging.Warning("[SaveLoadManager] LifecycleManager not available for scene restore");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentSaveData == null || currentSaveData.participantStates == null)
|
||||
{
|
||||
Logging.Debug("[SaveLoadManager] No scene data to restore (first visit or no save data)");
|
||||
|
||||
// Still broadcast restore completed so components can initialize properly
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastSceneRestoreCompleted();
|
||||
Logging.Debug($"[SaveLoadManager] Scene restore completed (no data)");
|
||||
Logging.Debug("[SaveLoadManager] No scene data to restore");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -530,12 +520,11 @@ namespace Core.SaveLoad
|
||||
}
|
||||
|
||||
// Restore scene data via LifecycleManager
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastSceneRestoreRequested(saveDataDict);
|
||||
Logging.Debug($"[SaveLoadManager] Broadcast scene restore to LifecycleManager");
|
||||
|
||||
// Broadcast scene restore completed - ALWAYS called, whether there's data or not
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastSceneRestoreCompleted();
|
||||
Logging.Debug($"[SaveLoadManager] Scene restore completed");
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastSceneRestoreRequested(saveDataDict);
|
||||
Logging.Debug($"[SaveLoadManager] Broadcast scene restore to LifecycleManager");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,80 +1,22 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
using System;
|
||||
using Core.Lifecycle;
|
||||
|
||||
[Serializable]
|
||||
public class LevelAudioObjectSaveData
|
||||
public class LevelAudioObject : MonoBehaviour
|
||||
{
|
||||
public bool hasPlayed;
|
||||
}
|
||||
|
||||
public class LevelAudioObject : ManagedBehaviour
|
||||
{
|
||||
[Header("Audio Settings")]
|
||||
public AppleAudioSource narratorAudioSource;
|
||||
public AudioResource firstNarration;
|
||||
|
||||
[Header("Playback Settings")]
|
||||
[Tooltip("If true, the audio will only play once and never again after being played")]
|
||||
public bool isOneTime;
|
||||
|
||||
private bool _hasPlayed;
|
||||
|
||||
#region ManagedBehaviour Overrides
|
||||
|
||||
public override bool AutoRegisterForSave => isOneTime; // Only save if one-time audio
|
||||
|
||||
protected override string OnSceneSaveRequested()
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
if (!isOneTime)
|
||||
return null; // No need to save if not one-time
|
||||
|
||||
LevelAudioObjectSaveData saveData = new LevelAudioObjectSaveData
|
||||
{
|
||||
hasPlayed = _hasPlayed
|
||||
};
|
||||
|
||||
return JsonUtility.ToJson(saveData);
|
||||
PlayNarrationAudio();
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreRequested(string serializedData)
|
||||
void PlayNarrationAudio()
|
||||
{
|
||||
if (!isOneTime || string.IsNullOrEmpty(serializedData))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
LevelAudioObjectSaveData saveData = JsonUtility.FromJson<LevelAudioObjectSaveData>(serializedData);
|
||||
_hasPlayed = saveData.hasPlayed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[LevelAudioObject] Failed to restore audio state: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSceneRestoreCompleted()
|
||||
{
|
||||
if (isOneTime && !_hasPlayed)
|
||||
{
|
||||
PlayNarrationAudio();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void PlayNarrationAudio()
|
||||
{
|
||||
if (narratorAudioSource == null || firstNarration == null)
|
||||
{
|
||||
Debug.LogWarning($"[LevelAudioObject] Missing audio source or narration resource on {gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
narratorAudioSource.audioSource.resource = firstNarration;
|
||||
narratorAudioSource.Play(0);
|
||||
|
||||
_hasPlayed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ namespace UI.CardSystem
|
||||
/// <summary>
|
||||
/// Singleton UI component for granting booster packs from minigames.
|
||||
/// Displays a booster pack with glow effect, waits for user to click continue,
|
||||
/// then shows the scrapbook button and animates the pack flying to it before granting the reward.
|
||||
/// The scrapbook button is automatically hidden after the animation completes.
|
||||
/// then animates the pack flying to bottom-left corner before granting the reward.
|
||||
/// </summary>
|
||||
public class MinigameBoosterGiver : MonoBehaviour
|
||||
{
|
||||
@@ -188,61 +187,23 @@ namespace UI.CardSystem
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Show scrapbook button temporarily using HUD visibility context
|
||||
PlayerHudManager.HudVisibilityContext hudContext = null;
|
||||
GameObject scrapbookButton = null;
|
||||
|
||||
scrapbookButton = PlayerHudManager.Instance.GetScrabookButton();
|
||||
if (scrapbookButton != null)
|
||||
{
|
||||
hudContext = PlayerHudManager.Instance.ShowElementTemporarily(scrapbookButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[MinigameBoosterGiver] Scrapbook button not found in PlayerHudManager.");
|
||||
}
|
||||
|
||||
// Calculate target position - use scrapbook button position if available
|
||||
// Calculate bottom-left corner position in local space
|
||||
RectTransform canvasRect = GetComponentInParent<Canvas>()?.GetComponent<RectTransform>();
|
||||
Vector3 targetPosition;
|
||||
|
||||
if (scrapbookButton != null)
|
||||
|
||||
if (canvasRect != null)
|
||||
{
|
||||
// Get the scrapbook button's position in the same coordinate space as boosterImage
|
||||
RectTransform scrapbookRect = scrapbookButton.GetComponent<RectTransform>();
|
||||
if (scrapbookRect != null)
|
||||
{
|
||||
// Convert scrapbook button's world position to local position relative to boosterImage's parent
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas != null && canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
// For overlay canvas, convert screen position to local position
|
||||
Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(null, scrapbookRect.position);
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
boosterImage.parent as RectTransform,
|
||||
screenPos,
|
||||
null,
|
||||
out Vector2 localPoint);
|
||||
targetPosition = localPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For world space or camera canvas
|
||||
targetPosition = boosterImage.parent.InverseTransformPoint(scrapbookRect.position);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[MinigameBoosterGiver] Scrapbook button has no RectTransform, using fallback position.");
|
||||
targetPosition = GetFallbackPosition();
|
||||
}
|
||||
// Convert bottom-left corner with offset to local position
|
||||
Vector2 bottomLeft = new Vector2(-canvasRect.rect.width / 2f, -canvasRect.rect.height / 2f);
|
||||
targetPosition = bottomLeft + targetBottomLeftOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to bottom-left corner
|
||||
targetPosition = GetFallbackPosition();
|
||||
// Fallback if no canvas found
|
||||
targetPosition = _boosterInitialPosition + new Vector3(-500f, -500f, 0f);
|
||||
}
|
||||
|
||||
// Tween to scrapbook button position
|
||||
// Tween to bottom-left corner
|
||||
Tween.LocalPosition(boosterImage, targetPosition, disappearDuration, 0f, Tween.EaseInBack);
|
||||
|
||||
// Scale down
|
||||
@@ -263,9 +224,6 @@ namespace UI.CardSystem
|
||||
Debug.LogWarning("[MinigameBoosterGiver] CardSystemManager not found, cannot grant booster pack.");
|
||||
}
|
||||
|
||||
// Hide scrapbook button by disposing the context
|
||||
hudContext?.Dispose();
|
||||
|
||||
// Hide the visual
|
||||
if (visualContainer != null)
|
||||
{
|
||||
@@ -279,22 +237,6 @@ namespace UI.CardSystem
|
||||
// Clear sequence reference
|
||||
_currentSequence = null;
|
||||
}
|
||||
|
||||
private Vector3 GetFallbackPosition()
|
||||
{
|
||||
RectTransform canvasRect = GetComponentInParent<Canvas>()?.GetComponent<RectTransform>();
|
||||
if (canvasRect != null)
|
||||
{
|
||||
// Convert bottom-left corner with offset to local position
|
||||
Vector2 bottomLeft = new Vector2(-canvasRect.rect.width / 2f, -canvasRect.rect.height / 2f);
|
||||
return bottomLeft + targetBottomLeftOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ultimate fallback if no canvas found
|
||||
return _boosterInitialPosition + new Vector3(-500f, -500f, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ using UnityEngine.Playables;
|
||||
using UnityEngine.UI;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
@@ -20,73 +19,6 @@ namespace UI
|
||||
{
|
||||
public enum UIMode { Overworld, Puzzle, Minigame, HideAll };
|
||||
|
||||
/// <summary>
|
||||
/// Context object for managing temporary HUD element visibility.
|
||||
/// Automatically restores previous visibility state when disposed.
|
||||
/// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementTemporarily(myButton)) { ... }
|
||||
/// </summary>
|
||||
public class HudVisibilityContext : IDisposable
|
||||
{
|
||||
private readonly GameObject _element;
|
||||
private readonly bool _previousState;
|
||||
private bool _disposed;
|
||||
|
||||
internal HudVisibilityContext(GameObject element, bool show)
|
||||
{
|
||||
_element = element;
|
||||
_previousState = element.activeSelf;
|
||||
_element.SetActive(show);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
if (_element != null)
|
||||
{
|
||||
_element.SetActive(_previousState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multi-element visibility context for managing multiple HUD elements at once.
|
||||
/// Automatically restores previous visibility states when disposed.
|
||||
/// </summary>
|
||||
public class MultiHudVisibilityContext : IDisposable
|
||||
{
|
||||
private readonly List<(GameObject element, bool previousState)> _elements;
|
||||
private bool _disposed;
|
||||
|
||||
internal MultiHudVisibilityContext(IEnumerable<GameObject> elements, bool show)
|
||||
{
|
||||
_elements = new List<(GameObject, bool)>();
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (element != null)
|
||||
{
|
||||
_elements.Add((element, element.activeSelf));
|
||||
element.SetActive(show);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
foreach (var (element, previousState) in _elements)
|
||||
{
|
||||
if (element != null)
|
||||
{
|
||||
element.SetActive(previousState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UIMode currentUIMode;
|
||||
|
||||
[Header("HUD Management")]
|
||||
@@ -101,7 +33,6 @@ namespace UI
|
||||
[Header("HUD Elements")]
|
||||
public GameObject eagleEye;
|
||||
public GameObject ramaSjangButton;
|
||||
public GameObject scrabBookButton;
|
||||
|
||||
[HideInInspector] public Image cinematicSprites;
|
||||
[HideInInspector] public Image cinematicBackgroundSprites;
|
||||
@@ -323,9 +254,6 @@ namespace UI
|
||||
if (visible)
|
||||
{
|
||||
eagleEye.SetActive(false);
|
||||
ramaSjangButton.SetActive(false);
|
||||
scrabBookButton.SetActive(false);
|
||||
|
||||
}
|
||||
break;
|
||||
case UIMode.HideAll:
|
||||
@@ -390,64 +318,6 @@ namespace UI
|
||||
UnityEngine.Debug.LogError("[PlayerHudManager] Cannot push page - UIPageController not found!");
|
||||
}
|
||||
}
|
||||
|
||||
#region HUD Element Getters
|
||||
|
||||
public GameObject GetEagleEye() => eagleEye;
|
||||
public GameObject GetRamaSjangButton() => ramaSjangButton;
|
||||
public GameObject GetScrabookButton() => scrabBookButton;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Context-Based Visibility Management
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily shows a HUD element. Returns a context object that restores the previous state when disposed.
|
||||
/// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementTemporarily(myButton)) { /* element is visible */ }
|
||||
/// </summary>
|
||||
public HudVisibilityContext ShowElementTemporarily(GameObject element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
Logging.Warning("[PlayerHudManager] Attempted to show null element");
|
||||
return null;
|
||||
}
|
||||
return new HudVisibilityContext(element, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily hides a HUD element. Returns a context object that restores the previous state when disposed.
|
||||
/// Usage: using (var ctx = PlayerHudManager.Instance.HideElementTemporarily(myButton)) { /* element is hidden */ }
|
||||
/// </summary>
|
||||
public HudVisibilityContext HideElementTemporarily(GameObject element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
Logging.Warning("[PlayerHudManager] Attempted to hide null element");
|
||||
return null;
|
||||
}
|
||||
return new HudVisibilityContext(element, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily shows multiple HUD elements. Returns a context object that restores all previous states when disposed.
|
||||
/// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementsTemporarily(button1, button2, button3)) { /* elements are visible */ }
|
||||
/// </summary>
|
||||
public MultiHudVisibilityContext ShowElementsTemporarily(params GameObject[] elements)
|
||||
{
|
||||
return new MultiHudVisibilityContext(elements, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily hides multiple HUD elements. Returns a context object that restores all previous states when disposed.
|
||||
/// Usage: using (var ctx = PlayerHudManager.Instance.HideElementsTemporarily(button1, button2, button3)) { /* elements are hidden */ }
|
||||
/// </summary>
|
||||
public MultiHudVisibilityContext HideElementsTemporarily(params GameObject[] elements)
|
||||
{
|
||||
return new MultiHudVisibilityContext(elements, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user