整理武器配置和重构

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
@@ -18,12 +18,55 @@ namespace SepCore.Definition
{
public const string DefaultLayerName = "Default";
public static readonly int DefaultLayerId = LayerMask.NameToLayer(DefaultLayerName);
public static readonly LayerMask DefaultLayerMask = 1 << DefaultLayerId;
public const string TransparentFXLayerName = "TransparentFX";
public static readonly int TransparentLayerId = LayerMask.NameToLayer(TransparentFXLayerName);
public static readonly LayerMask TransparentLayerMask = 1 << TransparentLayerId;
public const string IgnoreRaycastLayerName = "Ignore Raycast";
public static readonly int IgnoreLayerId = LayerMask.NameToLayer(IgnoreRaycastLayerName);
public static readonly LayerMask IgnoreLayerMask = 1 << IgnoreLayerId;
public const string WaterLayerName = "Water";
public static readonly int WaterLayerId = LayerMask.NameToLayer(WaterLayerName);
public static readonly LayerMask WaterLayerMask = 1 << WaterLayerId;
public const string UILayerName = "UI";
public static readonly int UILayerId = LayerMask.NameToLayer(UILayerName);
public static readonly LayerMask UILayerMask = 1 << UILayerId;
public const string TargetableObjectLayerName = "Targetable Object";
public static readonly int TargetableObjectLayerId = LayerMask.NameToLayer(TargetableObjectLayerName);
public static readonly LayerMask TargetableObjectLayerMask = 1 << TargetableObjectLayerId;
public const string PlayerLayerName = "Player";
public static readonly int PlayerLayerId = LayerMask.NameToLayer(PlayerLayerName);
public static readonly LayerMask PlayerLayerMask = 1 << PlayerLayerId;
public const string EnemyLayerName = "Enemy";
public static readonly int EnemyLayerId = LayerMask.NameToLayer(EnemyLayerName);
public static readonly LayerMask EnemyLayerMask = 1 << EnemyLayerId;
public const string PlayerBulletLayerName = "PlayerBullet";
public static readonly int PlayerBulletLayerId = LayerMask.NameToLayer(PlayerBulletLayerName);
public static readonly LayerMask PlayerBulletLayerMask = 1 << PlayerBulletLayerId;
public const string EnemyBulletLayerName = "EnemyBullet";
public static readonly int EnemyBulletLayerId = LayerMask.NameToLayer(EnemyBulletLayerName);
public static readonly LayerMask EnemyBulletLayerMask = 1 << EnemyBulletLayerId;
public const string PlayerWeaponLayerName = "PlayerWeapon";
public static readonly int PlayerWeaponLayerId = LayerMask.NameToLayer(PlayerWeaponLayerName);
public static readonly LayerMask PlayerWeaponLayerMask = 1 << PlayerWeaponLayerId;
public const string EnemyWeaponLayerName = "EnemyWeapon";
public static readonly int EnemyWeaponLayerId = LayerMask.NameToLayer(EnemyWeaponLayerName);
public static readonly LayerMask EnemyWeaponLayerMask = 1 << EnemyWeaponLayerId;
public const string DropLayerName = "Drop";
public static readonly int DropLayerId = LayerMask.NameToLayer(DropLayerName);
public static readonly LayerMask DropLayerMask = 1 << DropLayerId;
}
}
}
@@ -1,59 +1,66 @@
using System;
using System.Collections.Generic;
using SepCore.Definition;
using UnityGameFramework.Runtime;
namespace SepCore.Entity
{
[Serializable]
public class WeaponData : AccessoryObjectData
{
private readonly WeaponConfig _weaponConfig;
public WeaponConfig Config { get; }
public WeaponData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
: base(weaponConfig.Prefab, entityId, ownerId, ownerCamp)
{
_weaponConfig = weaponConfig;
Config = weaponConfig;
}
public WeaponConfig Config => _weaponConfig;
public WeaponType WeaponType => Config.WeaponType;
public WeaponType WeaponType => _weaponConfig.WeaponType;
public string LogicType => Config.LogicType;
public string LogicType => _weaponConfig.LogicType;
public int Attack => Config.Attack;
public int Attack => _weaponConfig.Attack;
public string Title => Config.WeaponType.ToString();
public string Title => _weaponConfig.WeaponType.ToString();
public string Sprite => Config.Sprite;
public string IconAssetName => _weaponConfig.Sprite;
public Rarity Rarity => Config.Rarity;
public Rarity Rarity => _weaponConfig.Rarity;
public int Price => Config.Price;
public int Price => _weaponConfig.Price;
public float Cooldown => Config.Cooldown / 1000f;
public float Cooldown => _weaponConfig.Cooldown / 1000f;
public float AttackRange => Config.AttackRange / 1000f;
public float AttackRange => _weaponConfig.AttackRange / 1000f;
public int AttackSoundId => Config.AttackSoundId;
public int AttackSoundId => _weaponConfig.AttackSoundId;
public IReadOnlyDictionary<ParamType, int> Parameters => Config.Parameters;
public IReadOnlyDictionary<ParamType, int> Parameters => _weaponConfig.Parameters;
public IReadOnlyList<modifier> Modifiers => Config.Modifiers;
public IReadOnlyList<modifier> Modifiers => _weaponConfig.Modifiers;
public int GetParam(ParamType paramType, int defaultValue = 0)
public int GetParam(ParamType paramType)
{
return _weaponConfig.Parameters.TryGetValue(paramType, out int value) ? value : defaultValue;
if (Config.Parameters.TryGetValue(paramType, out int value))
{
return value;
}
Log.Error("Weapon parameter '{0}' not found for weapon '{1}'.", paramType, Config.WeaponType);
return 0;
}
public float GetParamFloat(ParamType paramType, float defaultValue = 0f)
public float GetParamFloat(ParamType paramType)
{
if (_weaponConfig.Parameters.TryGetValue(paramType, out int val))
if (Config.Parameters.TryGetValue(paramType, out int val))
{
return paramType == ParamType.SectorAngle ? val : val / 1000f;
}
return defaultValue;
Log.Error("Weapon parameter '{0}' not found for weapon '{1}'.", paramType, Config.WeaponType);
return 0f;
}
}
}
@@ -1,21 +0,0 @@
using System;
using SepCore.Definition;
namespace SepCore.Entity
{
[Serializable]
public sealed class WeaponHandgunParamsData
{
}
public class WeaponHandgunData : WeaponData
{
public WeaponHandgunParamsData ParamsData { get; }
public WeaponHandgunData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
: base(entityId, weaponConfig, ownerId, ownerCamp)
{
ParamsData = new WeaponHandgunParamsData();
}
}
}
@@ -1,25 +0,0 @@
using System;
using SepCore.Definition;
namespace SepCore.Entity
{
[Serializable]
public sealed class WeaponKnifeParamsData
{
public float HitRadius { get; set; }
}
public class WeaponKnifeData : WeaponData
{
public WeaponKnifeParamsData ParamsData { get; }
public WeaponKnifeData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
: base(entityId, weaponConfig, ownerId, ownerCamp)
{
ParamsData = new WeaponKnifeParamsData
{
HitRadius = GetParamFloat(ParamType.HitRadius)
};
}
}
}
@@ -1,76 +0,0 @@
using System;
using SepCore.Definition;
namespace SepCore.Entity
{
[Serializable]
public sealed class WeaponLanceParamsData
{
/// <summary>
/// 横向半宽,表示前戳矩形判定的一半宽度。
/// </summary>
public float HitHalfWidth { get; set; }
/// <summary>
/// 旧字段兼容,未配置 HitHalfWidth 时回退使用。
/// </summary>
public float HitRadius { get; set; }
/// <summary>
/// 前戳判定盒体的总高度。
/// </summary>
public float HitHeight { get; set; }
/// <summary>
/// 判定盒体中心相对战斗平面的高度偏移。
/// </summary>
public float HitCenterYOffset { get; set; }
/// <summary>
/// 前刺距离,同时驱动武器位移和命中长度。
/// </summary>
public float PierceLength { get; set; }
/// <summary>
/// 旧字段兼容,未配置 PierceLength 时回退使用。
/// </summary>
public float ThrustDistance { get; set; }
/// <summary>
/// 判定起点相对武器当前位置的前置偏移。
/// </summary>
public float ForwardOffset { get; set; }
/// <summary>
/// 追踪目标时的转向速度。
/// </summary>
public float RotateSpeed { get; set; }
/// <summary>
/// 向前突刺阶段耗时。
/// </summary>
public float AttackDuration { get; set; }
/// <summary>
/// 收枪返回阶段耗时。
/// </summary>
public float ReturnDuration { get; set; }
}
[Serializable]
public class WeaponLanceData : WeaponData
{
public WeaponLanceParamsData ParamsData { get; }
public WeaponLanceData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
: base(entityId, weaponConfig, ownerId, ownerCamp)
{
ParamsData = new WeaponLanceParamsData
{
HitHalfWidth = GetParamFloat(ParamType.HitHalfWidth),
PierceLength = GetParamFloat(ParamType.PierceLength),
HitHeight = GetParamFloat(ParamType.HitHeight)
};
}
}
}
@@ -1,27 +0,0 @@
using System;
using SepCore.Definition;
namespace SepCore.Entity
{
[Serializable]
public sealed class WeaponLightningParamsData
{
public float HitRadius { get; set; }
public float HoverHeight { get; set; }
}
public class WeaponLightningData : WeaponData
{
public WeaponLightningParamsData ParamsData { get; }
public WeaponLightningData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
: base(entityId, weaponConfig, ownerId, ownerCamp)
{
ParamsData = new WeaponLightningParamsData
{
HitRadius = GetParamFloat(ParamType.HitRadius),
HoverHeight = 0f
};
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 25b0006918fd46959c7f6b8ec1bbc8ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,25 +0,0 @@
using System;
using SepCore.Definition;
namespace SepCore.Entity
{
[Serializable]
public sealed class WeaponSlashParamsData
{
public float SectorAngle { get; set; }
}
public class WeaponSlashData : WeaponData
{
public WeaponSlashParamsData ParamsData { get; }
public WeaponSlashData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
: base(entityId, weaponConfig, ownerId, ownerCamp)
{
ParamsData = new WeaponSlashParamsData
{
SectorAngle = GetParamFloat(ParamType.SectorAngle)
};
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4aa6473525dc4c62aeee02ff562ec453
timeCreated: 1771484661
@@ -0,0 +1,42 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SepCore.Definition
{
/// <summary>
/// 武器状态
/// </summary>
public enum WeaponStateType : byte
{
/// <summary>
/// 无
/// </summary>
None = 0,
/// <summary>
/// 攻击
/// </summary>
Attack = 1,
/// <summary>
/// 待机
/// </summary>
Idle = 2,
/// <summary>
/// 搜索
/// </summary>
Seek = 3,
/// <summary>
/// 瞄准
/// </summary>
Aim = 4,
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4d1a821e8ee1a9a4b912b70b0a1616eb
guid: 1065e2570786f184c9553db06202a6a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -41,7 +41,7 @@ namespace SepCore.UI
if (ItemType == ItemType.Weapon)
{
var data = rawData.WeaponData;
IconAssetName = data.IconAssetName;
IconAssetName = data.Sprite;
Title = data.Title;
Description = ItemDescUtility.CreateWeaponDescription(data);
}
@@ -95,7 +95,7 @@ namespace SepCore.UI
{
return new DisplayItemContext
{
IconAssetName = weaponBase.WeaponData.IconAssetName,
IconAssetName = weaponBase.WeaponData.Sprite,
Rarity = weaponBase.WeaponData.Rarity,
IsWeapon = true
};
@@ -124,7 +124,7 @@ namespace SepCore.UI
ItemType.Weapon => await SpriteLoader.LoadSpriteByKeyAsync(rawData.DataRow switch
{
WeaponConfig weaponConfig => weaponConfig.Sprite,
WeaponData weaponData => weaponData.IconAssetName,
WeaponData weaponData => weaponData.Sprite,
_ => string.Empty
}),
ItemType.Prop => await SpriteLoader.LoadSpriteByKeyAsync(((PropConfig)rawData.DataRow).Sprite),
@@ -2,7 +2,6 @@
using SepCore.AsyncTask;
using SepCore.Definition;
using SepCore.Entity;
using SepCore.Timer;
using UnityEngine;
public abstract class EnemyBase : TargetableObject
@@ -16,29 +15,6 @@ public abstract class EnemyBase : TargetableObject
protected EnemyData _enemyData;
protected bool _canAttack;
protected TimerHandle _attackTimerHandle;
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;
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
@@ -53,11 +53,11 @@ namespace SepCore.Entity
if (_projectileData.OwnerCamp == CampType.Player)
{
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
gameObject.layer = Constant.Layer.PlayerWeaponLayerId;
}
else if (_projectileData.OwnerCamp == CampType.Enemy)
{
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
gameObject.layer = Constant.Layer.EnemyWeaponLayerId;
}
SetColliderEnabled(false);
@@ -1,4 +1,5 @@
using SepCore.Entity;
using SepCore.Timer;
using GameFramework;
using UnityEngine;
using UnityGameFramework.Runtime;
@@ -9,6 +10,11 @@ namespace SepCore.Entity
{
[SerializeField] private EntityDataBase _entityData = null;
protected bool _canAttack;
protected TimerHandle _attackTimerHandle;
public bool CanAttack => _canAttack;
public int Id => Entity.Id;
public Animation CachedAnimation { get; private set; }
@@ -47,6 +53,26 @@ namespace SepCore.Entity
base.OnHide(isShutdown, userData);
}
protected void StartAttackCooldown(float cooldown)
{
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
}
public 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;
}
protected override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
{
base.OnAttached(childEntity, parentTransform, userData);
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 299f47fcfb4de4c4ea5a78d7ed206291
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,17 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 武器状态基类。
/// </summary>
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();
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 574d105bf89bf7546a682ed407708034
guid: 64f43677dac08f744848382cab8588fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 武器状态机。负责状态注册、切换与驱动。
/// </summary>
public class WeaponStateMachine
{
private readonly WeaponBase _weapon;
private readonly Dictionary<WeaponStateType, WeaponStateBase> _states = new();
private WeaponStateBase _currentState;
public WeaponStateMachine(WeaponBase weapon)
{
_weapon = weapon;
}
public bool IsBuilt => _states.Count > 0;
public bool Contains(WeaponStateType stateType)
{
return _states.ContainsKey(stateType);
}
public void Register(WeaponStateBase state)
{
state.OnInit(_weapon);
_states[state.State] = state;
}
public void Update(float elapseSeconds, float realElapseSeconds)
{
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
}
public bool TryTransition(WeaponStateType stateType)
{
if (!_states.TryGetValue(stateType, out WeaponStateBase nextState))
{
return false;
}
_currentState?.OnLeave();
_currentState = nextState;
_currentState.OnEnter();
return true;
}
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 54932952c9fa3fd4dbe6d21f2a484b4d
guid: d29897e3011246748bb0c939e54a0ca5
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -1,49 +0,0 @@
using System.Collections.Generic;
using SepCore.Components;
using SepCore.CustomUtility;
namespace SepCore.Entity
{
public sealed class HighestHealthTargetSelector : ITargetSelector
{
public EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)
{
if (weapon == null || candidates == null)
{
return null;
}
EntityBase target = null;
float maxHp = float.MinValue;
float maxSqr = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
foreach (var candidate in candidates)
{
if (candidate == null || !candidate.Available) continue;
if (maxSqrRange > 0f && AIUtility.GetSqrMagnitudeXZ(weapon, candidate) >= maxSqr) continue;
if (!TryGetCurrentHealth(candidate, out var hp)) continue;
if (hp <= maxHp) continue;
maxHp = hp;
target = candidate;
}
return target;
}
private static bool TryGetCurrentHealth(EntityBase target, out float health)
{
health = 0f;
if (target == null) return false;
var healthComponent = target.GetComponent<HealthComponent>();
if (healthComponent == null) return false;
health = healthComponent.CurrentHealth;
return true;
}
}
}
@@ -1,53 +0,0 @@
using System.Collections.Generic;
using SepCore.Components;
using SepCore.Definition;
using SepCore.CustomUtility;
namespace SepCore.Entity
{
public sealed class LowestHealthTargetSelector : ITargetSelector
{
public EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)
{
if (weapon == null || candidates == null)
{
return null;
}
EntityBase target = null;
float minHp = float.MaxValue;
float maxSqr = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
foreach (var candidate in candidates)
{
if (candidate == null || !candidate.Available) continue;
if (maxSqrRange > 0f && AIUtility.GetSqrMagnitudeXZ(weapon, candidate) >= maxSqr) continue;
if (!TryGetHealth(candidate, out var hp)) continue;
if (hp >= minHp) continue;
minHp = hp;
target = candidate;
}
return target;
}
private static bool TryGetHealth(EntityBase target, out float health)
{
health = 0f;
if (target == null) return false;
var statComponent = target.GetComponent<StatComponent>();
if (statComponent == null) return false;
var hpStat = statComponent.GetStat(StatType.MaxHealth);
if (hpStat == null) return false;
health = hpStat.Value;
return true;
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 081d67a491f5792479306d88091168e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,9 +0,0 @@
namespace SepCore.Entity
{
public enum TargetSelectorType
{
Nearest = 0,
HighestHealth = 1,
LowestHealth = 2
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 4eb1e0b99139fdd4280c8616804a8f77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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();
}
}
@@ -1,25 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponHandgun
{
private class AttackState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon._weaponData.Cooldown);
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
public override void OnLeave() { }
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4e72df067466464fa3be92bfd049dd0d
timeCreated: 1771579220
@@ -1,45 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponHandgun
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
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: bfd76c0fb7284ceaa28f78c702f595da
timeCreated: 1771579231
@@ -1,32 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponHandgun
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
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: 3a9f936d3c8643f7bf632734548f800c
timeCreated: 1771579241
@@ -1,26 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponHandgun
{
private class IdleState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
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: 9c5341944d1a467b8553d31bc3d81d5b
timeCreated: 1771579252
@@ -1,20 +1,12 @@
using SepCore.Definition;
using SepCore.CustomUtility;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace SepCore.Entity
{
public partial class WeaponHandgun : WeaponBase
public class WeaponHandgun : WeaponBase
{
#region Property
private WeaponHandgunData _weaponData;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 6f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private Vector3 _fireOriginOffset = Vector3.zero;
[SerializeField] private float _hitMarkerSize = 0.2f;
@@ -26,21 +18,15 @@ namespace SepCore.Entity
#endregion
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
}
protected override void BuildStates()
{
RegisterState(new IdleState());
RegisterState(new CheckOutRangeState());
RegisterState(new CheckInRangeState());
RegisterState(new AttackState());
RegisterState(new WeaponHandgunIdleState());
RegisterState(new WeaponHandgunSeekState());
RegisterState(new WeaponHandgunAimState());
RegisterState(new WeaponHandgunAttackState());
}
protected override void Attack()
public override void Attack()
{
FaceTargetImmediately();
@@ -49,43 +35,12 @@ namespace SepCore.Entity
return;
}
_attackEffect?.Play(this, hitPosition, targetable, 0f);
_attackEffect.Play(this, hitPosition, targetable, 0f);
_isAttacking = true;
AIUtility.PerformCollision(targetable, this);
_isAttacking = false;
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = (_target.CachedTransform.position - CachedTransform.position).normalized;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, 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;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
}
private bool TryResolveAttackTarget(out TargetableObject targetable, out Vector3 hitPosition)
{
targetable = _target as TargetableObject;
@@ -95,17 +50,12 @@ namespace SepCore.Entity
return false;
}
Transform targetTransform = targetable.CachedTransform;
if (targetTransform == null)
{
return false;
}
hitPosition = targetTransform.position;
Vector3 targetPosition = targetable.CachedTransform.position;
hitPosition = targetPosition;
Vector3 fireOrigin = CachedTransform.TransformPoint(_fireOriginOffset);
Vector3 directionToTarget = targetTransform.position - fireOrigin;
float maxDistance = Mathf.Max(0.1f, _weaponData.AttackRange);
Vector3 directionToTarget = targetPosition - fireOrigin;
float maxDistance = WeaponData.AttackRange;
if (directionToTarget.sqrMagnitude > Mathf.Epsilon &&
Physics.Raycast(fireOrigin, directionToTarget.normalized, out RaycastHit hit, maxDistance, _hitMask,
QueryTriggerInteraction.Collide))
@@ -124,46 +74,16 @@ namespace SepCore.Entity
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponHandgunData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
StartAttackCooldown(_weaponData.Cooldown);
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
_attackEffect = new HandgunHitMarkerAttackEffect(_hitMarkerSize, _hitMarkerYOffset, _hitMarkerDuration,
_hitMarkerColor);
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)
{
CancelAttackCooldown();
_attackEffect = null;
}
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
{
BindAttackStatFromOwner(parentEntity);
}
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
{
ReleaseAttackStatSubscription();
}
#endregion
}
}
@@ -0,0 +1,51 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 手枪的瞄准状态。
/// </summary>
public sealed class WeaponHandgunAimState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Aim;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)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()
{
}
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: da25d8e0669f0194d8f56f4283c67b38
guid: 71e311baa75a23448a593f2abb05999c
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -0,0 +1,34 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 手枪的攻击状态。
/// </summary>
public sealed class WeaponHandgunAttackState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)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: f0513d1d0592c344b93c8df22f892ea0
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 WeaponHandgunIdleState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)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: bd6af23da5725ce4385e23b49b0d4811
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 WeaponHandgunSeekState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Seek;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)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: 3e7a9fee7c4ec5f498756d6e17b1259e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,31 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponKnife
{
private class AttackState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
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: 417516ffe5574e0c998105fcaa073de7
timeCreated: 1771578955
@@ -1,48 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponKnife
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
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: 59644093364644768c26261b24bdb820
timeCreated: 1771578916
@@ -1,38 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponKnife
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
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: d9e1a63869694e72a360afe29cc94cfb
timeCreated: 1771578994
@@ -1,32 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponKnife
{
public class IdleState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
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: b4d66ec44b064640aa95c6aa71ff3846
timeCreated: 1771578662
@@ -2,50 +2,36 @@ using System.Collections.Generic;
using SepCore.CustomUtility;
using SepCore.Definition;
using DG.Tweening;
using SepCore.Entity;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace SepCore.Entity
{
public partial class WeaponKnife : WeaponBase
public class WeaponKnife : WeaponBase
{
private WeaponKnifeData _weaponData;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 4f;
private Sequence _attackSequence;
private Transform _attackParent;
private Vector3 _attackCenter;
[SerializeField] private float attackDuration = 0.15f;
[SerializeField] private float returnDuration = 0.2f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private float _attackDuration = 0.15f;
[SerializeField] private float _returnDuration = 0.2f;
[SerializeField] private int _maxHitColliders = 32;
private IWeaponAttackEffect _attackEffect;
private Collider[] _hitResults;
private readonly HashSet<int> _hitEntityIds = new();
private float _hitRadius;
private float _hitRadiusSqr;
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
}
public float HitRadius => WeaponData.GetParamFloat(ParamType.HitRadius);
protected override void BuildStates()
{
RegisterState(new IdleState());
RegisterState(new CheckInRangeState());
RegisterState(new CheckOutRangeState());
RegisterState(new AttackState());
RegisterState(new WeaponKnifeIdleState());
RegisterState(new WeaponKnifeAimState());
RegisterState(new WeaponKnifeSeekState());
RegisterState(new WeaponKnifeAttackState());
}
protected override void Attack()
public override void Attack()
{
StopAttackTween(false);
FaceTargetImmediately();
@@ -56,12 +42,12 @@ namespace SepCore.Entity
_attackCenter = _target != null ? _target.CachedTransform.position : CachedTransform.position;
CachedTransform.SetParent(null);
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * _weaponData.AttackRange;
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * WeaponData.AttackRange;
_attackSequence = DOTween.Sequence();
_attackSequence.Append(CachedTransform.DOMove(targetPos, attackDuration).SetEase(Ease.OutQuad));
_attackSequence.Append(CachedTransform.DOMove(targetPos, _attackDuration).SetEase(Ease.OutQuad));
_attackSequence.AppendCallback(() =>
{
_attackEffect?.Play(this, _attackCenter, null, _hitRadius);
_attackEffect.Play(this, _attackCenter, null, HitRadius);
ApplyGroundAreaDamage();
if (_attackParent != null)
{
@@ -69,7 +55,7 @@ namespace SepCore.Entity
}
});
_attackSequence.Append(
CachedTransform.DOLocalMove(Vector3.zero, returnDuration * 0.5f).SetEase(Ease.InQuad));
CachedTransform.DOLocalMove(Vector3.zero, _returnDuration * 0.5f).SetEase(Ease.InQuad));
_attackSequence.AppendCallback(() =>
{
_isAttacking = false;
@@ -78,54 +64,22 @@ namespace SepCore.Entity
});
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
CachedTransform.rotation =
Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
}
public 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;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
}
private void ApplyGroundAreaDamage()
{
if (_hitRadius <= 0f) return;
float radius = HitRadius;
if (radius <= 0f) return;
if (TryQueueAreaCollisionQuery(_attackCenter, _hitRadius, Mathf.Max(1, _maxHitColliders)))
if (TryQueueAreaCollisionQuery(_attackCenter, radius, Mathf.Max(1, _maxHitColliders)))
{
_hitEntityIds.Clear();
return;
}
if (_hitResults == null || _hitResults.Length == 0) return;
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, _hitRadius, _hitResults, _hitMask,
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, radius, _hitResults, _hitMask,
QueryTriggerInteraction.Collide);
float radiusSqr = radius * radius;
_hitEntityIds.Clear();
for (int i = 0; i < hitCount; i++)
{
@@ -138,7 +92,7 @@ namespace SepCore.Entity
Vector3 delta = targetable.CachedTransform.position - _attackCenter;
delta.y = 0f;
if (delta.sqrMagnitude > _hitRadiusSqr) continue;
if (delta.sqrMagnitude > radiusSqr) continue;
AIUtility.PerformCollision(targetable, this);
}
@@ -146,18 +100,6 @@ namespace SepCore.Entity
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponKnifeData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
StartAttackCooldown(_weaponData.Cooldown);
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
float configuredHitRadius = _weaponData.ParamsData != null ? _weaponData.ParamsData.HitRadius : 0f;
_hitRadius = configuredHitRadius > 0f ? Mathf.Max(0.1f, configuredHitRadius) : _weaponData.AttackRange;
_hitRadiusSqr = _hitRadius * _hitRadius;
_attackEffect = new KnifeRangeAttackEffect();
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
@@ -166,36 +108,19 @@ namespace SepCore.Entity
_hitResults = new Collider[colliderCapacity];
}
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)
@@ -0,0 +1,51 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 小刀的瞄准状态。
/// </summary>
public sealed class WeaponKnifeAimState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Aim;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)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: 0fb5d24a4e6deac4b8c64d1acc697adc
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 WeaponKnifeAttackState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)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: aad966d918f5cdb45912c0275826ef5c
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 WeaponKnifeIdleState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)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: 393f986a051df42409d687426bb213b7
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 WeaponKnifeSeekState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Seek;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)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: ac6681a70a0b3a6429a876bac61166cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,31 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLance
{
private class AttackState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
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,11 +0,0 @@
fileFormatVersion: 2
guid: d9991ee189b733b4e9d40395357f5ef2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,48 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLance
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
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,11 +0,0 @@
fileFormatVersion: 2
guid: 01edc8bb0ff46a14d8028e03af932b52
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,38 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLance
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
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,11 +0,0 @@
fileFormatVersion: 2
guid: 8009ad2c332a956438e3357ed5e30eb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,32 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLance
{
public class IdleState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
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,11 +0,0 @@
fileFormatVersion: 2
guid: 4c9e98f5381e579469dcdac7bf3e1e25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -9,15 +9,8 @@ using UnityGameFramework.Runtime;
namespace SepCore.Entity
{
public partial class WeaponLance : WeaponBase
public class WeaponLance : WeaponBase
{
// 长枪专用数据,包含强类型 ParamsData。
[SerializeField] private WeaponLanceData _weaponData;
private Quaternion _cachedRotation;
// 朝向目标时的旋转速度,可被 ParamsData.RotateSpeed 覆盖。
[SerializeField] private float _rotateSpeed = 5f;
private Sequence _attackSequence;
private Transform _attackParent;
@@ -26,57 +19,46 @@ namespace SepCore.Entity
// 收枪返回耗时。
[SerializeField] private float _returnDuration = 0.18f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private int _maxHitColliders = 32;
private IWeaponAttackEffect _attackEffect;
private Collider[] _hitResults;
private readonly HashSet<int> _hitEntityIds = new();
// 前戳矩形判定的横向半宽。
private float _hitHalfWidth;
// 前戳矩形判定盒体的总高度。
private float _hitHeight;
// 盒体中心相对战斗平面的高度偏移。
private float _hitCenterYOffset;
// 从判定起点向前延伸的有效刺击长度。
private float _pierceLength;
// 判定起点相对武器当前位置的前移量。
private float _forwardOffset;
public float HitHalfWidth => WeaponData.GetParamFloat(ParamType.HitHalfWidth);
public float HitHeight => WeaponData.GetParamFloat(ParamType.HitHeight);
public float PierceLength => WeaponData.GetParamFloat(ParamType.PierceLength);
public Vector3 StrikeDirection => _strikeDirection;
// 本次攻击锁定的前戳方向,避免受位移动画中的武器位置影响。
private Vector3 _strikeDirection = Vector3.forward;
// 本次攻击锁定的矩形判定中心。
private Vector3 _strikeCenter;
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 WeaponLanceIdleState());
RegisterState(new WeaponLanceAimState());
RegisterState(new WeaponLanceSeekState());
RegisterState(new WeaponLanceAttackState());
}
protected override void Attack()
public override void Attack()
{
StopAttackTween(false);
FaceTargetImmediately();
_attackParent = CachedTransform.parent;
CacheStrikeSnapshot();
_isAttacking = true;
_attackParent = CachedTransform.parent;
CachedTransform.SetParent(null);
Vector3 targetPos = CachedTransform.position + _strikeDirection * _pierceLength;
Vector3 targetPos = CachedTransform.position + _strikeDirection * PierceLength;
_attackSequence = DOTween.Sequence();
_attackSequence.Append(CachedTransform.DOMove(targetPos, _attackDuration).SetEase(Ease.OutQuad));
_attackSequence.AppendCallback(() =>
{
_attackEffect?.Play(this, _strikeCenter, _target, _hitHalfWidth);
_attackEffect.Play(this, _strikeCenter, _target, HitHalfWidth);
ApplyPierceDamage();
if (_attackParent != null)
@@ -93,49 +75,12 @@ namespace SepCore.Entity
});
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
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);
}
private void CacheStrikeSnapshot()
{
_strikeDirection = ResolvePlanarForward();
Vector3 strikeStart = CachedTransform.position;
strikeStart.y = ResolveStrikePlaneY();
strikeStart += _strikeDirection * _forwardOffset;
_strikeCenter = strikeStart + _strikeDirection * (_pierceLength * 0.5f);
_strikeCenter.y += _hitCenterYOffset;
_strikeCenter = strikeStart + _strikeDirection * (PierceLength * 0.5f);
}
private Vector3 ResolvePlanarForward()
@@ -153,7 +98,7 @@ namespace SepCore.Entity
private void ApplyPierceDamage()
{
if (_hitHalfWidth <= 0f || _pierceLength <= 0f) return;
if (HitHalfWidth <= 0f || PierceLength <= 0f) return;
Vector3 strikeDirection = _strikeDirection;
if (strikeDirection.sqrMagnitude <= Mathf.Epsilon) return;
@@ -161,20 +106,15 @@ namespace SepCore.Entity
Vector3 broadPhaseCenter = _strikeCenter;
Quaternion broadPhaseRotation = Quaternion.LookRotation(strikeDirection, Vector3.up);
if (TryQueueRectangleCollisionQuery(broadPhaseCenter, _hitHalfWidth, _pierceLength * 0.5f,
if (TryQueueRectangleCollisionQuery(broadPhaseCenter, HitHalfWidth, PierceLength * 0.5f,
strikeDirection, Mathf.Max(1, _maxHitColliders)))
{
_hitEntityIds.Clear();
return;
}
int capacity = Mathf.Max(1, _maxHitColliders);
if (_hitResults == null || _hitResults.Length != capacity)
{
_hitResults = new Collider[capacity];
}
if (_hitResults == null || _hitResults.Length == 0) return;
Vector3 halfExtents = new(_hitHalfWidth, _hitHeight * 0.5f, _pierceLength * 0.5f);
Vector3 halfExtents = new(HitHalfWidth, HitHeight * 0.5f, PierceLength * 0.5f);
int hitCount = Physics.OverlapBoxNonAlloc(broadPhaseCenter, halfExtents, _hitResults, broadPhaseRotation,
_hitMask, QueryTriggerInteraction.Collide);
_hitEntityIds.Clear();
@@ -203,8 +143,8 @@ namespace SepCore.Entity
float lateralDistance = Vector3.Dot(delta, right);
float targetRadius = ResolveTargetCollisionRadius(targetable);
return Mathf.Abs(forwardDistance) <= _pierceLength * 0.5f + targetRadius &&
Mathf.Abs(lateralDistance) <= _hitHalfWidth + targetRadius;
return Mathf.Abs(forwardDistance) <= PierceLength * 0.5f + targetRadius &&
Mathf.Abs(lateralDistance) <= HitHalfWidth + targetRadius;
}
private static float ResolveTargetCollisionRadius(TargetableObject targetable)
@@ -225,64 +165,11 @@ namespace SepCore.Entity
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;
return _attackParent != null ? _attackParent.position.y : CachedTransform.position.y;
}
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponLanceData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
StartAttackCooldown(_weaponData.Cooldown);
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
WeaponLanceParamsData paramsData = _weaponData.ParamsData;
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
: paramsData != null && paramsData.ThrustDistance > 0f
? paramsData.ThrustDistance
: _weaponData.AttackRange;
_forwardOffset = paramsData != null && paramsData.ForwardOffset > 0f
? paramsData.ForwardOffset
: 0f;
if (paramsData != null && paramsData.RotateSpeed > 0f)
{
_rotateSpeed = paramsData.RotateSpeed;
}
if (paramsData != null && paramsData.AttackDuration > 0f)
{
_attackDuration = paramsData.AttackDuration;
}
if (paramsData != null && paramsData.ReturnDuration > 0f)
{
_returnDuration = paramsData.ReturnDuration;
}
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
if (_hitResults == null || _hitResults.Length != colliderCapacity)
{
@@ -290,37 +177,19 @@ namespace SepCore.Entity
}
_attackEffect = new LanceThrustAttackEffect();
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)
@@ -351,9 +220,5 @@ namespace SepCore.Entity
_attackParent = null;
_hitEntityIds.Clear();
}
public float HitHalfWidth => _hitHalfWidth;
public float PierceLength => _pierceLength;
public Vector3 StrikeDirection => _strikeDirection;
}
}
@@ -0,0 +1,51 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 长枪的瞄准状态。
/// </summary>
public sealed class WeaponLanceAimState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Aim;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)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: b871ce786fd5a4b44832f9437a752880
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 WeaponLanceAttackState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)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: b3529471ff522fa4dbbf7ca2005b9358
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 WeaponLanceIdleState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)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: 1a33cf2ffd5d4d54194406a6d9eb68a5
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 WeaponLanceSeekState : WeaponStateBase
{
private WeaponLance _weapon;
public override WeaponStateType State => WeaponStateType.Seek;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)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: 366f9a214870cfb4b90fc2682c74f127
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,31 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLightning
{
private class AttackState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
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,11 +0,0 @@
fileFormatVersion: 2
guid: 25854832a7d3416dacab67bcfef2a2fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,48 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLightning
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
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,11 +0,0 @@
fileFormatVersion: 2
guid: 25404ad172434ba38609e797b6d96919
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,38 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLightning
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
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,11 +0,0 @@
fileFormatVersion: 2
guid: f3aa479abb79491d83e8de76806faf7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,32 +0,0 @@
namespace SepCore.Entity
{
public partial class WeaponLightning
{
private class IdleState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
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,11 +0,0 @@
fileFormatVersion: 2
guid: 3464037c29774c3ea326dc93fe33551e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -8,13 +8,8 @@ using UnityGameFramework.Runtime;
namespace SepCore.Entity
{
public partial class WeaponLightning : WeaponBase
public class WeaponLightning : WeaponBase
{
private WeaponLightningData _weaponData;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 5f;
[SerializeField] private float _takeOffDuration = 0.3f;
[SerializeField] private float _flyToTargetDuration = 0.2f;
[SerializeField] private float _strikeDuration = 0.1f;
@@ -22,7 +17,6 @@ namespace SepCore.Entity
[SerializeField] private float _hoverHeight = 15f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private int _maxHitColliders = 32;
private Sequence _attackSequence;
@@ -32,25 +26,19 @@ namespace SepCore.Entity
private Collider[] _hitResults;
private readonly HashSet<int> _hitEntityIds = new();
private float _hitRadius;
private float _hitRadiusSqr;
public float HitRadius => WeaponData.GetParamFloat(ParamType.HitRadius);
private IWeaponAttackEffect _attackEffect;
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
}
protected override void BuildStates()
{
RegisterState(new IdleState());
RegisterState(new CheckOutRangeState());
RegisterState(new CheckInRangeState());
RegisterState(new AttackState());
RegisterState(new WeaponLightningIdleState());
RegisterState(new WeaponLightningSeekState());
RegisterState(new WeaponLightningAimState());
RegisterState(new WeaponLightningAttackState());
}
protected override void Attack()
public override void Attack()
{
StopAttackTween(false);
FaceTargetImmediately();
@@ -70,7 +58,7 @@ namespace SepCore.Entity
_attackSequence.Append(CachedTransform.DOMove(_lockedStrikePoint, _strikeDuration).SetEase(Ease.InQuad));
_attackSequence.AppendCallback(() =>
{
_attackEffect?.Play(this, _lockedStrikePoint, _target, _hitRadius);
_attackEffect.Play(this, _lockedStrikePoint, _target, HitRadius);
ApplyGroundAreaDamage();
if (_attackParent != null)
@@ -87,11 +75,6 @@ namespace SepCore.Entity
});
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
private Vector3 ResolveStrikePoint()
{
if (_target != null && _target.Available)
@@ -104,19 +87,20 @@ namespace SepCore.Entity
private void ApplyGroundAreaDamage()
{
if (_hitRadius <= 0f) return;
float radius = HitRadius;
if (radius <= 0f) return;
if (TryQueueAreaCollisionQuery(_lockedStrikePoint, _hitRadius, Mathf.Max(1, _maxHitColliders)))
if (TryQueueAreaCollisionQuery(_lockedStrikePoint, radius, Mathf.Max(1, _maxHitColliders)))
{
_hitEntityIds.Clear();
return;
}
if (_hitResults == null || _hitResults.Length == 0) return;
int hitCount = Physics.OverlapSphereNonAlloc(_lockedStrikePoint, _hitRadius, _hitResults, _hitMask,
int hitCount = Physics.OverlapSphereNonAlloc(_lockedStrikePoint, radius, _hitResults, _hitMask,
QueryTriggerInteraction.Collide);
float radiusSqr = radius * radius;
_hitEntityIds.Clear();
for (int i = 0; i < hitCount; i++)
{
@@ -129,61 +113,14 @@ namespace SepCore.Entity
Vector3 delta = targetable.CachedTransform.position - _lockedStrikePoint;
delta.y = 0f;
if (delta.sqrMagnitude > _hitRadiusSqr) continue;
if (delta.sqrMagnitude > radiusSqr) continue;
AIUtility.PerformCollision(targetable, this);
}
}
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);
}
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponLightningData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
StartAttackCooldown(_weaponData.Cooldown);
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
float configuredHitRadius = _weaponData.ParamsData != null ? _weaponData.ParamsData.HitRadius : 0f;
_hitRadius = configuredHitRadius > 0f ? Mathf.Max(0.1f, configuredHitRadius) : _weaponData.AttackRange;
_hitRadiusSqr = _hitRadius * _hitRadius;
float configuredHoverHeight = _weaponData.ParamsData != null ? _weaponData.ParamsData.HoverHeight : 0f;
if (configuredHoverHeight > 0f)
{
_hoverHeight = Mathf.Max(0.1f, configuredHoverHeight);
}
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
if (_hitResults == null || _hitResults.Length != colliderCapacity)
{
@@ -191,37 +128,19 @@ namespace SepCore.Entity
}
_attackEffect = new LightningStrikeAttackEffect();
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)
@@ -0,0 +1,51 @@
using SepCore.Definition;
namespace SepCore.Entity
{
/// <summary>
/// 闪电的瞄准状态。
/// </summary>
public sealed class WeaponLightningAimState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Aim;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)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: 472efc8d8802b4941892df6907b98d03
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 WeaponLightningAttackState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)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: 9d4b2a83cb82e1442826effabf6c9351
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 WeaponLightningIdleState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)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: 1f6fccbd2de0c494fa2f33e0fba04d4f
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 WeaponLightningSeekState : WeaponStateBase
{
private WeaponLightning _weapon;
public override WeaponStateType State => WeaponStateType.Seek;
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)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: 9fe264206ae0d0d4492b94b952d6d698
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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()
{
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More