Files
daluandou/Assets/Scripts/关卡脚本/DefenseWaveLevelController.cs
T
huadexuan 23c559d9f7 ```
feat(gameplay): 提升单位检测范围并添加法师属性

- 将所有单位的检测范围从默认值提升至100
- 为法师预制体添加护盾、等级等新属性
- 为法师添加暴击、吸血、伤害减免等战斗属性
- 更换法师弹丸预设并调整相关参数
- 添加锚点交战Z线控制功能
- 更新玩家数据中配备的召唤师技能
- 移除多余的纹理配置文件
```
2026-10-07 23:59:17 +08:00

291 lines
9.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
[Header("UI 绑定")]
public Text waveTextUI; // 绑定 TowerDefenseCanvas/TopBar/WaveText
public Text killCountTextUI; // 绑定 TowerDefenseCanvas/TopBar/KillCountText
private LevelWaveFile level;
private readonly List<GameObject> activeEnemies = new List<GameObject>();
private int currentWaveIndex = -1;
private int totalEnemies;
private int defeatedEnemies;
private void Start()
{
if (!LoadLevel()) return;
EnsureUIReferences();
UpdateProgress(0, level.waves.Length, "准备中");
if (autoStart) StartCoroutine(RunLevel());
}
/// <summary>
/// 外部调用:开始关卡(用于游戏流程控制)
/// </summary>
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<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();
// 每波结束后,让英雄回到初始位置
ResetHeroPositionsAfterWave();
// 每 2 波结束(第 2、4、6、8...波)触发 3 选 1 战术增益弹窗
if ((currentWaveIndex + 1) % 2 == 0)
{
BuffSelectionController.Instance.ShowBuffSelection();
}
yield return new WaitForSeconds(Mathf.Max(0f, waveCompleteDelay));
}
UpdateProgress(level.waves.Length, level.waves.Length, "通关");
Debug.Log("DefenseWaveLevelController: 第一关波次全部完成。");
}
/// <summary>
/// 每波结束后让英雄回到初始位置
/// </summary>
private void ResetHeroPositionsAfterWave()
{
DefenseGameFlowController flowController = FindObjectOfType<DefenseGameFlowController>();
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<UnitTeam>();
if (enemyTeam == null) enemyTeam = enemy.AddComponent<UnitTeam>();
enemyTeam.teamId = 1;
// 增大敌人的寻敌距离
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++;
// 击杀金币奖励 (基础 15 金币 * 战术增益倍率)
if (GoldManager.Instance != null)
{
float killMult = BuffSelectionController.KillGoldMultiplier;
int goldAmount = Mathf.RoundToInt(15f * killMult);
GoldManager.Instance.AddGold(goldAmount);
}
UpdateProgress(currentWaveIndex + 1, level.waves.Length);
}
}
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<CharacterData>("Data");
CharacterData data = null;
foreach (var d in allData)
{
if (d != null && d.characterId == characterId)
{
data = d;
break;
}
}
var unit = enemy.GetComponent<UnitBase>();
if (data == null)
{
Debug.LogWarning($"ApplyCharacterData: 找不到 characterId={characterId} 的 CharacterData");
return;
}
if (unit == null) return;
// 小怪等级根据波次索引动态计算(前几波 Lv.1,随着波次推进适度成长,如 1~3 波 Lv.1,4~6 波 Lv.2,7~10 波 Lv.3)
int enemyLevel = Mathf.Clamp((currentWaveIndex / 3) + 1, 1, 10);
unit.InitializeFromPlayerData(data.GetMaxHealth(enemyLevel), data.GetAttackPower(enemyLevel), data.GetMoveSpeed(enemyLevel), enemyLevel);
}
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 EnsureUIReferences()
{
if (waveTextUI != null && killCountTextUI != null) return;
var canvasObject = GameObject.Find("TowerDefenseCanvas");
if (canvasObject == null) return;
var topBar = canvasObject.transform.Find("TopBar");
if (topBar == null) return;
if (waveTextUI == null)
{
var waveObj = topBar.Find("WaveText");
if (waveObj != null) waveTextUI = waveObj.GetComponent<Text>();
}
if (killCountTextUI == null)
{
var killObj = topBar.Find("KillCountText");
if (killObj != null) killCountTextUI = killObj.GetComponent<Text>();
}
}
private void UpdateProgress(int waveNumber, int waveCount, string statusHint = "")
{
EnsureUIReferences();
int currentDisplayWave = Mathf.Clamp(waveNumber > 0 ? waveNumber : 1, 1, waveCount);
if (waveTextUI != null)
{
waveTextUI.text = $"波次 {currentDisplayWave}/{waveCount}";
}
if (killCountTextUI != null)
{
killCountTextUI.text = $"击杀 {defeatedEnemies}/{totalEnemies}";
}
}
}