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

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

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

344 lines
13 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
/// <summary>
/// 荒野猎手技能:穿心猎矛体系(1/3/6/10 级完整阶梯)
/// 1级【穿心猎矛】:蓄力重投飞矛,自动锁定全场【距离自身最远的目标(优先狙杀敌方后排)】,造成 180% 物理伤害;且【距离越远伤害越高】(每相距 1 米额外提升 5% 伤害,最高 +45%)!附带金色破空流光特效!
/// 3级【极速重贯】:飞矛飞行速度 +40%,基础伤害提升至 230%,暴击率额外 +20%,远距离狙杀威慑力大增!
/// 6级【破阵震退】:蓄力重矛命中目标时产生破空震荡,【将其向后强力击退 2.0 米】(纯物理后退,打乱敌方后排阵型,且不产生眩晕冗余)!
/// 10级【绝命猎杀】:飞矛附带风暴雷霆之势,基础伤害飙升至 340% 极限制导高伤;【若成功斩杀目标,立即返还 50% 技能能量】,快速准备下一发致命狙杀!
/// </summary>
public class WildHunterSkill : UnitSkill
{
[Header("技能飞矛配置")]
[Tooltip("技能重矛预制体 (Assets/Prefabs/武器飞行物/技能重矛.prefab)")]
public GameObject skillSpearPrefab;
[Tooltip("每米增加的伤害比例 (默认 5%)")]
public float damageBonusPerMeter = 0.05f;
[Tooltip("距离伤害加成上限 (默认 45%)")]
public float maxDistanceBonus = 0.45f;
[Tooltip("6级击退距离 (米)")]
public float knockbackDistance = 2.0f;
private bool isSkillPending = false;
private GameObject currentSkillTarget;
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;
// 1级核心机制:自动锁定全场距离自身最远的敌方目标(后排核心/远程单位)
currentSkillTarget = FindFurthestEnemy();
if (currentSkillTarget == null) return;
// 技能施法朝向:应用 unit.modelFacingOffset 保证与普攻一致的朝向修正
Vector3 toTarget = currentSkillTarget.transform.position - unit.transform.position;
toTarget.y = 0f;
if (toTarget.sqrMagnitude > 0.001f)
{
Quaternion targetRotation = Quaternion.LookRotation(toTarget.normalized, Vector3.up);
unit.transform.rotation = Quaternion.Euler(0f, targetRotation.eulerAngles.y + unit.modelFacingOffset, 0f);
}
isSkillPending = true;
// 播放投掷动画,由 OnSkillHit1 帧投出重矛
unit.PlaySkillAnimation(OnSkillAnimationFinished, 2.0f);
Debug.Log($"<color=orange>【荒野猎手】开始蓄力【穿心猎矛】!</color> 目标锁定全场最远敌方: {currentSkillTarget.name} (距离: {Vector3.Distance(unit.transform.position, currentSkillTarget.transform.position):F1}m)");
}
/// <summary>
/// 动画事件帧方法:在手臂向后蓄力后猛烈投掷的瞬间触发 (OnSkillHit1)
/// </summary>
public void OnSkillHit1()
{
if (!isSkillPending) return;
isSkillPending = false;
EnsureUnit();
if (unit == null) return;
GameObject opponent = (currentSkillTarget != null && currentSkillTarget.activeInHierarchy) ? currentSkillTarget : FindFurthestEnemy();
if (opponent == null) return;
int level = unit.level;
// 1. 确定重矛预制体 (技能重矛自带金色破空粒子拖尾 SparkleMissileYellow)
GameObject prefab = skillSpearPrefab;
#if UNITY_EDITOR
if (prefab == null)
{
prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/武器飞行物/技能重矛.prefab");
}
#endif
if (prefab == null) prefab = unit.projectilePrefab;
if (prefab == null) return;
// 2. 确定出生点 (右手 pos)
Transform spawnPoint = unit.projectileSpawnPoint != null ? unit.projectileSpawnPoint : unit.transform;
Vector3 spawnPos = spawnPoint.position;
// 3. 计算伤害与距离加成 (1级 180%, 3级 230%, 10级 340%)
float baseMultiplier = 1.80f;
if (level >= 10) baseMultiplier = 3.40f;
else if (level >= 3) baseMultiplier = 2.30f;
float distance = Vector3.Distance(unit.transform.position, opponent.transform.position);
float distanceBonus = Mathf.Min(distance * damageBonusPerMeter, maxDistanceBonus);
float finalMultiplier = baseMultiplier * (1f + distanceBonus);
// 3级解锁:暴击率额外 +20%,飞行速度提升 40%
float spearSpeed = (level >= 3) ? (unit.projectileSpeed * 1.4f) : unit.projectileSpeed;
bool isCrit = (level >= 3) && (Random.value < (unit.critRate + 0.20f));
float totalDamage = unit.attackPower * finalMultiplier * (isCrit ? unit.critMultiplier : 1.0f);
// 4. 实例化技能重矛
Vector3 flatDirection = (opponent.transform.position - spawnPos);
flatDirection.y = 0f;
Quaternion rot = Quaternion.LookRotation(flatDirection.normalized, Vector3.up);
GameObject spearGo = Instantiate(prefab, spawnPos, rot);
// 5. 挂载技能专属落弹与结算处理脚本
var runner = spearGo.AddComponent<WildHunterSpearRunner>();
runner.Launch(opponent, spawnPos, totalDamage, unit, spearSpeed, level, knockbackDistance);
Debug.Log($"<color=yellow>【穿心猎矛】破空投出!</color> 目标: {opponent.name}, 基础倍率: {baseMultiplier * 100:F0}%, 距离加成: +{distanceBonus * 100:F0}%, 最终伤害: {totalDamage:F1} (等级: {level})");
}
/// <summary>
/// 寻找全场距离自身最远的存活敌方目标
/// </summary>
private GameObject FindFurthestEnemy()
{
if (unit == null) return null;
UnitTeam myTeam = unit.GetComponent<UnitTeam>();
var allTeams = Object.FindObjectsOfType<UnitTeam>();
GameObject furthestEnemy = null;
float maxDistanceSq = -1f;
Vector3 myPos = unit.transform.position;
foreach (var t in allTeams)
{
if (t == null || !t.gameObject.activeInHierarchy) continue;
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;
float distSq = (enemyGo.transform.position - myPos).sqrMagnitude;
if (distSq > maxDistanceSq)
{
maxDistanceSq = distSq;
furthestEnemy = enemyGo;
}
}
return furthestEnemy != null ? furthestEnemy : unit.Target;
}
private void OnSkillAnimationFinished()
{
isSkillPending = false;
}
private void OnDestroy()
{
isSkillPending = false;
}
}
/// <summary>
/// 负责技能重矛的抛物线飞行、目标命中、距离伤害结算、6级后仰击退与10级击杀返还能量
/// </summary>
public class WildHunterSpearRunner : MonoBehaviour
{
private GameObject target;
private float damage;
private UnitBase ownerUnit;
private float speed;
private int skillLevel;
private float knockbackDist;
private bool hasHit = false;
private Vector3 startPosition;
private float totalDistance;
private float traveledDistance;
public void Launch(GameObject targetObj, Vector3 startPos, float totalDmg, UnitBase owner, float moveSpeed, int level, float kbDist)
{
target = targetObj;
damage = totalDmg;
ownerUnit = owner;
speed = moveSpeed > 5f ? moveSpeed : 18f;
skillLevel = level;
knockbackDist = kbDist;
// 接管飞行,禁用自带的普通直线 Projectile 组件
var defaultProj = GetComponent<Projectile>();
if (defaultProj != null) defaultProj.enabled = false;
startPosition = startPos;
traveledDistance = 0f;
if (targetObj != null)
{
Vector3 diff = targetObj.transform.position - startPosition;
diff.y = 0f;
totalDistance = Mathf.Max(0.5f, diff.magnitude);
}
else
{
totalDistance = 10f;
}
Destroy(gameObject, 5f);
}
private void Update()
{
if (hasHit) return;
if (target == null || !target.activeInHierarchy)
{
Destroy(gameObject);
return;
}
Vector3 targetCurrentPos = target.transform.position;
Vector3 offset = targetCurrentPos - transform.position;
Vector3 flatOffset = new Vector3(offset.x, 0f, offset.z);
float step = speed * Time.deltaTime;
traveledDistance += step;
if (step >= flatOffset.magnitude || flatOffset.sqrMagnitude <= 0.05f)
{
HitTargetAndExecuteEffects(target);
return;
}
// 自然抛物线推进
float t = Mathf.Clamp01(traveledDistance / totalDistance);
Vector3 groundPos = Vector3.Lerp(new Vector3(startPosition.x, 0.1f, startPosition.z), new Vector3(targetCurrentPos.x, 0.1f, targetCurrentPos.z), t);
float startY = startPosition.y;
float targetY = targetCurrentPos.y + 0.8f;
float baseY = Mathf.Lerp(startY, targetY, t);
float arcY = 4f * 1.35f * t * (1f - t); // 1.35m 拱起高度
Vector3 nextPos = new Vector3(groundPos.x, baseY + arcY, groundPos.z);
// 矛头朝向飞行轨迹切线
Vector3 velocity = nextPos - transform.position;
if (velocity.sqrMagnitude > 1e-5f)
{
transform.rotation = Quaternion.LookRotation(velocity.normalized, Vector3.up);
}
transform.position = nextPos;
}
private void HitTargetAndExecuteEffects(GameObject hitTarget)
{
if (hasHit) return;
hasHit = true;
UnitBase targetUnit = hitTarget.GetComponentInParent<UnitBase>();
WorldHealthBar targetBar = hitTarget.GetComponentInParent<WorldHealthBar>();
if (targetBar == null && targetUnit != null)
{
targetBar = targetUnit.GetComponent<WorldHealthBar>();
}
float hpBefore = targetBar != null ? targetBar.currentHealth : 0f;
// 1. 造成伤害
if (targetUnit != null)
{
targetUnit.ReceiveDamage(damage, ownerUnit != null ? ownerUnit.gameObject : null);
}
else if (targetBar != null)
{
targetBar.TakeDamage(damage, ownerUnit != null ? ownerUnit.gameObject : null);
}
Debug.Log($"<color=yellow>【穿心猎矛】命中!</color> 目标: {hitTarget.name}, 承受伤害: {damage:F1} (目标血量: {hpBefore:F1} -> {(targetBar != null ? targetBar.currentHealth : 0f):F1})");
// 2. 6级解锁【破阵震退】:后仰强力击退 2.0 米(顺着投矛飞行方向向后推)
if (skillLevel >= 6 && hitTarget != null && hitTarget.activeInHierarchy)
{
Vector3 pushDir = (hitTarget.transform.position - startPosition);
pushDir.y = 0f;
if (pushDir.sqrMagnitude < 0.001f) pushDir = Vector3.forward;
pushDir.Normalize();
ApplySmoothKnockback(hitTarget, pushDir * knockbackDist);
}
// 3. 10级解锁【绝命猎杀】:若成功斩杀目标,立即返还 50% 技能能量
bool isKilled = (targetBar != null && (targetBar.IsDead || targetBar.currentHealth <= 0f)) || (targetUnit != null && targetUnit.IsDead);
if (skillLevel >= 10 && isKilled && ownerUnit != null)
{
WorldHealthBar ownerBar = ownerUnit.GetComponent<WorldHealthBar>();
if (ownerBar != null)
{
// 返还 50% 能量(即 50 点技能充能)
ownerBar.currentEnergy = Mathf.Clamp(ownerBar.currentEnergy + ownerBar.maxEnergy * 0.50f, 0f, ownerBar.maxEnergy);
Debug.Log($"<color=cyan>【绝命猎杀】成功斩杀后排目标!</color> 立即返还 50% 技能能量!(当前能量: {ownerBar.currentEnergy:F0}/{ownerBar.maxEnergy:F0})");
}
}
Destroy(gameObject);
}
private void ApplySmoothKnockback(GameObject victim, Vector3 displacement)
{
NavMeshAgent agent = victim.GetComponent<NavMeshAgent>();
Vector3 targetPos = victim.transform.position + displacement;
// 利用 NavMesh 采样保证不推出边界或障碍物
if (NavMesh.SamplePosition(targetPos, out NavMeshHit hit, 2.0f, NavMesh.AllAreas))
{
targetPos = hit.position;
}
if (agent != null && agent.enabled && agent.isOnNavMesh)
{
agent.Warp(targetPos);
}
else
{
victim.transform.position = targetPos;
}
Debug.Log($"<color=orange>【破阵震退】触发!</color> 目标 {victim.name} 被重矛强力震退 {displacement.magnitude:F1} 米!");
}
}