Coroutine updates WIP

This commit is contained in:
Michal Pikulski
2025-09-18 14:29:10 +02:00
parent 3f52de9e1e
commit c5ce2ae1af
3 changed files with 249 additions and 40 deletions

View File

@@ -1,30 +1,38 @@
using UnityEngine; using UnityEngine;
using System.Collections;
using Pooling; using Pooling;
namespace Minigames.DivingForPictures namespace Minigames.DivingForPictures
{ {
/// <summary> /// <summary>
/// Represents a single bubble, handling its movement, wobble effect, scaling, and sprite assignment. /// Represents a single bubble, handling its movement, wobble effect, scaling, and sprite assignment.
/// Uses coroutines for better performance instead of Update() calls.
/// </summary> /// </summary>
public class Bubble : MonoBehaviour, IPoolableWithReference<BubblePool> public class Bubble : MonoBehaviour, IPoolableWithReference<BubblePool>
{ {
public float speed = 1f; public float speed = 1f;
public float wobbleSpeed = 1f; public float wobbleSpeed = 1f;
private SpriteRenderer spriteRenderer; private SpriteRenderer spriteRenderer;
private SpriteRenderer bubbleSpriteRenderer; // Renamed from bottleSpriteRenderer private SpriteRenderer bubbleSpriteRenderer;
private float timeOffset; private float timeOffset;
private float minScale = 0.2f; private float minScale = 0.2f;
private float maxScale = 1.2f; private float maxScale = 1.2f;
private float baseScale = 1f; // Added to store the initial scale private float baseScale = 1f;
private Camera mainCamera; // Cache camera reference private Camera mainCamera;
private BubblePool parentPool; // Reference to the pool this bubble came from private BubblePool parentPool;
// Coroutine references
private Coroutine _movementCoroutine;
private Coroutine _wobbleCoroutine;
private Coroutine _offScreenCheckCoroutine;
void Awake() void Awake()
{ {
// Cache references and randomize time offset for wobble // Cache references and randomize time offset for wobble
spriteRenderer = GetComponent<SpriteRenderer>(); spriteRenderer = GetComponent<SpriteRenderer>();
timeOffset = Random.value * 100f; timeOffset = Random.value * 100f;
// Find the child named "BubbleSprite" and get its SpriteRenderer // Find the child named "BubbleSprite" and get its SpriteRenderer
Transform bubbleSpriteTransform = transform.Find("BubbleSprite"); Transform bubbleSpriteTransform = transform.Find("BubbleSprite");
if (bubbleSpriteTransform != null) if (bubbleSpriteTransform != null)
@@ -36,20 +44,101 @@ namespace Minigames.DivingForPictures
mainCamera = Camera.main; mainCamera = Camera.main;
} }
void Update() private void OnEnable()
{
StartBubbleBehavior();
}
private void OnDisable()
{
StopBubbleBehavior();
}
/// <summary>
/// Starts all bubble behavior coroutines
/// </summary>
private void StartBubbleBehavior()
{
_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 // Move bubble upward
transform.position += Vector3.up * (speed * Time.deltaTime); 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) // 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 t = (Mathf.Sin((Time.time + timeOffset) * wobbleSpeed) + 1f) * 0.5f; // t in [0,1]
float wobbleFactor = Mathf.Lerp(minScale, maxScale, t); float wobbleFactor = Mathf.Lerp(minScale, maxScale, t);
transform.localScale = Vector3.one * (baseScale * wobbleFactor); transform.localScale = Vector3.one * (baseScale * wobbleFactor);
// Destroy when off screen - using cached camera reference // 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) if (mainCamera != null && transform.position.y > mainCamera.orthographicSize + 2f)
{ {
OnBubbleDestroy(); OnBubbleDestroy();
yield break; // Exit coroutine since bubble is being destroyed
}
// Wait for the check interval
yield return new WaitForSeconds(checkInterval);
} }
} }
@@ -144,6 +233,24 @@ namespace Minigames.DivingForPictures
maxScale = max; 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> /// <summary>
/// Resets the bubble state for reuse from object pool /// Resets the bubble state for reuse from object pool
/// </summary> /// </summary>

View File

@@ -1,4 +1,5 @@
using UnityEngine; using UnityEngine;
using System.Collections;
using Pooling; using Pooling;
namespace Minigames.DivingForPictures namespace Minigames.DivingForPictures
@@ -7,6 +8,7 @@ namespace Minigames.DivingForPictures
/// Complete floating obstacle component that handles movement and pooling. /// Complete floating obstacle component that handles movement and pooling.
/// Obstacles move upward toward the surface. Collision detection is handled by the player. /// Obstacles move upward toward the surface. Collision detection is handled by the player.
/// Once an obstacle hits the player, its collider is disabled to prevent further collisions. /// Once an obstacle hits the player, its collider is disabled to prevent further collisions.
/// Uses coroutines for better performance instead of Update() calls.
/// </summary> /// </summary>
public class FloatingObstacle : MonoBehaviour, IPoolable public class FloatingObstacle : MonoBehaviour, IPoolable
{ {
@@ -42,6 +44,8 @@ namespace Minigames.DivingForPictures
private Collider2D _collider; private Collider2D _collider;
private Camera _mainCamera; private Camera _mainCamera;
private float _screenTop; private float _screenTop;
private Coroutine _movementCoroutine;
private Coroutine _offScreenCheckCoroutine;
private void Awake() private void Awake()
{ {
@@ -60,22 +64,77 @@ namespace Minigames.DivingForPictures
_mainCamera = Camera.main; _mainCamera = Camera.main;
} }
private void Update() private void OnEnable()
{ {
if (enableMovement) StartObstacleBehavior();
{
HandleMovement();
} }
CheckIfOffScreen(); private void OnDisable()
{
StopObstacleBehavior();
} }
/// <summary> /// <summary>
/// Moves the obstacle upward based on its speed /// Starts the obstacle behavior coroutines
/// </summary> /// </summary>
private void HandleMovement() private void StartObstacleBehavior()
{ {
if (enableMovement)
{
_movementCoroutine = StartCoroutine(MovementCoroutine());
}
_offScreenCheckCoroutine = StartCoroutine(OffScreenCheckCoroutine());
}
/// <summary>
/// Stops all obstacle behavior coroutines
/// </summary>
private void StopObstacleBehavior()
{
if (_movementCoroutine != null)
{
StopCoroutine(_movementCoroutine);
_movementCoroutine = null;
}
if (_offScreenCheckCoroutine != null)
{
StopCoroutine(_offScreenCheckCoroutine);
_offScreenCheckCoroutine = null;
}
}
/// <summary>
/// Coroutine that handles obstacle movement
/// </summary>
private IEnumerator MovementCoroutine()
{
while (enabled && gameObject.activeInHierarchy)
{
// Move the obstacle upward
transform.position += Vector3.up * (moveSpeed * Time.deltaTime); transform.position += Vector3.up * (moveSpeed * Time.deltaTime);
// Wait for next frame
yield return null;
}
}
/// <summary>
/// Coroutine that checks if obstacle has moved off-screen
/// Runs at a lower frequency than movement for better performance
/// </summary>
private IEnumerator OffScreenCheckCoroutine()
{
const float checkInterval = 0.2f; // Check every 200ms instead of every frame
while (enabled && gameObject.activeInHierarchy)
{
CheckIfOffScreen();
// Wait for the check interval
yield return new WaitForSeconds(checkInterval);
}
} }
/// <summary> /// <summary>
@@ -177,5 +236,22 @@ namespace Minigames.DivingForPictures
{ {
ReturnToPool(); ReturnToPool();
} }
/// <summary>
/// Public method to enable/disable movement at runtime
/// </summary>
public void SetMovementEnabled(bool enabled)
{
if (enableMovement == enabled) return;
enableMovement = enabled;
// Restart coroutines to apply movement change
if (gameObject.activeInHierarchy)
{
StopObstacleBehavior();
StartObstacleBehavior();
}
}
} }
} }

View File

@@ -71,6 +71,7 @@ namespace Minigames.DivingForPictures
private ObstaclePool _obstaclePool; private ObstaclePool _obstaclePool;
private Camera _mainCamera; private Camera _mainCamera;
private float _screenBottom; private float _screenBottom;
private float _spawnRangeX;
private Coroutine _spawnCoroutine; private Coroutine _spawnCoroutine;
private readonly List<GameObject> _activeObstacles = new List<GameObject>(); private readonly List<GameObject> _activeObstacles = new List<GameObject>();
@@ -174,7 +175,7 @@ namespace Minigames.DivingForPictures
} }
/// <summary> /// <summary>
/// Calculate screen bounds in world space /// Calculate screen bounds in world space dynamically
/// </summary> /// </summary>
private void CalculateScreenBounds() private void CalculateScreenBounds()
{ {
@@ -188,8 +189,19 @@ namespace Minigames.DivingForPictures
} }
} }
// Calculate screen bottom (Y spawn position will be 2 units below this)
Vector3 bottomWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.nearClipPlane)); Vector3 bottomWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.nearClipPlane));
_screenBottom = bottomWorldPoint.y; _screenBottom = bottomWorldPoint.y;
// Calculate screen width in world units
Vector3 leftWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0f, 0.5f, _mainCamera.nearClipPlane));
Vector3 rightWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(1f, 0.5f, _mainCamera.nearClipPlane));
float screenWidth = rightWorldPoint.x - leftWorldPoint.x;
// Calculate spawn range based on 80% of screen width (40% on each side from center)
_spawnRangeX = (screenWidth * 0.8f) / 2f;
Debug.Log($"[ObstacleSpawner] Screen calculated - Width: {screenWidth:F2}, Bottom: {_screenBottom:F2}, Spawn Range X: ±{_spawnRangeX:F2}");
} }
/// <summary> /// <summary>
@@ -273,8 +285,10 @@ namespace Minigames.DivingForPictures
/// </summary> /// </summary>
private Vector3 GetRandomSpawnPosition() private Vector3 GetRandomSpawnPosition()
{ {
float randomX = Random.Range(-spawnRangeX, spawnRangeX); // Use dynamically calculated spawn range (80% of screen width)
float spawnY = _screenBottom - spawnDistanceBelowScreen; float randomX = Random.Range(-_spawnRangeX, _spawnRangeX);
// Spawn 2 units below screen bottom
float spawnY = _screenBottom - 2f;
return new Vector3(randomX, spawnY, 0f); return new Vector3(randomX, spawnY, 0f);
} }
@@ -416,6 +430,14 @@ namespace Minigames.DivingForPictures
maxMoveSpeed = max; maxMoveSpeed = max;
} }
/// <summary>
/// Public method to recalculate screen bounds (useful if camera changes)
/// </summary>
public void RecalculateScreenBounds()
{
CalculateScreenBounds();
}
/// <summary> /// <summary>
/// Gets the count of currently active obstacles /// Gets the count of currently active obstacles
/// </summary> /// </summary>
@@ -424,16 +446,20 @@ namespace Minigames.DivingForPictures
#if UNITY_EDITOR #if UNITY_EDITOR
private void OnDrawGizmosSelected() private void OnDrawGizmosSelected()
{ {
// Draw spawn area // Only draw if screen bounds have been calculated
if (_spawnRangeX > 0f)
{
// Draw spawn area using dynamic calculations
Gizmos.color = Color.yellow; Gizmos.color = Color.yellow;
Vector3 center = new Vector3(0f, _screenBottom - spawnDistanceBelowScreen, 0f); Vector3 center = new Vector3(0f, _screenBottom - 2f, 0f);
Vector3 size = new Vector3(spawnRangeX * 2f, 1f, 1f); Vector3 size = new Vector3(_spawnRangeX * 2f, 1f, 1f);
Gizmos.DrawWireCube(center, size); Gizmos.DrawWireCube(center, size);
// Draw collision radius at spawn point // Draw collision radius at spawn point
Gizmos.color = Color.red; Gizmos.color = Color.red;
Gizmos.DrawWireSphere(center, spawnCollisionRadius); Gizmos.DrawWireSphere(center, spawnCollisionRadius);
} }
}
#endif #endif
} }
} }