Files
daluandou/Assets/Scripts/技能脚本/CastleMageSkill.cs
T
huadexuan ec35c6c154 根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
```
docs(readme): 更新文档说明

- 添加项目使用指南
- 完善API接口说明
- 修正错误的配置示例
```

注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
2026-10-12 18:06:46 +08:00

334 lines
12 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;
/// <summary>
/// 城堡法师技能:爆裂火球体系(1/3/6/10 级完整阶梯)
/// 1级【爆裂火球】:发射强力火球,对目标区域造成 140% 范围伤害(触发 MagicExplosionFire 爆破)
/// 3级【强化爆裂】:火球伤害提升至 175%,爆炸范围 +25%
/// 6级【灼烧火海】:爆裂处留下一片火海,对经过敌人造成 3 秒持续燃烧伤害
/// 10级【双发爆裂】:施法时连续轰出 2 颗爆裂火球,第二颗伤害提升 50% 且范围扩大
/// </summary>
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<UnitBase>();
}
}
/// <summary>
/// 当能量蓄满时触发:播放法师技能施法动作
/// </summary>
public override void ReleaseSkill()
{
EnsureUnit();
if (unit == null) return;
WorldHealthBar healthBar = unit.GetComponent<WorldHealthBar>();
if (healthBar == null || healthBar.IsDead) return;
if (unit.Target == null) return;
unit.FaceCurrentTarget(0f);
isSkillPending = true;
// 播放法师施法动作,由 OnSkillHit1 帧触发火球发射
unit.PlaySkillAnimation(OnSkillAnimationFinished, 1.8f);
Debug.Log($"<color=orange>城堡法师释放技能【爆裂火球】</color>,等待 OnSkillHit1 施法帧!");
}
/// <summary>
/// 动画命中帧方法:在法杖前挥法术成型的瞬间触发 (OnSkillHit1)
/// </summary>
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($"<color=orange>【爆裂火球】发射!</color> 伤害倍率: {damageMultiplier * 100:F0}%, 范围: {radius:F1}m (等级: {level})");
}
private IEnumerator LaunchSecondFireballDelayed(GameObject target, float damageMultiplier, float radius, bool createBurnField)
{
yield return new WaitForSeconds(0.25f);
if (target != null && target.activeInHierarchy && unit != null)
{
LaunchFireball(target, damageMultiplier, radius, createBurnField);
Debug.Log($"<color=red>【双发爆裂】追加第二发火球!</color> 伤害倍率: {damageMultiplier * 100:F0}%");
}
}
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<GameObject>("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<MageSkillFireballRunner>();
runner.Initialize(target, totalDamage, radius, unit.gameObject, projectileSpeed, explosionVfxPrefab, burnFieldVfxPrefab, createBurnField);
}
private void OnSkillAnimationFinished()
{
isSkillPending = false;
}
}
/// <summary>
/// 负责爆裂火球的飞行、落地爆炸伤害计算与灼烧火海生成
/// </summary>
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<Projectile>();
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<GameObject>("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<MageBurnArea>();
GameObject fieldPrefab = burnFieldVfx;
#if UNITY_EDITOR
if (fieldPrefab == null)
{
fieldPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("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<UnitTeam>();
if (myTeam == null) return;
float sqrRadius = radius * radius;
var allUnits = Object.FindObjectsOfType<UnitTeam>();
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<UnitBase>();
if (targetUnit != null)
{
targetUnit.ReceiveDamage(dmg, owner);
}
else
{
var bar = t.GetComponentInParent<WorldHealthBar>();
if (bar != null) bar.TakeDamage(dmg, owner);
}
}
}
}
}
/// <summary>
/// 6 级【灼烧火海】:在地面持续 3 秒,每秒对进入范围的敌人造成持续燃烧伤害
/// </summary>
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<UnitTeam>();
if (myTeam == null) break;
float sqrRadius = radius * radius;
var allUnits = Object.FindObjectsOfType<UnitTeam>();
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<UnitBase>();
if (unit != null)
{
unit.ReceiveDamage(dps * tickInterval, owner);
}
else
{
var bar = t.GetComponentInParent<WorldHealthBar>();
if (bar != null) bar.TakeDamage(dps * tickInterval, owner);
}
}
}
}
}
}