根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加项目使用指南 - 完善API接口说明 - 修正错误的配置示例 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
This commit is contained in:
1 parent
f6f6666244
commit
ec35c6c154
35 files changed
+54920
-311
No files matched your search
@@ -1,49 +1,332 @@
|
||||
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("小型魔爆参数")]
|
||||
[Min(0.1f)]
|
||||
public float explosionRadius = 1.8f;
|
||||
[Header("火球配置")]
|
||||
[Tooltip("火球飞行速度")]
|
||||
public float projectileSpeed = 16f;
|
||||
|
||||
[Min(0f)]
|
||||
public float explosionDamage = 85f;
|
||||
[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()
|
||||
{
|
||||
GameObject target = unit.Target;
|
||||
UnitTeam myTeam = unit.GetComponent<UnitTeam>();
|
||||
if (target == null || myTeam == null)
|
||||
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 center = target.transform.position;
|
||||
float radiusSquared = explosionRadius * explosionRadius;
|
||||
HashSet<UnitBase> hitUnits = new HashSet<UnitBase>();
|
||||
Vector3 targetPos = target.transform.position + Vector3.up * 0.8f;
|
||||
Vector3 toTarget = targetPos - transform.position;
|
||||
float step = speed * Time.deltaTime;
|
||||
|
||||
foreach (UnitTeam opponentTeam in GameObject.FindObjectsOfType<UnitTeam>())
|
||||
if (toTarget.sqrMagnitude <= step * step || toTarget.sqrMagnitude <= 0.15f)
|
||||
{
|
||||
GameObject opponent = opponentTeam.gameObject;
|
||||
if (opponent == null
|
||||
|| opponent.transform.root == unit.transform.root
|
||||
|| !myTeam.IsEnemy(opponent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ExplodeAtPosition(targetPos);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 offset = opponent.transform.position - center;
|
||||
offset.y = 0f;
|
||||
if (offset.sqrMagnitude > radiusSquared)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
transform.rotation = Quaternion.LookRotation(toTarget.normalized);
|
||||
transform.position += toTarget.normalized * step;
|
||||
}
|
||||
|
||||
UnitBase targetUnit = opponent.GetComponentInParent<UnitBase>();
|
||||
if (targetUnit != null && hitUnits.Add(targetUnit))
|
||||
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)
|
||||
{
|
||||
targetUnit.ReceiveDamage(explosionDamage, unit.gameObject);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user