236 lines
8.4 KiB
C#
236 lines
8.4 KiB
C#
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<GameObject> activeEnemies = new List<GameObject>();
|
|
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());
|
|
}
|
|
|
|
private bool LoadLevel()
|
|
{
|
|
if (levelJson == null)
|
|
{
|
|
Debug.LogError("DefenseWaveLevelController: 未配置关卡 JSON。");
|
|
return false;
|
|
}
|
|
|
|
level = JsonUtility.FromJson<LevelWaveFile>(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();
|
|
yield return new WaitForSeconds(Mathf.Max(0f, waveCompleteDelay));
|
|
}
|
|
|
|
UpdateProgress(level.waves.Length, level.waves.Length, "通关");
|
|
Debug.Log("DefenseWaveLevelController: 第一关波次全部完成。");
|
|
}
|
|
|
|
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";
|
|
|
|
// 增大敌人的寻敌距离
|
|
UnitBase unitBase = enemy.GetComponent<UnitBase>();
|
|
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)
|
|
{
|
|
var data = Resources.Load<CharacterData>("Data/" + characterId);
|
|
var unit = enemy.GetComponent<UnitBase>();
|
|
if (data == null || 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>();
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
canvas.sortingOrder = 500;
|
|
|
|
var root = canvasObject.GetComponent<RectTransform>();
|
|
var panel = new GameObject("WaveProgressPanel", typeof(RectTransform), typeof(Image));
|
|
panel.transform.SetParent(root, false);
|
|
var panelRect = panel.GetComponent<RectTransform>();
|
|
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<Image>().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<RectTransform>();
|
|
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<Slider>();
|
|
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<RectTransform>();
|
|
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<Text>();
|
|
progressText.alignment = TextAnchor.MiddleCenter;
|
|
progressText.font = Resources.GetBuiltinResource<Font>("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;
|
|
}
|
|
}
|