Files
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

249 lines
8.7 KiB
C#

// ==============================================
// ObstacleSpawner.cs - Hindernis-Generator
// ==============================================
// Spawnt Hindernisse, Items und Quiz-Trigger
// basierend auf Schwierigkeitsgrad.
using UnityEngine;
namespace BreakpilotDrive
{
public class ObstacleSpawner : MonoBehaviour
{
public static ObstacleSpawner Instance { get; private set; }
[Header("Hindernis-Prefabs")]
[SerializeField] private GameObject[] obstaclePrefabs; // Verschiedene Hindernisse
[SerializeField] private GameObject[] largObstaclePrefabs; // Grosse Hindernisse (2 Spuren)
[Header("Item-Prefabs")]
[SerializeField] private GameObject coinPrefab;
[SerializeField] private GameObject starPrefab;
[SerializeField] private GameObject shieldPrefab;
[Header("Quiz-Trigger-Prefabs")]
[SerializeField] private GameObject bridgePrefab;
[SerializeField] private GameObject treePrefab;
[SerializeField] private GameObject housePrefab;
[Header("Spawn-Einstellungen")]
[SerializeField] private float laneDistance = 3f;
[SerializeField] private float minObstacleSpacing = 10f;
[SerializeField] private float maxObstacleSpacing = 30f;
[Header("Wahrscheinlichkeiten (0-1)")]
[SerializeField] private float obstacleChance = 0.6f;
[SerializeField] private float coinChance = 0.3f;
[SerializeField] private float starChance = 0.05f;
[SerializeField] private float shieldChance = 0.02f;
[SerializeField] private float quizTriggerChance = 0.1f;
// Interner Zustand
private float lastObstacleZ = 0f;
private int quizTriggerCount = 0;
void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
void Start()
{
// Wahrscheinlichkeiten von Difficulty laden
UpdateFromDifficulty();
}
private void UpdateFromDifficulty()
{
if (GameManager.Instance?.DifficultySettings != null)
{
var settings = GameManager.Instance.DifficultySettings;
obstacleChance = settings.obstacle_frequency;
// Power-Up Chance beeinflusst Items
float powerUpChance = settings.power_up_chance;
coinChance = powerUpChance * 0.8f;
starChance = powerUpChance * 0.15f;
shieldChance = powerUpChance * 0.05f;
}
}
// ==============================================
// Spawning auf Segment
// ==============================================
public void SpawnOnSegment(GameObject segment, float segmentStartZ)
{
float segmentLength = 50f; // Sollte mit TrackGenerator uebereinstimmen
float currentZ = segmentStartZ + minObstacleSpacing;
while (currentZ < segmentStartZ + segmentLength - minObstacleSpacing)
{
// Zufaelliger Abstand zum naechsten Objekt
float spacing = Random.Range(minObstacleSpacing, maxObstacleSpacing);
// Was spawnen?
SpawnAtPosition(segment.transform, currentZ);
currentZ += spacing;
}
}
private void SpawnAtPosition(Transform parent, float zPosition)
{
float roll = Random.value;
// Quiz-Trigger (selten, aber wichtig)
if (roll < quizTriggerChance && quizTriggerCount < 3)
{
SpawnQuizTrigger(parent, zPosition);
quizTriggerCount++;
return;
}
// Hindernis
roll = Random.value;
if (roll < obstacleChance)
{
SpawnObstacle(parent, zPosition);
}
// Items (koennen neben Hindernissen sein)
roll = Random.value;
if (roll < coinChance)
{
SpawnCoinRow(parent, zPosition);
}
else if (roll < coinChance + starChance)
{
SpawnItem(starPrefab, parent, zPosition, GetRandomLane());
}
else if (roll < coinChance + starChance + shieldChance)
{
SpawnItem(shieldPrefab, parent, zPosition, GetRandomLane());
}
}
// ==============================================
// Hindernisse
// ==============================================
private void SpawnObstacle(Transform parent, float zPosition)
{
if (obstaclePrefabs == null || obstaclePrefabs.Length == 0) return;
// Zufaelliges Hindernis und Spur waehlen
int prefabIndex = Random.Range(0, obstaclePrefabs.Length);
int lane = GetRandomLane();
// Manchmal grosses Hindernis (blockiert 2 Spuren)
if (largObstaclePrefabs != null && largObstaclePrefabs.Length > 0 && Random.value < 0.2f)
{
prefabIndex = Random.Range(0, largObstaclePrefabs.Length);
SpawnObstacleAtLane(largObstaclePrefabs[prefabIndex], parent, zPosition, lane);
}
else
{
SpawnObstacleAtLane(obstaclePrefabs[prefabIndex], parent, zPosition, lane);
}
}
private void SpawnObstacleAtLane(GameObject prefab, Transform parent, float zPosition, int lane)
{
float xPos = (lane - 1) * laneDistance;
Vector3 position = new Vector3(xPos, 0, zPosition);
GameObject obstacle = Instantiate(prefab, parent);
obstacle.transform.localPosition = position;
obstacle.tag = "Obstacle";
}
// ==============================================
// Items
// ==============================================
private void SpawnItem(GameObject prefab, Transform parent, float zPosition, int lane)
{
if (prefab == null) return;
float xPos = (lane - 1) * laneDistance;
Vector3 position = new Vector3(xPos, 0.5f, zPosition); // Leicht ueber dem Boden
GameObject item = Instantiate(prefab, parent);
item.transform.localPosition = position;
}
private void SpawnCoinRow(Transform parent, float zPosition)
{
if (coinPrefab == null) return;
int lane = GetRandomLane();
int coinCount = Random.Range(3, 6);
for (int i = 0; i < coinCount; i++)
{
SpawnItem(coinPrefab, parent, zPosition + (i * 2f), lane);
}
}
// ==============================================
// Quiz-Trigger
// ==============================================
private void SpawnQuizTrigger(Transform parent, float zPosition)
{
GameObject prefab = GetRandomTriggerPrefab();
if (prefab == null) return;
// Trigger sind meist in der Mitte oder ueber der Strasse
Vector3 position = new Vector3(0, 0, zPosition);
GameObject trigger = Instantiate(prefab, parent);
trigger.transform.localPosition = position;
trigger.tag = "QuizTrigger";
// VisualTrigger-Komponente hinzufuegen falls nicht vorhanden
if (trigger.GetComponent<VisualTrigger>() == null)
{
VisualTrigger vt = trigger.AddComponent<VisualTrigger>();
vt.TriggerType = GetTriggerTypeFromPrefab(prefab);
}
}
private GameObject GetRandomTriggerPrefab()
{
GameObject[] triggers = new GameObject[] { bridgePrefab, treePrefab, housePrefab };
var validTriggers = System.Array.FindAll(triggers, t => t != null);
if (validTriggers.Length == 0) return null;
return validTriggers[Random.Range(0, validTriggers.Length)];
}
private string GetTriggerTypeFromPrefab(GameObject prefab)
{
if (prefab == bridgePrefab) return "bridge";
if (prefab == treePrefab) return "tree";
if (prefab == housePrefab) return "house";
return "unknown";
}
// ==============================================
// Helper
// ==============================================
private int GetRandomLane()
{
return Random.Range(0, 3); // 0, 1, oder 2
}
public void ResetSpawner()
{
lastObstacleZ = 0f;
quizTriggerCount = 0;
UpdateFromDifficulty();
}
}
}