优化 WeaponLance 的攻击逻辑
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Components;
|
||||
using CustomUtility;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
@@ -33,14 +34,20 @@ namespace Entity.Weapon
|
||||
private Collider[] _hitResults;
|
||||
private readonly HashSet<int> _hitEntityIds = new();
|
||||
|
||||
// 枪尖判定半径。
|
||||
private float _hitRadius;
|
||||
// 前戳矩形判定的横向半宽。
|
||||
private float _hitHalfWidth;
|
||||
// 前戳矩形判定盒体的总高度。
|
||||
private float _hitHeight;
|
||||
// 盒体中心相对战斗平面的高度偏移。
|
||||
private float _hitCenterYOffset;
|
||||
// 从判定起点向前延伸的有效刺击长度。
|
||||
private float _pierceLength;
|
||||
// 判定起点相对武器当前位置的前移量。
|
||||
private float _forwardOffset;
|
||||
// 武器模型本身向前突刺的位移长度。
|
||||
private float _thrustDistance;
|
||||
// 本次攻击锁定的前戳方向,避免受位移动画中的武器位置影响。
|
||||
private Vector3 _strikeDirection = Vector3.forward;
|
||||
// 本次攻击锁定的矩形判定中心。
|
||||
private Vector3 _strikeCenter;
|
||||
|
||||
public override ImpactData GetImpactData()
|
||||
{
|
||||
@@ -59,18 +66,18 @@ namespace Entity.Weapon
|
||||
{
|
||||
StopAttackTween(false);
|
||||
FaceTargetImmediately();
|
||||
CacheStrikeSnapshot();
|
||||
|
||||
_isAttacking = true;
|
||||
_attackParent = CachedTransform.parent;
|
||||
CachedTransform.SetParent(null);
|
||||
|
||||
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * _thrustDistance;
|
||||
Vector3 targetPos = CachedTransform.position + _strikeDirection * _pierceLength;
|
||||
_attackSequence = DOTween.Sequence();
|
||||
_attackSequence.Append(CachedTransform.DOMove(targetPos, _attackDuration).SetEase(Ease.OutQuad));
|
||||
_attackSequence.AppendCallback(() =>
|
||||
{
|
||||
Vector3 strikeCenter = GetStrikeCenter();
|
||||
_attackEffect?.Play(this, strikeCenter, _target, _hitRadius);
|
||||
_attackEffect?.Play(this, _strikeCenter, _target, _hitHalfWidth);
|
||||
ApplyPierceDamage();
|
||||
|
||||
if (_attackParent != null)
|
||||
@@ -122,7 +129,17 @@ namespace Entity.Weapon
|
||||
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
||||
}
|
||||
|
||||
private Vector3 GetStrikeStart()
|
||||
private void CacheStrikeSnapshot()
|
||||
{
|
||||
_strikeDirection = ResolvePlanarForward();
|
||||
Vector3 strikeStart = CachedTransform.position;
|
||||
strikeStart.y = ResolveStrikePlaneY();
|
||||
strikeStart += _strikeDirection * _forwardOffset;
|
||||
_strikeCenter = strikeStart + _strikeDirection * (_pierceLength * 0.5f);
|
||||
_strikeCenter.y += _hitCenterYOffset;
|
||||
}
|
||||
|
||||
private Vector3 ResolvePlanarForward()
|
||||
{
|
||||
Vector3 forward = CachedTransform.forward;
|
||||
forward.y = 0f;
|
||||
@@ -132,59 +149,35 @@ namespace Entity.Weapon
|
||||
}
|
||||
|
||||
forward.Normalize();
|
||||
return CachedTransform.position + forward * _forwardOffset;
|
||||
}
|
||||
|
||||
private Vector3 GetStrikeEnd()
|
||||
{
|
||||
Vector3 start = GetStrikeStart();
|
||||
Vector3 forward = CachedTransform.forward;
|
||||
forward.y = 0f;
|
||||
if (forward.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
forward = Vector3.forward;
|
||||
}
|
||||
|
||||
forward.Normalize();
|
||||
return start + forward * _pierceLength;
|
||||
}
|
||||
|
||||
private Vector3 GetStrikeCenter()
|
||||
{
|
||||
return Vector3.Lerp(GetStrikeStart(), GetStrikeEnd(), 0.5f);
|
||||
return forward;
|
||||
}
|
||||
|
||||
private void ApplyPierceDamage()
|
||||
{
|
||||
if (_hitRadius <= 0f || _pierceLength <= 0f) return;
|
||||
if (_hitHalfWidth <= 0f || _pierceLength <= 0f) return;
|
||||
|
||||
Vector3 strikeStart = GetStrikeStart();
|
||||
Vector3 strikeEnd = GetStrikeEnd();
|
||||
Vector3 strikeDirection = strikeEnd - strikeStart;
|
||||
strikeDirection.y = 0f;
|
||||
Vector3 strikeDirection = _strikeDirection;
|
||||
if (strikeDirection.sqrMagnitude <= Mathf.Epsilon) return;
|
||||
|
||||
strikeDirection.Normalize();
|
||||
float halfAngle = Mathf.Rad2Deg * Mathf.Atan2(_hitRadius, Mathf.Max(0.01f, _pierceLength));
|
||||
if (TryQueueSectorCollisionQuery(strikeStart, _pierceLength, in strikeDirection, halfAngle,
|
||||
Mathf.Max(1, _maxHitColliders)))
|
||||
Vector3 broadPhaseCenter = _strikeCenter;
|
||||
Quaternion broadPhaseRotation = Quaternion.LookRotation(strikeDirection, Vector3.up);
|
||||
|
||||
if (TryQueueRectangleCollisionQuery(broadPhaseCenter, _hitHalfWidth, _pierceLength * 0.5f,
|
||||
strikeDirection, Mathf.Max(1, _maxHitColliders)))
|
||||
{
|
||||
_hitEntityIds.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
int capacity = Mathf.Max(1, _maxHitColliders);
|
||||
float broadPhaseRadius = _pierceLength * 0.5f + _hitRadius;
|
||||
Vector3 broadPhaseCenter = Vector3.Lerp(strikeStart, strikeEnd, 0.5f);
|
||||
|
||||
if (_hitResults == null || _hitResults.Length != capacity)
|
||||
{
|
||||
_hitResults = new Collider[capacity];
|
||||
}
|
||||
|
||||
int hitCount = Physics.OverlapSphereNonAlloc(broadPhaseCenter, broadPhaseRadius, _hitResults, _hitMask,
|
||||
QueryTriggerInteraction.Collide);
|
||||
|
||||
Vector3 halfExtents = new(_hitHalfWidth, _hitHeight * 0.5f, _pierceLength * 0.5f);
|
||||
int hitCount = Physics.OverlapBoxNonAlloc(broadPhaseCenter, halfExtents, _hitResults, broadPhaseRotation,
|
||||
_hitMask, QueryTriggerInteraction.Collide);
|
||||
_hitEntityIds.Clear();
|
||||
for (int i = 0; i < hitCount; i++)
|
||||
{
|
||||
@@ -195,24 +188,55 @@ namespace Entity.Weapon
|
||||
if (targetable == null || !targetable.Available || targetable.IsDead) continue;
|
||||
if (!_hitEntityIds.Add(targetable.Id)) continue;
|
||||
|
||||
if (!IsTargetInsidePierce(targetable, strikeStart, strikeDirection)) continue;
|
||||
if (!IsTargetInsidePierce(targetable, broadPhaseCenter, strikeDirection)) continue;
|
||||
|
||||
AIUtility.PerformCollision(targetable, this);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsTargetInsidePierce(TargetableObject targetable, Vector3 strikeStart, Vector3 strikeDirection)
|
||||
private bool IsTargetInsidePierce(TargetableObject targetable, Vector3 strikeCenter, Vector3 strikeDirection)
|
||||
{
|
||||
Vector3 toTarget = targetable.CachedTransform.position - strikeStart;
|
||||
toTarget.y = 0f;
|
||||
|
||||
float projection = Vector3.Dot(toTarget, strikeDirection);
|
||||
if (projection < 0f || projection > _pierceLength) return false;
|
||||
|
||||
Vector3 closestPoint = strikeStart + strikeDirection * projection;
|
||||
Vector3 delta = targetable.CachedTransform.position - closestPoint;
|
||||
Vector3 delta = targetable.CachedTransform.position - strikeCenter;
|
||||
delta.y = 0f;
|
||||
return delta.sqrMagnitude <= _hitRadius * _hitRadius;
|
||||
|
||||
Vector3 right = Vector3.Cross(Vector3.up, strikeDirection);
|
||||
float forwardDistance = Vector3.Dot(delta, strikeDirection);
|
||||
float lateralDistance = Vector3.Dot(delta, right);
|
||||
float targetRadius = ResolveTargetCollisionRadius(targetable);
|
||||
|
||||
return Mathf.Abs(forwardDistance) <= _pierceLength * 0.5f + targetRadius &&
|
||||
Mathf.Abs(lateralDistance) <= _hitHalfWidth + targetRadius;
|
||||
}
|
||||
|
||||
private static float ResolveTargetCollisionRadius(TargetableObject targetable)
|
||||
{
|
||||
if (targetable == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
MovementComponent movementComponent = targetable.GetComponent<MovementComponent>();
|
||||
return movementComponent != null ? Mathf.Max(0f, movementComponent.EnemyBodyRadius) : 0f;
|
||||
}
|
||||
|
||||
private float ResolveStrikePlaneY()
|
||||
{
|
||||
if (_target != null && _target.Available)
|
||||
{
|
||||
return _target.CachedTransform.position.y;
|
||||
}
|
||||
|
||||
if (_attackParent != null)
|
||||
{
|
||||
return _attackParent.position.y;
|
||||
}
|
||||
|
||||
if (CachedTransform.parent != null)
|
||||
{
|
||||
return CachedTransform.parent.position.y;
|
||||
}
|
||||
|
||||
return CachedTransform.position.y;
|
||||
}
|
||||
|
||||
protected override bool OnWeaponShow(object userData)
|
||||
@@ -226,15 +250,21 @@ namespace Entity.Weapon
|
||||
_cachedRotation = CachedTransform.rotation;
|
||||
|
||||
WeaponLanceParamsData paramsData = _weaponData.ParamsData;
|
||||
_hitRadius = paramsData != null && paramsData.HitRadius > 0f
|
||||
? Mathf.Max(0.1f, paramsData.HitRadius)
|
||||
: 0.45f;
|
||||
_thrustDistance = paramsData != null && paramsData.ThrustDistance > 0f
|
||||
? paramsData.ThrustDistance
|
||||
: _weaponData.AttackRange;
|
||||
float configuredHalfWidth = paramsData != null && paramsData.HitHalfWidth > 0f
|
||||
? paramsData.HitHalfWidth
|
||||
: paramsData != null && paramsData.HitRadius > 0f
|
||||
? paramsData.HitRadius
|
||||
: 0.45f;
|
||||
_hitHalfWidth = Mathf.Max(0.1f, configuredHalfWidth);
|
||||
_hitHeight = paramsData != null && paramsData.HitHeight > 0f
|
||||
? Mathf.Max(0.2f, paramsData.HitHeight)
|
||||
: 4f;
|
||||
_hitCenterYOffset = paramsData != null ? paramsData.HitCenterYOffset : 0f;
|
||||
_pierceLength = paramsData != null && paramsData.PierceLength > 0f
|
||||
? paramsData.PierceLength
|
||||
: Mathf.Max(_weaponData.AttackRange, _thrustDistance);
|
||||
: paramsData != null && paramsData.ThrustDistance > 0f
|
||||
? paramsData.ThrustDistance
|
||||
: _weaponData.AttackRange;
|
||||
_forwardOffset = paramsData != null && paramsData.ForwardOffset > 0f
|
||||
? paramsData.ForwardOffset
|
||||
: 0f;
|
||||
@@ -260,7 +290,7 @@ namespace Entity.Weapon
|
||||
_hitResults = new Collider[colliderCapacity];
|
||||
}
|
||||
|
||||
_attackEffect = new KnifeRangeAttackEffect();
|
||||
_attackEffect = new LanceThrustAttackEffect();
|
||||
|
||||
if (_weaponData.OwnerCamp == CampType.Player)
|
||||
{
|
||||
@@ -321,5 +351,9 @@ namespace Entity.Weapon
|
||||
_attackParent = null;
|
||||
_hitEntityIds.Clear();
|
||||
}
|
||||
|
||||
public float HitHalfWidth => _hitHalfWidth;
|
||||
public float PierceLength => _pierceLength;
|
||||
public Vector3 StrikeDirection => _strikeDirection;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user