using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UI; public class DefenseWaveLevelController : MonoBehaviour { [System.Serializable] private class LevelWaveFile { public string levelId; public string levelName; public WaveEntry[] waves; } [System.Serializable] private class WaveEntry { public int waveIndex; public float restBeforeWave = 2f; public float spawnInterval = 1f; public string[] enemyIds; public int[] counts; public float[] spawnX; } [System.Serializable] public class EnemyPrefabEntry { public string id; public GameObject prefab; } [Header("关卡 JSON")] public TextAsset levelJson; [Header("JSON enemyId 对应 Prefab")] public EnemyPrefabEntry[] enemyPrefabs; [Header("自动模拟")] public bool autoStart = true; public float spawnZ = 20f; // 敌人生成的Z位置 public float spawnY = 0.16f; public float waveCompleteDelay = 3f; private LevelWaveFile level; private readonly List activeEnemies = new List(); private Slider progressSlider; private Text progressText; private int currentWaveIndex = -1; private int totalEnemies; private int defeatedEnemies; private void Start() { if (!LoadLevel()) return; CreateProgressBar(); if (autoStart) StartCoroutine(RunLevel()); } /// /// 外部调用:开始关卡(用于游戏流程控制) /// public IEnumerator StartLevel() { if (level == null || level.waves == null || level.waves.Length == 0) { Debug.LogError("DefenseWaveLevelController: 无法启动关卡,波次数据未加载"); yield break; } yield return RunLevel(); } private bool LoadLevel() { if (levelJson == null) { Debug.LogError("DefenseWaveLevelController: 未配置关卡 JSON。"); return false; } level = JsonUtility.FromJson(levelJson.text); if (level == null || level.waves == null || level.waves.Length == 0) { Debug.LogError("DefenseWaveLevelController: JSON 没有有效波次。"); return false; } totalEnemies = level.waves.Sum(w => TotalCount(w)); defeatedEnemies = 0; return true; } private IEnumerator RunLevel() { for (int i = 0; i < level.waves.Length; i++) { currentWaveIndex = i; var wave = level.waves[i]; yield return new WaitForSeconds(Mathf.Max(0f, wave.restBeforeWave)); yield return SpawnWave(wave); yield return WaitForWaveCleared(); // 每波结束后,让英雄回到初始位置 ResetHeroPositionsAfterWave(); yield return new WaitForSeconds(Mathf.Max(0f, waveCompleteDelay)); } UpdateProgress(level.waves.Length, level.waves.Length, "通关"); Debug.Log("DefenseWaveLevelController: 第一关波次全部完成。"); } /// /// 每波结束后让英雄回到初始位置 /// private void ResetHeroPositionsAfterWave() { DefenseGameFlowController flowController = FindObjectOfType(); if (flowController != null) { flowController.ResetHeroPositions(); Debug.Log($"第 {currentWaveIndex + 1} 波结束,英雄回到初始位置"); } } private IEnumerator SpawnWave(WaveEntry wave) { int total = TotalCount(wave); int spawned = 0; UpdateProgress(wave.waveIndex, level.waves.Length, $"第 {wave.waveIndex} 波 0/{total}"); for (int typeIndex = 0; typeIndex < wave.enemyIds.Length; typeIndex++) { var prefab = FindPrefab(wave.enemyIds[typeIndex]); if (prefab == null) continue; int count = wave.counts != null && typeIndex < wave.counts.Length ? wave.counts[typeIndex] : 0; for (int i = 0; i < count; i++) { float x = wave.spawnX != null && wave.spawnX.Length > 0 ? wave.spawnX[(spawned) % wave.spawnX.Length] : (spawned % 2 == 0 ? -3f : 3f); var enemy = Instantiate(prefab, new Vector3(x, spawnY, spawnZ), Quaternion.identity); enemy.tag = "Enemy"; // 设置敌方阵营(teamId=1),否则会被当成友军 UnitTeam enemyTeam = enemy.GetComponent(); if (enemyTeam == null) enemyTeam = enemy.AddComponent(); enemyTeam.teamId = 1; // 增大敌人的寻敌距离 UnitBase unitBase = enemy.GetComponent(); if (unitBase != null) { unitBase.detectionRange = 100f; // 从默认30增加到100 } ApplyCharacterData(enemy, wave.enemyIds[typeIndex]); activeEnemies.Add(enemy); spawned++; UpdateProgress(wave.waveIndex, level.waves.Length, $"第 {wave.waveIndex} 波 {spawned}/{total}"); yield return new WaitForSeconds(Mathf.Max(0.1f, wave.spawnInterval)); } } } private IEnumerator WaitForWaveCleared() { while (true) { for (int i = activeEnemies.Count - 1; i >= 0; i--) { var enemy = activeEnemies[i]; if (enemy == null || !enemy.activeInHierarchy) { activeEnemies.RemoveAt(i); defeatedEnemies++; } } if (activeEnemies.Count == 0) yield break; yield return new WaitForSeconds(0.25f); } } private void ApplyCharacterData(GameObject enemy, string characterId) { // 从所有 CharacterData 中按 characterId 字段查找,而不是按文件名 var allData = Resources.LoadAll("Data"); CharacterData data = null; foreach (var d in allData) { if (d != null && d.characterId == characterId) { data = d; break; } } var unit = enemy.GetComponent(); if (data == null) { Debug.LogWarning($"ApplyCharacterData: 找不到 characterId={characterId} 的 CharacterData"); return; } if (unit == null) return; unit.InitializeFromPlayerData(data.GetMaxHealth(1), data.GetAttackPower(1), data.GetMoveSpeed(1)); } private int TotalCount(WaveEntry wave) { if (wave == null || wave.counts == null) return 0; return wave.counts.Sum(c => Mathf.Max(0, c)); } private GameObject FindPrefab(string id) { if (enemyPrefabs == null) return null; var entry = enemyPrefabs.FirstOrDefault(e => e != null && e.id == id); if (entry == null || entry.prefab == null) { Debug.LogError("DefenseWaveLevelController: 找不到小怪 Prefab: " + id); return null; } return entry.prefab; } private void CreateProgressBar() { var canvasObject = new GameObject("WaveProgressHUD", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)); var canvas = canvasObject.GetComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = 500; var root = canvasObject.GetComponent(); var panel = new GameObject("WaveProgressPanel", typeof(RectTransform), typeof(Image)); panel.transform.SetParent(root, false); var panelRect = panel.GetComponent(); panelRect.anchorMin = new Vector2(0.5f, 1f); panelRect.anchorMax = new Vector2(0.5f, 1f); panelRect.pivot = new Vector2(0.5f, 1f); panelRect.anchoredPosition = new Vector2(0f, -20f); panelRect.sizeDelta = new Vector2(420f, 64f); panel.GetComponent().color = new Color(0f, 0f, 0f, 0.45f); var sliderObject = new GameObject("WaveProgress", typeof(RectTransform), typeof(Slider)); sliderObject.transform.SetParent(panel.transform, false); var sliderRect = sliderObject.GetComponent(); sliderRect.anchorMin = new Vector2(0.5f, 0f); sliderRect.anchorMax = new Vector2(0.5f, 0f); sliderRect.pivot = new Vector2(0.5f, 0f); sliderRect.anchoredPosition = new Vector2(0f, 10f); sliderRect.sizeDelta = new Vector2(360f, 16f); progressSlider = sliderObject.GetComponent(); progressSlider.minValue = 0f; progressSlider.maxValue = 1f; progressSlider.value = 0f; progressSlider.interactable = false; var textObject = new GameObject("WaveProgressText", typeof(RectTransform), typeof(Text)); textObject.transform.SetParent(panel.transform, false); var textRect = textObject.GetComponent(); textRect.anchorMin = new Vector2(0.5f, 1f); textRect.anchorMax = new Vector2(0.5f, 1f); textRect.pivot = new Vector2(0.5f, 1f); textRect.anchoredPosition = new Vector2(0f, -8f); textRect.sizeDelta = new Vector2(390f, 28f); progressText = textObject.GetComponent(); progressText.alignment = TextAnchor.MiddleCenter; progressText.font = Resources.GetBuiltinResource("LegacyRuntime.ttf"); progressText.fontSize = 18; progressText.color = Color.white; } private void UpdateProgress(int waveNumber, int waveCount, string text) { if (progressSlider != null) { float defeatedRatio = totalEnemies > 0 ? (float)defeatedEnemies / totalEnemies : 0f; progressSlider.value = Mathf.Clamp01((waveNumber - 1 + defeatedRatio) / Mathf.Max(1, waveCount)); } if (progressText != null) progressText.text = text; } }