根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加项目使用指南 - 完善API接口说明 - 修正错误的配置示例 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
This commit is contained in:
1 parent
8cbe8e000b
commit
1a84ea999e
36 files changed
+32672
-136
No files matched your search
@@ -0,0 +1,343 @@
|
||||
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} 米!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: Ci9JsCyrAnOV0Wkgm0Ge0UhdD9F0uLHn/+8LRyJJlz3lAohHk0flNZI=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user