Files
daluandou/Assets/Scripts/技能脚本/PlayerSkillSystem.cs
T
huadexuan 8c0832e7ca ```
chore(bridge): 移除桥接包中的提交记录和 Git 属性配置

移除了 .codely-bridge-commit 文件中存储的提交哈希
移除了 .gitattributes 文件中的行结束符配置规则
```
2026-10-08 19:32:10 +08:00

610 lines
21 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 UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 玩家技能系统(召唤师技能)
/// 依据 PlayerData 中 equippedSummonerSkills 动态初始化图标与释放相匹配的技能效果
/// </summary>
public class PlayerSkillSystem : MonoBehaviour
{
public static PlayerSkillSystem Instance { get; private set; }
[Header("战术增益加成倍率")]
public float cooldownMultiplier = 1f; // 冷却缩减倍率
public float powerMultiplier = 1f; // 威力加成倍率
[Header("技能1组件绑定")]
public Button skill1Button;
public Image skill1CooldownMask;
public Text skill1CooldownText;
public string skill1CurrentName = "神圣赞歌";
public float skill1Cooldown = 25f;
private float skill1RemainingCooldown = 0f;
[Header("技能2组件绑定")]
public Button skill2Button;
public Image skill2CooldownMask;
public Text skill2CooldownText;
public string skill2CurrentName = "天雷狂轰";
public float skill2Cooldown = 20f;
private float skill2RemainingCooldown = 0f;
[Header("通用特效预制体引用")]
public GameObject cureBallPrefab; // 飞球预制体 (Assets/Prefabs/特效/飞球/cureBall.prefab)
public GameObject healNovaPrefab; // 治疗冲击波 (Assets/TJGeneratorLibEffects/Prefabs/Interactive/Healing/HealNova.prefab)
public GameObject lightningStrikePrefab; // 落雷击中 (Assets/TJGeneratorLibEffects/Prefabs/Combat/Explosions/LightningExplosion/LightningExplosionBlue.prefab)
public GameObject lightningNovaPrefab; // 雷暴全屏光环 (Assets/TJGeneratorLibEffects/Prefabs/Combat/Nova/Lightning/NovaLightningBlue.prefab)
public Transform castStartTransform; // 施法起始点
private void Awake()
{
Instance = this;
}
private void Start()
{
EnsureSkillUIReferences();
LoadEquippedSkillIcons();
if (skill1Button != null)
{
skill1Button.onClick.RemoveAllListeners();
skill1Button.onClick.AddListener(UseSkill1);
}
if (skill2Button != null)
{
skill2Button.onClick.RemoveAllListeners();
skill2Button.onClick.AddListener(UseSkill2);
}
}
private void EnsureSkillUIReferences()
{
var canvas = GameObject.Find("TowerDefenseCanvas");
if (canvas == null) return;
var skillsPanel = canvas.transform.Find("BottomUI/SkillsPanel");
if (skillsPanel == null) return;
if (skillsPanel.childCount > 0)
{
var item1 = skillsPanel.GetChild(0);
if (skill1Button == null)
{
skill1Button = item1.GetComponent<Button>();
if (skill1Button == null) skill1Button = item1.gameObject.AddComponent<Button>();
}
if (skill1CooldownMask == null)
{
var maskTr = item1.Find("CooldownMask");
if (maskTr != null) skill1CooldownMask = maskTr.GetComponent<Image>();
}
if (skill1CooldownText == null)
{
var textTr = item1.Find("CooldownText");
if (textTr != null) skill1CooldownText = textTr.GetComponent<Text>();
}
}
if (skillsPanel.childCount > 1)
{
var item2 = skillsPanel.GetChild(1);
if (skill2Button == null)
{
skill2Button = item2.GetComponent<Button>();
if (skill2Button == null) skill2Button = item2.gameObject.AddComponent<Button>();
}
if (skill2CooldownMask == null)
{
var maskTr = item2.Find("CooldownMask");
if (maskTr != null) skill2CooldownMask = maskTr.GetComponent<Image>();
}
if (skill2CooldownText == null)
{
var textTr = item2.Find("CooldownText");
if (textTr != null) skill2CooldownText = textTr.GetComponent<Text>();
}
}
}
/// <summary>
/// 从 PlayerData 读取玩家配置的 2 个召唤师技能名称,并绑定图标到 UI 位置
/// </summary>
public void LoadEquippedSkillIcons()
{
skill1CurrentName = "神圣赞歌";
skill2CurrentName = "天雷狂轰";
if (PlayerDataManager.Instance != null && PlayerDataManager.Instance.equippedSummonerSkills != null && PlayerDataManager.Instance.equippedSummonerSkills.Length >= 2)
{
skill1CurrentName = PlayerDataManager.Instance.equippedSummonerSkills[0];
skill2CurrentName = PlayerDataManager.Instance.equippedSummonerSkills[1];
}
SetSkillIcon(skill1Button, skill1CurrentName);
SetSkillIcon(skill2Button, skill2CurrentName);
skill1Cooldown = GetSkillBaseCooldown(skill1CurrentName);
skill2Cooldown = GetSkillBaseCooldown(skill2CurrentName);
}
private float GetSkillBaseCooldown(string skillName)
{
return skillName switch
{
"神圣赞歌" => 25f,
"天雷狂轰" => 20f,
"陨石轰炸" => 22f,
"绝对零度" => 30f,
"斩杀风暴" => 25f,
"庇护圣盾" => 24f,
"虚弱诅咒" => 20f,
"岩石傀儡" => 28f,
_ => 20f
};
}
private void SetSkillIcon(Button skillBtn, string skillName)
{
if (skillBtn == null || string.IsNullOrEmpty(skillName)) return;
Image img = skillBtn.GetComponent<Image>();
if (img != null)
{
Sprite icon = GetSkillIconSprite(skillName);
if (icon != null)
{
img.sprite = icon;
}
}
}
public static Sprite GetSkillIconSprite(string skillName)
{
if (string.IsNullOrEmpty(skillName)) return null;
Sprite icon = Resources.Load<Sprite>($"Picture/UI/召唤师技能图标/{skillName}");
#if UNITY_EDITOR
if (icon == null)
{
icon = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>($"Assets/Picture/UI/召唤师技能图标/{skillName}.png");
}
#endif
return icon;
}
private void Update()
{
EnsureSkillUIReferences();
// 技能 1 冷却更新
float totalCD1 = skill1Cooldown * cooldownMultiplier;
if (skill1RemainingCooldown > 0)
{
skill1RemainingCooldown -= Time.deltaTime;
if (skill1Button != null) skill1Button.interactable = false;
if (skill1CooldownMask != null)
{
skill1CooldownMask.gameObject.SetActive(true);
skill1CooldownMask.fillAmount = totalCD1 > 0 ? Mathf.Clamp01(skill1RemainingCooldown / totalCD1) : 0f;
}
if (skill1CooldownText != null)
{
skill1CooldownText.text = Mathf.CeilToInt(skill1RemainingCooldown).ToString();
skill1CooldownText.gameObject.SetActive(true);
}
}
else
{
if (skill1Button != null) skill1Button.interactable = true;
if (skill1CooldownMask != null) skill1CooldownMask.gameObject.SetActive(false);
if (skill1CooldownText != null) skill1CooldownText.gameObject.SetActive(false);
}
// 技能 2 冷却更新
float totalCD2 = skill2Cooldown * cooldownMultiplier;
if (skill2RemainingCooldown > 0)
{
skill2RemainingCooldown -= Time.deltaTime;
if (skill2Button != null) skill2Button.interactable = false;
if (skill2CooldownMask != null)
{
skill2CooldownMask.gameObject.SetActive(true);
skill2CooldownMask.fillAmount = totalCD2 > 0 ? Mathf.Clamp01(skill2RemainingCooldown / totalCD2) : 0f;
}
if (skill2CooldownText != null)
{
skill2CooldownText.text = Mathf.CeilToInt(skill2RemainingCooldown).ToString();
skill2CooldownText.gameObject.SetActive(true);
}
}
else
{
if (skill2Button != null) skill2Button.interactable = true;
if (skill2CooldownMask != null) skill2CooldownMask.gameObject.SetActive(false);
if (skill2CooldownText != null) skill2CooldownText.gameObject.SetActive(false);
}
}
/// <summary>
/// 触发技能1
/// </summary>
public void UseSkill1()
{
if (skill1RemainingCooldown > 0) return;
ExecuteSkillByName(skill1CurrentName);
skill1RemainingCooldown = skill1Cooldown * cooldownMultiplier;
}
/// <summary>
/// 触发技能2
/// </summary>
public void UseSkill2()
{
if (skill2RemainingCooldown > 0) return;
ExecuteSkillByName(skill2CurrentName);
skill2RemainingCooldown = skill2Cooldown * cooldownMultiplier;
}
/// <summary>
/// 统一按技能名字调度执行对应的技能效果
/// </summary>
public void ExecuteSkillByName(string skillName)
{
Debug.Log($"<color=yellow>【召唤师技能】释放:{skillName}(威力倍率: {powerMultiplier:F2})</color>");
switch (skillName)
{
case "神圣赞歌":
ExecuteDivineHymn();
break;
case "天雷狂轰":
ExecuteLightningStorm();
break;
case "陨石轰炸":
ExecuteMeteorStrike();
break;
case "绝对零度":
ExecuteAbsoluteZero();
break;
case "斩杀风暴":
ExecuteExecuteStorm();
break;
case "庇护圣盾":
ExecuteSanctuaryShield();
break;
case "虚弱诅咒":
ExecuteWeaknessCurse();
break;
case "岩石傀儡":
ExecuteRockGolemSummon();
break;
default:
Debug.LogWarning($"未找到技能 [{skillName}] 的处理分支,默认执行天雷狂轰");
ExecuteLightningStorm();
break;
}
}
#region 技能具体逻辑实现
/// <summary>
/// 1. 神圣赞歌:群体治疗
/// </summary>
private void ExecuteDivineHymn()
{
float finalHeal = (100f + 25f) * powerMultiplier;
Vector3 targetPos = GetAllyCenterPosition();
Vector3 startPos = castStartTransform != null ? castStartTransform.position : new Vector3(targetPos.x, targetPos.y + 10f, targetPos.z - 8f);
GameObject ballPrefab = cureBallPrefab;
if (ballPrefab == null) ballPrefab = Resources.Load<GameObject>("Prefabs/特效/飞球/cureBall");
#if UNITY_EDITOR
if (ballPrefab == null) ballPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/特效/飞球/cureBall.prefab");
#endif
GameObject novaPrefab = healNovaPrefab;
#if UNITY_EDITOR
if (novaPrefab == null) novaPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Interactive/Healing/HealNova.prefab");
#endif
if (ballPrefab != null)
{
GameObject ballGo = Instantiate(ballPrefab, startPos, Quaternion.identity);
var parabola = ballGo.GetComponent<ParabolaFlyObject>();
if (parabola == null) parabola = ballGo.AddComponent<ParabolaFlyObject>();
parabola.Launch(startPos, targetPos, 0.8f, 4.5f, novaPrefab, () =>
{
BuffSystem.Instance.ApplyGroupHeal(finalHeal);
});
}
else
{
BuffSystem.Instance.ApplyGroupHeal(finalHeal);
}
}
/// <summary>
/// 2. 天雷狂轰:全屏落雷
/// </summary>
private void ExecuteLightningStorm()
{
float finalDamage = 120f * powerMultiplier;
GameObject strikePrefab = lightningStrikePrefab;
#if UNITY_EDITOR
if (strikePrefab == null) strikePrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Combat/Explosions/LightningExplosion/LightningExplosionBlue.prefab");
#endif
GameObject novaPrefab = lightningNovaPrefab;
#if UNITY_EDITOR
if (novaPrefab == null) novaPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Combat/Nova/Lightning/NovaLightningBlue.prefab");
#endif
if (novaPrefab != null)
{
Vector3 centerPos = new Vector3(0f, 0.1f, 0f);
var novaGo = Instantiate(novaPrefab, centerPos, novaPrefab.transform.rotation);
Destroy(novaGo, 3.5f);
}
var targetEnemies = GetActiveEnemyUnits();
StartCoroutine(ExecuteLightningStrikesRoutine(targetEnemies, strikePrefab, finalDamage));
}
private IEnumerator ExecuteLightningStrikesRoutine(List<UnitBase> targetEnemies, GameObject strikePrefab, float finalDamage)
{
if (targetEnemies.Count > 0)
{
for (int i = 0; i < targetEnemies.Count; i++)
{
var enemy = targetEnemies[i];
if (enemy != null && enemy.gameObject.activeInHierarchy)
{
Vector3 strikePos = enemy.transform.position;
strikePos.y = 0.1f;
if (strikePrefab != null)
{
var strikeGo = Instantiate(strikePrefab, strikePos, strikePrefab.transform.rotation);
Destroy(strikeGo, 3f);
}
var healthBar = enemy.GetComponent<WorldHealthBar>();
if (healthBar != null && !healthBar.IsDead)
{
healthBar.TakeDamage(finalDamage);
// 30% 概率眩晕 1.5 秒
if (Random.value <= 0.3f)
{
enemy.ApplyStun(1.5f);
}
}
}
yield return new WaitForSeconds(0.08f);
}
}
else
{
for (int i = 0; i < 6; i++)
{
Vector3 randomPos = new Vector3(Random.Range(-5f, 5f), 0.1f, Random.Range(2f, 12f));
if (strikePrefab != null)
{
var strikeGo = Instantiate(strikePrefab, randomPos, strikePrefab.transform.rotation);
Destroy(strikeGo, 3f);
}
yield return new WaitForSeconds(0.08f);
}
}
}
/// <summary>
/// 3. 陨石轰炸:全屏/多目标烈焰砸击
/// </summary>
private void ExecuteMeteorStrike()
{
float finalDamage = 300f * powerMultiplier;
var targetEnemies = GetActiveEnemyUnits();
GameObject expPrefab = null;
#if UNITY_EDITOR
expPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Combat/Explosions/MagicExplosion/MagicExplosionFire.prefab");
#endif
if (targetEnemies.Count > 0)
{
foreach (var enemy in targetEnemies)
{
if (enemy == null || !enemy.gameObject.activeInHierarchy) continue;
Vector3 pos = enemy.transform.position;
pos.y = 0.1f;
if (expPrefab != null)
{
var exp = Instantiate(expPrefab, pos, expPrefab.transform.rotation);
exp.transform.localScale = Vector3.one * 2f;
Destroy(exp, 3f);
}
var healthBar = enemy.GetComponent<WorldHealthBar>();
if (healthBar != null && !healthBar.IsDead)
{
healthBar.TakeDamage(finalDamage);
}
}
}
else
{
Vector3 centerPos = new Vector3(0f, 0.1f, 5f);
if (expPrefab != null)
{
var exp = Instantiate(expPrefab, centerPos, expPrefab.transform.rotation);
exp.transform.localScale = Vector3.one * 3f;
Destroy(exp, 3f);
}
}
}
/// <summary>
/// 4. 绝对零度:全场冰冻 3.5 秒
/// </summary>
private void ExecuteAbsoluteZero()
{
var targetEnemies = GetActiveEnemyUnits();
foreach (var enemy in targetEnemies)
{
if (enemy == null || !enemy.gameObject.activeInHierarchy) continue;
enemy.ApplyStun(3.5f);
var healthBar = enemy.GetComponent<WorldHealthBar>();
if (healthBar != null && !healthBar.IsDead)
{
healthBar.TakeDamage(40f * powerMultiplier);
}
}
}
/// <summary>
/// 5. 斩杀风暴:全屏基础伤害 + 残血 (<25%) 斩杀
/// </summary>
private void ExecuteExecuteStorm()
{
float baseDamage = 80f * powerMultiplier;
var targetEnemies = GetActiveEnemyUnits();
foreach (var enemy in targetEnemies)
{
if (enemy == null || !enemy.gameObject.activeInHierarchy) continue;
var healthBar = enemy.GetComponent<WorldHealthBar>();
if (healthBar != null && !healthBar.IsDead)
{
float hpRatio = healthBar.currentHealth / healthBar.maxHealth;
if (hpRatio < 0.25f)
{
healthBar.TakeDamage(healthBar.currentHealth + 9999f); // 斩杀
}
else
{
healthBar.TakeDamage(baseDamage);
}
}
}
}
/// <summary>
/// 6. 庇护圣盾:全场己方群体护盾 (临时效果:为全员施加持续治疗/伤害吸收)
/// </summary>
private void ExecuteSanctuaryShield()
{
float healShieldAmount = 150f * powerMultiplier;
BuffSystem.Instance.ApplyGroupHeal(healShieldAmount);
}
/// <summary>
/// 7. 虚弱诅咒:全场敌人易伤并受到伤害
/// </summary>
private void ExecuteWeaknessCurse()
{
float curseDamage = 60f * powerMultiplier;
var targetEnemies = GetActiveEnemyUnits();
foreach (var enemy in targetEnemies)
{
if (enemy == null || !enemy.gameObject.activeInHierarchy) continue;
var healthBar = enemy.GetComponent<WorldHealthBar>();
if (healthBar != null && !healthBar.IsDead)
{
healthBar.TakeDamage(curseDamage);
enemy.ApplyStun(1.0f);
}
}
}
/// <summary>
/// 8. 岩石傀儡:在全场引发冲击并造成震击控制
/// </summary>
private void ExecuteRockGolemSummon()
{
float rockDamage = 160f * powerMultiplier;
var targetEnemies = GetActiveEnemyUnits();
foreach (var enemy in targetEnemies)
{
if (enemy == null || !enemy.gameObject.activeInHierarchy) continue;
var healthBar = enemy.GetComponent<WorldHealthBar>();
if (healthBar != null && !healthBar.IsDead)
{
healthBar.TakeDamage(rockDamage);
enemy.ApplyStun(1.5f);
}
}
}
#endregion
#region 工具辅助方法
private List<UnitBase> GetActiveEnemyUnits()
{
var allUnits = FindObjectsOfType<UnitBase>();
var list = new List<UnitBase>();
foreach (var unit in allUnits)
{
if (unit == null || !unit.gameObject.activeInHierarchy) continue;
var team = unit.GetComponent<UnitTeam>();
if (team != null && team.teamId == 1) // 敌方
{
var healthBar = unit.GetComponent<WorldHealthBar>();
if (healthBar != null && !healthBar.IsDead)
{
list.Add(unit);
}
}
}
return list;
}
private Vector3 GetAllyCenterPosition()
{
var heroes = FindObjectsOfType<UnitBase>();
Vector3 sumPos = Vector3.zero;
int count = 0;
foreach (var hero in heroes)
{
if (hero == null || !hero.gameObject.activeInHierarchy) continue;
UnitTeam team = hero.GetComponent<UnitTeam>();
if (team != null && team.IsLocalPlayerTeam())
{
sumPos += hero.transform.position;
count++;
}
}
if (count > 0)
{
Vector3 center = sumPos / count;
center.y = 0.1f;
return center;
}
return new Vector3(0f, 0.1f, -10f);
}
public void ResetAllCooldowns()
{
skill1RemainingCooldown = 0f;
skill2RemainingCooldown = 0f;
}
#endregion
}