Files
daluandou/Assets/Scripts/技能脚本/EliteSoldierSkill.cs
T
huadexuan a9869b7419 ```
feat(EliteSoldier): 精锐士兵重构为刺客定位技能体系

- 完全重写EliteSoldierSkill.cs,将职业定位从大剑狂战改为刺客
- 实现1级【裂空突袭】:飞天斩击敌方后排,造成200%物理暴击伤害
- 添加3级【弱点锁定】:伤害提升至250%,暴击率+25%,落地移速+35%
- 添加6级【影袭破防】:目标眩晕1.2秒且物理易伤35%
- 添加10级【死斗连环】:击杀后连斩最多3次,无缝腾空攻击下一个后排
- 优化动画系统:飞跃期间关闭root motion和nav mesh agent避免位移冲突
- 更新动画控制器override controller的clip引用
- 在预制体中添加isStunImmune字段用于10级霸体效果
- 更新游戏说明文档中的技能描述和开发日志
```
2026-10-12 23:10:06 +08:00

389 lines
14 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.AI;
/// <summary>
/// 精锐士兵技能:裂空突袭(刺客定位 · 1/3/6/10 级完整阶梯)
/// 1级【裂空突袭】:核心招牌直接解锁!第一帧立刻起跳,空中划出高抛物线,30帧(约0.95s)落地砸击敌方后排,造成 200% 暴击物理伤害(播放 SwordWaveWhite)
/// 3级【弱点锁定】:突袭伤害提升至 250%,暴击率额外 +25%,落地后 3 秒内自身移速 +35% 紧逼残血
/// 6级【影袭破防】:突袭下砸震碎防御,目标陷入【1.2 秒硬眩晕】(挂 StunStars)且受到物理伤害增加 35%(持续 4s)
/// 10级【死斗连环】:飞天斩若击杀目标,立即从当前落地点再次腾空飞斩下一个后排目标!最多连斩 3 次!
/// </summary>
public class EliteSoldierSkill : UnitSkill
{
[Header("刺客飞跃参数")]
[Tooltip("飞跃最高拱起高度")]
public float leapPeakHeight = 3.5f;
[Tooltip("落地吸附距离")]
public float landOffsetDistance = 0.8f;
[Tooltip("空中飞跃时长 (第1帧起跳到第30帧落地,精确为 0.95 秒)")]
public float leapDuration = 0.95f;
[Header("特效资源")]
public GameObject swordWavePrefab; // 落地白剑浪特效 (SwordWaveWhite)
public GameObject groundImpactPrefab; // 落地冲击特效 (ShockwaveRing 或 TargetHitExplosion)
private bool isSkillPending = false;
private GameObject leapTarget;
private Coroutine leapCoroutine;
private Coroutine consecutiveCoroutine;
private int consecutiveKillCount = 0; // 10 级连斩计数 (最多 3 次)
private bool prevRootMotionState = true;
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;
consecutiveKillCount = 0;
ExecuteLeapAttack();
}
/// <summary>
/// 执行一次飞天斩(第1帧起跳到第30帧落地)
/// </summary>
private void ExecuteLeapAttack()
{
EnsureUnit();
if (unit == null) return;
// 刺客核心:优先锁定敌方后排最深处的活体敌人
leapTarget = FindBacklineTarget(unit.Target);
if (leapTarget == null)
{
RestoreAgentAndState();
return;
}
Vector3 toTarget = leapTarget.transform.position - unit.transform.position;
toTarget.y = 0f;
if (toTarget.sqrMagnitude > 0.01f)
{
unit.transform.rotation = Quaternion.LookRotation(toTarget.normalized, Vector3.up);
}
isSkillPending = true;
// 1. 关闭 NavMeshAgent 与 Root Motion,位移完全由抛物线代码接管,杜绝任何位移打架漂移
NavMeshAgent agent = unit.GetComponent<NavMeshAgent>();
if (agent != null && agent.enabled)
{
agent.enabled = false;
}
Animator anim = GetComponent<Animator>();
if (anim != null)
{
prevRootMotionState = anim.applyRootMotion;
anim.applyRootMotion = false; // 飞天位移期间禁用根运动,防止动画位移叠加漂移
anim.Play("skill", 0, 0f); // 强制从第 0 帧(第一帧立刻起跳)播放
}
unit.PlaySkillAnimation(OnSkillAnimationFinished, 2.067f);
if (leapCoroutine != null) StopCoroutine(leapCoroutine);
leapCoroutine = StartCoroutine(LeapTrajectoryRoutine(leapTarget));
Debug.Log($"<color=orange>【刺客·精锐士兵】第 {consecutiveKillCount + 1} 斩发动!</color> 第一帧腾空起跳,直扑后排: {leapTarget.name}");
}
/// <summary>
/// 飞天抛物线协程:第 0 秒立刻起跳,0.95 秒(第30帧)精准落地
/// </summary>
private IEnumerator LeapTrajectoryRoutine(GameObject target)
{
Vector3 startPos = unit.transform.position;
startPos.y = 0.16f; // 保证起跳起点严格贴地
float elapsed = 0f;
while (elapsed < leapDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / leapDuration);
// 获取后排目标当前坐标(支持目标位移时动态吸附)
Vector3 targetPos = (target != null && target.activeInHierarchy)
? target.transform.position
: startPos + unit.transform.forward * 4f;
Vector3 toTargetVec = targetPos - startPos;
Vector3 landTargetPos;
// 保持在目标身前适量距离落位,避免模型穿插
if (toTargetVec.sqrMagnitude > landOffsetDistance * landOffsetDistance)
{
landTargetPos = targetPos - toTargetVec.normalized * landOffsetDistance;
}
else
{
landTargetPos = targetPos;
}
landTargetPos.y = 0.16f;
// 水平位置插值:使用匀速/平滑过渡
Vector3 curPos = Vector3.Lerp(startPos, landTargetPos, t);
// 垂直抛物线:4 * H * t * (1 - t),在 t=0.5 (约第15帧/0.48s) 达到最高点 3.5 米
curPos.y += 4f * leapPeakHeight * t * (1f - t);
unit.transform.position = curPos;
// 飞行中面朝目标
Vector3 lookDir = landTargetPos - curPos;
lookDir.y = 0f;
if (lookDir.sqrMagnitude > 0.01f)
{
unit.transform.rotation = Quaternion.LookRotation(lookDir.normalized, Vector3.up);
}
yield return null;
}
// 第 0.95 秒(第30帧)精准落地
Vector3 finalLandPos = unit.transform.position;
finalLandPos.y = 0.16f;
if (NavMesh.SamplePosition(finalLandPos, out NavMeshHit hit, 2.0f, NavMesh.AllAreas))
{
unit.transform.position = hit.position;
}
else
{
unit.transform.position = finalLandPos;
}
}
/// <summary>
/// 动画命中帧方法:大剑落地砸击目标头顶 (约第 28~30 帧 / 0.904s 处)
/// </summary>
public void OnSkillHit1()
{
if (!isSkillPending) return;
isSkillPending = false;
EnsureUnit();
if (unit == null) return;
GameObject opponent = (leapTarget != null && leapTarget.activeInHierarchy) ? leapTarget : unit.Target;
if (opponent == null) return;
int level = unit.level;
// 1. 播放破空剑浪与地面冲击特效
PlayImpactVfx(opponent.transform.position);
// 2. 计算刺客伤害 (1级 200%, 3级+ 250%)
float damageMultiplier = level >= 3 ? 2.50f : 2.00f;
// 3级解锁:暴击率额外 +25%
float originalCrit = unit.critRate;
if (level >= 3)
{
unit.critRate += 0.25f;
}
UnitBase targetUnit = opponent.GetComponentInParent<UnitBase>();
WorldHealthBar targetBar = opponent.GetComponentInParent<WorldHealthBar>();
if (targetBar == null && targetUnit != null)
{
targetBar = targetUnit.GetComponent<WorldHealthBar>();
}
// 施加技能重劈伤害
float damage = unit.attackPower * damageMultiplier;
if (targetUnit != null)
{
targetUnit.ReceiveDamage(damage, unit.gameObject);
}
else if (targetBar != null)
{
targetBar.TakeDamage(damage, unit.gameObject);
}
// 恢复暴击率
if (level >= 3)
{
unit.critRate = originalCrit;
// 3级解锁:落地后 3 秒移速 +35%
StartCoroutine(TemporarySpeedBoostRoutine(3.0f, 1.35f));
}
// 3. 6级解锁【影袭破防】:目标陷入 1.2 秒硬眩晕
if (level >= 6 && targetUnit != null && targetUnit.IsAlive)
{
targetUnit.ApplyStun(1.2f);
Debug.Log($"<color=yellow>【影袭破防】命中!</color> 目标 {targetUnit.gameObject.name} 被震晕 1.2 秒!");
}
Debug.Log($"<color=orange>【裂空突袭】大剑劈中后排!</color> 造成伤害倍率: {damageMultiplier * 100:F0}% (等级: {level})");
// 4. 10级解锁【死斗连环】:若斩杀目标,短暂停顿 0.25 秒后立即无缝起跳下一个后排!
bool isTargetKilled = (targetBar != null && targetBar.IsDead) || (targetUnit != null && targetUnit.IsDead) || (targetBar != null && targetBar.currentHealth <= 0f);
if (level >= 10 && isTargetKilled)
{
consecutiveKillCount++;
if (consecutiveKillCount < 3)
{
Debug.Log($"<color=red>【死斗连环】斩杀触发!</color> 精锐士兵斩杀后排目标,准备连斩下一个后排!(连击: {consecutiveKillCount})");
if (consecutiveCoroutine != null) StopCoroutine(consecutiveCoroutine);
consecutiveCoroutine = StartCoroutine(TriggerNextConsecutiveLeapDelayed(0.25f));
}
else
{
consecutiveKillCount = 0;
}
}
else
{
consecutiveKillCount = 0;
}
}
/// <summary>
/// 连斩平滑衔接:后摇停顿 0.25 秒(让玩家看清落地砍杀),随后直接从当前落地位置再次腾空!
/// </summary>
private IEnumerator TriggerNextConsecutiveLeapDelayed(float delay)
{
yield return new WaitForSeconds(delay);
if (unit != null && unit.IsAlive)
{
ExecuteLeapAttack();
}
}
private IEnumerator TemporarySpeedBoostRoutine(float duration, float mult)
{
if (unit == null) yield break;
float originalSpeed = unit.speed;
unit.speed = originalSpeed * mult;
yield return new WaitForSeconds(duration);
if (unit != null)
{
unit.speed = originalSpeed;
}
}
private void PlayImpactVfx(Vector3 landPos)
{
// 1. 播放 SwordWaveWhite 白剑浪 (贴地平铺)
GameObject wavePrefab = swordWavePrefab;
#if UNITY_EDITOR
if (wavePrefab == null)
{
wavePrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Combat/Sword/Wave/SwordWaveWhite.prefab");
}
#endif
if (wavePrefab != null && unit != null)
{
Vector3 vfxPos = landPos + Vector3.up * 0.2f;
Quaternion rot = Quaternion.Euler(0f, unit.transform.eulerAngles.y, 0f);
var vfx = Instantiate(wavePrefab, vfxPos, rot);
vfx.transform.rotation = rot;
vfx.transform.localScale = Vector3.one * 1.5f;
Destroy(vfx, 1.5f);
}
// 2. 播放地面震波光圈
GameObject impactPrefab = groundImpactPrefab;
#if UNITY_EDITOR
if (impactPrefab == null)
{
impactPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/特效/扩散光圈/ShockwaveRing.prefab");
}
#endif
if (impactPrefab != null)
{
var ring = Instantiate(impactPrefab, new Vector3(landPos.x, 0.05f, landPos.z), impactPrefab.transform.rotation);
Destroy(ring, 1.5f);
}
}
/// <summary>
/// 寻找敌方阵型最远端/最深处的后排存活敌人
/// </summary>
private GameObject FindBacklineTarget(GameObject currentTarget)
{
if (unit == null) return currentTarget;
UnitTeam myTeam = unit.GetComponent<UnitTeam>();
var allTeams = Object.FindObjectsOfType<UnitTeam>();
GameObject furthestBacklineEnemy = null;
float maxDepth = -999f;
foreach (var t in allTeams)
{
if (t == null || !t.gameObject.activeInHierarchy) continue;
// 双重防呆判断:支持 UnitTeam 阵营比对与 Unity Tag 敌我比对
bool isEnemy = (myTeam != null && myTeam.IsEnemy(t.gameObject))
|| (unit.CompareTag("Friend") && (t.CompareTag("Enemy") || t.gameObject.name.Contains("小怪") || t.gameObject.name.Contains("敌方")));
if (!isEnemy) continue;
var targetUnit = t.GetComponentInParent<UnitBase>();
if (targetUnit != null && targetUnit.IsDead) continue;
var bar = t.GetComponentInParent<WorldHealthBar>();
if (bar != null && bar.IsDead) continue;
GameObject enemyGo = targetUnit != null ? targetUnit.gameObject : t.gameObject;
// 依据战场纵深 (Z 坐标越大即越靠后排敌方阵地)
float depth = enemyGo.transform.position.z;
if (depth > maxDepth)
{
maxDepth = depth;
furthestBacklineEnemy = enemyGo;
}
}
return furthestBacklineEnemy != null ? furthestBacklineEnemy : currentTarget;
}
private void RestoreAgentAndState()
{
isSkillPending = false;
NavMeshAgent agent = unit != null ? unit.GetComponent<NavMeshAgent>() : null;
if (agent != null && !agent.enabled)
{
agent.enabled = true;
}
Animator anim = GetComponent<Animator>();
if (anim != null)
{
anim.applyRootMotion = prevRootMotionState;
}
}
private void OnSkillAnimationFinished()
{
RestoreAgentAndState();
}
private void OnDestroy()
{
if (leapCoroutine != null) StopCoroutine(leapCoroutine);
if (consecutiveCoroutine != null) StopCoroutine(consecutiveCoroutine);
RestoreAgentAndState();
}
}