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.
245 lines
7.5 KiB
C#
245 lines
7.5 KiB
C#
1 month ago
|
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 CheckForJump()
|
||
|
{
|
||
|
if (currentHurdle == null) return;
|
||
|
|
||
|
float remainingDistance = GetRemainingDistance(playerRunner.transform.position, currentHurdle.position);
|
||
|
|
||
|
// When very close to hurdle
|
||
|
if (remainingDistance < 1f && challengeCompleted)
|
||
|
{
|
||
|
// Trigger jump
|
||
|
TriggerJump();
|
||
|
|
||
|
// Remove hurdle and end challenge after jump
|
||
|
hurdles.Remove(currentHurdle);
|
||
|
if (activeChallenge != null)
|
||
|
StopCoroutine(activeChallenge);
|
||
|
EndChallengeUI();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TriggerJump()
|
||
|
{
|
||
|
Debug.Log("🦘 JUMP! Player successfully jumped over the hurdle!");
|
||
|
GetComponentInChildren<Animator>().SetTrigger("Jump");
|
||
|
}
|
||
|
|
||
|
void EndChallengeUI()
|
||
|
{
|
||
|
challengeRunning = false;
|
||
|
currentHurdle = null;
|
||
|
if (distanceToHurdleText != null) distanceToHurdleText.text = "";
|
||
|
if (tapsRequiredText != null) tapsRequiredText.text = "";
|
||
|
activeChallenge = null;
|
||
|
}
|
||
|
|
||
|
void CountTaps()
|
||
|
{
|
||
|
if (Input.GetMouseButtonDown(0) || (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began))
|
||
|
{
|
||
|
currentTapCount++;
|
||
|
Debug.Log($"Tap {currentTapCount}/{requiredTaps}");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
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!");
|
||
|
GetComponentInChildren<Animator>().enabled = false;
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
// Removed the old IsBetween method as it's no longer needed
|
||
|
}
|