修了很多bug和增加功能,优化不少问题

This commit is contained in:
2026-02-28 06:59:22 +08:00
parent 508a40bba0
commit e11cc1da7a
345 changed files with 173803 additions and 69332 deletions
+260 -103
View File
@@ -1,4 +1,4 @@
using UnityEngine;
using UnityEngine;
using Spine;
using Spine.Unity;
using SmoothShakeFree;
@@ -18,6 +18,24 @@ public class teamUIController : MonoBehaviour
private set;
}
public bool IsAnyAllyActive()
{
return isAlly01_active || isAlly02_active || isAlly03_active || isAlly04_active || isAlly05_active;
}
public bool IsAllySlotActive(int slotIndex)
{
switch (slotIndex)
{
case 0: return isAlly01_active;
case 1: return isAlly02_active;
case 2: return isAlly03_active;
case 3: return isAlly04_active;
case 4: return isAlly05_active;
default: return false;
}
}
// Documentation text normalized.
[Header("UI Effects")]
@@ -48,6 +66,8 @@ public class teamUIController : MonoBehaviour
// Documentation text normalized.
public EnemyData_SO[] recognizedEnemySOs = new EnemyData_SO[0];
private int CurrentDifficultyID => BeatmapManager.Instance != null ? BeatmapManager.Instance.assignedDifficulty : 0;
// previous active flags for detecting external changes
private bool[] prevAllyActive = new bool[5];
@@ -236,14 +256,14 @@ public class teamUIController : MonoBehaviour
currentAllySOs[i] = null;
switch (i)
{
case 0: isAlly01_active = false; break;
case 1: isAlly02_active = false; break;
case 2: isAlly03_active = false; break;
case 3: isAlly04_active = false; break;
case 4: isAlly05_active = false; break;
case 0: isAlly01_active = true; break;
case 1: isAlly02_active = true; break;
case 2: isAlly03_active = true; break;
case 3: isAlly04_active = true; break;
case 4: isAlly05_active = true; break;
}
ToggleAllyObject(i, false);
Debug.Log($"[teamUIController] slot {i + 1} id=0 -> disabled");
ToggleAllyObject(i, true);
Debug.Log($"[teamUIController] slot {i + 1} id=0 -> kept active (empty display)");
continue;
}
@@ -299,16 +319,16 @@ public class teamUIController : MonoBehaviour
if (found == null)
{
Debug.LogWarning($"[teamUIController] 未找到 id={id} 对应的 SO (slot {i + 1})");
// disable UI slot when not found
// keep UI slot active even when not found
switch (i)
{
case 0: isAlly01_active = false; break;
case 1: isAlly02_active = false; break;
case 2: isAlly03_active = false; break;
case 3: isAlly04_active = false; break;
case 4: isAlly05_active = false; break;
case 0: isAlly01_active = true; break;
case 1: isAlly02_active = true; break;
case 2: isAlly03_active = true; break;
case 3: isAlly04_active = true; break;
case 4: isAlly05_active = true; break;
}
ToggleAllyObject(i, false);
ToggleAllyObject(i, true);
}
else
{
@@ -405,16 +425,16 @@ public class teamUIController : MonoBehaviour
currentAllySOs[i] = null;
switch (i)
{
case 0: isAlly01_active = false; break;
case 1: isAlly02_active = false; break;
case 2: isAlly03_active = false; break;
case 3: isAlly04_active = false; break;
case 4: isAlly05_active = false; break;
case 0: isAlly01_active = true; break;
case 1: isAlly02_active = true; break;
case 2: isAlly03_active = true; break;
case 3: isAlly04_active = true; break;
case 4: isAlly05_active = true; break;
}
// also toggle associated object
ToggleAllyObject(i, false);
// keep associated object active
ToggleAllyObject(i, true);
Debug.Log($"[teamUIController] (Runtime) slot {i+1} id=0 -> disabled");
Debug.Log($"[teamUIController] (Runtime) slot {i+1} id=0 -> kept active (empty display)");
continue;
}
@@ -494,18 +514,20 @@ public class teamUIController : MonoBehaviour
currentAllySOs[i] = found;
if (found == null)
{
Debug.LogWarning($"[teamUIController] (Runtime) 未找到 id={id} 对应的 SO (slot {i + 1})");
switch (i)
{
case 0: isAlly01_active = false; break;
case 1: isAlly02_active = false; break;
case 2: isAlly03_active = false; break;
case 3: isAlly04_active = false; break;
case 4: isAlly05_active = false; break;
Debug.LogWarning($"[teamUIController] (Runtime) 未找到 id={id} 对应的 SO (slot {i + 1})");
// 保持插槽显示,即使未找到数据
switch (i)
{
case 0: isAlly01_active = true; break;
case 1: isAlly02_active = true; break;
case 2: isAlly03_active = true; break;
case 3: isAlly04_active = true; break;
case 4: isAlly05_active = true; break;
}
ToggleAllyObject(i, true);
continue;
}
ToggleAllyObject(i, false);
}
else
{
// try to get a readable source for the matched object
@@ -629,25 +651,40 @@ public class teamUIController : MonoBehaviour
currentEnemySOs = results.ToArray();
#else
string resourcesPath = ResolveResourcesRelativePath(!string.IsNullOrEmpty(runtimeEnemyResourcesFolderPath) ? runtimeEnemyResourcesFolderPath : selectedProjectFolderPath);
List<Object> runtimeDiscovered = new List<Object>();
if (!string.IsNullOrEmpty(resourcesPath))
List<Object> runtimeDiscovered = null;
if (_runtimeEnemyDiscovered != null && _runtimeEnemyResourcesPath == resourcesPath)
{
resourcesPath = resourcesPath.Trim('/');
try
{
var arr = Resources.LoadAll(resourcesPath);
if (arr != null && arr.Length > 0) runtimeDiscovered.AddRange(arr);
if (runtimeDiscovered.Count == 0)
{
var arrAll = Resources.LoadAll("");
if (arrAll != null && arrAll.Length > 0) runtimeDiscovered.AddRange(arrAll); // Fixed: arr.Length to arrAll.Length
}
}
catch { var arrAll = Resources.LoadAll(""); if (arrAll != null && arrAll.Length > 0) runtimeDiscovered.AddRange(arrAll); } // Fixed: arr.Length to arrAll.Length
runtimeDiscovered = _runtimeEnemyDiscovered;
}
else
{
var arr = Resources.LoadAll(""); if (arr != null && arr.Length > 0) runtimeDiscovered.AddRange(arr);
runtimeDiscovered = new List<Object>();
if (!string.IsNullOrEmpty(resourcesPath))
{
resourcesPath = resourcesPath.Trim('/');
try
{
var arr = Resources.LoadAll(resourcesPath);
if (arr != null && arr.Length > 0) runtimeDiscovered.AddRange(arr);
if (runtimeDiscovered.Count == 0)
{
var arrAll = Resources.LoadAll("");
if (arrAll != null && arrAll.Length > 0) runtimeDiscovered.AddRange(arrAll);
}
}
catch
{
var arrAll = Resources.LoadAll("");
if (arrAll != null && arrAll.Length > 0) runtimeDiscovered.AddRange(arrAll);
}
}
else
{
var arr = Resources.LoadAll("");
if (arr != null && arr.Length > 0) runtimeDiscovered.AddRange(arr);
}
_runtimeEnemyResourcesPath = resourcesPath;
_runtimeEnemyDiscovered = runtimeDiscovered;
}
List<EnemyData_SO> results = new List<EnemyData_SO>();
@@ -693,13 +730,6 @@ public class teamUIController : MonoBehaviour
enemyCurrentCount = 0;
SpawnNextEnemy();
}
// If enemies were populated and we have some, start spawning from the beginning
if (recognizedEnemySOs.Length > 0)
{
enemyCurrentCount = 0;
SpawnNextEnemy();
}
}
// Build recognizedEnemySOs from currentEnemySOs by filtering out null entries (id==0 or unresolved)
@@ -761,6 +791,7 @@ public class teamUIController : MonoBehaviour
public TextMeshProUGUI enemyCounter;
[SerializeField] private int enemyCounterMax; // Documentation text normalized.
[SerializeField] private int enemyCurrentCount; // Documentation text normalized.
private bool _allEnemiesDefeatedSkillTriggered;
[Tooltip("Documentation text normalized.")]
public TextMeshProUGUI currentTotalScore;
[Header("Inspector")]
@@ -782,6 +813,8 @@ public class teamUIController : MonoBehaviour
private Coroutine _enemySpineDeathCoroutine;
private const string EnemyIdleAnimationName = "idle";
private const string EnemyDieAnimationName = "die";
private string _runtimeEnemyResourcesPath;
private List<Object> _runtimeEnemyDiscovered;
// For total enemy health bar
private int totalMaxHP = 0; // Documentation text normalized.
@@ -1424,14 +1457,8 @@ public class teamUIController : MonoBehaviour
if (_spineSystemObject == null)
{
_spineSystemObject = new GameObject("spinesystemobject", typeof(RectTransform));
}
if (spine_to_put == null)
{
Transform targetParent = objectFather_enemy != null ? objectFather_enemy.transform : (currentEnemy_characterImage != null ? currentEnemy_characterImage.transform : transform);
if (_spineSystemObject.transform.parent != targetParent)
{
_spineSystemObject.transform.SetParent(targetParent, false);
}
Transform targetParent = objectFather_enemy != null ? objectFather_enemy.transform : (currentEnemy_characterImage != null ? currentEnemy_characterImage.transform.parent : transform);
_spineSystemObject.transform.SetParent(targetParent, false);
}
if (_enemySkeletonGraphic == null)
{
@@ -1449,34 +1476,26 @@ public class teamUIController : MonoBehaviour
{
if (_imgSystemObject == null)
{
Transform searchRoot = objectFather_enemy != null ? objectFather_enemy.transform : transform;
var existing = searchRoot.Find("imgSys");
if (existing != null)
if (currentEnemy_characterImage != null)
{
_imgSystemObject = existing.gameObject;
_imgSystemObject = currentEnemy_characterImage.gameObject;
}
else
{
Transform searchRoot = objectFather_enemy != null ? objectFather_enemy.transform : transform;
var existing = searchRoot.Find("imgSys");
if (existing != null)
{
_imgSystemObject = existing.gameObject;
}
}
}
if (_imgSystemObject == null)
{
_imgSystemObject = new GameObject("imgSys", typeof(RectTransform));
}
Transform targetParent = objectFather_enemy != null ? objectFather_enemy.transform : (currentEnemy_characterImage != null ? currentEnemy_characterImage.transform.parent : transform);
if (_imgSystemObject.transform.parent != targetParent)
{
Transform targetParent = objectFather_enemy != null ? objectFather_enemy.transform : transform;
_imgSystemObject.transform.SetParent(targetParent, false);
}
var rect = _imgSystemObject.GetComponent<RectTransform>();
if (rect != null)
{
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
}
if (currentEnemy_characterImage != null && currentEnemy_characterImage.transform.parent != _imgSystemObject.transform)
{
currentEnemy_characterImage.transform.SetParent(_imgSystemObject.transform, false);
}
}
private void UpdateSpineRectForHeight(EnemyData_SO so)
@@ -1538,6 +1557,87 @@ public class teamUIController : MonoBehaviour
{
yield return new WaitForSeconds(delay);
_enemySpineDeathCoroutine = null;
// Check if this was the last enemy and it was using Spine
if (enemyCurrentCount == recognizedEnemySOs.Length - 1)
{
var so = recognizedEnemySOs[enemyCurrentCount];
if (so != null && so.skeletonDataAsset != null)
{
// Start transition to static image for the last Spine enemy
if (gameObject.activeInHierarchy)
StartCoroutine(TransitionSpineToStaticImage(so));
yield break; // Do not increment enemyCurrentCount or call SpawnNextEnemy yet
}
}
enemyCurrentCount++;
SpawnNextEnemy();
}
private IEnumerator TransitionSpineToStaticImage(EnemyData_SO so)
{
// 1. Ensure Img System Object is ready
EnsureImgSystemObject();
if (_imgSystemObject != null) _imgSystemObject.SetActive(true);
// Stop any pending hurt flash
StopCoroutine("EnemyHurtFlash");
if (_spineFlashCoroutine != null)
{
StopCoroutine(_spineFlashCoroutine);
_spineFlashCoroutine = null;
}
// 2. Setup the static image
if (currentEnemy_hurtRedImage != null)
{
Color hurtColor = currentEnemy_hurtRedImage.color;
hurtColor.a = 0f;
currentEnemy_hurtRedImage.color = hurtColor;
}
if (currentEnemy_characterImage != null)
{
// Set the sprite from SO
var sprite = so.enemy_Image != null ? so.enemy_Image : (so.enemy_Profile != null ? so.enemy_Profile : null);
currentEnemy_characterImage.sprite = sprite;
// Apply grayscale material
if (grayScaleMaterial != null)
{
currentEnemy_characterImage.material = new Material(grayScaleMaterial);
currentEnemy_characterImage.material.SetFloat(SaturationID, 0f); // 0 means grayscale
}
// Start with alpha 0 for fade in
Color c = currentEnemy_characterImage.color;
c.a = 0f;
currentEnemy_characterImage.color = c;
// 3. Fade in over 1 second
float timer = 0f;
float duration = 1.0f;
while (timer < duration)
{
timer += Time.deltaTime;
float alpha = Mathf.Clamp01(timer / duration);
c.a = alpha;
currentEnemy_characterImage.color = c;
yield return null;
}
// Ensure fully opaque at the end
c.a = 1f;
currentEnemy_characterImage.color = c;
}
// 4. Finally advance to the "All Defeated" state
if (currentEnemy_hurtRedImage != null)
{
Color hurtColor = currentEnemy_hurtRedImage.color;
hurtColor.a = 0f;
currentEnemy_hurtRedImage.color = hurtColor;
}
enemyCurrentCount++;
SpawnNextEnemy();
}
@@ -1555,18 +1655,24 @@ public class teamUIController : MonoBehaviour
if (so.skeletonDataAsset != null)
{
_currentEnemySkeletonDataAsset = so.skeletonDataAsset;
EnsureSpineSystemObject();
EnsureImgSystemObject();
if (_enemySkeletonAnimation != null)
if (_currentEnemySkeletonDataAsset != so.skeletonDataAsset)
{
_enemySkeletonAnimation.skeletonDataAsset = _currentEnemySkeletonDataAsset;
_enemySkeletonAnimation.Initialize(true);
_currentEnemySkeletonDataAsset = so.skeletonDataAsset;
if (_enemySkeletonAnimation != null)
{
_enemySkeletonAnimation.skeletonDataAsset = _currentEnemySkeletonDataAsset;
_enemySkeletonAnimation.Initialize(true);
}
if (_enemySkeletonGraphic != null)
{
_enemySkeletonGraphic.skeletonDataAsset = _currentEnemySkeletonDataAsset;
_enemySkeletonGraphic.Initialize(true);
}
}
if (_enemySkeletonGraphic != null)
{
_enemySkeletonGraphic.skeletonDataAsset = _currentEnemySkeletonDataAsset;
_enemySkeletonGraphic.Initialize(true);
UpdateSpineRectForHeight(so);
TryPlayEnemySpineAnimation(EnemyIdleAnimationName, true);
}
@@ -1617,13 +1723,31 @@ public class teamUIController : MonoBehaviour
}
if (enemyCurrentCount < 0) enemyCurrentCount = 0;
if (enemyCurrentCount == 0) _allEnemiesDefeatedSkillTriggered = false;
if (enemyCurrentCount >= recognizedEnemySOs.Length)
{
// All enemies processed: show empty/finished state but keep UI visible
_currentEnemySkeletonDataAsset = null;
if (_spineSystemObject != null) _spineSystemObject.SetActive(false);
if (_imgSystemObject != null) _imgSystemObject.SetActive(false);
// Fix: Keep _imgSystemObject active if we just transitioned to a static image
if (_imgSystemObject != null && !(_imgSystemObject.activeSelf && currentEnemy_characterImage != null && currentEnemy_characterImage.sprite != null))
{
_imgSystemObject.SetActive(false);
}
else if (_imgSystemObject != null)
{
// Ensure it stays active
_imgSystemObject.SetActive(true);
}
if (objectFather_enemy != null) objectFather_enemy.SetActive(true);
if (currentEnemy_hurtRedImage != null)
{
Color hurtColor = currentEnemy_hurtRedImage.color;
hurtColor.a = 0f;
currentEnemy_hurtRedImage.color = hurtColor;
}
if (currentEnemy_nameText != null) currentEnemy_nameText.text = "所有敌人已被击败";
// if (currentEnemy_characterImage != null) currentEnemy_characterImage.sprite = null;
if (currentEnemy_healthImage != null) currentEnemy_healthImage.fillAmount = 0f;
@@ -1638,6 +1762,26 @@ public class teamUIController : MonoBehaviour
// Trigger victory animation
PlayVictoryAnimation();
// Fire "all enemies defeated" skill trigger exactly once for this run.
if (!_allEnemiesDefeatedSkillTriggered && Application.isPlaying)
{
_allEnemiesDefeatedSkillTriggered = true;
if (SkillBuilder.Instance != null)
{
SkillBuilder.Instance.TriggerOnAllEnemiesDefeated();
Debug.Log("[teamUIController] All enemies defeated -> TriggerOnAllEnemiesDefeated fired.");
}
else
{
var sb = UnityEngine.Object.FindAnyObjectByType<SkillBuilder>();
if (sb != null)
{
sb.TriggerOnAllEnemiesDefeated();
Debug.LogWarning("[teamUIController] SkillBuilder.Instance was null when firing AllEnemiesDefeated; used FindObjectOfType fallback");
}
}
}
return;
}
@@ -1652,7 +1796,8 @@ public class teamUIController : MonoBehaviour
}
InitializeEnemyInstance();
enemyCombatantInstance.InitializeFromSO(so);
int difficultyID = BeatmapManager.Instance != null ? BeatmapManager.Instance.assignedDifficulty : 0;
enemyCombatantInstance.InitializeFromSO(so, difficultyID);
// set UI visuals (name / sprites)
string enemyName = so.enemyName ?? so.name;
@@ -1677,6 +1822,9 @@ public class teamUIController : MonoBehaviour
case EnemyData_SO.EnemyType.Legend:
currentEnemy_typeText.text = "(史诗野怪)";
break;
case EnemyData_SO.EnemyType.Guardian:
currentEnemy_typeText.text = "(远古巨龙)";
break;
default:
currentEnemy_typeText.text = "(普通敌人)";
break;
@@ -1730,7 +1878,8 @@ public class teamUIController : MonoBehaviour
if (victoryMoveObject != null)
{
if (_victoryMoveCoroutine != null) StopCoroutine(_victoryMoveCoroutine);
_victoryMoveCoroutine = StartCoroutine(VictoryMoveRoutine());
if (gameObject.activeInHierarchy)
_victoryMoveCoroutine = StartCoroutine(VictoryMoveRoutine());
}
// 2. Victory Saturation Set (Immediate)
@@ -1849,7 +1998,8 @@ public class teamUIController : MonoBehaviour
_enemySpineDeathCoroutine = null;
}
_pendingSpineSpawnFadeIn = true;
_enemySpineDeathCoroutine = StartCoroutine(PlayEnemyDieThenAdvance(delay));
if (gameObject.activeInHierarchy)
_enemySpineDeathCoroutine = StartCoroutine(PlayEnemyDieThenAdvance(delay));
return;
}
}
@@ -1900,7 +2050,7 @@ public class teamUIController : MonoBehaviour
if (so != null)
{
int newHP = (i < individualHPList.Count) ? individualHPList[i] : 0;
so.enemy_maxHP = newHP;
so.SetMaxHPByDifficulty(CurrentDifficultyID, newHP);
}
}
@@ -1911,8 +2061,10 @@ public class teamUIController : MonoBehaviour
var currentSO = recognizedEnemySOs[enemyCurrentCount];
if (currentSO != null)
{
Debug.Log($"[teamUIController] Re-initializing current enemy instance with new HP: {currentSO.enemy_maxHP}");
enemyCombatantInstance.InitializeFromSO(currentSO);
int diffID = BeatmapManager.Instance != null ? BeatmapManager.Instance.assignedDifficulty : 0;
var stats = currentSO.GetStatsByDifficultyID(diffID);
Debug.Log($"[teamUIController] Re-initializing current enemy instance with new HP: {stats.enemy_maxHP}");
enemyCombatantInstance.InitializeFromSO(currentSO, diffID);
UpdateEnemyUIImmediate();
}
}
@@ -1932,7 +2084,7 @@ public class teamUIController : MonoBehaviour
{
foreach (var so in recognizedEnemySOs)
{
if (so != null) totalMaxHP += so.enemy_maxHP;
if (so != null) totalMaxHP += so.GetStatsByDifficultyID(CurrentDifficultyID).enemy_maxHP;
}
}
Debug.Log($"[teamUIController] Total Max HP has been recalculated to: {totalMaxHP}");
@@ -1973,7 +2125,8 @@ public class teamUIController : MonoBehaviour
if (so == null) continue;
// Documentation text normalized.
if (i == enemyCurrentCount) totalCur += Mathf.Max(0, newCurrentHP); else totalCur += Mathf.Max(0, so.enemy_maxHP);
if (i == enemyCurrentCount) totalCur += Mathf.Max(0, newCurrentHP);
else totalCur += Mathf.Max(0, so.GetStatsByDifficultyID(CurrentDifficultyID).enemy_maxHP);
}
}
@@ -2037,11 +2190,11 @@ public class teamUIController : MonoBehaviour
{
// Documentation text normalized.
if (enemyCombatantInstance != null) totalCur += Mathf.Max(0, enemyCombatantInstance.currentHP);
else totalCur += Mathf.Max(0, so.enemy_maxHP); // Fallback
else totalCur += Mathf.Max(0, so.GetStatsByDifficultyID(CurrentDifficultyID).enemy_maxHP); // Fallback
}
else
{
totalCur += Mathf.Max(0, so.enemy_maxHP);
totalCur += Mathf.Max(0, so.GetStatsByDifficultyID(CurrentDifficultyID).enemy_maxHP);
}
}
}
@@ -2126,7 +2279,7 @@ public class teamUIController : MonoBehaviour
if (currentEnemy_fadehealthImage != null)
{
float target = currentEnemy_healthImage != null ? currentEnemy_healthImage.fillAmount : (enemyCombatantInstance.maxHP > 0 ? (float)newHP / enemyCombatantInstance.maxHP : 0f);
if (animateFade && Application.isPlaying)
if (animateFade && Application.isPlaying && gameObject.activeInHierarchy)
enemyFadeHealthCoroutine = StartCoroutine(EnemyFadeHealthCoroutine(target));
else
currentEnemy_fadehealthImage.fillAmount = target;
@@ -2163,7 +2316,7 @@ public class teamUIController : MonoBehaviour
if (currentEnemy_fademanaImage != null)
{
float target = currentEnemy_manaImage != null ? currentEnemy_manaImage.fillAmount : (enemyCombatantInstance.maxMana > 0 ? (float)newMana / enemyCombatantInstance.maxMana : 0f);
if (animateFade && Application.isPlaying)
if (animateFade && Application.isPlaying && gameObject.activeInHierarchy)
enemyFadeManaCoroutine = StartCoroutine(EnemyFadeManaCoroutine(target));
else if (currentEnemy_fademanaImage != null)
currentEnemy_fademanaImage.fillAmount = target;
@@ -2208,6 +2361,9 @@ public class teamUIController : MonoBehaviour
public void TriggerEnemyHurtFlash()
{
// Don't flash if all enemies are defeated or if we are in the middle of a spine death transition
if (enemyCurrentCount >= recognizedEnemySOs.Length || _enemySpineDeathCoroutine != null) return;
if (_currentEnemySkeletonDataAsset != null && _enemySkeletonGraphic != null)
{
if (_spineFlashCoroutine != null) StopCoroutine(_spineFlashCoroutine);
@@ -2378,7 +2534,8 @@ public class teamUIController : MonoBehaviour
}
if (img == null) return;
StartCoroutine(AllyHurtFlashCoroutine(img));
if (gameObject.activeInHierarchy)
StartCoroutine(AllyHurtFlashCoroutine(img));
}
private IEnumerator AllyHurtFlashCoroutine(Image img)