``` docs(readme): 更新文档说明 - 添加项目使用指南 - 完善API接口说明 - 修正错误的配置示例 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
344 lines
13 KiB
C#
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} 米!");
|
|
}
|
|
}
|