using UnityEngine; using AppleHills.Core.Settings; using Input; namespace Minigames.DivingForPictures { /// /// Handles endless descender movement in response to tap and hold input events. /// Moves the character horizontally to follow the finger or tap position. /// public class PlayerController : MonoBehaviour, ITouchInputConsumer { // Settings reference private IDivingMinigameSettings _settings; private float _targetFingerX; private bool _isTouchActive; private float _originY; // Tap impulse system variables private float _tapImpulseStrength = 0f; private float _tapDirection = 0f; // Initialization flag private bool _isInitialized = false; void Awake() { _originY = transform.position.y; // Get settings from GameManager _settings = GameManager.GetSettingsObject(); if (_settings == null) { Debug.LogError("[PlayerController] Failed to load diving minigame settings!"); } } void Start() { // Initialize target to current position _targetFingerX = transform.position.x; _isTouchActive = false; // Find DivingGameManager and subscribe to its initialization event DivingGameManager gameManager = FindFirstObjectByType(); if (gameManager != null) { 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(); } } /// /// Initializes the player controller when triggered by DivingGameManager /// private void Initialize() { if (_isInitialized) return; // Register as default consumer for input InputManager.Instance?.SetDefaultConsumer(this); _isInitialized = true; Debug.Log("[PlayerController] Initialized"); } private void OnDestroy() { // Unsubscribe from events to prevent memory leaks DivingGameManager gameManager = FindFirstObjectByType(); if (gameManager != null) { gameManager.OnGameInitialized -= Initialize; } } /// /// Handles tap input. Applies an impulse in the tapped direction. /// public void OnTap(Vector2 worldPosition) { // Debug.Log($"[EndlessDescenderController] OnTap at {worldPosition}"); float targetX = Mathf.Clamp(worldPosition.x, _settings.ClampXMin, _settings.ClampXMax); // Calculate tap direction (+1 for right, -1 for left) _tapDirection = Mathf.Sign(targetX - transform.position.x); // Set impulse strength to full _tapImpulseStrength = 1.0f; // Store target X for animation purposes _targetFingerX = targetX; // Do not set _isTouchActive for taps anymore // _isTouchActive = true; - Removed to prevent continuous movement } /// /// Handles the start of a hold input. Begins tracking the finger. /// public void OnHoldStart(Vector2 worldPosition) { // Debug.Log($"[EndlessDescenderController] OnHoldStart at {worldPosition}"); _targetFingerX = Mathf.Clamp(worldPosition.x, _settings.ClampXMin, _settings.ClampXMax); _isTouchActive = true; } /// /// Handles hold move input. Updates the target X position as the finger moves. /// public void OnHoldMove(Vector2 worldPosition) { // Debug.Log($"[EndlessDescenderController] OnHoldMove at {worldPosition}"); _targetFingerX = Mathf.Clamp(worldPosition.x, _settings.ClampXMin, _settings.ClampXMax); } /// /// Handles the end of a hold input. Stops tracking. /// public void OnHoldEnd(Vector2 worldPosition) { // Debug.Log($"[EndlessDescenderController] OnHoldEnd at {worldPosition}"); _isTouchActive = false; } void Update() { // Handle hold movement if (_isTouchActive) { float currentX = transform.position.x; float lerpSpeed = _settings.LerpSpeed; float maxOffset = _settings.MaxOffset; float exponent = _settings.SpeedExponent; float targetX = _targetFingerX; float offset = targetX - currentX; offset = Mathf.Clamp(offset, -maxOffset, maxOffset); float absOffset = Mathf.Abs(offset); float t = Mathf.Pow(absOffset / maxOffset, exponent); // Non-linear drop-off float moveStep = Mathf.Sign(offset) * maxOffset * t * Time.deltaTime * lerpSpeed; // Prevent overshooting moveStep = Mathf.Clamp(moveStep, -absOffset, absOffset); float newX = currentX + moveStep; newX = Mathf.Clamp(newX, _settings.ClampXMin, _settings.ClampXMax); UpdatePosition(newX); } // Handle tap impulse movement else if (_tapImpulseStrength > 0) { float currentX = transform.position.x; float maxOffset = _settings.MaxOffset; float lerpSpeed = _settings.LerpSpeed; // Calculate move distance based on impulse strength float moveDistance = maxOffset * _tapImpulseStrength * Time.deltaTime * lerpSpeed; // Limit total movement from single tap moveDistance = Mathf.Min(moveDistance, _settings.TapMaxDistance * _tapImpulseStrength); // Apply movement in tap direction float newX = currentX + (moveDistance * _tapDirection); newX = Mathf.Clamp(newX, _settings.ClampXMin, _settings.ClampXMax); // Reduce impulse strength over time _tapImpulseStrength -= Time.deltaTime * _settings.TapDecelerationRate; if (_tapImpulseStrength < 0.01f) { _tapImpulseStrength = 0f; } UpdatePosition(newX); } } /// /// Updates the player's position with the given X coordinate /// private void UpdatePosition(float newX) { float newY = _originY; // Add vertical offset from WobbleBehavior if present WobbleBehavior wobble = GetComponent(); if (wobble != null) { newY += wobble.VerticalOffset; } transform.position = new Vector3(newX, newY, transform.position.z); } } }