整理武器配置和重构

This commit is contained in:
2026-09-15 10:13:58 +08:00
parent 17ee268416
commit 421f8bf619
115 changed files with 1431 additions and 1905 deletions
@@ -1,30 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponSlash
{
private class AttackState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash;
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon._weaponData.Cooldown);
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (!_weapon._isAttacking)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave()
{
}
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 96b136ec326941a0821f66ddf750de29
timeCreated: 1771579419
@@ -1,47 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponSlash
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash;
public override void OnEnter()
{
if (_weapon._canAttack)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
return;
}
if (_weapon._canAttack)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnLeave()
{
}
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 57a936178b5a4ff2938df369b77631e6
timeCreated: 1771579430
@@ -1,37 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponSlash
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave()
{
}
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 89988d7be9104b0f83a423d1de2398b1
timeCreated: 1771579440
@@ -1,31 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponSlash
{
private class IdleState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToOrigin(elapseSeconds);
if (_weapon._target != null && _weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
}
}
public override void OnLeave()
{
}
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: bec8a4a6cb974111bbd70072754f9fdb
timeCreated: 1771579452
@@ -8,51 +8,37 @@ using UnityGameFramework.Runtime;
namespace SepCore.Entity
{
public partial class WeaponSlash : WeaponBase
public class WeaponSlash : WeaponBase
{
#region Property
private WeaponSlashData _weaponData;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 4f;
private Sequence _attackSequence;
private Vector3 _attackCenter;
private Quaternion _cachedAttackRotation;
private IWeaponAttackEffect _attackEffect;
[SerializeField] private float attackDuration = 0.4f;
[SerializeField] private float returnDuration = 0.4f;
[SerializeField] private float _attackDuration = 0.4f;
[SerializeField] private float _returnDuration = 0.4f;
[SerializeField] private float _swingAngleScale = 1f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private int _maxHitColliders = 32;
private Collider[] _hitResults;
private readonly HashSet<int> _hitEntityIds = new();
private float _attackRadius;
private float _attackRadiusSqr;
private float _sectorAngle;
public float SectorAngle => WeaponData.GetParamFloat(ParamType.SectorAngle);
#endregion
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
}
protected override void BuildStates()
{
RegisterState(new IdleState());
RegisterState(new CheckInRangeState());
RegisterState(new CheckOutRangeState());
RegisterState(new AttackState());
RegisterState(new WeaponSlashIdleState());
RegisterState(new WeaponSlashAimState());
RegisterState(new WeaponSlashSeekState());
RegisterState(new WeaponSlashAttackState());
}
protected override void Attack()
public override void Attack()
{
StopAttackTween(false);
FaceTargetImmediately();
@@ -61,20 +47,20 @@ namespace SepCore.Entity
_attackCenter = CachedTransform.position;
_cachedAttackRotation = CachedTransform.rotation;
float swingAngle = Mathf.Clamp(_sectorAngle * Mathf.Max(0.1f, _swingAngleScale), 15f, 170f);
float swingAngle = Mathf.Clamp(SectorAngle * Mathf.Max(0.1f, _swingAngleScale), 15f, 170f);
Quaternion startRotation = Quaternion.AngleAxis(-swingAngle * 0.5f, Vector3.up) * _cachedAttackRotation;
Quaternion endRotation = Quaternion.AngleAxis(swingAngle * 0.5f, Vector3.up) * _cachedAttackRotation;
CachedTransform.rotation = startRotation;
_attackSequence = DOTween.Sequence();
_attackSequence.Append(
CachedTransform.DORotateQuaternion(endRotation, attackDuration).SetEase(Ease.OutSine));
_attackSequence.InsertCallback(attackDuration * 0.5f, () =>
CachedTransform.DORotateQuaternion(endRotation, _attackDuration).SetEase(Ease.OutSine));
_attackSequence.InsertCallback(_attackDuration * 0.5f, () =>
{
_attackEffect?.Play(this, _attackCenter, _target, _attackRadius);
_attackEffect.Play(this, _attackCenter, _target, WeaponData.AttackRange);
ApplySectorDamage();
});
_attackSequence.Append(CachedTransform.DORotateQuaternion(_cachedAttackRotation, returnDuration)
_attackSequence.Append(CachedTransform.DORotateQuaternion(_cachedAttackRotation, _returnDuration)
.SetEase(Ease.InSine));
_attackSequence.AppendCallback(() =>
{
@@ -83,14 +69,10 @@ namespace SepCore.Entity
});
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
private void ApplySectorDamage()
{
if (_attackRadius <= 0f) return;
float attackRadius = WeaponData.AttackRange;
if (attackRadius <= 0f) return;
Vector3 forward = CachedTransform.forward;
forward.y = 0f;
@@ -100,19 +82,19 @@ namespace SepCore.Entity
}
forward.Normalize();
float halfAngle = _sectorAngle * 0.5f;
if (TryQueueSectorCollisionQuery(_attackCenter, _attackRadius, in forward, halfAngle,
float halfAngle = SectorAngle * 0.5f;
if (TryQueueSectorCollisionQuery(_attackCenter, attackRadius, in forward, halfAngle,
Mathf.Max(1, _maxHitColliders)))
{
_hitEntityIds.Clear();
return;
}
if (_hitResults == null || _hitResults.Length == 0) return;
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, _attackRadius, _hitResults, _hitMask,
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, attackRadius, _hitResults, _hitMask,
QueryTriggerInteraction.Collide);
float attackRadiusSqr = attackRadius * attackRadius;
_hitEntityIds.Clear();
for (int i = 0; i < hitCount; i++)
{
@@ -127,7 +109,7 @@ namespace SepCore.Entity
toTarget.y = 0f;
float sqrDistance = toTarget.sqrMagnitude;
if (sqrDistance <= Mathf.Epsilon || sqrDistance > _attackRadiusSqr) continue;
if (sqrDistance <= Mathf.Epsilon || sqrDistance > attackRadiusSqr) continue;
float angle = Vector3.Angle(forward, toTarget.normalized);
if (angle > halfAngle) continue;
@@ -136,53 +118,11 @@ namespace SepCore.Entity
}
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
directionToTarget.y = 0f;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
CachedTransform.rotation =
Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
}
private void RotateToOrigin(float elapseSeconds)
{
CachedTransform.rotation =
Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
}
private void FaceTargetImmediately()
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
directionToTarget.y = 0f;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
}
#region Lifecycle
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponSlashData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
StartAttackCooldown(_weaponData.Cooldown);
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
_attackRadius = Mathf.Max(0.1f, _weaponData.AttackRange);
_attackRadiusSqr = _attackRadius * _attackRadius;
float configuredSectorAngle = _weaponData.ParamsData != null ? _weaponData.ParamsData.SectorAngle : 0f;
_sectorAngle = configuredSectorAngle > 0f ? Mathf.Clamp(configuredSectorAngle, 1f, 360f) : 90f;
_cachedAttackRotation = _cachedRotation;
int capacity = Mathf.Max(1, _maxHitColliders);
if (_hitResults == null || _hitResults.Length != capacity)
@@ -191,37 +131,19 @@ namespace SepCore.Entity
}
_attackEffect = new SlashSectorAttackEffect();
if (_weaponData.OwnerCamp == CampType.Player)
{
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
_hitMask = LayerMask.GetMask("Enemy");
}
else if (_weaponData.OwnerCamp == CampType.Enemy)
{
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
_hitMask = LayerMask.GetMask("Player");
}
return true;
}
protected override void OnWeaponHide(object userData)
{
StopAttackTween(true);
CancelAttackCooldown();
_attackEffect = null;
}
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
{
BindAttackStatFromOwner(parentEntity);
}
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
{
StopAttackTween(true);
ReleaseAttackStatSubscription();
base.OnWeaponDetach(parentEntity, userData);
}
protected override void OnEnabledChanged(bool enabled)
@@ -234,7 +156,6 @@ namespace SepCore.Entity
#endregion
private void StopAttackTween(bool resetTransform)
{
if (_attackSequence != null)
@@ -252,7 +173,5 @@ namespace SepCore.Entity
_hitEntityIds.Clear();
}
public float SectorAngle => _sectorAngle;
}
}
@@ -0,0 +1,51 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 斩击的瞄准状态。
/// </summary>
public sealed class WeaponSlashAimState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Aim;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon;
public override void OnEnter()
{
if (_weapon.CanAttack)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
if (_weapon.Target == null || !_weapon.Target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (!_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
{
_weapon.TransitionTo(WeaponStateType.Seek);
return;
}
if (_weapon.CanAttack)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnLeave()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7414da82794ead4da61b1999fc7e744
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 斩击的攻击状态。
/// </summary>
public sealed class WeaponSlashAttackState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon;
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon.AttackCooldown);
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (!_weapon.IsAttacking)
{
_weapon.TransitionTo(WeaponStateType.Aim);
}
}
public override void OnLeave()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8eb95376ed449de4482bcdce19c7fbb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,35 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 斩击的待机状态。
/// </summary>
public sealed class WeaponSlashIdleState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToOrigin(elapseSeconds);
if (_weapon.Target != null && _weapon.Target.Available)
{
_weapon.TransitionTo(WeaponStateType.Seek);
}
}
public override void OnLeave()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 001c3370c7d371b40a0d7821281a14f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 斩击的搜寻状态。
/// </summary>
public sealed class WeaponSlashSeekState : WeaponStateBase
{
private WeaponSlash _weapon;
public override WeaponStateType State => WeaponStateType.Seek;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
if (_weapon.Target == null || !_weapon.Target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
{
_weapon.TransitionTo(WeaponStateType.Aim);
}
}
public override void OnLeave()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f296d8dc7c2e6a459556c44496a4619
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: