You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ReactRacetrack/Assets/Scripts/HurdleManager.cs

240 lines
7.4 KiB
C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using TMPro;
using DG.Tweening;
public class HurdleManager : MonoBehaviour
{
public PlayerPathRunner playerRunner;
public List<Transform> hurdles = new();
public TMP_Text distanceToHurdleText;
public TMP_Text tapsRequiredText;
public DOTweenPath pathSource;
private bool challengeRunning = false;
private int currentTapCount = 0;
public Transform currentHurdle = null;
private int requiredTaps;
private Coroutine activeChallenge;
private Vector3[] drawPoints;
private float gameStartTime;
private bool challengeCompleted = false;
private bool gameOver = false;
void Start()
{
if (pathSource != null)
drawPoints = pathSource.GetDrawPoints();
gameStartTime = Time.time;
}
void Update()
{
// Stop everything if game is over
if (gameOver) return;
// Wait a bit before starting challenges to let speed stabilize
if (Time.time - gameStartTime < 1f) return;
if (!challengeRunning && hurdles.Count > 0)
{
StartNextChallenge();
}
else if (challengeRunning)
{
CountTaps();
CheckForJump();
}
}
void StartNextChallenge()
{
if (playerRunner == null || drawPoints == null || hurdles.Count == 0) return;
currentHurdle = hurdles[0];
requiredTaps = UnityEngine.Random.Range(2, 8);
currentTapCount = 0;
challengeCompleted = false;
activeChallenge = StartCoroutine(StartTapChallenge());
challengeRunning = true;
}
IEnumerator StartTapChallenge()
{
Debug.Log($"🟢 Challenge started! Tap {requiredTaps} times before reaching the hurdle!");
while (currentHurdle != null && hurdles.Contains(currentHurdle))
{
// Calculate distance in real-time
float remainingDistance = GetRemainingDistance(playerRunner.transform.position, currentHurdle.position);
// Update UI with distance
if (distanceToHurdleText != null)
distanceToHurdleText.text = $"{remainingDistance:F1}m";
if (tapsRequiredText != null)
{
if (challengeCompleted)
tapsRequiredText.text = "✓ Complete!";
else
tapsRequiredText.text = (requiredTaps - currentTapCount).ToString();
}
// Check if challenge is completed
if (currentTapCount >= requiredTaps && !challengeCompleted)
{
Debug.Log("✅ Challenge Completed! Ready to jump!");
challengeCompleted = true;
// Don't remove hurdle or end challenge yet - wait for jump
}
yield return null;
}
}
void CountTaps()
{
if (Input.GetMouseButtonDown(0) || (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began))
{
currentTapCount++;
Debug.Log($"Tap {currentTapCount}/{requiredTaps}");
}
}
void CheckForJump()
{
if (currentHurdle == null || playerRunner.IsJumping()) return;
float remainingDistance = GetRemainingDistance(playerRunner.transform.position, currentHurdle.position);
// When very close to hurdle and challenge is completed
if (remainingDistance < 5f && challengeCompleted)
{
// Trigger jump in the PlayerPathRunner
playerRunner.TriggerJump(() => {
// This callback runs after jump completes
if (currentHurdle != null)
{
hurdles.Remove(currentHurdle);
if (activeChallenge != null)
StopCoroutine(activeChallenge);
EndChallengeUI();
}
});
}
}
void EndChallengeUI()
{
challengeRunning = false;
currentHurdle = null;
if (distanceToHurdleText != null) distanceToHurdleText.text = "";
if (tapsRequiredText != null) tapsRequiredText.text = "";
activeChallenge = null;
}
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Hurdle"))
{
// Check if this is the current hurdle and challenge wasn't completed
if (other.transform == currentHurdle && !challengeCompleted && !playerRunner.IsJumping())
{
Debug.Log("❌ GAME OVER! Failed to complete tap challenge!");
GameOver();
}
}
}
void GameOver()
{
gameOver = true;
// Stop all coroutines
if (activeChallenge != null)
StopCoroutine(activeChallenge);
// Clear UI
EndChallengeUI();
// Stop player movement
playerRunner.enabled = false;
// Show game over UI
if (tapsRequiredText != null)
tapsRequiredText.text = "GAME OVER!";
Debug.Log("💀 GAME OVER - Player failed to clear hurdle!");
// You can add more game over logic here:
// - Show game over screen
// - Play game over sound
// - Show restart button
// - Save score, etc.
}
float GetRemainingDistance(Vector3 playerPos, Vector3 hurdlePos)
{
if (drawPoints == null || drawPoints.Length < 2) return 0f;
// Find closest point indices for player and hurdle
int playerIndex = GetClosestPathSegmentIndex(playerPos);
int hurdleIndex = GetClosestPathSegmentIndex(hurdlePos);
// If player is past the hurdle, return 0
if (playerIndex >= hurdleIndex) return 0f;
float totalDistance = 0f;
// Calculate distance from player to the end of its current segment
Vector3 playerClosestPoint = GetClosestPointOnSegment(drawPoints[playerIndex], drawPoints[playerIndex + 1], playerPos);
totalDistance += Vector3.Distance(playerClosestPoint, drawPoints[playerIndex + 1]);
// Add distances of complete segments between player and hurdle
for (int i = playerIndex + 1; i < hurdleIndex; i++)
{
totalDistance += Vector3.Distance(drawPoints[i], drawPoints[i + 1]);
}
// Add distance from start of hurdle's segment to hurdle position
if (playerIndex < hurdleIndex)
{
Vector3 hurdleClosestPoint = GetClosestPointOnSegment(drawPoints[hurdleIndex], drawPoints[hurdleIndex + 1], hurdlePos);
totalDistance += Vector3.Distance(drawPoints[hurdleIndex], hurdleClosestPoint);
}
return totalDistance;
}
int GetClosestPathSegmentIndex(Vector3 worldPos)
{
float minDist = float.MaxValue;
int closestIndex = 0;
for (int i = 0; i < drawPoints.Length - 1; i++)
{
Vector3 closestPoint = GetClosestPointOnSegment(drawPoints[i], drawPoints[i + 1], worldPos);
float dist = Vector3.Distance(worldPos, closestPoint);
if (dist < minDist)
{
minDist = dist;
closestIndex = i;
}
}
return closestIndex;
}
Vector3 GetClosestPointOnSegment(Vector3 a, Vector3 b, Vector3 point)
{
Vector3 ab = b - a;
float t = Mathf.Clamp01(Vector3.Dot(point - a, ab) / Vector3.Dot(ab, ab));
return a + t * ab;
}
}