Add backbone for card creation and implement Camera minigame mechanics
This commit is contained in:
@@ -20,7 +20,7 @@ namespace Minigames.DivingForPictures
|
||||
private float maxScale = 1.2f;
|
||||
private float baseScale = 1f;
|
||||
|
||||
private Camera mainCamera;
|
||||
private UnityEngine.Camera mainCamera;
|
||||
private BubblePool parentPool;
|
||||
|
||||
// Coroutine references
|
||||
@@ -48,7 +48,7 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
|
||||
// Cache camera reference
|
||||
mainCamera = Camera.main;
|
||||
mainCamera = UnityEngine.Camera.main;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
@@ -78,7 +78,7 @@ namespace Minigames.DivingForPictures
|
||||
/// <summary>
|
||||
/// Resumes all bubble behaviors
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
public void DoResume()
|
||||
{
|
||||
if (!_isPaused) return; // Already running
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
private void StartBubbleBehavior()
|
||||
{
|
||||
if (_isPaused) return; // Don't start if paused
|
||||
if (_isPaused || !isActiveAndEnabled) return; // Don't start if paused
|
||||
|
||||
_movementCoroutine = StartCoroutine(MovementCoroutine());
|
||||
_wobbleCoroutine = StartCoroutine(WobbleCoroutine());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using Pooling;
|
||||
using AppleHills.Core.Settings;
|
||||
using AppleHills.Core.Interfaces;
|
||||
|
||||
@@ -20,7 +19,7 @@ namespace Minigames.DivingForPictures
|
||||
private float _timer;
|
||||
private float _nextSpawnInterval;
|
||||
private BubblePool _bubblePool;
|
||||
private Camera _mainCamera; // Cache camera reference
|
||||
private UnityEngine.Camera _mainCamera; // Cache camera reference
|
||||
private bool _isSurfacing = false;
|
||||
|
||||
// Pause state
|
||||
@@ -34,7 +33,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
|
||||
// Get developer settings and game settings
|
||||
_devSettings = GameManager.GetDeveloperSettings<DivingDeveloperSettings>();
|
||||
@@ -67,12 +66,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Register with DivingGameManager for pause/resume events
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.RegisterPausableComponent(this);
|
||||
}
|
||||
DivingGameManager.Instance.RegisterPausableComponent(this);
|
||||
|
||||
// Start spawning if not paused
|
||||
StartSpawningCoroutine();
|
||||
@@ -80,12 +74,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// Unregister from DivingGameManager
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.UnregisterPausableComponent(this);
|
||||
}
|
||||
DivingGameManager.Instance.UnregisterPausableComponent(this);
|
||||
|
||||
// Clean up any active coroutines
|
||||
StopAllCoroutines();
|
||||
@@ -123,7 +112,7 @@ namespace Minigames.DivingForPictures
|
||||
/// <summary>
|
||||
/// Resumes the bubble spawner and all bubbles
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
public void DoResume()
|
||||
{
|
||||
if (!_isPaused) return; // Already running
|
||||
|
||||
@@ -138,7 +127,7 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
if (bubble != null)
|
||||
{
|
||||
bubble.Resume();
|
||||
bubble.DoResume();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Playables;
|
||||
using AppleHills.Core.Settings;
|
||||
using Utility;
|
||||
using AppleHills.Core.Interfaces;
|
||||
using Input;
|
||||
using UI;
|
||||
using Minigames.DivingForPictures.PictureCamera;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
@@ -24,9 +27,11 @@ namespace Minigames.DivingForPictures
|
||||
[Header("Surfacing Settings")]
|
||||
[Tooltip("Reference to the PlayableDirector that will play the surfacing timeline")]
|
||||
[SerializeField] private PlayableDirector surfacingTimeline;
|
||||
|
||||
private CameraViewfinderManager viewfinderManager;
|
||||
|
||||
// Settings reference
|
||||
private IDivingMinigameSettings _settings;
|
||||
private IDivingMinigameSettings settings;
|
||||
|
||||
// Private state variables
|
||||
private int playerScore = 0;
|
||||
@@ -37,13 +42,13 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Velocity management
|
||||
// Velocity state tracking
|
||||
private float _currentVelocityFactor = 1.0f; // 1.0 = normal descent speed, -1.0 * surfacingSpeedFactor = full surfacing speed
|
||||
private Coroutine _velocityTransitionCoroutine;
|
||||
private Coroutine _surfacingSequenceCoroutine;
|
||||
private float currentVelocityFactor = 1.0f; // 1.0 = normal descent speed, -1.0 * surfacingSpeedFactor = full surfacing speed
|
||||
private Coroutine velocityTransitionCoroutine;
|
||||
private Coroutine surfacingSequenceCoroutine;
|
||||
|
||||
// Public properties
|
||||
public int PlayerScore => playerScore;
|
||||
public float CurrentVelocityFactor => _currentVelocityFactor;
|
||||
public float CurrentVelocityFactor => currentVelocityFactor;
|
||||
|
||||
// Events
|
||||
public event Action<int> OnScoreChanged;
|
||||
@@ -76,18 +81,58 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// IPausable implementation
|
||||
public bool IsPaused => _isPaused;
|
||||
|
||||
// Photo sequence state
|
||||
private bool _isPhotoSequenceActive = false;
|
||||
private Monster _currentPhotoTarget = null;
|
||||
private Dictionary<IPausable, bool> _pauseStateBackup = new Dictionary<IPausable, bool>();
|
||||
private float _capturedProximity = 0f; // New: tracks how close to target the photo was taken (0-1)
|
||||
|
||||
// List of components to exempt from pausing during photo sequence
|
||||
private List<IPausable> _exemptFromPhotoSequencePausing = new List<IPausable>();
|
||||
|
||||
// Photo sequence events
|
||||
public event Action<Monster> OnPhotoSequenceStarted;
|
||||
public event Action<Monster, float> OnPhotoSequenceCompleted; // Now includes proximity score
|
||||
public event Action<float> OnPhotoSequenceProgressUpdated;
|
||||
|
||||
private static DivingGameManager _instance;
|
||||
private static bool _isQuitting = false;
|
||||
public static DivingGameManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null && Application.isPlaying && !_isQuitting)
|
||||
{
|
||||
_instance = FindAnyObjectByType<DivingGameManager>();
|
||||
if (_instance == null)
|
||||
{
|
||||
var go = new GameObject("DivingGameManager");
|
||||
_instance = go.AddComponent<DivingGameManager>();
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Get settings from GameManager
|
||||
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
if (_settings == null)
|
||||
{
|
||||
Debug.LogError("[DivingGameManager] Failed to load diving minigame settings!");
|
||||
}
|
||||
settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
currentSpawnProbability = settings?.BaseSpawnProbability ?? 0.2f;
|
||||
|
||||
// Initialize with base probability from settings
|
||||
currentSpawnProbability = _settings?.BaseSpawnProbability ?? 0.2f;
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = this;
|
||||
}
|
||||
else if (_instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
_isQuitting = true;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@@ -97,7 +142,7 @@ namespace Minigames.DivingForPictures
|
||||
if (pauseMenu != null)
|
||||
{
|
||||
pauseMenu.OnGamePaused += Pause;
|
||||
pauseMenu.OnGameResumed += Resume;
|
||||
pauseMenu.OnGameResumed += DoResume;
|
||||
|
||||
Debug.Log("[DivingGameManager] Subscribed to PauseMenu events");
|
||||
}
|
||||
@@ -135,35 +180,26 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Validate rope references (this doesn't depend on initialization)
|
||||
ValidateRopeReferences();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the game components once the orientation is correct.
|
||||
/// This is called by SceneOrientationEnforcer when the device is properly oriented.
|
||||
/// </summary>
|
||||
public void InitializeGame()
|
||||
{
|
||||
// Prevent double initialization
|
||||
if (_isGameInitialized) return;
|
||||
viewfinderManager = CameraViewfinderManager.Instance;
|
||||
|
||||
Debug.Log("[DivingGameManager] Initializing game");
|
||||
|
||||
// Subscribe to tile spawned event
|
||||
TrenchTileSpawner tileSpawner = FindFirstObjectByType<TrenchTileSpawner>();
|
||||
if (tileSpawner != null)
|
||||
// Subscribe to viewfinder events if found
|
||||
if (viewfinderManager != null)
|
||||
{
|
||||
tileSpawner.onTileSpawned.AddListener(OnTileSpawned);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("No TrenchTileSpawner found in scene. Monster spawning won't work.");
|
||||
viewfinderManager.OnAnimationCompleted += OnViewfinderAnimationCompleted;
|
||||
viewfinderManager.OnViewfinderTapped += OnViewfinderTapped;
|
||||
viewfinderManager.OnProximityUpdated += OnProximityUpdated;
|
||||
viewfinderManager.OnViewfinderTappedDuringAnimation += OnViewfinderTappedDuringAnimation;
|
||||
viewfinderManager.OnReverseAnimationStarted += OnReverseAnimationStarted;
|
||||
|
||||
// Add the viewfinder manager to exempt list to ensure it keeps working during photo sequences
|
||||
if (viewfinderManager is IPausable viewfinderPausable)
|
||||
{
|
||||
RegisterExemptFromPhotoSequencePausing(viewfinderPausable);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark as initialized
|
||||
_isGameInitialized = true;
|
||||
|
||||
// Notify all listeners that the game is initialized
|
||||
OnGameInitialized?.Invoke();
|
||||
OnMonsterSpawned += DoMonsterSpawned;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
@@ -181,7 +217,7 @@ namespace Minigames.DivingForPictures
|
||||
if (pauseMenu != null)
|
||||
{
|
||||
pauseMenu.OnGamePaused -= Pause;
|
||||
pauseMenu.OnGameResumed -= Resume;
|
||||
pauseMenu.OnGameResumed -= DoResume;
|
||||
}
|
||||
|
||||
// Unregister from GameManager
|
||||
@@ -192,6 +228,16 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Unregister all pausable components
|
||||
_pausableComponents.Clear();
|
||||
|
||||
// Unsubscribe from viewfinder events
|
||||
if (viewfinderManager != null)
|
||||
{
|
||||
viewfinderManager.OnAnimationCompleted -= OnViewfinderAnimationCompleted;
|
||||
viewfinderManager.OnViewfinderTapped -= OnViewfinderTapped;
|
||||
viewfinderManager.OnProximityUpdated -= OnProximityUpdated;
|
||||
viewfinderManager.OnViewfinderTappedDuringAnimation -= OnViewfinderTappedDuringAnimation;
|
||||
viewfinderManager.OnReverseAnimationStarted -= OnReverseAnimationStarted;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -200,10 +246,10 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Gradually increase spawn probability over time
|
||||
float previousProbability = currentSpawnProbability;
|
||||
if (currentSpawnProbability < _settings.MaxSpawnProbability)
|
||||
if (currentSpawnProbability < settings.MaxSpawnProbability)
|
||||
{
|
||||
currentSpawnProbability += _settings.ProbabilityIncreaseRate * Time.deltaTime;
|
||||
currentSpawnProbability = Mathf.Min(currentSpawnProbability, _settings.MaxSpawnProbability);
|
||||
currentSpawnProbability += settings.ProbabilityIncreaseRate * Time.deltaTime;
|
||||
currentSpawnProbability = Mathf.Min(currentSpawnProbability, settings.MaxSpawnProbability);
|
||||
|
||||
// Only fire event if probability changed significantly
|
||||
if (Mathf.Abs(currentSpawnProbability - previousProbability) > 0.01f)
|
||||
@@ -223,8 +269,8 @@ namespace Minigames.DivingForPictures
|
||||
// If we're surfacing, don't spawn new monsters
|
||||
if (_isSurfacing) return;
|
||||
|
||||
bool forceSpawn = timeSinceLastSpawn >= _settings.GuaranteedSpawnTime;
|
||||
bool onCooldown = timeSinceLastSpawn < _settings.SpawnCooldown;
|
||||
bool forceSpawn = timeSinceLastSpawn >= settings.GuaranteedSpawnTime;
|
||||
bool onCooldown = timeSinceLastSpawn < settings.SpawnCooldown;
|
||||
|
||||
// Don't spawn if on cooldown, unless forced
|
||||
if (onCooldown && !forceSpawn) return;
|
||||
@@ -241,7 +287,7 @@ namespace Minigames.DivingForPictures
|
||||
// Reset timer and adjust probability
|
||||
lastSpawnTime = Time.time;
|
||||
timeSinceLastSpawn = 0f;
|
||||
currentSpawnProbability = _settings.BaseSpawnProbability;
|
||||
currentSpawnProbability = settings.BaseSpawnProbability;
|
||||
OnSpawnProbabilityChanged?.Invoke(currentSpawnProbability);
|
||||
}
|
||||
}
|
||||
@@ -265,14 +311,7 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
// Parent the monster to the spawn point so it moves with the tile
|
||||
monsterObj.transform.SetParent(spawnPoint);
|
||||
|
||||
// Subscribe to monster events
|
||||
monster.OnPictureTaken += OnMonsterPictureTaken;
|
||||
monster.OnMonsterDespawned += OnMonsterDespawned;
|
||||
|
||||
// Add to active monsters list
|
||||
activeMonsters.Add(monster);
|
||||
|
||||
|
||||
// Fire event
|
||||
OnMonsterSpawned?.Invoke(monster);
|
||||
}
|
||||
@@ -283,11 +322,11 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMonsterPictureTaken(Monster monster)
|
||||
private void DoPictureTaken(Monster monster)
|
||||
{
|
||||
// Calculate points based on depth
|
||||
int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * _settings.DepthMultiplier);
|
||||
int pointsAwarded = _settings.BasePoints + depthBonus;
|
||||
int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * settings.DepthMultiplier);
|
||||
int pointsAwarded = settings.BasePoints + depthBonus;
|
||||
|
||||
// Add score
|
||||
playerScore += pointsAwarded;
|
||||
@@ -296,17 +335,7 @@ namespace Minigames.DivingForPictures
|
||||
OnScoreChanged?.Invoke(playerScore);
|
||||
OnPictureTaken?.Invoke(monster, pointsAwarded);
|
||||
}
|
||||
|
||||
private void OnMonsterDespawned(Monster monster)
|
||||
{
|
||||
// Remove from active list
|
||||
activeMonsters.Remove(monster);
|
||||
|
||||
// Unsubscribe from events
|
||||
monster.OnPictureTaken -= OnMonsterPictureTaken;
|
||||
monster.OnMonsterDespawned -= OnMonsterDespawned;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player takes damage from any collision
|
||||
/// </summary>
|
||||
@@ -438,7 +467,7 @@ namespace Minigames.DivingForPictures
|
||||
_isSurfacing = true;
|
||||
|
||||
// 1. Initiate smooth velocity transition to surfacing speed
|
||||
float targetVelocityFactor = -1.0f * _settings.SurfacingSpeedFactor;
|
||||
float targetVelocityFactor = -1.0f * settings.SurfacingSpeedFactor;
|
||||
SetVelocityFactor(targetVelocityFactor);
|
||||
|
||||
// 2. Find and notify trench tile spawner about direction change (for spawning/despawning logic)
|
||||
@@ -457,7 +486,7 @@ namespace Minigames.DivingForPictures
|
||||
tileSpawner.StartSurfacing();
|
||||
|
||||
// Immediately send current velocity factor
|
||||
tileSpawner.OnVelocityFactorChanged(_currentVelocityFactor);
|
||||
tileSpawner.OnVelocityFactorChanged(currentVelocityFactor);
|
||||
}
|
||||
|
||||
// Handle the Rock object - disable components and animate it falling offscreen
|
||||
@@ -525,15 +554,15 @@ namespace Minigames.DivingForPictures
|
||||
obstacleSpawner.StartSurfacing();
|
||||
|
||||
// Immediately send current velocity factor
|
||||
obstacleSpawner.OnVelocityFactorChanged(_currentVelocityFactor);
|
||||
obstacleSpawner.OnVelocityFactorChanged(currentVelocityFactor);
|
||||
}
|
||||
|
||||
// Start the surfacing sequence coroutine
|
||||
if (_surfacingSequenceCoroutine != null)
|
||||
if (surfacingSequenceCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_surfacingSequenceCoroutine);
|
||||
StopCoroutine(surfacingSequenceCoroutine);
|
||||
}
|
||||
_surfacingSequenceCoroutine = StartCoroutine(SurfacingSequence());
|
||||
surfacingSequenceCoroutine = StartCoroutine(SurfacingSequence());
|
||||
|
||||
Debug.Log($"[DivingGameManager] Started surfacing with target velocity factor: {targetVelocityFactor}");
|
||||
}
|
||||
@@ -546,7 +575,7 @@ namespace Minigames.DivingForPictures
|
||||
Vector3 startPosition = rockTransform.position;
|
||||
|
||||
// Calculate position below the screen
|
||||
Camera mainCamera = Camera.main;
|
||||
UnityEngine.Camera mainCamera = UnityEngine.Camera.main;
|
||||
if (mainCamera == null)
|
||||
{
|
||||
Debug.LogWarning("[DivingGameManager] Cannot find main camera to calculate offscreen position");
|
||||
@@ -609,7 +638,7 @@ namespace Minigames.DivingForPictures
|
||||
private IEnumerator SurfacingSequence()
|
||||
{
|
||||
// Wait for the configured delay
|
||||
yield return new WaitForSeconds(_settings.SurfacingSpawnDelay);
|
||||
yield return new WaitForSeconds(settings.SurfacingSpawnDelay);
|
||||
|
||||
// Find tile spawner and tell it to stop spawning
|
||||
TrenchTileSpawner tileSpawner = FindFirstObjectByType<TrenchTileSpawner>();
|
||||
@@ -662,12 +691,12 @@ namespace Minigames.DivingForPictures
|
||||
/// <param name="targetFactor">Target velocity factor (e.g., -1.0 for surfacing speed)</param>
|
||||
public void SetVelocityFactor(float targetFactor)
|
||||
{
|
||||
if (_velocityTransitionCoroutine != null)
|
||||
if (velocityTransitionCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_velocityTransitionCoroutine);
|
||||
StopCoroutine(velocityTransitionCoroutine);
|
||||
}
|
||||
|
||||
_velocityTransitionCoroutine = StartCoroutine(TransitionVelocityFactor(targetFactor));
|
||||
velocityTransitionCoroutine = StartCoroutine(TransitionVelocityFactor(targetFactor));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -675,29 +704,29 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
private IEnumerator<WaitForEndOfFrame> TransitionVelocityFactor(float targetFactor)
|
||||
{
|
||||
float startFactor = _currentVelocityFactor;
|
||||
float startFactor = currentVelocityFactor;
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < _settings.SpeedTransitionDuration)
|
||||
while (elapsed < settings.SpeedTransitionDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / _settings.SpeedTransitionDuration);
|
||||
float t = Mathf.Clamp01(elapsed / settings.SpeedTransitionDuration);
|
||||
|
||||
// Smooth step interpolation
|
||||
float smoothStep = t * t * (3f - 2f * t);
|
||||
|
||||
_currentVelocityFactor = Mathf.Lerp(startFactor, targetFactor, smoothStep);
|
||||
currentVelocityFactor = Mathf.Lerp(startFactor, targetFactor, smoothStep);
|
||||
|
||||
// Notify listeners about the velocity factor change
|
||||
OnVelocityFactorChanged?.Invoke(_currentVelocityFactor);
|
||||
OnVelocityFactorChanged?.Invoke(currentVelocityFactor);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
_currentVelocityFactor = targetFactor;
|
||||
currentVelocityFactor = targetFactor;
|
||||
|
||||
// Final assignment to ensure exact target value
|
||||
OnVelocityFactorChanged?.Invoke(_currentVelocityFactor);
|
||||
OnVelocityFactorChanged?.Invoke(currentVelocityFactor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -737,6 +766,11 @@ namespace Minigames.DivingForPictures
|
||||
/// Pause the game and all registered components
|
||||
/// </summary>
|
||||
public void Pause()
|
||||
{
|
||||
DoPause();
|
||||
}
|
||||
|
||||
public void DoPause(bool turnOffGameInput = true)
|
||||
{
|
||||
if (_isPaused) return; // Already paused
|
||||
|
||||
@@ -748,13 +782,17 @@ namespace Minigames.DivingForPictures
|
||||
component.Pause();
|
||||
}
|
||||
|
||||
// Change input mode to UI when menu is open
|
||||
if(turnOffGameInput)
|
||||
InputManager.Instance.SetInputMode(InputMode.UI);
|
||||
|
||||
Debug.Log($"[DivingGameManager] Game paused. Paused {_pausableComponents.Count} components.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume the game and all registered components
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
public void DoResume()
|
||||
{
|
||||
if (!_isPaused) return; // Already running
|
||||
|
||||
@@ -763,10 +801,281 @@ namespace Minigames.DivingForPictures
|
||||
// Resume all registered components
|
||||
foreach (var component in _pausableComponents)
|
||||
{
|
||||
component.Resume();
|
||||
component.DoResume();
|
||||
}
|
||||
|
||||
// Change input mode to UI when menu is open
|
||||
InputManager.Instance.SetInputMode(InputMode.GameAndUI);
|
||||
|
||||
Debug.Log($"[DivingGameManager] Game resumed. Resumed {_pausableComponents.Count} components.");
|
||||
}
|
||||
|
||||
#region Photo Sequence Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the viewfinder animation phase changes to reverse (zoom out)
|
||||
/// </summary>
|
||||
private void OnReverseAnimationStarted()
|
||||
{
|
||||
Debug.Log("[DivingGameManager] Viewfinder animation entering reverse (zoom-out) phase");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when proximity value is updated during animation
|
||||
/// </summary>
|
||||
private void OnProximityUpdated(float proximity)
|
||||
{
|
||||
// Store the current proximity value for potential use in scoring
|
||||
_capturedProximity = proximity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player taps during the viewfinder animation
|
||||
/// </summary>
|
||||
private void OnViewfinderTappedDuringAnimation(float proximity)
|
||||
{
|
||||
if (!_isPhotoSequenceActive || _currentPhotoTarget == null)
|
||||
return;
|
||||
|
||||
// Store the proximity value at the time of tap for scoring
|
||||
_capturedProximity = proximity;
|
||||
|
||||
Debug.Log($"[DivingGameManager] Player tapped during animation! Proximity: {proximity:F2}");
|
||||
|
||||
// Take the picture at the current proximity
|
||||
TakePicture();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes the picture with the current proximity value for scoring
|
||||
/// </summary>
|
||||
private void TakePicture()
|
||||
{
|
||||
if (!_isPhotoSequenceActive || _currentPhotoTarget == null)
|
||||
return;
|
||||
|
||||
// Notify the monster that its picture was taken
|
||||
_currentPhotoTarget.NotifyPictureTaken();
|
||||
|
||||
// Calculate score based on proximity and depth
|
||||
CalculateScore(_currentPhotoTarget, _capturedProximity);
|
||||
|
||||
// Complete the sequence
|
||||
CompletePhotoSequence();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score for a picture based on proximity to target and monster depth
|
||||
/// </summary>
|
||||
private void CalculateScore(Monster monster, float proximity)
|
||||
{
|
||||
if (monster == null) return;
|
||||
|
||||
// Calculate base points from depth
|
||||
int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * settings.DepthMultiplier);
|
||||
|
||||
// Apply proximity multiplier (0-100%)
|
||||
float proximityMultiplier = Mathf.Clamp01(proximity); // Ensure it's in 0-1 range
|
||||
int proximityBonus = Mathf.RoundToInt(settings.BasePoints * proximityMultiplier);
|
||||
|
||||
// Calculate total score
|
||||
int pointsAwarded = settings.BasePoints + proximityBonus + depthBonus;
|
||||
|
||||
Debug.Log($"[DivingGameManager] Picture score calculation: base={proximityBonus} (proximity={proximity:F2}), " +
|
||||
$"depth bonus={depthBonus}, total={pointsAwarded}");
|
||||
|
||||
// Add score
|
||||
playerScore += pointsAwarded;
|
||||
|
||||
// Fire events
|
||||
OnScoreChanged?.Invoke(playerScore);
|
||||
OnPictureTaken?.Invoke(monster, pointsAwarded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles completion of the viewfinder animation
|
||||
/// </summary>
|
||||
private void OnViewfinderAnimationCompleted()
|
||||
{
|
||||
if (!_isPhotoSequenceActive || _currentPhotoTarget == null)
|
||||
return;
|
||||
|
||||
// Take the picture at whatever the final proximity was
|
||||
TakePicture();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up and completes the picture-taking sequence
|
||||
/// </summary>
|
||||
private void CompletePhotoSequence()
|
||||
{
|
||||
if (!_isPhotoSequenceActive)
|
||||
return;
|
||||
|
||||
// Notify listeners that photo sequence has completed (with proximity score)
|
||||
if (_currentPhotoTarget != null)
|
||||
{
|
||||
OnPhotoSequenceCompleted?.Invoke(_currentPhotoTarget, _capturedProximity);
|
||||
|
||||
// We no longer despawn the monster after taking a picture
|
||||
// _currentPhotoTarget.Despawn(false);
|
||||
|
||||
// Just mark the photo sequence as no longer in progress
|
||||
_currentPhotoTarget.NotifyPictureTaken();
|
||||
}
|
||||
|
||||
DoResume();
|
||||
|
||||
// Reset state
|
||||
_isPhotoSequenceActive = false;
|
||||
_currentPhotoTarget = null;
|
||||
|
||||
Debug.Log($"[DivingGameManager] Completed photo sequence with proximity score: {_capturedProximity:F2}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a component to be exempt from pausing during photo sequence
|
||||
/// </summary>
|
||||
public void RegisterExemptFromPhotoSequencePausing(IPausable component)
|
||||
{
|
||||
if (!_exemptFromPhotoSequencePausing.Contains(component))
|
||||
{
|
||||
_exemptFromPhotoSequencePausing.Add(component);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Game Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the game components once the orientation is correct.
|
||||
/// This is called by SceneOrientationEnforcer when the device is properly oriented.
|
||||
/// </summary>
|
||||
public void InitializeGame()
|
||||
{
|
||||
// Prevent double initialization
|
||||
if (_isGameInitialized) return;
|
||||
|
||||
Debug.Log("[DivingGameManager] Initializing game");
|
||||
|
||||
// Subscribe to tile spawned event
|
||||
TrenchTileSpawner tileSpawner = FindFirstObjectByType<TrenchTileSpawner>();
|
||||
if (tileSpawner != null)
|
||||
{
|
||||
tileSpawner.onTileSpawned.AddListener(OnTileSpawned);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("No TrenchTileSpawner found in scene. Monster spawning won't work.");
|
||||
}
|
||||
|
||||
// Mark as initialized
|
||||
_isGameInitialized = true;
|
||||
|
||||
// Notify all listeners that the game is initialized
|
||||
OnGameInitialized?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void DoMonsterSpawned(Monster monster)
|
||||
{
|
||||
if (monster != null)
|
||||
{
|
||||
// Subscribe to monster enter/exit events
|
||||
monster.OnPlayerEnterDetectionRange += OnPlayerEnterMonsterRange;
|
||||
monster.OnPlayerExitDetectionRange += OnPlayerExitMonsterRange;
|
||||
monster.OnMonsterDespawned += DoMonsterDespawned;
|
||||
|
||||
// Add to active monsters list
|
||||
activeMonsters.Add(monster);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoMonsterDespawned(Monster monster)
|
||||
{
|
||||
// Remove from active list
|
||||
activeMonsters.Remove(monster);
|
||||
|
||||
// Unsubscribe from monster events
|
||||
if (monster != null)
|
||||
{
|
||||
monster.OnPlayerEnterDetectionRange -= OnPlayerEnterMonsterRange;
|
||||
monster.OnPlayerExitDetectionRange -= OnPlayerExitMonsterRange;
|
||||
monster.OnMonsterDespawned -= DoMonsterDespawned;
|
||||
}
|
||||
}
|
||||
|
||||
// Handles player entering monster detection range
|
||||
private void OnPlayerEnterMonsterRange(Monster monster)
|
||||
{
|
||||
if (monster != null && !_isPhotoSequenceActive)
|
||||
{
|
||||
// Store current target for later use
|
||||
_currentPhotoTarget = monster;
|
||||
|
||||
// Show the full-screen viewfinder (first step in two-step process)
|
||||
if (viewfinderManager != null)
|
||||
{
|
||||
viewfinderManager.ShowFullScreenViewfinder();
|
||||
Debug.Log($"[DivingGameManager] Player entered range of monster {monster.name}, showing full-screen viewfinder");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles player exiting monster detection range
|
||||
private void OnPlayerExitMonsterRange(Monster monster)
|
||||
{
|
||||
// Only hide viewfinder if we're not already in a photo sequence
|
||||
if (!_isPhotoSequenceActive && monster == _currentPhotoTarget)
|
||||
{
|
||||
// Hide the viewfinder
|
||||
if (viewfinderManager != null)
|
||||
{
|
||||
viewfinderManager.HideViewfinder();
|
||||
Debug.Log($"[DivingGameManager] Player exited range of monster {monster.name}, hiding viewfinder");
|
||||
}
|
||||
|
||||
// Clear current target
|
||||
_currentPhotoTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the player taps on the viewfinder
|
||||
private void OnViewfinderTapped()
|
||||
{
|
||||
// Only proceed if we have a valid target and not already in a sequence
|
||||
if (_currentPhotoTarget != null && !_isPhotoSequenceActive && _currentPhotoTarget.IsPlayerInDetectionRange)
|
||||
{
|
||||
// Pause the game immediately
|
||||
DoPause(false);
|
||||
Debug.Log($"[DivingGameManager] Pausing game before starting viewfinder animation");
|
||||
|
||||
// Mark the photo sequence as active
|
||||
_isPhotoSequenceActive = true;
|
||||
|
||||
// Mark monster as in photo sequence
|
||||
_currentPhotoTarget.SetPhotoSequenceInProgress();
|
||||
|
||||
// Reset captured proximity
|
||||
_capturedProximity = 0f;
|
||||
|
||||
// Notify listeners that photo sequence has started
|
||||
OnPhotoSequenceStarted?.Invoke(_currentPhotoTarget);
|
||||
|
||||
// Start the complete animation sequence to target monster
|
||||
if (viewfinderManager != null)
|
||||
{
|
||||
viewfinderManager.StartViewfinderSequence(_currentPhotoTarget.transform);
|
||||
Debug.Log($"[DivingGameManager] Viewfinder tapped for monster {_currentPhotoTarget.name}, starting animation sequence");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[DivingGameManager] No ViewfinderManager available!");
|
||||
CompletePhotoSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,25 +9,15 @@ namespace Minigames.DivingForPictures
|
||||
[SerializeField] private GameObject scorePopupPrefab;
|
||||
[SerializeField] private Transform popupParent;
|
||||
|
||||
private DivingGameManager gameManager;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
// Subscribe to events
|
||||
DivingGameManager.Instance.OnScoreChanged += UpdateScoreDisplay;
|
||||
DivingGameManager.Instance.OnPictureTaken += ShowScorePopup;
|
||||
|
||||
if (gameManager != null)
|
||||
{
|
||||
// Subscribe to events
|
||||
gameManager.OnScoreChanged += UpdateScoreDisplay;
|
||||
gameManager.OnPictureTaken += ShowScorePopup;
|
||||
|
||||
// Initialize display
|
||||
UpdateScoreDisplay(gameManager.PlayerScore);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("No DivingGameManager found in scene.");
|
||||
}
|
||||
// Initialize display
|
||||
UpdateScoreDisplay(DivingGameManager.Instance.PlayerScore);
|
||||
|
||||
|
||||
// Create popup parent if needed
|
||||
if (popupParent == null)
|
||||
@@ -38,12 +28,9 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (gameManager != null)
|
||||
{
|
||||
// Unsubscribe from events
|
||||
gameManager.OnScoreChanged -= UpdateScoreDisplay;
|
||||
gameManager.OnPictureTaken -= ShowScorePopup;
|
||||
}
|
||||
// Unsubscribe from events
|
||||
DivingGameManager.Instance.OnScoreChanged -= UpdateScoreDisplay;
|
||||
DivingGameManager.Instance.OnPictureTaken -= ShowScorePopup;
|
||||
}
|
||||
|
||||
private void UpdateScoreDisplay(int score)
|
||||
|
||||
@@ -10,22 +10,29 @@ namespace Minigames.DivingForPictures
|
||||
[SerializeField] private CircleCollider2D detectionCollider;
|
||||
|
||||
private bool pictureAlreadyTaken = false;
|
||||
private Camera mainCamera;
|
||||
private bool photoSequenceInProgress = false;
|
||||
private UnityEngine.Camera mainCamera;
|
||||
|
||||
// Track if player is in detection range
|
||||
private bool playerInDetectionRange = false;
|
||||
|
||||
// Events
|
||||
public event Action<Monster> OnPictureTaken;
|
||||
public event Action<Monster> OnMonsterSpawned;
|
||||
public event Action<Monster> OnMonsterDespawned;
|
||||
public event Action<Monster> OnPlayerEnterDetectionRange;
|
||||
public event Action<Monster> OnPlayerExitDetectionRange;
|
||||
|
||||
// Properties
|
||||
public float PictureRadius => detectionCollider != null ? detectionCollider.radius : 0f;
|
||||
public bool IsPhotoSequenceInProgress => photoSequenceInProgress;
|
||||
public bool IsPlayerInDetectionRange => playerInDetectionRange;
|
||||
public bool IsPictureTaken => pictureAlreadyTaken;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (detectionCollider == null)
|
||||
detectionCollider = GetComponent<CircleCollider2D>();
|
||||
|
||||
mainCamera = Camera.main;
|
||||
mainCamera = UnityEngine.Camera.main;
|
||||
|
||||
// Start checking if monster is off-screen
|
||||
StartCoroutine(CheckIfOffScreen());
|
||||
@@ -34,7 +41,7 @@ namespace Minigames.DivingForPictures
|
||||
private void OnEnable()
|
||||
{
|
||||
pictureAlreadyTaken = false;
|
||||
OnMonsterSpawned?.Invoke(this);
|
||||
photoSequenceInProgress = false;
|
||||
}
|
||||
|
||||
private IEnumerator CheckIfOffScreen()
|
||||
@@ -56,7 +63,7 @@ namespace Minigames.DivingForPictures
|
||||
private bool IsVisibleToCamera()
|
||||
{
|
||||
if (mainCamera == null)
|
||||
mainCamera = Camera.main;
|
||||
mainCamera = UnityEngine.Camera.main;
|
||||
|
||||
if (mainCamera == null)
|
||||
return false;
|
||||
@@ -81,18 +88,67 @@ namespace Minigames.DivingForPictures
|
||||
// Check if it's the player
|
||||
if (other.CompareTag("Player") && !pictureAlreadyTaken)
|
||||
{
|
||||
TakePicture();
|
||||
playerInDetectionRange = true;
|
||||
// Fire the event so the game manager can display the viewfinder without pausing
|
||||
OnPlayerEnterDetectionRange?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a picture is taken of this monster
|
||||
public void TakePicture()
|
||||
private void OnTriggerExit2D(Collider2D other)
|
||||
{
|
||||
// Check if it's the player
|
||||
if (other.CompareTag("Player"))
|
||||
{
|
||||
playerInDetectionRange = false;
|
||||
// Fire the event so the game manager can hide the viewfinder
|
||||
OnPlayerExitDetectionRange?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark this monster as having its photo sequence in progress
|
||||
/// </summary>
|
||||
public void SetPhotoSequenceInProgress(bool inProgress = true)
|
||||
{
|
||||
if (pictureAlreadyTaken) return;
|
||||
photoSequenceInProgress = inProgress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notify that a picture has been taken of this monster
|
||||
/// This is now called by DivingGameManager instead of handling the logic internally
|
||||
/// </summary>
|
||||
public void NotifyPictureTaken()
|
||||
{
|
||||
if (pictureAlreadyTaken) return;
|
||||
|
||||
pictureAlreadyTaken = true;
|
||||
OnPictureTaken?.Invoke(this);
|
||||
photoSequenceInProgress = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Despawn this monster (can be called externally)
|
||||
/// </summary>
|
||||
/// <param name="immediate">Whether to despawn immediately or after a delay</param>
|
||||
public void Despawn(bool immediate = false)
|
||||
{
|
||||
if (immediate)
|
||||
{
|
||||
DespawnMonster();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add small delay before despawning
|
||||
StartCoroutine(DelayedDespawn());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to despawn after a short delay
|
||||
/// </summary>
|
||||
private IEnumerator DelayedDespawn()
|
||||
{
|
||||
yield return new WaitForSeconds(1.0f); // 1-second delay before despawn
|
||||
DespawnMonster();
|
||||
}
|
||||
|
||||
@@ -106,20 +162,7 @@ namespace Minigames.DivingForPictures
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Visualization for the picture radius in editor
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// Get the collider in edit mode
|
||||
if (detectionCollider == null)
|
||||
detectionCollider = GetComponent<CircleCollider2D>();
|
||||
|
||||
if (detectionCollider != null)
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(transform.position, detectionCollider.radius / 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Update collider radius in editor
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Private fields
|
||||
private Collider2D _collider;
|
||||
private Camera _mainCamera;
|
||||
private UnityEngine.Camera _mainCamera;
|
||||
private float _screenTop;
|
||||
private float _screenBottom; // Added to track bottom of screen
|
||||
private Coroutine _movementCoroutine;
|
||||
@@ -78,7 +78,7 @@ namespace Minigames.DivingForPictures
|
||||
Debug.LogError($"[FloatingObstacle] No Collider2D found on {gameObject.name}!");
|
||||
}
|
||||
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
|
||||
// Get settings
|
||||
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
@@ -153,7 +153,7 @@ namespace Minigames.DivingForPictures
|
||||
/// <summary>
|
||||
/// Resume this obstacle's movement and behavior
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
public void DoResume()
|
||||
{
|
||||
if (!_isPaused) return; // Already running
|
||||
|
||||
@@ -168,6 +168,9 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
private void StartObstacleCoroutines()
|
||||
{
|
||||
if (!isActiveAndEnabled)
|
||||
return;
|
||||
|
||||
if (enableMovement && _movementCoroutine == null)
|
||||
{
|
||||
_movementCoroutine = StartCoroutine(MovementCoroutine());
|
||||
@@ -281,7 +284,7 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
if (_mainCamera == null) return;
|
||||
}
|
||||
|
||||
@@ -357,7 +360,7 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
// Reset all state first
|
||||
_screenTop = 0f; // Reset cached screen bounds
|
||||
_mainCamera = Camera.main; // Refresh camera reference
|
||||
_mainCamera = UnityEngine.Camera.main; // Refresh camera reference
|
||||
|
||||
// Re-enable the collider for reuse
|
||||
if (_collider != null)
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Private fields
|
||||
private ObstaclePool _obstaclePool;
|
||||
private Camera _mainCamera;
|
||||
private UnityEngine.Camera _mainCamera;
|
||||
private float _screenBottom;
|
||||
private float _spawnRangeX;
|
||||
private Coroutine _spawnCoroutine;
|
||||
@@ -50,7 +50,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
|
||||
// Get settings from GameManager
|
||||
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
@@ -84,38 +84,23 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Find DivingGameManager and subscribe to its initialization event
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
DivingGameManager.Instance.OnGameInitialized += Initialize;
|
||||
|
||||
// Register with the DivingGameManager for pause/resume events
|
||||
DivingGameManager.Instance.RegisterPausableComponent(this);
|
||||
|
||||
// If game is already initialized, initialize immediately
|
||||
if (DivingGameManager.Instance.GetType().GetField("_isGameInitialized",
|
||||
System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance)?.GetValue(DivingGameManager.Instance) is bool isInitialized && isInitialized)
|
||||
{
|
||||
gameManager.OnGameInitialized += Initialize;
|
||||
|
||||
// Register with the DivingGameManager for pause/resume events
|
||||
gameManager.RegisterPausableComponent(this);
|
||||
|
||||
// If game is already initialized, initialize immediately
|
||||
if (gameManager.GetType().GetField("_isGameInitialized",
|
||||
System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance)?.GetValue(gameManager) is bool isInitialized && isInitialized)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[ObstacleSpawner] DivingGameManager not found. Initializing immediately.");
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Unregister from DivingGameManager
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.UnregisterPausableComponent(this);
|
||||
}
|
||||
DivingGameManager.Instance.UnregisterPausableComponent(this);
|
||||
|
||||
// Clean up any active coroutines
|
||||
StopAllCoroutines();
|
||||
@@ -172,7 +157,7 @@ namespace Minigames.DivingForPictures
|
||||
/// <summary>
|
||||
/// Resumes the spawner and all associated processes
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
public void DoResume()
|
||||
{
|
||||
if (!_isPaused) return; // Already running
|
||||
|
||||
@@ -192,7 +177,7 @@ namespace Minigames.DivingForPictures
|
||||
FloatingObstacle obstacleComponent = obstacle.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
obstacleComponent.Resume();
|
||||
obstacleComponent.DoResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,7 +299,7 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
Debug.LogError("[ObstacleSpawner] No main camera found!");
|
||||
@@ -751,7 +736,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
yield return new WaitForSeconds(checkInterval);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddda7563ddf04d63ae19527266695bd2
|
||||
timeCreated: 1760016807
|
||||
@@ -0,0 +1,776 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using AppleHills.Core.Settings;
|
||||
|
||||
namespace Minigames.DivingForPictures.PictureCamera
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the camera viewfinder visual representation and animation
|
||||
/// Responsible only for visual aspects with no game logic
|
||||
/// </summary>
|
||||
public class CameraViewfinderManager : MonoBehaviour
|
||||
{
|
||||
// Singleton instance
|
||||
private static CameraViewfinderManager _instance;
|
||||
private static bool _isQuitting = false;
|
||||
|
||||
public static CameraViewfinderManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null && Application.isPlaying && !_isQuitting)
|
||||
{
|
||||
_instance = FindAnyObjectByType<CameraViewfinderManager>();
|
||||
if (_instance == null)
|
||||
{
|
||||
var go = new GameObject("CameraViewfinderManager");
|
||||
_instance = go.AddComponent<CameraViewfinderManager>();
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
[Header("References")] [Tooltip("The Canvas to spawn the viewfinder UI on")] [SerializeField]
|
||||
private Canvas targetCanvas;
|
||||
|
||||
// References
|
||||
private GameObject viewfinderInstance;
|
||||
private RectTransform viewfinderRectTransform;
|
||||
private Viewfinder viewfinderComponent;
|
||||
private UnityEngine.Camera mainCamera;
|
||||
|
||||
// Animation state
|
||||
private float animationProgress = 0f;
|
||||
private bool isAnimating = false;
|
||||
private bool isReversePhase = false; // Whether we're in the zoom-out phase
|
||||
private float currentProximity = 0f; // How close we are to the target (0=far, 1=directly over target)
|
||||
private Transform targetTransform;
|
||||
private Coroutine animationCoroutine;
|
||||
|
||||
// Target position and size (calculated once at start)
|
||||
private Vector3 targetScreenPosition;
|
||||
private float targetViewfinderSize;
|
||||
private Vector2 targetAnchoredPosition; // target position in canvas units
|
||||
|
||||
// Store settings
|
||||
private IDivingMinigameSettings settings;
|
||||
|
||||
// Events for progress milestones
|
||||
public event Action<float> OnProgressUpdated; // Continuous progress updates (0-1)
|
||||
public event Action OnAnimationStarted;
|
||||
public event Action OnAnimationCompleted;
|
||||
public event Action OnReverseAnimationStarted; // New event for when zoom-out phase starts
|
||||
public event Action<float> OnProximityUpdated; // New event for proximity updates
|
||||
public event Action<float> OnProgressThresholdReached; // Fires at configured thresholds (25%, 50%, etc.)
|
||||
public event Action OnViewfinderTapped; // Event when viewfinder is tapped during normal display
|
||||
public event Action<float> OnViewfinderTappedDuringAnimation; // Event when viewfinder is tapped during animation (with proximity)
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = this;
|
||||
}
|
||||
else if (_instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
_isQuitting = true;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
mainCamera = UnityEngine.Camera.main;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begin the viewfinder animation targeting a specific transform
|
||||
/// </summary>
|
||||
/// <param name="target">The transform to focus on (usually the monster)</param>
|
||||
public void StartViewfinderAnimation(Transform target)
|
||||
{
|
||||
if (isAnimating)
|
||||
{
|
||||
StopViewfinderAnimation();
|
||||
}
|
||||
|
||||
if (settings.ViewfinderPrefab == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] No viewfinder prefab assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetCanvas == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] No canvas assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
targetTransform = target;
|
||||
|
||||
// Calculate target screen position and size only once at the start
|
||||
CalculateTargetScreenPositionAndSize();
|
||||
|
||||
// Create viewfinder as UI element
|
||||
viewfinderInstance = Instantiate(settings.ViewfinderPrefab, targetCanvas.transform);
|
||||
viewfinderRectTransform = viewfinderInstance.GetComponent<RectTransform>();
|
||||
viewfinderComponent = viewfinderInstance.GetComponent<Viewfinder>();
|
||||
if (viewfinderRectTransform == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] Viewfinder prefab doesn't have a RectTransform component!");
|
||||
Destroy(viewfinderInstance);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize viewfinder with a reference to this manager
|
||||
if (viewfinderComponent != null)
|
||||
{
|
||||
viewfinderComponent.Initialize(this);
|
||||
viewfinderComponent.SetupInputOverride();
|
||||
viewfinderComponent.OnViewfinderTapped += HandleViewfinderTapped;
|
||||
}
|
||||
|
||||
// Reset state
|
||||
animationProgress = 0f;
|
||||
isAnimating = true;
|
||||
|
||||
// Determine canvas width for CanvasScaler-consistent sizing
|
||||
RectTransform canvasRect = targetCanvas.transform as RectTransform;
|
||||
float canvasWidth = canvasRect != null ? canvasRect.rect.width : Screen.width;
|
||||
|
||||
// Initialize viewfinder size and position to full canvas width (square) at center
|
||||
float startSize = canvasWidth;
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(startSize, startSize);
|
||||
viewfinderRectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.anchoredPosition = Vector2.zero;
|
||||
|
||||
// Compute target anchored position in canvas units from previously calculated screen position
|
||||
targetAnchoredPosition = ScreenToAnchoredPosition(targetScreenPosition);
|
||||
|
||||
// Fire starting event
|
||||
OnAnimationStarted?.Invoke();
|
||||
|
||||
// Start animation coroutine
|
||||
animationCoroutine = StartCoroutine(AnimateViewfinder());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the target screen position and size based on monster's sprite bounds
|
||||
/// </summary>
|
||||
private void CalculateTargetScreenPositionAndSize()
|
||||
{
|
||||
if (targetTransform == null || mainCamera == null) return;
|
||||
|
||||
RectTransform canvasRect = targetCanvas != null ? targetCanvas.transform as RectTransform : null;
|
||||
if (canvasRect == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] Target canvas RectTransform not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose UI camera for coordinate conversion
|
||||
Camera uiCamera = null;
|
||||
if (targetCanvas.renderMode == RenderMode.ScreenSpaceCamera ||
|
||||
targetCanvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
uiCamera = targetCanvas.worldCamera;
|
||||
}
|
||||
|
||||
// Get sprite renderer from the monster
|
||||
SpriteRenderer spriteRenderer = targetTransform.GetComponent<SpriteRenderer>();
|
||||
if (spriteRenderer == null || spriteRenderer.sprite == null)
|
||||
{
|
||||
// Fallback to transform position and default size if no sprite renderer
|
||||
targetScreenPosition = mainCamera.WorldToScreenPoint(targetTransform.position);
|
||||
// Convert to anchored UI position
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, targetScreenPosition, uiCamera,
|
||||
out targetAnchoredPosition);
|
||||
|
||||
// Default size: fraction of canvas width (canvas units)
|
||||
float canvasWidth = canvasRect.rect.width;
|
||||
targetViewfinderSize = canvasWidth * 0.25f;
|
||||
|
||||
Debug.LogWarning("[CameraViewfinderManager] No SpriteRenderer found on target, using default size");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate world bounds of the sprite and apply padding
|
||||
Bounds spriteBounds = spriteRenderer.bounds;
|
||||
Vector3 paddedSize = spriteBounds.size * settings.PaddingFactor;
|
||||
Bounds paddedBounds = new Bounds(spriteBounds.center, paddedSize);
|
||||
|
||||
// Convert bounds corners to screen space
|
||||
Vector3[] worldCorners = new Vector3[4];
|
||||
worldCorners[0] = new Vector3(paddedBounds.min.x, paddedBounds.min.y, paddedBounds.center.z); // BL
|
||||
worldCorners[1] = new Vector3(paddedBounds.max.x, paddedBounds.min.y, paddedBounds.center.z); // BR
|
||||
worldCorners[2] = new Vector3(paddedBounds.min.x, paddedBounds.max.y, paddedBounds.center.z); // TL
|
||||
worldCorners[3] = new Vector3(paddedBounds.max.x, paddedBounds.max.y, paddedBounds.center.z); // TR
|
||||
|
||||
// Convert screen-space corners to canvas local points (canvas units)
|
||||
bool anyFailed = false;
|
||||
Vector2[] localCorners = new Vector2[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
Vector3 screenPos = mainCamera.WorldToScreenPoint(worldCorners[i]);
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, screenPos, uiCamera,
|
||||
out localCorners[i]))
|
||||
{
|
||||
anyFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: if conversion failed for some reason, keep original behavior (pixels -> clamp -> convert later)
|
||||
if (anyFailed)
|
||||
{
|
||||
Vector2 minScreen = new Vector2(float.MaxValue, float.MaxValue);
|
||||
Vector2 maxScreen = new Vector2(float.MinValue, float.MinValue);
|
||||
foreach (Vector3 corner in worldCorners)
|
||||
{
|
||||
Vector3 sp = mainCamera.WorldToScreenPoint(corner);
|
||||
minScreen.x = Mathf.Min(minScreen.x, sp.x);
|
||||
minScreen.y = Mathf.Min(minScreen.y, sp.y);
|
||||
maxScreen.x = Mathf.Max(maxScreen.x, sp.x);
|
||||
maxScreen.y = Mathf.Max(maxScreen.y, sp.y);
|
||||
}
|
||||
|
||||
float widthPx = maxScreen.x - minScreen.x;
|
||||
float heightPx = maxScreen.y - minScreen.y;
|
||||
float canvasWidth = canvasRect.rect.width;
|
||||
float scaleX = Screen.width > 0 ? canvasWidth / Screen.width : 1f;
|
||||
targetViewfinderSize = Mathf.Max(widthPx, heightPx) * scaleX; // approximate conversion
|
||||
float minCanvas = canvasWidth * settings.MinSizePercent;
|
||||
float maxCanvas = canvasWidth * settings.MaxSizePercent;
|
||||
targetViewfinderSize = Mathf.Clamp(targetViewfinderSize, minCanvas, maxCanvas);
|
||||
|
||||
// Target position
|
||||
targetScreenPosition = mainCamera.WorldToScreenPoint(spriteBounds.center);
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, targetScreenPosition, uiCamera,
|
||||
out targetAnchoredPosition);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute width/height in canvas units from local corners
|
||||
float minX = float.MaxValue, minY = float.MaxValue, maxX = float.MinValue, maxY = float.MinValue;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
minX = Mathf.Min(minX, localCorners[i].x);
|
||||
minY = Mathf.Min(minY, localCorners[i].y);
|
||||
maxX = Mathf.Max(maxX, localCorners[i].x);
|
||||
maxY = Mathf.Max(maxY, localCorners[i].y);
|
||||
}
|
||||
|
||||
float widthCanvas = Mathf.Max(0f, maxX - minX);
|
||||
float heightCanvas = Mathf.Max(0f, maxY - minY);
|
||||
float canvasSquare = Mathf.Max(widthCanvas, heightCanvas);
|
||||
|
||||
// Clamp using canvas width
|
||||
float canvasW = canvasRect.rect.width;
|
||||
float minSizeCanvas = canvasW * settings.MinSizePercent;
|
||||
float maxSizeCanvas = canvasW * settings.MaxSizePercent;
|
||||
targetViewfinderSize = Mathf.Clamp(canvasSquare, minSizeCanvas, maxSizeCanvas);
|
||||
|
||||
// Target position in both screen and canvas units
|
||||
targetScreenPosition = mainCamera.WorldToScreenPoint(spriteBounds.center);
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, targetScreenPosition, uiCamera,
|
||||
out targetAnchoredPosition);
|
||||
|
||||
Debug.Log(
|
||||
$"[CameraViewfinderManager] Target size (canvas): {targetViewfinderSize}, target anchored: {targetAnchoredPosition}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the current viewfinder animation and clean up
|
||||
/// </summary>
|
||||
public void StopViewfinderAnimation()
|
||||
{
|
||||
if (animationCoroutine != null)
|
||||
{
|
||||
StopCoroutine(animationCoroutine);
|
||||
animationCoroutine = null;
|
||||
}
|
||||
|
||||
if (viewfinderInstance != null)
|
||||
{
|
||||
Destroy(viewfinderInstance);
|
||||
viewfinderInstance = null;
|
||||
}
|
||||
|
||||
isAnimating = false;
|
||||
targetTransform = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles the viewfinder animation
|
||||
/// </summary>
|
||||
private IEnumerator AnimateViewfinder()
|
||||
{
|
||||
RectTransform canvasRect = targetCanvas.transform as RectTransform;
|
||||
float canvasWidth = canvasRect != null ? canvasRect.rect.width : Screen.width;
|
||||
|
||||
// IMPORTANT: We're animating from full canvas width DOWN to the target size
|
||||
float startSize = canvasWidth; // Always start with full canvas width
|
||||
float endSize = targetViewfinderSize; // End at the calculated target size
|
||||
|
||||
float elapsedTime = 0f;
|
||||
bool[] thresholdTriggered = new bool[settings.ViewfinderProgressThresholds.Length];
|
||||
|
||||
// Debug the actual values
|
||||
Debug.Log($"[CameraViewfinderManager] Animation starting: startSize={startSize}, endSize={endSize}, " +
|
||||
$"current sizeDelta={viewfinderRectTransform.sizeDelta}");
|
||||
|
||||
// Verify the initial size is set correctly
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(startSize, startSize);
|
||||
|
||||
while (elapsedTime < settings.ViewfinderShrinkDuration)
|
||||
{
|
||||
if (targetTransform == null)
|
||||
{
|
||||
StopViewfinderAnimation();
|
||||
yield break;
|
||||
}
|
||||
|
||||
elapsedTime += Time.unscaledDeltaTime;
|
||||
animationProgress = Mathf.Clamp01(elapsedTime / settings.ViewfinderShrinkDuration);
|
||||
|
||||
// The curve value might be inverted - ensure we're shrinking not growing
|
||||
float curveValue = settings.ViewfinderShrinkCurve.Evaluate(animationProgress);
|
||||
|
||||
// FIX: Ensure we're interpolating from large (start) to small (end)
|
||||
// This is critical - we must ensure the start and end are in the right order
|
||||
float currentSize = Mathf.Lerp(startSize, endSize, curveValue);
|
||||
|
||||
// Additional check to ensure size is actually shrinking
|
||||
if (startSize > endSize && currentSize > startSize)
|
||||
{
|
||||
Debug.LogWarning($"[CameraViewfinderManager] Animation curve producing wrong direction! " +
|
||||
$"progress={animationProgress:F2}, curveValue={curveValue:F2}");
|
||||
// Force correct behavior
|
||||
curveValue = animationProgress; // Override with linear interpolation
|
||||
currentSize = Mathf.Lerp(startSize, endSize, curveValue);
|
||||
}
|
||||
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(currentSize, currentSize);
|
||||
|
||||
// Move in UI space towards the anchored target position
|
||||
Vector2 currentPos = viewfinderRectTransform.anchoredPosition;
|
||||
Vector2 newPos = Vector2.Lerp(currentPos, targetAnchoredPosition,
|
||||
settings.ViewfinderMoveSpeed * Time.unscaledDeltaTime);
|
||||
viewfinderRectTransform.anchoredPosition = newPos;
|
||||
|
||||
// Log the animation state occasionally for debugging
|
||||
if (animationProgress % 0.25f <= 0.01f || animationProgress >= 0.99f)
|
||||
{
|
||||
Debug.Log($"[CameraViewfinderManager] Animation progress: {animationProgress:F2}, " +
|
||||
$"curveValue={curveValue:F2}, currentSize={currentSize:F2}, currentPos={newPos}");
|
||||
}
|
||||
|
||||
for (int i = 0; i < settings.ViewfinderProgressThresholds.Length; i++)
|
||||
{
|
||||
if (!thresholdTriggered[i] && animationProgress >= settings.ViewfinderProgressThresholds[i])
|
||||
{
|
||||
thresholdTriggered[i] = true;
|
||||
OnProgressThresholdReached?.Invoke(settings.ViewfinderProgressThresholds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
OnProgressUpdated?.Invoke(animationProgress);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Ensure we reach the exact end size
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(endSize, endSize);
|
||||
|
||||
animationProgress = 1f;
|
||||
OnProgressUpdated?.Invoke(animationProgress);
|
||||
OnAnimationCompleted?.Invoke();
|
||||
|
||||
// Log final state to confirm we reached the target size
|
||||
Debug.Log(
|
||||
$"[CameraViewfinderManager] Animation completed: final size={viewfinderRectTransform.sizeDelta}");
|
||||
|
||||
yield return new WaitForSecondsRealtime(0.5f);
|
||||
StopViewfinderAnimation();
|
||||
}
|
||||
|
||||
// Converts a screen-space point to a RectTransform anchoredPosition for the target canvas
|
||||
private Vector2 ScreenToAnchoredPosition(Vector3 screenPosition)
|
||||
{
|
||||
if (targetCanvas == null)
|
||||
{
|
||||
return new Vector2(screenPosition.x, screenPosition.y);
|
||||
}
|
||||
|
||||
RectTransform canvasRect = targetCanvas.transform as RectTransform;
|
||||
Camera uiCamera = null;
|
||||
if (targetCanvas.renderMode == RenderMode.ScreenSpaceCamera ||
|
||||
targetCanvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
uiCamera = targetCanvas.worldCamera;
|
||||
}
|
||||
|
||||
Vector2 localPoint;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, screenPosition, uiCamera,
|
||||
out localPoint);
|
||||
return localPoint;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
StopViewfinderAnimation();
|
||||
|
||||
if (_instance == this)
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles tap event from the viewfinder and forwards it
|
||||
/// </summary>
|
||||
private void HandleViewfinderTapped()
|
||||
{
|
||||
// Forward the tap event to any listeners
|
||||
OnViewfinderTapped?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the currently displayed viewfinder
|
||||
/// </summary>
|
||||
public void HideViewfinder()
|
||||
{
|
||||
if (viewfinderInstance != null)
|
||||
{
|
||||
// Unsubscribe from the viewfinder's tap event
|
||||
if (viewfinderComponent != null)
|
||||
{
|
||||
viewfinderComponent.OnViewfinderTapped -= HandleViewfinderTapped;
|
||||
}
|
||||
|
||||
Destroy(viewfinderInstance);
|
||||
viewfinderInstance = null;
|
||||
viewfinderComponent = null;
|
||||
viewfinderRectTransform = null;
|
||||
Debug.Log("[CameraViewfinderManager] Hid viewfinder");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the viewfinder in the middle of the screen at full size without animation
|
||||
/// This is the first step in the two-step process, showing the UI without targeting a monster yet
|
||||
/// </summary>
|
||||
public void ShowFullScreenViewfinder()
|
||||
{
|
||||
if (viewfinderInstance != null)
|
||||
{
|
||||
// Already showing a viewfinder, destroy it first
|
||||
Destroy(viewfinderInstance);
|
||||
}
|
||||
|
||||
if (settings.ViewfinderPrefab == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] No viewfinder prefab assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetCanvas == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] No canvas assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
isAnimating = false;
|
||||
|
||||
// Create viewfinder as UI element
|
||||
viewfinderInstance = Instantiate(settings.ViewfinderPrefab, targetCanvas.transform);
|
||||
viewfinderRectTransform = viewfinderInstance.GetComponent<RectTransform>();
|
||||
viewfinderComponent = viewfinderInstance.GetComponent<Viewfinder>();
|
||||
|
||||
if (viewfinderRectTransform == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] Viewfinder prefab doesn't have a RectTransform component!");
|
||||
Destroy(viewfinderInstance);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize viewfinder with a reference to this manager
|
||||
if (viewfinderComponent != null)
|
||||
{
|
||||
viewfinderComponent.Initialize(this);
|
||||
viewfinderComponent.SetupInputOverride();
|
||||
viewfinderComponent.OnViewfinderTapped += HandleViewfinderTapped;
|
||||
|
||||
}
|
||||
|
||||
// Determine canvas width for full-screen sizing
|
||||
RectTransform canvasRect = targetCanvas.transform as RectTransform;
|
||||
float canvasWidth = canvasRect != null ? canvasRect.rect.width : Screen.width;
|
||||
|
||||
// Set up the viewfinder in the center of the screen at full canvas width
|
||||
viewfinderRectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(canvasWidth, canvasWidth);
|
||||
viewfinderRectTransform.anchoredPosition = Vector2.zero;
|
||||
|
||||
Debug.Log($"[CameraViewfinderManager] Showed full-screen viewfinder with size {canvasWidth}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the complete viewfinder animation sequence (zoom in, then zoom out)
|
||||
/// </summary>
|
||||
/// <param name="target">The transform to focus on (usually the monster)</param>
|
||||
public void StartViewfinderSequence(Transform target)
|
||||
{
|
||||
if (isAnimating)
|
||||
{
|
||||
StopViewfinderAnimation();
|
||||
}
|
||||
|
||||
if (settings.ViewfinderPrefab == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] No viewfinder prefab assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetCanvas == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] No canvas assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
targetTransform = target;
|
||||
isReversePhase = false;
|
||||
currentProximity = 0f;
|
||||
|
||||
// Calculate target screen position and size based on monster's sprite bounds
|
||||
CalculateTargetScreenPositionAndSize();
|
||||
|
||||
// Reuse existing viewfinder instance if it exists
|
||||
if (viewfinderInstance == null)
|
||||
{
|
||||
// Create viewfinder as UI element if it doesn't exist yet
|
||||
viewfinderInstance = Instantiate(settings.ViewfinderPrefab, targetCanvas.transform);
|
||||
viewfinderRectTransform = viewfinderInstance.GetComponent<RectTransform>();
|
||||
viewfinderComponent = viewfinderInstance.GetComponent<Viewfinder>();
|
||||
|
||||
if (viewfinderRectTransform == null)
|
||||
{
|
||||
Debug.LogError("[CameraViewfinderManager] Viewfinder prefab doesn't have a RectTransform component!");
|
||||
Destroy(viewfinderInstance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unsubscribe from any existing event to prevent multiple subscriptions
|
||||
if (viewfinderComponent != null)
|
||||
{
|
||||
viewfinderComponent.OnViewfinderTapped -= HandleViewfinderTapped;
|
||||
viewfinderComponent.OnViewfinderTapped -= HandleViewfinderTappedDuringAnimation;
|
||||
}
|
||||
|
||||
// Subscribe to the viewfinder's tap event for interrupting the animation
|
||||
viewfinderComponent.OnViewfinderTapped += HandleViewfinderTappedDuringAnimation;
|
||||
}
|
||||
|
||||
// Reset state
|
||||
animationProgress = 0f;
|
||||
isAnimating = true;
|
||||
|
||||
// Determine canvas width for CanvasScaler-consistent sizing
|
||||
RectTransform canvasRect = targetCanvas.transform as RectTransform;
|
||||
float canvasWidth = canvasRect != null ? canvasRect.rect.width : Screen.width;
|
||||
|
||||
// Initialize viewfinder size and position to full canvas width (square) at center
|
||||
float startSize = canvasWidth;
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(startSize, startSize);
|
||||
viewfinderRectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||||
viewfinderRectTransform.anchoredPosition = Vector2.zero;
|
||||
|
||||
// Compute target anchored position in canvas units from previously calculated screen position
|
||||
targetAnchoredPosition = ScreenToAnchoredPosition(targetScreenPosition);
|
||||
|
||||
// Fire starting event
|
||||
OnAnimationStarted?.Invoke();
|
||||
|
||||
// Start sequence coroutine
|
||||
animationCoroutine = StartCoroutine(AnimateViewfinderSequence());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles the complete viewfinder animation sequence (zoom in, then zoom out)
|
||||
/// </summary>
|
||||
private IEnumerator AnimateViewfinderSequence()
|
||||
{
|
||||
RectTransform canvasRect = targetCanvas.transform as RectTransform;
|
||||
float canvasWidth = canvasRect != null ? canvasRect.rect.width : Screen.width;
|
||||
|
||||
// Phase 1: Zoom in (from full screen to monster)
|
||||
float startSize = canvasWidth; // Always start with full canvas width
|
||||
float endSize = targetViewfinderSize; // End at the calculated target size
|
||||
Vector2 startPos = Vector2.zero; // Center of screen
|
||||
Vector2 endPos = targetAnchoredPosition; // Target position
|
||||
|
||||
float elapsedTime = 0f;
|
||||
bool[] thresholdTriggered = new bool[settings.ViewfinderProgressThresholds.Length];
|
||||
|
||||
// Debug the actual values
|
||||
Debug.Log($"[CameraViewfinderManager] Animation sequence starting: startSize={startSize}, endSize={endSize}");
|
||||
|
||||
// Verify the initial size is set correctly
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(startSize, startSize);
|
||||
|
||||
// Phase 1: Zoom In
|
||||
while (elapsedTime < settings.ViewfinderShrinkDuration && !isReversePhase)
|
||||
{
|
||||
if (targetTransform == null)
|
||||
{
|
||||
StopViewfinderAnimation();
|
||||
yield break;
|
||||
}
|
||||
|
||||
elapsedTime += Time.unscaledDeltaTime;
|
||||
animationProgress = Mathf.Clamp01(elapsedTime / settings.ViewfinderShrinkDuration);
|
||||
|
||||
// Calculate proximity - it increases as we get closer to the target
|
||||
currentProximity = animationProgress;
|
||||
OnProximityUpdated?.Invoke(currentProximity);
|
||||
|
||||
// The curve value might be inverted - ensure we're shrinking not growing
|
||||
float curveValue = settings.ViewfinderShrinkCurve.Evaluate(animationProgress);
|
||||
|
||||
// Interpolate size
|
||||
float currentSize = Mathf.Lerp(startSize, endSize, curveValue);
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(currentSize, currentSize);
|
||||
|
||||
// Interpolate position
|
||||
Vector2 currentPos = viewfinderRectTransform.anchoredPosition;
|
||||
Vector2 newPos = Vector2.Lerp(startPos, endPos, curveValue);
|
||||
viewfinderRectTransform.anchoredPosition = newPos;
|
||||
|
||||
// Log the animation state occasionally for debugging
|
||||
if (animationProgress % 0.25f <= 0.01f || animationProgress >= 0.99f)
|
||||
{
|
||||
Debug.Log($"[CameraViewfinderManager] Animation progress: {animationProgress:F2}, " +
|
||||
$"curveValue={curveValue:F2}, currentSize={currentSize:F2}, currentPos={newPos}, proximity={currentProximity:F2}");
|
||||
}
|
||||
|
||||
for (int i = 0; i < settings.ViewfinderProgressThresholds.Length; i++)
|
||||
{
|
||||
if (!thresholdTriggered[i] && animationProgress >= settings.ViewfinderProgressThresholds[i])
|
||||
{
|
||||
thresholdTriggered[i] = true;
|
||||
OnProgressThresholdReached?.Invoke(settings.ViewfinderProgressThresholds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
OnProgressUpdated?.Invoke(animationProgress);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Ensure we reach the exact end size and position for phase 1
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(endSize, endSize);
|
||||
viewfinderRectTransform.anchoredPosition = endPos;
|
||||
currentProximity = 1.0f; // At target
|
||||
OnProximityUpdated?.Invoke(currentProximity);
|
||||
|
||||
// Brief pause at target
|
||||
yield return new WaitForSecondsRealtime(0.2f);
|
||||
|
||||
// Start phase 2 - reverse animation
|
||||
isReversePhase = true;
|
||||
OnReverseAnimationStarted?.Invoke();
|
||||
|
||||
// Reset for reverse phase
|
||||
elapsedTime = 0f;
|
||||
animationProgress = 0f;
|
||||
|
||||
// Phase 2: Zoom Out (from monster back to full screen)
|
||||
while (elapsedTime < settings.ViewfinderShrinkDuration && isReversePhase)
|
||||
{
|
||||
if (targetTransform == null)
|
||||
{
|
||||
StopViewfinderAnimation();
|
||||
yield break;
|
||||
}
|
||||
|
||||
elapsedTime += Time.unscaledDeltaTime;
|
||||
animationProgress = Mathf.Clamp01(elapsedTime / settings.ViewfinderShrinkDuration);
|
||||
|
||||
// Calculate proximity - it decreases as we move away from target
|
||||
currentProximity = 1.0f - animationProgress;
|
||||
OnProximityUpdated?.Invoke(currentProximity);
|
||||
|
||||
// The curve value for zooming out
|
||||
float curveValue = settings.ViewfinderShrinkCurve.Evaluate(animationProgress);
|
||||
|
||||
// Interpolate size (now growing)
|
||||
float currentSize = Mathf.Lerp(endSize, startSize, curveValue);
|
||||
viewfinderRectTransform.sizeDelta = new Vector2(currentSize, currentSize);
|
||||
|
||||
// Interpolate position (now returning to center)
|
||||
Vector2 currentPos = viewfinderRectTransform.anchoredPosition;
|
||||
Vector2 newPos = Vector2.Lerp(endPos, startPos, curveValue);
|
||||
viewfinderRectTransform.anchoredPosition = newPos;
|
||||
|
||||
// Log the animation state occasionally for debugging
|
||||
if (animationProgress % 0.25f <= 0.01f || animationProgress >= 0.99f)
|
||||
{
|
||||
Debug.Log($"[CameraViewfinderManager] Reverse animation progress: {animationProgress:F2}, " +
|
||||
$"curveValue={curveValue:F2}, currentSize={currentSize:F2}, currentPos={newPos}, proximity={currentProximity:F2}");
|
||||
}
|
||||
|
||||
OnProgressUpdated?.Invoke(animationProgress);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Animation completed (both phases)
|
||||
HandleViewfinderTappedDuringAnimation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle tap events during animation (for capturing at current proximity)
|
||||
/// </summary>
|
||||
private void HandleViewfinderTappedDuringAnimation()
|
||||
{
|
||||
if (isAnimating)
|
||||
{
|
||||
// Fire event with current proximity value for scoring
|
||||
OnViewfinderTappedDuringAnimation?.Invoke(currentProximity);
|
||||
|
||||
// Complete the animation immediately
|
||||
if (animationCoroutine != null)
|
||||
{
|
||||
StopCoroutine(animationCoroutine);
|
||||
animationCoroutine = null;
|
||||
}
|
||||
|
||||
// Fire completed event
|
||||
OnAnimationCompleted?.Invoke();
|
||||
isAnimating = false;
|
||||
isReversePhase = false;
|
||||
|
||||
// Hide the viewfinder on the second tap
|
||||
viewfinderComponent.RemoveInputOverride();
|
||||
HideViewfinder();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular tap handling when not animating
|
||||
HandleViewfinderTapped();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5a6d17776b84719a2599fdc35c7076d
|
||||
timeCreated: 1760016840
|
||||
@@ -0,0 +1,119 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using Input;
|
||||
|
||||
namespace Minigames.DivingForPictures.PictureCamera
|
||||
{
|
||||
/// <summary>
|
||||
/// Attached to the viewfinder UI prefab. Handles tap detection and other viewfinder-specific operations.
|
||||
/// </summary>
|
||||
public class Viewfinder : MonoBehaviour, ITouchInputConsumer
|
||||
{
|
||||
[SerializeField]
|
||||
private Image[] viewfinderImages; // Array of Image components to tint based on proximity
|
||||
|
||||
// Events
|
||||
public event System.Action OnViewfinderTapped;
|
||||
|
||||
// State
|
||||
private bool isActive = true;
|
||||
private RectTransform _rectTransform;
|
||||
private CameraViewfinderManager _viewfinderManager;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the viewfinder with a reference to its manager
|
||||
/// </summary>
|
||||
/// <param name="manager">Reference to the CameraViewfinderManager</param>
|
||||
public void Initialize(CameraViewfinderManager manager)
|
||||
{
|
||||
_viewfinderManager = manager;
|
||||
|
||||
if (_viewfinderManager != null)
|
||||
{
|
||||
_viewfinderManager.OnProximityUpdated += UpdateProximityColor;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// Unsubscribe from events when disabled
|
||||
if (_viewfinderManager != null)
|
||||
{
|
||||
_viewfinderManager.OnProximityUpdated -= UpdateProximityColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the color of all viewfinder images based on proximity value
|
||||
/// </summary>
|
||||
/// <param name="proximity">Proximity value between 0 and 1 (0=far, 1=close)</param>
|
||||
private void UpdateProximityColor(float proximity)
|
||||
{
|
||||
// Lerp between green (close) and red (far)
|
||||
Color tintColor = Color.Lerp(Color.red, Color.green, proximity);
|
||||
|
||||
// Apply the color to all referenced image components
|
||||
if (viewfinderImages != null && viewfinderImages.Length > 0)
|
||||
{
|
||||
foreach (Image image in viewfinderImages)
|
||||
{
|
||||
if (image != null)
|
||||
{
|
||||
image.color = tintColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable input reception
|
||||
/// </summary>
|
||||
public void SetActive(bool active)
|
||||
{
|
||||
isActive = active;
|
||||
}
|
||||
|
||||
#region ITouchInputConsumer Implementation
|
||||
|
||||
public void SetupInputOverride()
|
||||
{
|
||||
InputManager.Instance.RegisterOverrideConsumer(this);
|
||||
}
|
||||
|
||||
public void RemoveInputOverride()
|
||||
{
|
||||
InputManager.Instance.UnregisterOverrideConsumer(this);
|
||||
}
|
||||
|
||||
public void OnTap(Vector2 position)
|
||||
{
|
||||
if (isActive)
|
||||
{
|
||||
// Fire the tap event that PhotoSequenceController will listen to
|
||||
OnViewfinderTapped?.Invoke();
|
||||
Debug.Log($"[MDPI] Viewfinder OnTap: {position}");
|
||||
}
|
||||
}
|
||||
|
||||
// Required interface methods (no-op implementations)
|
||||
public void OnHoldStart(Vector2 position) { }
|
||||
public void OnHoldMove(Vector2 position) { }
|
||||
public void OnHoldEnd(Vector2 position) { }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Check if a point is within the viewfinder's bounds
|
||||
/// </summary>
|
||||
public bool ContainsPoint(Vector2 screenPoint)
|
||||
{
|
||||
return RectTransformUtility.RectangleContainsScreenPoint(_rectTransform, screenPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b4b818dd8cb454abe85b8edcaece1db
|
||||
timeCreated: 1760032922
|
||||
@@ -42,23 +42,13 @@ namespace Minigames.DivingForPictures
|
||||
_targetFingerX = transform.position.x;
|
||||
_isTouchActive = false;
|
||||
|
||||
// Find DivingGameManager and subscribe to its initialization event
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
DivingGameManager.Instance.OnGameInitialized += Initialize;
|
||||
|
||||
// If game is already initialized, initialize immediately
|
||||
if (DivingGameManager.Instance.GetType().GetField("_isGameInitialized",
|
||||
System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance)?.GetValue(DivingGameManager.Instance) is bool isInitialized && isInitialized)
|
||||
{
|
||||
gameManager.OnGameInitialized += Initialize;
|
||||
|
||||
// If game is already initialized, initialize immediately
|
||||
if (gameManager.GetType().GetField("_isGameInitialized",
|
||||
System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance)?.GetValue(gameManager) is bool isInitialized && isInitialized)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[PlayerController] DivingGameManager not found. Initializing immediately.");
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
@@ -79,12 +69,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Unsubscribe from events to prevent memory leaks
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.OnGameInitialized -= Initialize;
|
||||
}
|
||||
DivingGameManager.Instance.OnGameInitialized -= Initialize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,7 +31,7 @@ public class RopeEndPhysicsFollower : MonoBehaviour
|
||||
private Vector2 offset;
|
||||
private Vector3 lastTargetPosition;
|
||||
private bool initialized = false;
|
||||
private bool debugLog = true;
|
||||
private bool debugLog = false;
|
||||
|
||||
// Rope reference to get the actual rope length
|
||||
private Rope attachedRope;
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private int _spawnCounter;
|
||||
private float _speedUpTimer;
|
||||
private Camera _mainCamera;
|
||||
private UnityEngine.Camera _mainCamera;
|
||||
private float _screenBottom;
|
||||
private float _screenTop;
|
||||
private TrenchTilePool _tilePool;
|
||||
@@ -76,7 +76,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
|
||||
// Get settings from GameManager
|
||||
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
@@ -125,38 +125,23 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Find DivingGameManager and subscribe to its initialization event
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
DivingGameManager.Instance.OnGameInitialized += Initialize;
|
||||
|
||||
// Register with the DivingGameManager for pause/resume events
|
||||
DivingGameManager.Instance.RegisterPausableComponent(this);
|
||||
|
||||
// If game is already initialized, initialize immediately
|
||||
if (DivingGameManager.Instance.GetType().GetField("_isGameInitialized",
|
||||
System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance)?.GetValue(DivingGameManager.Instance) is bool isInitialized && isInitialized)
|
||||
{
|
||||
gameManager.OnGameInitialized += Initialize;
|
||||
|
||||
// Register with the DivingGameManager for pause/resume events
|
||||
gameManager.RegisterPausableComponent(this);
|
||||
|
||||
// If game is already initialized, initialize immediately
|
||||
if (gameManager.GetType().GetField("_isGameInitialized",
|
||||
System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance)?.GetValue(gameManager) is bool isInitialized && isInitialized)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[TrenchTileSpawner] DivingGameManager not found. Initializing immediately.");
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Unregister from DivingGameManager
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.UnregisterPausableComponent(this);
|
||||
}
|
||||
DivingGameManager.Instance.UnregisterPausableComponent(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -199,7 +184,7 @@ namespace Minigames.DivingForPictures
|
||||
/// <summary>
|
||||
/// Resumes the spawner and all associated processes
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
public void DoResume()
|
||||
{
|
||||
if (!_isPaused) return; // Already running
|
||||
|
||||
@@ -386,7 +371,7 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_mainCamera = UnityEngine.Camera.main;
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
Debug.LogError("No main camera found!");
|
||||
@@ -904,7 +889,7 @@ namespace Minigames.DivingForPictures
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
// Only try to calculate this if _screenBottom hasn't been set by the game
|
||||
Camera editorCam = Camera.main;
|
||||
UnityEngine.Camera editorCam = UnityEngine.Camera.main;
|
||||
if (editorCam != null)
|
||||
{
|
||||
Vector3 bottom = editorCam.ViewportToWorldPoint(new Vector3(0.5f, 0f, editorCam.nearClipPlane));
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0892ac3d8370a274b8d5847ca51ac046
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using AppleHills.Core.Interfaces;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minigames.DivingForPictures.Utilities
|
||||
{
|
||||
public class BottlePauser : MonoBehaviour, IPausable
|
||||
{
|
||||
[SerializeField] private WobbleBehavior wobbleReference;
|
||||
|
||||
private bool isPaused = false;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
DivingGameManager.Instance.RegisterPausableComponent(this);
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
wobbleReference.enabled = false;
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
public void DoResume()
|
||||
{
|
||||
wobbleReference.enabled = true;
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
public bool IsPaused => isPaused;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf50e6f7c05d5a14296152b8c3fbb923
|
||||
@@ -0,0 +1,34 @@
|
||||
using AppleHills.Core.Interfaces;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minigames.DivingForPictures.Utilities
|
||||
{
|
||||
public class RockPauser : MonoBehaviour, IPausable
|
||||
{
|
||||
[SerializeField] private RockFollower rockReference;
|
||||
[SerializeField] private WobbleBehavior rockWobbleReference;
|
||||
|
||||
private bool isPaused = false;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
DivingGameManager.Instance.RegisterPausableComponent(this);
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
rockReference.enabled = false;
|
||||
rockWobbleReference.enabled = false;
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
public void DoResume()
|
||||
{
|
||||
rockReference.enabled = true;
|
||||
rockWobbleReference.enabled = true;
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
public bool IsPaused => isPaused;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a392c919de2df0340b700309e221b7b1
|
||||
Reference in New Issue
Block a user