Add a dirty booster pack opening sequence
This commit is contained in:
@@ -685,6 +685,12 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Call this when the game ends
|
||||
public void EndGame()
|
||||
{
|
||||
// Start the end game sequence that grants a booster, waits for the UI animation, then shows Game Over.
|
||||
StartCoroutine(EndGameSequence());
|
||||
}
|
||||
|
||||
private IEnumerator EndGameSequence()
|
||||
{
|
||||
// Clean up active monsters
|
||||
foreach (var monster in activeMonsters.ToArray())
|
||||
@@ -694,10 +700,38 @@ namespace Minigames.DivingForPictures
|
||||
monster.DespawnMonster();
|
||||
}
|
||||
}
|
||||
CinematicsManager.Instance.ShowGameOverScreen();
|
||||
|
||||
activeMonsters.Clear();
|
||||
|
||||
// 1) Call the booster pack giver if available
|
||||
bool completed = false;
|
||||
var giver = UI.CardSystem.BoosterPackGiver.Instance;
|
||||
if (giver != null)
|
||||
{
|
||||
// Temporarily subscribe to completion
|
||||
UnityAction onDone = null;
|
||||
onDone = () => { completed = true; giver.OnCompleted.RemoveListener(onDone); };
|
||||
giver.OnCompleted.AddListener(onDone);
|
||||
giver.GiveBoosterPack();
|
||||
|
||||
// 2) Wait for it to finish (with a safety timeout in case it's not wired)
|
||||
float timeout = 5f; // fallback to avoid blocking forever
|
||||
float elapsed = 0f;
|
||||
while (!completed && elapsed < timeout)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no giver is present, proceed immediately
|
||||
Logging.Debug("[DivingGameManager] BoosterPackGiver not found; skipping booster animation.");
|
||||
}
|
||||
|
||||
// 3) Only then show the game over screen
|
||||
CinematicsManager.Instance.ShowGameOverScreen();
|
||||
|
||||
// Final score could be saved to player prefs or other persistence
|
||||
Logging.Debug($"Final Score: {playerScore}");
|
||||
}
|
||||
|
||||
219
Assets/Scripts/UI/CardSystem/BoosterPackGiver.cs
Normal file
219
Assets/Scripts/UI/CardSystem/BoosterPackGiver.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using System.Collections;
|
||||
using Data.CardSystem;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UI.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// One-off helper to visually grant a booster pack.
|
||||
/// Place this on a UI GameObject with two Image children (a background "glow" and a booster pack image).
|
||||
/// Access via BoosterPackGiver.Instance and call GiveBoosterPack().
|
||||
/// The sequence:
|
||||
/// 1) Shows the object (enables children)
|
||||
/// 2) Pulses the glow scale for a fixed duration
|
||||
/// 3) Hides the glow, tweens the booster image towards the backpack icon and scales to zero
|
||||
/// 4) Invokes OnCompleted
|
||||
/// </summary>
|
||||
public class BoosterPackGiver : MonoBehaviour
|
||||
{
|
||||
public static BoosterPackGiver Instance { get; private set; }
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("Canvas that contains these UI elements. If null, will search up the hierarchy.")]
|
||||
[SerializeField] private Canvas canvas;
|
||||
[Tooltip("Background glow RectTransform (child image)")]
|
||||
[SerializeField] private RectTransform backgroundGlow;
|
||||
[Tooltip("Booster pack image RectTransform (child image)")]
|
||||
[SerializeField] private RectTransform boosterImage;
|
||||
[Tooltip("Target RectTransform for the backpack icon (where the booster flies to)")]
|
||||
[SerializeField] private RectTransform targetBackpackIcon;
|
||||
|
||||
[Header("Timing")]
|
||||
[Tooltip("How long the glow should pulse before the booster flies to the backpack")]
|
||||
[SerializeField] private float pulseDuration = 2.0f;
|
||||
[Tooltip("Duration of the flight/scale-down animation")]
|
||||
[SerializeField] private float moveDuration = 0.6f;
|
||||
|
||||
[Header("Glow Pulse")]
|
||||
[Tooltip("Minimum scale during pulse")] [SerializeField] private float glowScaleMin = 0.9f;
|
||||
[Tooltip("Maximum scale during pulse")] [SerializeField] private float glowScaleMax = 1.1f;
|
||||
[Tooltip("Pulse speed in cycles per second")] [SerializeField] private float glowPulseSpeed = 2.0f;
|
||||
|
||||
[Header("Move/Scale Easing")]
|
||||
[SerializeField] private AnimationCurve moveCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
||||
[SerializeField] private AnimationCurve scaleCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
||||
|
||||
[Header("Behaviour")]
|
||||
[Tooltip("Hide visuals when the sequence completes")] [SerializeField] private bool hideOnComplete = true;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent OnCompleted;
|
||||
|
||||
private Coroutine _sequenceCoroutine;
|
||||
private Vector3 _boosterInitialScale;
|
||||
private Vector2 _boosterInitialAnchoredPos;
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Debug.LogWarning("[BoosterPackGiver] Duplicate instance detected. Destroying this component.");
|
||||
Destroy(this);
|
||||
yield break;
|
||||
}
|
||||
Instance = this;
|
||||
|
||||
if (canvas == null)
|
||||
{
|
||||
canvas = GetComponentInParent<Canvas>();
|
||||
}
|
||||
|
||||
CacheInitialBoosterState();
|
||||
// Start hidden (keep GameObject active so the singleton remains accessible)
|
||||
SetVisualsActive(false);
|
||||
|
||||
// yield return new WaitForSeconds(1f);
|
||||
|
||||
// GiveBoosterPack();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
private void CacheInitialBoosterState()
|
||||
{
|
||||
if (boosterImage != null)
|
||||
{
|
||||
_boosterInitialScale = boosterImage.localScale;
|
||||
_boosterInitialAnchoredPos = boosterImage.anchoredPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public entry point: run the grant animation.
|
||||
/// </summary>
|
||||
public void GiveBoosterPack()
|
||||
{
|
||||
if (backgroundGlow == null || boosterImage == null)
|
||||
{
|
||||
Debug.LogError("[BoosterPackGiver] Missing references. Assign Background Glow and Booster Image in the inspector.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset and start fresh
|
||||
if (_sequenceCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_sequenceCoroutine);
|
||||
_sequenceCoroutine = null;
|
||||
}
|
||||
|
||||
// Ensure canvas reference
|
||||
if (canvas == null)
|
||||
{
|
||||
canvas = GetComponentInParent<Canvas>();
|
||||
}
|
||||
|
||||
// Reset booster transform
|
||||
boosterImage.localScale = _boosterInitialScale;
|
||||
boosterImage.anchoredPosition = _boosterInitialAnchoredPos;
|
||||
|
||||
// Show visuals
|
||||
SetVisualsActive(true);
|
||||
|
||||
_sequenceCoroutine = StartCoroutine(RunSequence());
|
||||
}
|
||||
|
||||
private IEnumerator RunSequence()
|
||||
{
|
||||
// 1) Pulse the glow
|
||||
float elapsed = 0f;
|
||||
Vector3 baseGlowScale = backgroundGlow.localScale;
|
||||
while (elapsed < pulseDuration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Sin(elapsed * Mathf.PI * 2f * glowPulseSpeed) * 0.5f + 0.5f; // 0..1
|
||||
float s = Mathf.Lerp(glowScaleMin, glowScaleMax, t);
|
||||
backgroundGlow.localScale = baseGlowScale * s;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// 2) Hide glow
|
||||
backgroundGlow.gameObject.SetActive(false);
|
||||
|
||||
// 3) Move booster to backpack icon and scale to zero
|
||||
Vector2 startPos = boosterImage.anchoredPosition;
|
||||
Vector2 targetPos = startPos;
|
||||
|
||||
// Convert target to booster parent space if available
|
||||
if (targetBackpackIcon != null)
|
||||
{
|
||||
var parentRect = boosterImage.parent as RectTransform;
|
||||
if (parentRect != null)
|
||||
{
|
||||
Vector2 localPoint;
|
||||
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas != null ? canvas.worldCamera : null, targetBackpackIcon.position);
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, screenPoint, canvas != null ? canvas.worldCamera : null, out localPoint))
|
||||
{
|
||||
targetPos = localPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elapsed = 0f;
|
||||
while (elapsed < moveDuration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / moveDuration);
|
||||
float mt = moveCurve != null ? moveCurve.Evaluate(t) : t;
|
||||
float st = scaleCurve != null ? scaleCurve.Evaluate(t) : t;
|
||||
|
||||
boosterImage.anchoredPosition = Vector2.LerpUnclamped(startPos, targetPos, mt);
|
||||
boosterImage.localScale = Vector3.LerpUnclamped(_boosterInitialScale, Vector3.zero, st);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Ensure final state
|
||||
boosterImage.anchoredPosition = targetPos;
|
||||
boosterImage.localScale = Vector3.zero;
|
||||
|
||||
if (hideOnComplete)
|
||||
{
|
||||
SetVisualsActive(false);
|
||||
// Restore booster for the next run
|
||||
boosterImage.localScale = _boosterInitialScale;
|
||||
boosterImage.anchoredPosition = _boosterInitialAnchoredPos;
|
||||
backgroundGlow.localScale = Vector3.one; // reset pulse scaling
|
||||
}
|
||||
|
||||
_sequenceCoroutine = null;
|
||||
OnCompleted?.Invoke();
|
||||
CardSystemManager.Instance.AddBoosterPack(1);
|
||||
}
|
||||
|
||||
private void SetVisualsActive(bool active)
|
||||
{
|
||||
if (backgroundGlow != null) backgroundGlow.gameObject.SetActive(active);
|
||||
if (boosterImage != null) boosterImage.gameObject.SetActive(active);
|
||||
}
|
||||
|
||||
// Optional: quick editor hookup to validate references
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
if (canvas == null)
|
||||
{
|
||||
canvas = GetComponentInParent<Canvas>();
|
||||
}
|
||||
if (boosterImage != null && _boosterInitialScale == Vector3.zero)
|
||||
{
|
||||
_boosterInitialScale = boosterImage.localScale;
|
||||
_boosterInitialAnchoredPos = boosterImage.anchoredPosition;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/UI/CardSystem/BoosterPackGiver.cs.meta
Normal file
3
Assets/Scripts/UI/CardSystem/BoosterPackGiver.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e805057df6a34bd4b881031b5f460fe5
|
||||
timeCreated: 1761053022
|
||||
89
Assets/Scripts/UI/CardSystem/CardSystemSceneVisibility.cs
Normal file
89
Assets/Scripts/UI/CardSystem/CardSystemSceneVisibility.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using Bootstrap;
|
||||
using Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// One-off helper that shows/hides the Card System root based on scene loads.
|
||||
/// Attach this to your Card System root GameObject. It subscribes to SceneManagerService.SceneLoadCompleted
|
||||
/// and applies visibility: hidden in "StartingScene" (configurable), visible in all other gameplay scenes.
|
||||
/// </summary>
|
||||
public class CardSystemSceneVisibility : MonoBehaviour
|
||||
{
|
||||
[Header("Target Root")]
|
||||
[Tooltip("The GameObject to show/hide. Defaults to this GameObject if not assigned.")]
|
||||
[SerializeField] private GameObject targetRoot;
|
||||
|
||||
[Header("Rules")]
|
||||
[Tooltip("The scene name in which the Card System should be hidden.")]
|
||||
[SerializeField] private string startingSceneName = "StartingScene";
|
||||
[Tooltip("Also hide when SceneManagerService reports the Bootstrap scene.")]
|
||||
[SerializeField] private bool hideInBootstrapScene = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (targetRoot == null)
|
||||
targetRoot = gameObject;
|
||||
|
||||
// Defer subscription to after boot so SceneManagerService is guaranteed ready.
|
||||
BootCompletionService.RegisterInitAction(InitializePostBoot, priority: 95, name: "CardSystem Scene Visibility Init");
|
||||
}
|
||||
|
||||
private void InitializePostBoot()
|
||||
{
|
||||
var sceneSvc = SceneManagerService.Instance;
|
||||
if (sceneSvc == null)
|
||||
{
|
||||
Debug.LogWarning("[CardSystemSceneVisibility] SceneManagerService.Instance is null post-boot.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to scene load completion notifications
|
||||
sceneSvc.SceneLoadCompleted += OnSceneLoaded;
|
||||
|
||||
// Apply initial state based on current gameplay scene
|
||||
ApplyVisibility(sceneSvc.CurrentGameplayScene);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
var sceneSvc = SceneManagerService.Instance;
|
||||
if (sceneSvc != null)
|
||||
{
|
||||
sceneSvc.SceneLoadCompleted -= OnSceneLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(string sceneName)
|
||||
{
|
||||
ApplyVisibility(sceneName);
|
||||
}
|
||||
|
||||
private void ApplyVisibility(string sceneName)
|
||||
{
|
||||
if (targetRoot == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrEmpty(sceneName))
|
||||
return;
|
||||
|
||||
if (hideInBootstrapScene && sceneName == "BootstrapScene")
|
||||
{
|
||||
SetActiveSafe(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldShow = sceneName != startingSceneName;
|
||||
SetActiveSafe(shouldShow);
|
||||
}
|
||||
|
||||
private void SetActiveSafe(bool active)
|
||||
{
|
||||
if (targetRoot.activeSelf != active)
|
||||
{
|
||||
targetRoot.SetActive(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd47485d27c34e138bbc5fbd894a3dea
|
||||
timeCreated: 1761053493
|
||||
Reference in New Issue
Block a user