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.
240 lines
7.4 KiB
C#
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;
|
|
}
|
|
} |