using System.Collections; using System.Collections.Generic; using UnityEngine; /// /// 城堡法师技能:爆裂火球体系(1/3/6/10 级完整阶梯) /// 1级【爆裂火球】:发射强力火球,对目标区域造成 140% 范围伤害(触发 MagicExplosionFire 爆破) /// 3级【强化爆裂】:火球伤害提升至 175%,爆炸范围 +25% /// 6级【灼烧火海】:爆裂处留下一片火海,对经过敌人造成 3 秒持续燃烧伤害 /// 10级【双发爆裂】:施法时连续轰出 2 颗爆裂火球,第二颗伤害提升 50% 且范围扩大 /// public class CastleMageSkill : UnitSkill { [Header("火球配置")] [Tooltip("火球飞行速度")] public float projectileSpeed = 16f; [Tooltip("爆裂基础半径")] public float baseExplosionRadius = 2.5f; [Header("特效资源")] public GameObject fireballMissilePrefab; // 火球飞弹 (FireballSharpMissileFire) public GameObject explosionVfxPrefab; // 爆破特效 (MagicExplosionFire) public GameObject burnFieldVfxPrefab; // 灼烧火海地面特效 (FireFieldRed) private bool isSkillPending = false; private void EnsureUnit() { if (unit == null) { unit = GetComponent(); } } /// /// 当能量蓄满时触发:播放法师技能施法动作 /// public override void ReleaseSkill() { EnsureUnit(); if (unit == null) return; WorldHealthBar healthBar = unit.GetComponent(); if (healthBar == null || healthBar.IsDead) return; if (unit.Target == null) return; unit.FaceCurrentTarget(0f); isSkillPending = true; // 播放法师施法动作,由 OnSkillHit1 帧触发火球发射 unit.PlaySkillAnimation(OnSkillAnimationFinished, 1.8f); Debug.Log($"城堡法师释放技能【爆裂火球】,等待 OnSkillHit1 施法帧!"); } /// /// 动画命中帧方法:在法杖前挥法术成型的瞬间触发 (OnSkillHit1) /// public void OnSkillHit1() { if (!isSkillPending) return; isSkillPending = false; EnsureUnit(); if (unit == null || unit.Target == null) return; int level = unit.level; // 1级 140%,3级+ 175% float damageMultiplier = level >= 3 ? 1.75f : 1.40f; float radius = level >= 3 ? baseExplosionRadius * 1.25f : baseExplosionRadius; // 发射第一颗爆裂火球 LaunchFireball(unit.Target, damageMultiplier, radius, level >= 6); // 10级大招【双发爆裂】:间隔 0.2 秒追加第二颗强化火球 if (level >= 10) { StartCoroutine(LaunchSecondFireballDelayed(unit.Target, damageMultiplier * 1.50f, radius * 1.30f, level >= 6)); } Debug.Log($"【爆裂火球】发射! 伤害倍率: {damageMultiplier * 100:F0}%, 范围: {radius:F1}m (等级: {level})"); } private IEnumerator LaunchSecondFireballDelayed(GameObject primaryTarget, float damageMultiplier, float radius, bool createBurnField) { yield return new WaitForSeconds(0.25f); if (unit == null) yield break; // 优先打目标后方的敌人;如果只有一个怪或没有更后方的人,直接打同一个目标,绝不打空 GameObject secondTarget = FindBacklineTarget(primaryTarget); if (secondTarget != null && secondTarget.activeInHierarchy) { LaunchFireball(secondTarget, damageMultiplier, radius, createBurnField); bool isSameTarget = secondTarget == primaryTarget; Debug.Log($"【双发爆裂】追加第二发火球! 目标: {secondTarget.name} ({(isSameTarget ? "单目标连续轰炸" : "后排敌群轰炸")}),伤害倍率: {damageMultiplier * 100:F0}%"); } } /// /// 寻找位于当前目标后方的敌人。若无后排敌人,回退返回当前目标。 /// private GameObject FindBacklineTarget(GameObject currentTarget) { if (unit == null) return currentTarget; UnitTeam myTeam = unit.GetComponent(); if (myTeam == null) return currentTarget; // 如果原目标已失效/死亡,回退找任意当前有效目标 if (currentTarget == null || !currentTarget.activeInHierarchy) { return unit.FindNearestEnemyForSkill(); } Vector3 magePos = unit.transform.position; Vector3 targetPos = currentTarget.transform.position; Vector3 forwardDir = (targetPos - magePos).normalized; if (forwardDir.sqrMagnitude < 0.001f) forwardDir = unit.transform.forward; var allTeams = Object.FindObjectsOfType(); GameObject bestBacklineTarget = null; float bestDistance = float.MaxValue; foreach (var t in allTeams) { if (t == null || !t.gameObject.activeInHierarchy) continue; if (!myTeam.IsEnemy(t.gameObject)) continue; if (t.gameObject == currentTarget || t.transform.root == currentTarget.transform.root) continue; // 检查敌方单位是否存活 var targetUnit = t.GetComponentInParent(); if (targetUnit != null && targetUnit.IsDead) continue; var bar = t.GetComponentInParent(); if (bar != null && bar.IsDead) continue; GameObject enemyGo = targetUnit != null ? targetUnit.gameObject : t.gameObject; // 计算该敌人相对当前目标的深度 (沿法师射向的距离) Vector3 offset = enemyGo.transform.position - targetPos; float depth = Vector3.Dot(offset, forwardDir); // 只要位于目标后方 (深度 > 0.8米) if (depth > 0.8f) { // 优先选择距离当前目标最近的后排敌人(紧邻的后排敌群),形成层次分明的前后封锁 float dist = offset.magnitude; if (dist < bestDistance) { bestDistance = dist; bestBacklineTarget = enemyGo; } } } // 如果找到了后排敌人,打后排敌人;如果场上只有一个怪或无后排敌人,打同一个目标 return bestBacklineTarget != null ? bestBacklineTarget : currentTarget; } private void LaunchFireball(GameObject target, float damageMultiplier, float radius, bool createBurnField) { GameObject missilePrefab = fireballMissilePrefab; if (missilePrefab == null && unit != null) { missilePrefab = unit.projectilePrefab; } #if UNITY_EDITOR if (missilePrefab == null) { missilePrefab = UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/TJGeneratorLibEffects/Prefabs/Combat/Missiles/FireballSharp/FireballSharpMissileFire.prefab"); } #endif if (missilePrefab == null || target == null || unit == null) return; Vector3 spawnPos = unit.projectileSpawnPoint != null ? unit.projectileSpawnPoint.position : unit.transform.position + Vector3.up * 1.2f + unit.transform.forward * 0.5f; Vector3 dir = (target.transform.position - spawnPos).normalized; GameObject missileGo = Instantiate(missilePrefab, spawnPos, Quaternion.LookRotation(dir)); float totalDamage = unit.attackPower * damageMultiplier; // 使用自定义弧线/追踪脚本,并在命中时引发范围爆炸 var runner = missileGo.AddComponent(); runner.Initialize(target, totalDamage, radius, unit.gameObject, projectileSpeed, explosionVfxPrefab, burnFieldVfxPrefab, createBurnField); } private void OnSkillAnimationFinished() { isSkillPending = false; } } /// /// 负责爆裂火球的飞行、落地爆炸伤害计算与灼烧火海生成 /// public class MageSkillFireballRunner : MonoBehaviour { private GameObject target; private float damage; private float explosionRadius; private GameObject owner; private float speed; private GameObject explosionVfx; private GameObject burnFieldVfx; private bool createBurnField; private bool hasExploded = false; public void Initialize(GameObject targetObj, float dmg, float radius, GameObject ownerObj, float moveSpeed, GameObject expVfx, GameObject burnVfx, bool burnField) { target = targetObj; damage = dmg; explosionRadius = radius; owner = ownerObj; speed = moveSpeed; explosionVfx = expVfx; burnFieldVfx = burnVfx; createBurnField = burnField; // 屏蔽原有 Projectile,接管飞行与碰撞 var defaultProj = GetComponent(); if (defaultProj != null) defaultProj.enabled = false; Destroy(gameObject, 4.0f); } private void Update() { if (hasExploded) return; if (target == null) { ExplodeAtPosition(transform.position); return; } Vector3 targetPos = target.transform.position + Vector3.up * 0.8f; Vector3 toTarget = targetPos - transform.position; float step = speed * Time.deltaTime; if (toTarget.sqrMagnitude <= step * step || toTarget.sqrMagnitude <= 0.15f) { ExplodeAtPosition(targetPos); return; } transform.rotation = Quaternion.LookRotation(toTarget.normalized); transform.position += toTarget.normalized * step; } private void ExplodeAtPosition(Vector3 pos) { if (hasExploded) return; hasExploded = true; // 1. 生成爆炸视觉特效 GameObject expPrefab = explosionVfx; #if UNITY_EDITOR if (expPrefab == null) { expPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/TJGeneratorLibEffects/Prefabs/Combat/Explosions/MagicExplosion/MagicExplosionFire.prefab"); } #endif if (expPrefab != null) { var expGo = Instantiate(expPrefab, pos, Quaternion.identity); expGo.transform.localScale = Vector3.one * (explosionRadius / 1.8f); Destroy(expGo, 3.0f); } // 2. 结算范围伤害 ApplyAreaDamage(pos, explosionRadius, damage); // 3. 6级解锁:生成持续 3 秒的灼烧火海(带有地面燃烧火焰特效) if (createBurnField) { GameObject burnArea = new GameObject("BurnField"); burnArea.transform.position = new Vector3(pos.x, 0.05f, pos.z); var area = burnArea.AddComponent(); GameObject fieldPrefab = burnFieldVfx; #if UNITY_EDITOR if (fieldPrefab == null) { fieldPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/TJGeneratorLibEffects/Prefabs/Environment/Fire/Cartoon/Field Intense/FireFieldIntenseRed.prefab"); } #endif area.Initialize(owner, damage * 0.15f, explosionRadius, 3.0f, fieldPrefab); } Destroy(gameObject); } private void ApplyAreaDamage(Vector3 center, float radius, float dmg) { if (owner == null) return; UnitTeam myTeam = owner.GetComponent(); if (myTeam == null) return; float sqrRadius = radius * radius; var allUnits = Object.FindObjectsOfType(); foreach (var t in allUnits) { if (t == null || !t.gameObject.activeInHierarchy || !myTeam.IsEnemy(t.gameObject)) continue; Vector3 flatOffset = t.transform.position - center; flatOffset.y = 0f; if (flatOffset.sqrMagnitude <= sqrRadius) { var targetUnit = t.GetComponentInParent(); if (targetUnit != null) { targetUnit.ReceiveDamage(dmg, owner); } else { var bar = t.GetComponentInParent(); if (bar != null) bar.TakeDamage(dmg, owner); } } } } } /// /// 6 级【灼烧火海】:在地面持续 3 秒,每秒对进入范围的敌人造成持续燃烧伤害 /// public class MageBurnArea : MonoBehaviour { private GameObject owner; private float dps; private float radius; private float lifetime; public void Initialize(GameObject ownerObj, float burnDps, float r, float duration, GameObject vfxPrefab) { owner = ownerObj; dps = burnDps; radius = r; lifetime = duration; // 实例化地面烈焰燃烧粒子特效(严格保持 X = -90° 旋转水平平铺地面) if (vfxPrefab != null) { Quaternion rot = Quaternion.Euler(-90f, 0f, 0f); var vfxInstance = Instantiate(vfxPrefab, transform.position, rot); vfxInstance.transform.rotation = rot; vfxInstance.transform.SetParent(transform, true); vfxInstance.transform.localScale = Vector3.one * (radius / 1.5f); } StartCoroutine(BurnRoutine()); Destroy(gameObject, duration + 0.1f); } private IEnumerator BurnRoutine() { float timer = 0f; float tickInterval = 0.5f; while (timer < lifetime) { yield return new WaitForSeconds(tickInterval); timer += tickInterval; if (owner == null) break; UnitTeam myTeam = owner.GetComponent(); if (myTeam == null) break; float sqrRadius = radius * radius; var allUnits = Object.FindObjectsOfType(); foreach (var t in allUnits) { if (t == null || !t.gameObject.activeInHierarchy || !myTeam.IsEnemy(t.gameObject)) continue; Vector3 flatOffset = t.transform.position - transform.position; flatOffset.y = 0f; if (flatOffset.sqrMagnitude <= sqrRadius) { var unit = t.GetComponentInParent(); if (unit != null) { unit.ReceiveDamage(dps * tickInterval, owner); } else { var bar = t.GetComponentInParent(); if (bar != null) bar.TakeDamage(dps * tickInterval, owner); } } } } } }