- Revamp pausing and centralize management in GameManager - Switch Pause implementation to be counter-based to solve corner case of multiple pause requests - Remove duplicated Pause logic from other components - Add pausing when browsing the card album - Fully deliver the exclusive UI implementation - Spruce up the MiniGame tutorial with correct pausing, hiding other UI - Correctly unpause after showing tutorial - Fix minigame ending sequence. The cinematic correctly plays only once now - Replaying the minigame works Co-authored-by: Michal Adam Pikulski <michal@foolhardyhorizons.com> Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com> Reviewed-on: #39
288 lines
9.0 KiB
C#
288 lines
9.0 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using Pooling;
|
|
using AppleHills.Core.Interfaces;
|
|
using Core;
|
|
|
|
namespace Minigames.DivingForPictures
|
|
{
|
|
/// <summary>
|
|
/// Represents a single bubble, handling its movement, wobble effect, scaling, and sprite assignment.
|
|
/// Uses coroutines for better performance instead of Update() calls.
|
|
/// </summary>
|
|
public class Bubble : MonoBehaviour, IPoolableWithReference<BubblePool>, IPausable
|
|
{
|
|
public float speed = 1f;
|
|
public float wobbleSpeed = 1f;
|
|
private SpriteRenderer spriteRenderer;
|
|
private SpriteRenderer bubbleSpriteRenderer;
|
|
private float timeOffset;
|
|
private float minScale = 0.2f;
|
|
private float maxScale = 1.2f;
|
|
private float baseScale = 1f;
|
|
|
|
private UnityEngine.Camera mainCamera;
|
|
private BubblePool parentPool;
|
|
|
|
// Coroutine references
|
|
private Coroutine _movementCoroutine;
|
|
private Coroutine _wobbleCoroutine;
|
|
private Coroutine _offScreenCheckCoroutine;
|
|
|
|
void Awake()
|
|
{
|
|
// Cache references and randomize time offset for wobble
|
|
spriteRenderer = GetComponent<SpriteRenderer>();
|
|
timeOffset = Random.value * 100f;
|
|
|
|
// Find the child named "BubbleSprite" and get its SpriteRenderer
|
|
Transform bubbleSpriteTransform = transform.Find("BubbleSprite");
|
|
if (bubbleSpriteTransform != null)
|
|
{
|
|
bubbleSpriteRenderer = bubbleSpriteTransform.GetComponent<SpriteRenderer>();
|
|
}
|
|
|
|
// Cache camera reference
|
|
mainCamera = UnityEngine.Camera.main;
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
StartBubbleBehavior();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
StopBubbleBehavior();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pauses all bubble behaviors
|
|
/// </summary>
|
|
public void Pause()
|
|
{
|
|
StopBubbleBehavior();
|
|
|
|
// Debug log for troubleshooting
|
|
Logging.Debug($"[Bubble] Paused bubble: {name}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resumes all bubble behaviors
|
|
/// </summary>
|
|
public void DoResume()
|
|
{
|
|
StartBubbleBehavior();
|
|
|
|
// Debug log for troubleshooting
|
|
Logging.Debug($"[Bubble] Resumed bubble: {name}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts all bubble behavior coroutines
|
|
/// </summary>
|
|
private void StartBubbleBehavior()
|
|
{
|
|
if (GameManager.Instance.IsPaused || !isActiveAndEnabled) return; // Don't start if paused
|
|
|
|
_movementCoroutine = StartCoroutine(MovementCoroutine());
|
|
_wobbleCoroutine = StartCoroutine(WobbleCoroutine());
|
|
_offScreenCheckCoroutine = StartCoroutine(OffScreenCheckCoroutine());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops all bubble behavior coroutines
|
|
/// </summary>
|
|
private void StopBubbleBehavior()
|
|
{
|
|
if (_movementCoroutine != null)
|
|
{
|
|
StopCoroutine(_movementCoroutine);
|
|
_movementCoroutine = null;
|
|
}
|
|
|
|
if (_wobbleCoroutine != null)
|
|
{
|
|
StopCoroutine(_wobbleCoroutine);
|
|
_wobbleCoroutine = null;
|
|
}
|
|
|
|
if (_offScreenCheckCoroutine != null)
|
|
{
|
|
StopCoroutine(_offScreenCheckCoroutine);
|
|
_offScreenCheckCoroutine = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Coroutine that handles bubble upward movement
|
|
/// </summary>
|
|
private IEnumerator MovementCoroutine()
|
|
{
|
|
while (enabled && gameObject.activeInHierarchy)
|
|
{
|
|
// Move bubble upward
|
|
transform.position += Vector3.up * (speed * Time.deltaTime);
|
|
|
|
// Wait for next frame
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Coroutine that handles the wobble scaling effect
|
|
/// </summary>
|
|
private IEnumerator WobbleCoroutine()
|
|
{
|
|
while (enabled && gameObject.activeInHierarchy)
|
|
{
|
|
// Wobble effect (smooth oscillation between min and max scale)
|
|
float t = (Mathf.Sin((Time.time + timeOffset) * wobbleSpeed) + 1f) * 0.5f; // t in [0,1]
|
|
float wobbleFactor = Mathf.Lerp(minScale, maxScale, t);
|
|
transform.localScale = Vector3.one * (baseScale * wobbleFactor);
|
|
|
|
// Wait for next frame
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Coroutine that checks if bubble has moved off-screen
|
|
/// Runs at a lower frequency for better performance
|
|
/// </summary>
|
|
private IEnumerator OffScreenCheckCoroutine()
|
|
{
|
|
const float checkInterval = 0.1f; // Check every 100ms instead of every frame
|
|
|
|
while (enabled && gameObject.activeInHierarchy)
|
|
{
|
|
// Check if bubble is off screen
|
|
if (mainCamera != null && transform.position.y > mainCamera.orthographicSize + 2f)
|
|
{
|
|
OnBubbleDestroy();
|
|
yield break; // Exit coroutine since bubble is being destroyed
|
|
}
|
|
|
|
// Wait for the check interval
|
|
yield return new WaitForSeconds(checkInterval);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when bubble is about to be destroyed
|
|
/// </summary>
|
|
private void OnBubbleDestroy()
|
|
{
|
|
// Use the cached pool reference instead of finding it each time
|
|
if (parentPool != null)
|
|
{
|
|
parentPool.ReturnBubble(this);
|
|
}
|
|
else
|
|
{
|
|
// Fallback to find the pool if the reference is somehow lost
|
|
BubblePool pool = FindFirstObjectByType<BubblePool>();
|
|
if (pool != null)
|
|
{
|
|
Logging.Warning("Bubble is missing its parent pool reference, finding pool as fallback");
|
|
pool.ReturnBubble(this);
|
|
}
|
|
else
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the parent pool for this bubble
|
|
/// </summary>
|
|
/// <param name="pool">The bubble pool that created this bubble</param>
|
|
public void SetPool(BubblePool pool)
|
|
{
|
|
parentPool = pool;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the object is retrieved from the pool.
|
|
/// </summary>
|
|
public void OnSpawn()
|
|
{
|
|
ResetState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the object is returned to the pool.
|
|
/// </summary>
|
|
public void OnDespawn()
|
|
{
|
|
// Nothing to do here for now, but we could clean up resources
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the main sprite for the bubble.
|
|
/// </summary>
|
|
/// <param name="sprite">Sprite to assign.</param>
|
|
public void SetSprite(Sprite sprite)
|
|
{
|
|
if (spriteRenderer != null)
|
|
spriteRenderer.sprite = sprite;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the sprite for the child "BubbleSprite" renderer.
|
|
/// </summary>
|
|
/// <param name="sprite">Sprite to assign.</param>
|
|
public void SetBubbleSprite(Sprite sprite)
|
|
{
|
|
if (bubbleSpriteRenderer != null)
|
|
bubbleSpriteRenderer.sprite = sprite;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the base scale for the bubble
|
|
/// </summary>
|
|
/// <param name="scale">Base scale value</param>
|
|
public void SetBaseScale(float scale)
|
|
{
|
|
baseScale = scale;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the minimum and maximum scale for the wobble effect.
|
|
/// </summary>
|
|
/// <param name="min">Minimum scale.</param>
|
|
/// <param name="max">Maximum scale.</param>
|
|
public void SetWobbleScaleLimits(float min, float max)
|
|
{
|
|
minScale = min;
|
|
maxScale = max;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the movement speed at runtime
|
|
/// </summary>
|
|
/// <param name="newSpeed">New movement speed</param>
|
|
public void SetSpeed(float newSpeed)
|
|
{
|
|
speed = newSpeed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the wobble speed at runtime
|
|
/// </summary>
|
|
/// <param name="newWobbleSpeed">New wobble speed</param>
|
|
public void SetWobbleSpeed(float newWobbleSpeed)
|
|
{
|
|
wobbleSpeed = newWobbleSpeed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the bubble state for reuse from object pool
|
|
/// </summary>
|
|
public void ResetState()
|
|
{
|
|
timeOffset = Random.value * 100f;
|
|
}
|
|
}
|
|
} |