编队系统重置 选角页面基本完成 等待存入playerprefs
This commit is contained in:
@@ -2,6 +2,7 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
@@ -18,8 +19,17 @@ public class teamUIController : MonoBehaviour
|
||||
[Tooltip("IDs for the 5 ally slots (top to bottom). These are used by PopulateAllySOsFromIds to resolve SOs into currentAllySOs.")]
|
||||
public List<int> allySlotIds = new List<int> { 0, 0, 0, 0, 0 };
|
||||
|
||||
// --- 新增:敌人队列槽 ID 列表(按出场顺序) ---
|
||||
[Header("Runtime enemy configuration")]
|
||||
[Tooltip("IDs for enemies to appear in sequence. All enemies share a single GameObject and will be initialized from these SOs in order.")]
|
||||
public List<int> enemySlotIds = new List<int>();
|
||||
|
||||
// --- 新增:解析后的当前五个 SO 引用(按顺序) ---
|
||||
private TeamCharacterDataInfo[] currentAllySOs = new TeamCharacterDataInfo[5];
|
||||
// 新增:解析后的当前敌人 SO 列表(按出场顺序)
|
||||
private EnemyData_SO[] currentEnemySOs = new EnemyData_SO[0];
|
||||
// 新增:仅包含已解析到的、有效的敌人 SO(不包含 id==0 或未找到的项)
|
||||
private EnemyData_SO[] recognizedEnemySOs = new EnemyData_SO[0];
|
||||
|
||||
// previous active flags for detecting external changes
|
||||
private bool[] prevAllyActive = new bool[5];
|
||||
@@ -33,6 +43,13 @@ public class teamUIController : MonoBehaviour
|
||||
[Tooltip("Runtime: Resources subfolder path (no 'Resources/' prefix). Example: so/ally")]
|
||||
public string runtimeResourcesFolderPath = "so/ally";
|
||||
|
||||
// 新增:敌人 SO 路径(Editor / Runtime)
|
||||
[Tooltip("Editor-only: project path or absolute disk path to the folder that contains enemy SOs. Example: Assets/Resources/so/enemies")]
|
||||
public string editorEnemySOFolderPath = "Assets/Resources/so/enemies";
|
||||
|
||||
[Tooltip("Runtime: Resources subfolder path (no 'Resources/' prefix) for enemy SOs. Example: so/enemies")]
|
||||
public string runtimeEnemyResourcesFolderPath = "so/enemies";
|
||||
|
||||
// Optional per-ally object to toggle together with isAllyX_active. If null falls back to objectFather_allyXX.
|
||||
[Header("Ally runtime objects (optional)")]
|
||||
public GameObject ally01_object;
|
||||
@@ -263,7 +280,7 @@ public class teamUIController : MonoBehaviour
|
||||
if (runtimeDiscovered.Count == 0)
|
||||
{
|
||||
var arrAll = Resources.LoadAll("");
|
||||
if (arrAll != null && arrAll.Length > 0)
|
||||
if (arrAll != null && arr.Length > 0)
|
||||
{
|
||||
runtimeDiscovered.AddRange(arrAll);
|
||||
Debug.LogWarning($"[teamUIController] (Runtime) Resources.LoadAll returned 0 in '{resourcesPath}', fell back to loading all ScriptableObjects ({arrAll.Length})");
|
||||
@@ -276,7 +293,7 @@ public class teamUIController : MonoBehaviour
|
||||
{
|
||||
Debug.LogWarning($"[teamUIController] (Runtime) Resources.LoadAll failed for '{resourcesPath}': {ex.Message}");
|
||||
var arrAll = Resources.LoadAll("");
|
||||
if (arrAll != null && arrAll.Length > 0)
|
||||
if (arrAll != null && arr.Length > 0)
|
||||
runtimeDiscovered.AddRange(arrAll);
|
||||
}
|
||||
}
|
||||
@@ -446,27 +463,152 @@ public class teamUIController : MonoBehaviour
|
||||
#endif
|
||||
}
|
||||
|
||||
// Resolve various forms of selectedProjectFolderPath into a Resources.Load relative path (no 'Resources/' prefix, no extension)
|
||||
private string ResolveResourcesRelativePath(string selectedPath)
|
||||
// Populate enemy SOs from enemySlotIds using editor/runtime paths similar to allies
|
||||
public void PopulateEnemySOsFromIds()
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedPath)) return string.Empty;
|
||||
string p = selectedPath.Replace("\\", "/");
|
||||
// If user provided a path starting with "Assets/Resources/", strip to get relative
|
||||
int idx = p.IndexOf("Assets/Resources/");
|
||||
if (idx >= 0)
|
||||
if (enemySlotIds == null || enemySlotIds.Count == 0)
|
||||
{
|
||||
return p.Substring(idx + "Assets/Resources/".Length).Trim('/');
|
||||
currentEnemySOs = new EnemyData_SO[0];
|
||||
// update recognized list and UI
|
||||
FilterRecognizedEnemies();
|
||||
UpdateEnemyListText();
|
||||
return;
|
||||
}
|
||||
// If user provided an absolute path that contains Application.dataPath and Resources
|
||||
string appData = Application.dataPath.Replace("\\", "/");
|
||||
idx = p.IndexOf(appData + "/Resources/");
|
||||
if (idx >= 0)
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string folderToUse = !string.IsNullOrEmpty(editorEnemySOFolderPath) ? editorEnemySOFolderPath : selectedProjectFolderPath;
|
||||
string projectRelative = null;
|
||||
if (!string.IsNullOrEmpty(folderToUse))
|
||||
{
|
||||
return p.Substring((appData + "/Resources/").Length).Trim('/');
|
||||
string projectPath = folderToUse.Replace("\\", "/");
|
||||
if (projectPath.StartsWith(Application.dataPath)) projectRelative = "Assets" + projectPath.Substring(Application.dataPath.Length);
|
||||
else if (projectPath.StartsWith("Assets/")) projectRelative = projectPath;
|
||||
else Debug.LogWarning("editorEnemySOFolderPath should be inside project Assets for editor search. Falling back to project-wide search.");
|
||||
}
|
||||
// If user already provided a Resources relative path (like "so/ally"), return it trimmed
|
||||
if (p.StartsWith("Resources/")) p = p.Substring("Resources/".Length);
|
||||
return p.Trim('/');
|
||||
|
||||
List<EnemyData_SO> results = new List<EnemyData_SO>();
|
||||
List<Object> discovered = new List<Object>();
|
||||
if (!string.IsNullOrEmpty(projectRelative))
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:EnemyData_SO", new[] { projectRelative });
|
||||
foreach (var g in guids)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(g);
|
||||
var so = AssetDatabase.LoadAssetAtPath<EnemyData_SO>(path);
|
||||
if (so != null) discovered.Add(so);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:EnemyData_SO");
|
||||
foreach (var g in guids)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(g);
|
||||
var so = AssetDatabase.LoadAssetAtPath<EnemyData_SO>(path);
|
||||
if (so != null) discovered.Add(so);
|
||||
}
|
||||
}
|
||||
|
||||
// For each id in enemySlotIds try to resolve an EnemyData_SO; if id==0 or not found push null (skip)
|
||||
foreach (var id in enemySlotIds)
|
||||
{
|
||||
if (id == 0) { results.Add(null); continue; }
|
||||
EnemyData_SO found = null;
|
||||
for (int j = 0; j < discovered.Count; j++)
|
||||
{
|
||||
var obj = discovered[j] as EnemyData_SO;
|
||||
if (obj == null) continue;
|
||||
string path = AssetDatabase.GetAssetPath(obj);
|
||||
if (!string.IsNullOrEmpty(path) && path.Contains(id.ToString())) { found = obj; break; }
|
||||
if (obj.enemyID == id) { found = obj; break; }
|
||||
}
|
||||
results.Add(found);
|
||||
}
|
||||
|
||||
currentEnemySOs = results.ToArray();
|
||||
#else
|
||||
string resourcesPath = ResolveResourcesRelativePath(!string.IsNullOrEmpty(runtimeEnemyResourcesFolderPath) ? runtimeEnemyResourcesFolderPath : selectedProjectFolderPath);
|
||||
List<Object> 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);
|
||||
}
|
||||
|
||||
List<EnemyData_SO> results = new List<EnemyData_SO>();
|
||||
foreach (var id in enemySlotIds)
|
||||
{
|
||||
if (id == 0) { results.Add(null); continue; }
|
||||
EnemyData_SO found = null;
|
||||
foreach (var o in runtimeDiscovered)
|
||||
{
|
||||
if (o == null) continue;
|
||||
if (o is EnemyData_SO e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.name) && e.name.Contains(id.ToString())) { found = e; break; }
|
||||
if (e.enemyID == id) { found = e; break; }
|
||||
}
|
||||
}
|
||||
// final attempt: Resources.Load by path
|
||||
if (found == null && !string.IsNullOrEmpty(resourcesPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
var byId = Resources.Load<EnemyData_SO>($"{resourcesPath}/{id}");
|
||||
if (byId != null) found = byId;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
results.Add(found);
|
||||
}
|
||||
|
||||
currentEnemySOs = results.ToArray();
|
||||
#endif
|
||||
// build recognized list and update UI
|
||||
FilterRecognizedEnemies();
|
||||
UpdateEnemyListText();
|
||||
}
|
||||
|
||||
// Build recognizedEnemySOs from currentEnemySOs by filtering out null entries (id==0 or unresolved)
|
||||
private void FilterRecognizedEnemies()
|
||||
{
|
||||
var list = new List<EnemyData_SO>();
|
||||
if (currentEnemySOs != null)
|
||||
{
|
||||
foreach (var so in currentEnemySOs)
|
||||
{
|
||||
if (so != null)
|
||||
{
|
||||
list.Add(so);
|
||||
}
|
||||
}
|
||||
}
|
||||
recognizedEnemySOs = list.ToArray();
|
||||
// update enemyCounterMax to reflect actual number of recognized enemies
|
||||
enemyCounterMax = recognizedEnemySOs != null ? recognizedEnemySOs.Length : 0;
|
||||
// clamp current index
|
||||
if (enemyCurrentCount < 0) enemyCurrentCount = 0;
|
||||
if (enemyCurrentCount > enemyCounterMax) enemyCurrentCount = enemyCounterMax;
|
||||
}
|
||||
|
||||
// 返回解析到的敌人 SO 列表(副本)
|
||||
public EnemyData_SO[] GetCurrentEnemySOs()
|
||||
{
|
||||
return (EnemyData_SO[])currentEnemySOs.Clone();
|
||||
}
|
||||
|
||||
// Debug helper to print key fields of a TeamCharacterDataInfo
|
||||
@@ -502,6 +644,13 @@ public class teamUIController : MonoBehaviour
|
||||
[Tooltip("当前总分")]
|
||||
public TextMeshProUGUI currentTotalScore;
|
||||
|
||||
// new: runtime enemy instance and UI sync fields
|
||||
private EnemyCombatant enemyCombatantInstance;
|
||||
private Coroutine enemyFadeHealthCoroutine;
|
||||
private Coroutine enemyFadeManaCoroutine;
|
||||
private int prevEnemyHP = -1;
|
||||
private int prevEnemyMana = -1;
|
||||
|
||||
[Header("Combo判定最低要求")]
|
||||
public ComboJudgeType comboJudgeType = ComboJudgeType.Perfect;
|
||||
|
||||
@@ -696,16 +845,32 @@ public class teamUIController : MonoBehaviour
|
||||
public Image currentEnemy_healthImage;
|
||||
[Tooltip("虚血条")]
|
||||
public Image currentEnemy_fadehealthImage;
|
||||
[Tooltip("法力条")]
|
||||
public Image currentEnemy_manaImage;
|
||||
[Tooltip("虚法力条")]
|
||||
public Image currentEnemy_fademanaImage;
|
||||
[Tooltip("共计血条")]
|
||||
public Image allEnemy_totalHealthImage;
|
||||
[Tooltip("共计虚血条")]
|
||||
public Image allEnemy_totalFadehealthImage;
|
||||
[Tooltip("名字文本")]
|
||||
public TextMeshProUGUI currentEnemy_nameText;
|
||||
[Tooltip("当前生命值/最大生命值")]
|
||||
public Text currentEnemy_nameText;
|
||||
[Tooltip("类型文本")]
|
||||
public Text currentEnemy_typeText;
|
||||
[Tooltip("当前敌人生命值/最大生命值")]
|
||||
public TextMeshProUGUI currentEnemy_healthRate;
|
||||
[Tooltip("所有敌人生命值/最大生命值总计")]
|
||||
public TextMeshProUGUI totalEnemy_healthRate;
|
||||
[Tooltip("当前敌人法力值/最大法力值")]
|
||||
public TextMeshProUGUI currentEnemy_manaRate;
|
||||
[Tooltip("当前分数/轨道分数上限")]
|
||||
public TextMeshProUGUI currentEnemy_current_scoreText; // rate : now score / max score
|
||||
[Tooltip("角色唯一识别码")]
|
||||
[Tooltip("敌人唯一识别码")]
|
||||
[SerializeField] private int enemy_id;
|
||||
[Tooltip("受击闪红")]
|
||||
public Image currentEnemy_hurtRedImage;
|
||||
[Tooltip("敌人排队")]
|
||||
public TextMeshProUGUI enemyList_rateText;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
@@ -721,6 +886,17 @@ public class teamUIController : MonoBehaviour
|
||||
|
||||
// Ensure score text references are resolved so ScoreManager and AllyCombatant can write to them
|
||||
ResolveScoreTextReferences();
|
||||
|
||||
// Initialize enemies list and spawn first enemy if any
|
||||
PopulateEnemySOsFromIds();
|
||||
// set enemyCounterMax based on recognized (resolved non-null) enemies so zeros/unresolved don't count
|
||||
enemyCounterMax = recognizedEnemySOs != null ? recognizedEnemySOs.Length : 0;
|
||||
enemyCurrentCount = 0;
|
||||
InitializeEnemyInstance();
|
||||
if (recognizedEnemySOs != null && recognizedEnemySOs.Length > 0)
|
||||
{
|
||||
SpawnNextEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
@@ -736,6 +912,23 @@ public class teamUIController : MonoBehaviour
|
||||
prevAllyActive[i] = current[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Sync enemy UI with EnemyCombatant values
|
||||
if (enemyCombatantInstance != null)
|
||||
{
|
||||
int hp = enemyCombatantInstance.currentHP;
|
||||
int mana = enemyCombatantInstance.currentMana;
|
||||
if (prevEnemyHP != hp)
|
||||
{
|
||||
UpdateEnemyHealthVisuals(prevEnemyHP, hp, true);
|
||||
prevEnemyHP = hp;
|
||||
}
|
||||
if (prevEnemyMana != mana)
|
||||
{
|
||||
UpdateEnemyManaVisuals(prevEnemyMana, mana, true);
|
||||
prevEnemyMana = mana;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
@@ -890,4 +1083,298 @@ public class teamUIController : MonoBehaviour
|
||||
case 4: if (teammate05_characterImage != null) teammate05_characterImage.sprite = sprite; break;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- Enemy spawn & UI sync helpers -------------------
|
||||
private void InitializeEnemyInstance()
|
||||
{
|
||||
// Try to find an existing enemy object in scene
|
||||
enemyCombatantInstance = FindFirstObjectByType<EnemyCombatant>();
|
||||
if (enemyCombatantInstance == null)
|
||||
{
|
||||
var go = new GameObject("thisEnemy");
|
||||
enemyCombatantInstance = go.AddComponent<EnemyCombatant>();
|
||||
}
|
||||
|
||||
// attach listeners
|
||||
if (enemyCombatantInstance != null)
|
||||
{
|
||||
enemyCombatantInstance.OnEnemyDied -= OnEnemyDiedHandler;
|
||||
enemyCombatantInstance.OnEnemyRevived -= OnEnemyRevivedHandler;
|
||||
enemyCombatantInstance.OnEnemyDied += OnEnemyDiedHandler;
|
||||
enemyCombatantInstance.OnEnemyRevived += OnEnemyRevivedHandler;
|
||||
|
||||
// initialize previous tracked values
|
||||
prevEnemyHP = enemyCombatantInstance.currentHP;
|
||||
prevEnemyMana = enemyCombatantInstance.currentMana;
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnNextEnemy()
|
||||
{
|
||||
// handle case: no configured/recognized enemies -> show empty state but keep UI visible
|
||||
if (recognizedEnemySOs == null || recognizedEnemySOs.Length == 0)
|
||||
{
|
||||
if (objectFather_enemy != null) objectFather_enemy.SetActive(true);
|
||||
if (currentEnemy_nameText != null) currentEnemy_nameText.text = "(No Enemy)";
|
||||
if (currentEnemy_characterImage != null) currentEnemy_characterImage.sprite = null;
|
||||
if (currentEnemy_healthImage != null) currentEnemy_healthImage.fillAmount = 0f;
|
||||
if (currentEnemy_fadehealthImage != null) currentEnemy_fadehealthImage.fillAmount = 0f;
|
||||
if (currentEnemy_healthRate != null) currentEnemy_healthRate.text = "0/0";
|
||||
if (currentEnemy_manaImage != null) currentEnemy_manaImage.fillAmount = 0f;
|
||||
if (currentEnemy_fademanaImage != null) currentEnemy_fademanaImage.fillAmount = 0f;
|
||||
if (enemyCounter != null) enemyCounter.text = "0/0";
|
||||
if (currentEnemy_typeText != null) currentEnemy_typeText.text = "(普通)";
|
||||
UpdateEnemyListText();
|
||||
return;
|
||||
}
|
||||
|
||||
if (enemyCurrentCount < 0) enemyCurrentCount = 0;
|
||||
if (enemyCurrentCount >= recognizedEnemySOs.Length)
|
||||
{
|
||||
// All enemies processed: show empty/finished state but keep UI visible
|
||||
if (objectFather_enemy != null) objectFather_enemy.SetActive(true);
|
||||
if (currentEnemy_nameText != null) currentEnemy_nameText.text = "(All Defeated)";
|
||||
if (currentEnemy_characterImage != null) currentEnemy_characterImage.sprite = null;
|
||||
if (currentEnemy_healthImage != null) currentEnemy_healthImage.fillAmount = 0f;
|
||||
if (currentEnemy_fadehealthImage != null) currentEnemy_fadehealthImage.fillAmount = 0f;
|
||||
if (currentEnemy_healthRate != null) currentEnemy_healthRate.text = "0/0";
|
||||
if (currentEnemy_manaImage != null) currentEnemy_manaImage.fillAmount = 0f;
|
||||
if (currentEnemy_fademanaImage != null) currentEnemy_fademanaImage.fillAmount = 0f;
|
||||
if (enemyCounter != null) enemyCounter.text = "0/" + enemyCounterMax.ToString();
|
||||
if (currentEnemy_typeText != null) currentEnemy_typeText.text = "(普通)";
|
||||
UpdateEnemyListText();
|
||||
return;
|
||||
}
|
||||
|
||||
var so = recognizedEnemySOs[enemyCurrentCount];
|
||||
if (so == null)
|
||||
{
|
||||
Debug.LogWarning($"[teamUIController] SpawnNextEnemy: SO is null at index {enemyCurrentCount}");
|
||||
// skip null slots (treated as empty) and advance
|
||||
enemyCurrentCount++;
|
||||
SpawnNextEnemy();
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeEnemyInstance();
|
||||
enemyCombatantInstance.InitializeFromSO(so);
|
||||
|
||||
// set UI visuals (name / sprites)
|
||||
if (currentEnemy_nameText != null) currentEnemy_nameText.text = so.enemyName ?? so.name;
|
||||
if (currentEnemy_characterImage != null)
|
||||
{
|
||||
var sprite = so.enemy_Profile != null ? so.enemy_Profile : so.enemy_Image;
|
||||
if (sprite != null) currentEnemy_characterImage.sprite = sprite;
|
||||
else currentEnemy_characterImage.sprite = null;
|
||||
}
|
||||
|
||||
// set type text: show for Simple/Elite/Boss/Legend
|
||||
if (currentEnemy_typeText != null)
|
||||
{
|
||||
switch (so.enemyType)
|
||||
{
|
||||
case EnemyData_SO.EnemyType.Elite:
|
||||
currentEnemy_typeText.text = "(精英)";
|
||||
break;
|
||||
case EnemyData_SO.EnemyType.Boss:
|
||||
currentEnemy_typeText.text = "(首领)";
|
||||
break;
|
||||
case EnemyData_SO.EnemyType.Legend:
|
||||
currentEnemy_typeText.text = "(史诗)";
|
||||
break;
|
||||
default:
|
||||
currentEnemy_typeText.text = "(普通)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure UI parent is visible
|
||||
if (objectFather_enemy != null) objectFather_enemy.SetActive(true);
|
||||
|
||||
// reset previous values so Update picks up initial state
|
||||
prevEnemyHP = enemyCombatantInstance.currentHP;
|
||||
prevEnemyMana = enemyCombatantInstance.currentMana;
|
||||
UpdateEnemyUIImmediate();
|
||||
|
||||
// update enemy counter text
|
||||
if (enemyCounter != null)
|
||||
{
|
||||
enemyCounter.text = $"{Mathf.Clamp(enemyCurrentCount+1,1,999)}/{enemyCounterMax}";
|
||||
}
|
||||
|
||||
// refresh the enemy list display
|
||||
UpdateEnemyListText();
|
||||
}
|
||||
|
||||
// Build a readable representation of the enemy queue and write to enemyList_rateText
|
||||
private void UpdateEnemyListText()
|
||||
{
|
||||
if (enemyList_rateText == null) return;
|
||||
int total = recognizedEnemySOs != null ? recognizedEnemySOs.Length : 0;
|
||||
int displayIndex = 0;
|
||||
if (total == 0)
|
||||
displayIndex = 0;
|
||||
else
|
||||
displayIndex = Mathf.Clamp(enemyCurrentCount + 1, 1, total);
|
||||
|
||||
// show as "current/total" (e.g. 1/3). When no enemies, show 0/0
|
||||
enemyList_rateText.text = total == 0 ? "0/0" : $"{displayIndex}/{total}";
|
||||
}
|
||||
|
||||
private void OnEnemyDiedHandler(EnemyCombatant e)
|
||||
{
|
||||
// advance to next enemy after death
|
||||
enemyCurrentCount++;
|
||||
SpawnNextEnemy();
|
||||
}
|
||||
|
||||
private void OnEnemyRevivedHandler(EnemyCombatant e)
|
||||
{
|
||||
// refresh UI
|
||||
UpdateEnemyUIImmediate();
|
||||
}
|
||||
|
||||
private void UpdateEnemyUIImmediate()
|
||||
{
|
||||
if (enemyCombatantInstance == null) return;
|
||||
// health
|
||||
if (currentEnemy_healthImage != null)
|
||||
currentEnemy_healthImage.fillAmount = enemyCombatantInstance.maxHP > 0 ? (float)enemyCombatantInstance.currentHP / enemyCombatantInstance.maxHP : 0f;
|
||||
if (currentEnemy_fadehealthImage != null)
|
||||
currentEnemy_fadehealthImage.fillAmount = currentEnemy_healthImage != null ? currentEnemy_healthImage.fillAmount : (enemyCombatantInstance.maxHP > 0 ? (float)enemyCombatantInstance.currentHP / enemyCombatantInstance.maxHP : 0f);
|
||||
if (currentEnemy_healthRate != null)
|
||||
currentEnemy_healthRate.text = $"{enemyCombatantInstance.currentHP}/{enemyCombatantInstance.maxHP}";
|
||||
|
||||
// mana visuals (new)
|
||||
if (currentEnemy_manaImage != null)
|
||||
currentEnemy_manaImage.fillAmount = enemyCombatantInstance.maxMana > 0 ? (float)enemyCombatantInstance.currentMana / enemyCombatantInstance.maxMana : 0f;
|
||||
if (currentEnemy_fademanaImage != null)
|
||||
currentEnemy_fademanaImage.fillAmount = currentEnemy_manaImage != null ? currentEnemy_manaImage.fillAmount : (enemyCombatantInstance.maxMana > 0 ? (float)enemyCombatantInstance.currentMana / enemyCombatantInstance.maxMana : 0f);
|
||||
if (currentEnemy_manaRate != null)
|
||||
currentEnemy_manaRate.text = enemyCombatantInstance.maxMana > 0 ? $"{enemyCombatantInstance.currentMana}/{enemyCombatantInstance.maxMana}" : "0/0";
|
||||
|
||||
if (allEnemy_totalHealthImage != null)
|
||||
{
|
||||
// optional: aggregate total health across remaining enemies
|
||||
int totalCur = 0, totalMax = 0;
|
||||
if (currentEnemySOs != null)
|
||||
{
|
||||
for (int i = enemyCurrentCount; i < currentEnemySOs.Length; i++)
|
||||
{
|
||||
var so = currentEnemySOs[i];
|
||||
if (so == null) continue;
|
||||
totalMax += Mathf.Max(1, so.enemy_maxHP);
|
||||
// use max for not-yet-spawned enemies
|
||||
if (i == enemyCurrentCount) totalCur += enemyCombatantInstance.currentHP; else totalCur += so.enemy_maxHP;
|
||||
}
|
||||
}
|
||||
if (totalMax > 0)
|
||||
{
|
||||
allEnemy_totalHealthImage.fillAmount = (float)totalCur / totalMax;
|
||||
if (totalEnemy_healthRate != null) totalEnemy_healthRate.text = $"{totalCur}/{totalMax}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEnemyHealthVisuals(int oldHP, int newHP, bool animateFade)
|
||||
{
|
||||
if (enemyCombatantInstance == null) return;
|
||||
if (currentEnemy_healthImage != null)
|
||||
{
|
||||
float newFill = enemyCombatantInstance.maxHP > 0 ? (float)newHP / enemyCombatantInstance.maxHP : 0f;
|
||||
currentEnemy_healthImage.fillAmount = Mathf.MoveTowards(currentEnemy_healthImage.fillAmount, newFill, 1f);
|
||||
}
|
||||
if (currentEnemy_healthRate != null)
|
||||
currentEnemy_healthRate.text = $"{newHP}/{enemyCombatantInstance.maxHP}";
|
||||
|
||||
if (enemyFadeHealthCoroutine != null) StopCoroutine(enemyFadeHealthCoroutine);
|
||||
if (currentEnemy_fadehealthImage != null)
|
||||
{
|
||||
float target = currentEnemy_healthImage != null ? currentEnemy_healthImage.fillAmount : (enemyCombatantInstance.maxHP > 0 ? (float)newHP / enemyCombatantInstance.maxHP : 0f);
|
||||
if (animateFade && Application.isPlaying)
|
||||
enemyFadeHealthCoroutine = StartCoroutine(EnemyFadeHealthCoroutine(target));
|
||||
else
|
||||
currentEnemy_fadehealthImage.fillAmount = target;
|
||||
}
|
||||
|
||||
// hurt flash if hp decreased
|
||||
if (oldHP >= 0 && newHP < oldHP && currentEnemy_hurtRedImage != null && Application.isPlaying)
|
||||
{
|
||||
StopCoroutine("EnemyHurtFlash");
|
||||
StartCoroutine(EnemyHurtFlash());
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEnemyManaVisuals(int oldMana, int newMana, bool animateFade)
|
||||
{
|
||||
if (enemyCombatantInstance == null) return;
|
||||
if (currentEnemy_manaRate != null)
|
||||
currentEnemy_manaRate.text = enemyCombatantInstance.maxMana > 0 ? $"{newMana}/{enemyCombatantInstance.maxMana}" : "0/0";
|
||||
|
||||
if (enemyFadeManaCoroutine != null) StopCoroutine(enemyFadeManaCoroutine);
|
||||
if (currentEnemy_fademanaImage != null)
|
||||
{
|
||||
float target = currentEnemy_manaImage != null ? currentEnemy_manaImage.fillAmount : (enemyCombatantInstance.maxMana > 0 ? (float)newMana / enemyCombatantInstance.maxMana : 0f);
|
||||
if (animateFade && Application.isPlaying)
|
||||
enemyFadeManaCoroutine = StartCoroutine(EnemyFadeManaCoroutine(target));
|
||||
else if (currentEnemy_fademanaImage != null)
|
||||
currentEnemy_fademanaImage.fillAmount = target;
|
||||
}
|
||||
|
||||
// update main mana bar immediately
|
||||
if (currentEnemy_manaImage != null)
|
||||
{
|
||||
currentEnemy_manaImage.fillAmount = enemyCombatantInstance.maxMana > 0 ? (float)newMana / enemyCombatantInstance.maxMana : 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator EnemyFadeHealthCoroutine(float targetFill)
|
||||
{
|
||||
if (currentEnemy_fadehealthImage == null) yield break;
|
||||
float start = currentEnemy_fadehealthImage.fillAmount;
|
||||
float duration = 0.6f;
|
||||
float t = 0f;
|
||||
while (t < duration)
|
||||
{
|
||||
t += Time.deltaTime;
|
||||
currentEnemy_fadehealthImage.fillAmount = Mathf.Lerp(start, targetFill, t / duration);
|
||||
yield return null;
|
||||
}
|
||||
currentEnemy_fadehealthImage.fillAmount = targetFill;
|
||||
}
|
||||
|
||||
private IEnumerator EnemyFadeManaCoroutine(float targetFill)
|
||||
{
|
||||
if (currentEnemy_fademanaImage == null) yield break;
|
||||
float start = currentEnemy_fademanaImage.fillAmount;
|
||||
float duration = 0.4f;
|
||||
float t = 0f;
|
||||
while (t < duration)
|
||||
{
|
||||
t += Time.deltaTime;
|
||||
currentEnemy_fademanaImage.fillAmount = Mathf.Lerp(start, targetFill, t / duration);
|
||||
yield return null;
|
||||
}
|
||||
currentEnemy_fademanaImage.fillAmount = targetFill;
|
||||
}
|
||||
|
||||
private IEnumerator EnemyHurtFlash()
|
||||
{
|
||||
if (currentEnemy_hurtRedImage == null) yield break;
|
||||
Color c = currentEnemy_hurtRedImage.color;
|
||||
c.a = 1f;
|
||||
currentEnemy_hurtRedImage.color = c;
|
||||
yield return new WaitForSeconds(0.2f);
|
||||
float dur = 0.4f;
|
||||
float t = 0f;
|
||||
while (t < dur)
|
||||
{
|
||||
t += Time.deltaTime;
|
||||
c.a = Mathf.Lerp(1f, 0f, t / dur);
|
||||
currentEnemy_hurtRedImage.color = c;
|
||||
yield return null;
|
||||
}
|
||||
c.a = 0f;
|
||||
currentEnemy_hurtRedImage.color = c;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user