// ============================================== // TrackGenerator.cs - Endlose Streckengenerierung // ============================================== // Erzeugt endlose Streckenabschnitte durch // Object-Pooling und Recycling. using System.Collections.Generic; using UnityEngine; namespace BreakpilotDrive { public class TrackGenerator : MonoBehaviour { public static TrackGenerator Instance { get; private set; } [Header("Track Prefabs")] [SerializeField] private GameObject[] trackSegmentPrefabs; // Verschiedene Streckenabschnitte [SerializeField] private GameObject startSegmentPrefab; // Start-Segment [Header("Generierung")] [SerializeField] private float segmentLength = 50f; // Laenge eines Segments [SerializeField] private int visibleSegments = 5; // Anzahl sichtbarer Segmente [SerializeField] private float despawnDistance = -20f; // Wann Segment recycelt wird [Header("Bewegung")] [SerializeField] private float baseSpeed = 10f; // Basis-Geschwindigkeit private float currentSpeed; // Object Pool private List activeSegments = new List(); private Queue segmentPool = new Queue(); // Position private float nextSpawnZ = 0f; void Awake() { if (Instance == null) { Instance = this; } else { Destroy(gameObject); } } void Start() { currentSpeed = baseSpeed; // Geschwindigkeit von Difficulty laden if (GameManager.Instance?.DifficultySettings != null) { currentSpeed = GameManager.Instance.DifficultySettings.lane_speed; } // Initiale Segmente erstellen InitializeTrack(); } void Update() { if (GameManager.Instance?.CurrentState != GameState.Playing) return; // Segmente bewegen MoveSegments(); // Alte Segmente recyceln RecycleSegments(); // Neue Segmente spawnen SpawnSegmentsIfNeeded(); } // ============================================== // Initialisierung // ============================================== private void InitializeTrack() { nextSpawnZ = 0f; // Start-Segment if (startSegmentPrefab != null) { SpawnSegment(startSegmentPrefab); } // Weitere Segmente fuer sichtbaren Bereich for (int i = 0; i < visibleSegments; i++) { SpawnRandomSegment(); } } // ============================================== // Segment-Spawning // ============================================== private void SpawnRandomSegment() { if (trackSegmentPrefabs == null || trackSegmentPrefabs.Length == 0) { Debug.LogWarning("Keine Track-Segment-Prefabs zugewiesen!"); return; } int index = Random.Range(0, trackSegmentPrefabs.Length); SpawnSegment(trackSegmentPrefabs[index]); } private void SpawnSegment(GameObject prefab) { GameObject segment; // Aus Pool holen oder neu erstellen if (segmentPool.Count > 0) { segment = segmentPool.Dequeue(); segment.SetActive(true); } else { segment = Instantiate(prefab, transform); } // Position setzen segment.transform.position = new Vector3(0, 0, nextSpawnZ); nextSpawnZ += segmentLength; activeSegments.Add(segment); // Hindernisse und Items auf Segment spawnen ObstacleSpawner.Instance?.SpawnOnSegment(segment, nextSpawnZ - segmentLength); } private void SpawnSegmentsIfNeeded() { // Pruefe ob wir mehr Segmente brauchen float playerZ = 0; // Spieler ist bei Z=0, Welt bewegt sich float maxVisibleZ = playerZ + (visibleSegments * segmentLength); while (nextSpawnZ < maxVisibleZ) { SpawnRandomSegment(); } } // ============================================== // Bewegung // ============================================== private void MoveSegments() { Vector3 movement = Vector3.back * currentSpeed * Time.deltaTime; foreach (var segment in activeSegments) { segment.transform.position += movement; } // Auch nextSpawnZ anpassen nextSpawnZ -= currentSpeed * Time.deltaTime; } // ============================================== // Recycling // ============================================== private void RecycleSegments() { for (int i = activeSegments.Count - 1; i >= 0; i--) { if (activeSegments[i].transform.position.z < despawnDistance) { RecycleSegment(activeSegments[i]); activeSegments.RemoveAt(i); } } } private void RecycleSegment(GameObject segment) { // Kinder-Objekte (Hindernisse, Items) entfernen foreach (Transform child in segment.transform) { if (child.CompareTag("Obstacle") || child.CompareTag("Coin") || child.CompareTag("Star") || child.CompareTag("QuizTrigger")) { Destroy(child.gameObject); } } // Segment deaktivieren und zurueck in Pool segment.SetActive(false); segmentPool.Enqueue(segment); } // ============================================== // Geschwindigkeit // ============================================== public void SetSpeed(float speed) { currentSpeed = speed; } public float GetSpeed() { return currentSpeed; } public void IncreaseSpeed(float amount) { currentSpeed += amount; } // ============================================== // Reset // ============================================== public void ResetTrack() { // Alle aktiven Segmente recyceln foreach (var segment in activeSegments) { RecycleSegment(segment); } activeSegments.Clear(); // Neu initialisieren nextSpawnZ = 0f; currentSpeed = baseSpeed; InitializeTrack(); } } }