777 lines
34 KiB
C#
777 lines
34 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
}
|