整理武器配置和重构

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,58 +1,48 @@
using System.Collections.Generic;
using SepCore.Components;
using SepCore.Definition;
using SepCore.Entity;
using GameFramework;
using SepCore.CustomUtility;
using SepCore.Timer;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace SepCore.Entity
{
public enum WeaponStateType
{
Disabled,
Attack,
Idle,
Check_OutRange,
Check_InRange,
}
/// <summary>
/// 武器类。
/// </summary>
public abstract class WeaponBase : EntityBase
{
protected const string AttachPoint = "Weapon Point";
protected bool _isAttacking = false;
[SerializeField] protected bool _isEnabled = false;
public WeaponData WeaponData { get; protected set; }
public float AttackCooldown => WeaponData.Cooldown;
public bool IsAttacking => _isAttacking;
protected ITargetSelector TargetSelector { get; set; }
protected ITargetSelector TargetSelector { get; private set; }
protected Dictionary<WeaponStateType, WeaponStateBase> _states;
protected WeaponStateBase _currentState;
protected WeaponStateMachine StateMachine { get; private set; }
protected EntityBase _target;
protected bool _canAttack;
protected TimerHandle _attackTimerHandle;
protected float _sqrRange;
public EntityBase Target => _target;
public float SqrRange => _sqrRange;
protected StatProperty AttackStat { get; private set; } = new();
private StatComponent _attackStatComponent;
private System.Action<modifier, bool> _attackStatCallback;
private static readonly List<EntityBase> s_EmptyCandidates = new();
[SerializeField] protected float _rotateSpeed = 5f;
[SerializeField] protected LayerMask _hitMask = ~0;
protected Quaternion _cachedRotation;
#region Lifecycle
@@ -60,16 +50,31 @@ namespace SepCore.Entity
{
base.OnInit(userData);
if (TargetSelector == null)
{
TargetSelector = CreateSelector(DefaultTargetSelectorType);
}
TargetSelector = new NearestTargetSelector();
}
protected sealed override void OnShow(object userData)
{
base.OnShow(userData);
WeaponData = RequireWeaponData<WeaponData>(userData);
if (WeaponData == null) return;
StartAttackCooldown(WeaponData.Cooldown);
_sqrRange = WeaponData.AttackRange * WeaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
if (WeaponData.OwnerCamp == CampType.Player)
{
gameObject.layer = Constant.Layer.PlayerWeaponLayerId;
_hitMask = Constant.Layer.EnemyLayerMask;
}
else if (WeaponData.OwnerCamp == CampType.Enemy)
{
gameObject.layer = Constant.Layer.EnemyWeaponLayerId;
_hitMask = Constant.Layer.PlayerLayerMask;
}
if (!OnWeaponShow(userData)) return;
EnsureStatesBuilt();
@@ -78,6 +83,7 @@ namespace SepCore.Entity
protected sealed override void OnHide(bool isShutdown, object userData)
{
CancelAttackCooldown();
ReleaseAttackStatSubscription();
OnWeaponHide(userData);
base.OnHide(isShutdown, userData);
@@ -107,29 +113,69 @@ namespace SepCore.Entity
protected virtual void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
{
BindAttackStatFromOwner(parentEntity);
}
protected virtual void OnWeaponDetach(EntityLogic parentEntity, object userData)
{
ReleaseAttackStatSubscription();
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (!_isEnabled) return;
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
if (!_isEnabled || StateMachine == null) return;
StateMachine.Update(elapseSeconds, realElapseSeconds);
}
#endregion
public abstract ImpactData GetImpactData();
protected abstract void Attack();
protected abstract void Check();
public virtual ImpactData GetImpactData()
{
return new ImpactData(WeaponData.OwnerCamp, WeaponData.Attack, AttackStat);
}
public abstract void Attack();
public virtual void Check()
{
_target = SelectTarget(_sqrRange);
}
public virtual 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 startRotation = CachedTransform.rotation;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
CachedTransform.rotation = Quaternion.Slerp(startRotation, targetRotation, _rotateSpeed * elapseSeconds);
}
public virtual void RotateToOrigin(float elapseSeconds)
{
Quaternion startRotation = CachedTransform.rotation;
CachedTransform.rotation = Quaternion.Slerp(startRotation, _cachedRotation, _rotateSpeed * elapseSeconds);
}
protected 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);
}
protected abstract void BuildStates();
protected virtual WeaponStateType InitialState => WeaponStateType.Idle;
protected virtual TargetSelectorType DefaultTargetSelectorType => TargetSelectorType.Nearest;
protected WeaponStateType InitialState => WeaponStateType.Idle;
protected void RegisterState(WeaponStateBase state)
{
@@ -139,38 +185,34 @@ namespace SepCore.Entity
return;
}
state.OnInit(this);
_states[state.State] = state;
StateMachine.Register(state);
}
protected void EnsureStatesBuilt()
{
if (_states != null) return;
if (StateMachine != null) return;
_states = new Dictionary<WeaponStateType, WeaponStateBase>();
StateMachine = new WeaponStateMachine(this);
BuildStates();
}
protected virtual void TransitionTo(WeaponStateType newState)
public void TransitionTo(WeaponStateType newState)
{
if (_states == null || !_states.TryGetValue(newState, out var nextState))
if (!StateMachine.TryTransition(newState))
{
Log.Error("Weapon state not found: {0}", newState);
return;
}
_currentState?.OnLeave();
_currentState = nextState;
_currentState.OnEnter();
}
public void SetEnabled(bool value)
{
if (value == _isEnabled) return;
_isEnabled = value;
OnEnabledChanged(value);
if (_states == null || _states.Count == 0) return;
if (StateMachine == null || !StateMachine.IsBuilt) return;
if (value)
{
@@ -178,9 +220,9 @@ namespace SepCore.Entity
return;
}
if (_states.ContainsKey(WeaponStateType.Disabled))
if (StateMachine.Contains(WeaponStateType.None))
{
TransitionTo(WeaponStateType.Disabled);
TransitionTo(WeaponStateType.None);
}
}
@@ -199,23 +241,17 @@ namespace SepCore.Entity
return data;
}
protected virtual IEnumerable<EntityBase> GetCandidates()
protected IEnumerable<EntityBase> GetCandidates()
{
var enemyManager = GameEntry.EnemyManager;
return enemyManager != null ? enemyManager.Enemies : s_EmptyCandidates;
return GameEntry.EnemyManager.Enemies;
}
protected EntityBase SelectTarget(float maxSqrRange)
{
return TargetSelector != null ? TargetSelector.SelectTarget(this, GetCandidates(), maxSqrRange) : null;
return TargetSelector.SelectTarget(this, GetCandidates(), maxSqrRange);
}
protected float GetSqrDistance(EntityBase target)
{
return target != null ? AIUtility.GetSqrMagnitudeXZ(this, target) : float.MaxValue;
}
protected bool IsInRange(EntityBase target, float sqrRange)
public bool IsInRange(EntityBase target, float sqrRange)
{
if (target == null) return false;
return AIUtility.GetSqrMagnitudeXZ(this, target) < sqrRange;
@@ -223,61 +259,22 @@ namespace SepCore.Entity
protected bool TryQueueAreaCollisionQuery(in Vector3 center, float radius, int maxTargets = 16)
{
var simulationWorld = GameEntry.SimulationWorld;
if (simulationWorld == null)
{
return false;
}
int ownerEntityId = WeaponData != null ? WeaponData.OwnerId : Id;
return simulationWorld.TryRequestAreaCollision(Id, ownerEntityId, in center, radius, maxTargets);
return GameEntry.SimulationWorld.TryRequestAreaCollision(Id, WeaponData.OwnerId, in center, radius,
maxTargets);
}
protected bool TryQueueSectorCollisionQuery(in Vector3 center, float radius, in Vector3 direction,
float halfAngleDeg, int maxTargets = 16)
{
var simulationWorld = GameEntry.SimulationWorld;
if (simulationWorld == null)
{
return false;
}
int ownerEntityId = WeaponData != null ? WeaponData.OwnerId : Id;
return simulationWorld.TryRequestSectorCollision(Id, ownerEntityId, in center, radius, in direction,
halfAngleDeg, maxTargets);
return GameEntry.SimulationWorld.TryRequestSectorCollision(Id, WeaponData.OwnerId, in center, radius,
in direction, halfAngleDeg, maxTargets);
}
protected bool TryQueueRectangleCollisionQuery(in Vector3 center, float halfWidth, float halfLength,
in Vector3 direction, int maxTargets = 16)
{
var simulationWorld = GameEntry.SimulationWorld;
if (simulationWorld == null)
{
return false;
}
int ownerEntityId = WeaponData != null ? WeaponData.OwnerId : Id;
return simulationWorld.TryRequestRectangleCollision(Id, ownerEntityId, in center, halfWidth, halfLength,
in direction, maxTargets);
}
protected void SetTargetSelector(TargetSelectorType selectorType)
{
TargetSelector = CreateSelector(selectorType);
}
protected static ITargetSelector CreateSelector(TargetSelectorType selectorType)
{
switch (selectorType)
{
case TargetSelectorType.HighestHealth:
return new HighestHealthTargetSelector();
case TargetSelectorType.LowestHealth:
return new LowestHealthTargetSelector();
case TargetSelectorType.Nearest:
default:
return new NearestTargetSelector();
}
return GameEntry.SimulationWorld.TryRequestRectangleCollision(Id, WeaponData.OwnerId, in center,
halfWidth, halfLength, in direction, maxTargets);
}
protected void BindAttackStatFromOwner(EntityLogic parentEntity)
@@ -308,35 +305,5 @@ namespace SepCore.Entity
_attackStatCallback = null;
AttackStat = new StatProperty();
}
protected void StartAttackCooldown(float cooldown)
{
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
}
protected void ResetAttackCooldown(float cooldown)
{
GameEntry.Timer.Cancel(_attackTimerHandle);
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
}
protected void CancelAttackCooldown()
{
GameEntry.Timer.Cancel(_attackTimerHandle);
_attackTimerHandle = TimerHandle.Invalid;
_canAttack = false;
}
}
public abstract class WeaponStateBase
{
public abstract WeaponStateType State { get; }
public abstract void OnInit(WeaponBase weapon);
public abstract void OnEnter();
public abstract void OnUpdate(float elapseSeconds, float realElapseSeconds);
public abstract void OnLeave();
public override string ToString() => State.ToString();
}
}