From 421f8bf619bae6c935f4dd9fe636154bc999988c Mon Sep 17 00:00:00 2001 From: basil <2428390463@qq.com> Date: Tue, 15 Sep 2026 10:13:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E6=AD=A6=E5=99=A8=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=92=8C=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Definition/Constant/Constant.Layer.cs | 43 ++++ .../Base/EntityData/Weapon/WeaponData.cs | 49 ++-- .../EntityData/Weapon/WeaponHandgunData.cs | 21 -- .../Base/EntityData/Weapon/WeaponKnifeData.cs | 25 -- .../Base/EntityData/Weapon/WeaponLanceData.cs | 76 ------ .../EntityData/Weapon/WeaponLightningData.cs | 27 --- .../Weapon/WeaponLightningData.cs.meta | 11 - .../Base/EntityData/Weapon/WeaponSlashData.cs | 25 -- .../EntityData/Weapon/WeaponSlashData.cs.meta | 3 - .../Scripts/Base/Gen/WeaponStateType.cs | 42 ++++ .../WeaponStateType.cs.meta} | 2 +- .../ItemTooltip/ItemTooltipContext.cs | 2 +- .../Shop/ShopController.BuildContext.cs | 4 +- .../Runtime/EntityLogic/Enemy/EnemyBase.cs | 24 -- .../EntityLogic/Enemy/EnemyProjectile.cs | 4 +- .../Scripts/Runtime/EntityLogic/EntityBase.cs | 26 ++ .../Runtime/EntityLogic/Weapon/FSM.meta | 8 + .../EntityLogic/Weapon/FSM/WeaponStateBase.cs | 17 ++ .../Weapon/FSM/WeaponStateBase.cs.meta} | 2 +- .../Weapon/FSM/WeaponStateMachine.cs | 52 ++++ .../WeaponStateMachine.cs.meta} | 2 +- .../HighestHealthTargetSelector.cs | 49 ---- .../LowestHealthTargetSelector.cs | 53 ---- .../LowestHealthTargetSelector.cs.meta | 11 - .../TargetSelector/TargetSelectorType.cs | 9 - .../TargetSelector/TargetSelectorType.cs.meta | 11 - .../Runtime/EntityLogic/Weapon/WeaponBase.cs | 229 ++++++++---------- .../WeaponHandgun.AttackState.cs | 25 -- .../WeaponHandgun.AttackState.cs.meta | 3 - .../WeaponHandgun.CheckInRangeState.cs | 45 ---- .../WeaponHandgun.CheckInRangeState.cs.meta | 3 - .../WeaponHandgun.CheckOutRangeState.cs | 32 --- .../WeaponHandgun.CheckOutRangeState.cs.meta | 3 - .../WeaponHandgun/WeaponHandgun.IdleState.cs | 26 -- .../WeaponHandgun.IdleState.cs.meta | 3 - .../Weapon/WeaponHandgun/WeaponHandgun.cs | 102 +------- .../WeaponHandgun/WeaponHandgunAimState.cs | 51 ++++ .../WeaponHandgunAimState.cs.meta} | 2 +- .../WeaponHandgun/WeaponHandgunAttackState.cs | 34 +++ .../WeaponHandgunAttackState.cs.meta | 11 + .../WeaponHandgun/WeaponHandgunIdleState.cs | 35 +++ .../WeaponHandgunIdleState.cs.meta | 11 + .../WeaponHandgun/WeaponHandgunSeekState.cs | 41 ++++ .../WeaponHandgunSeekState.cs.meta | 11 + .../WeaponKnife/WeaponKnife.AttackState.cs | 31 --- .../WeaponKnife.AttackState.cs.meta | 3 - .../WeaponKnife.CheckInRangeState.cs | 48 ---- .../WeaponKnife.CheckInRangeState.cs.meta | 3 - .../WeaponKnife.CheckOutRangeState.cs | 38 --- .../WeaponKnife.CheckOutRangeState.cs.meta | 3 - .../WeaponKnife/WeaponKnife.IdleState.cs | 32 --- .../WeaponKnife/WeaponKnife.IdleState.cs.meta | 3 - .../Weapon/WeaponKnife/WeaponKnife.cs | 115 ++------- .../Weapon/WeaponKnife/WeaponKnifeAimState.cs | 51 ++++ .../WeaponKnife/WeaponKnifeAimState.cs.meta | 11 + .../WeaponKnife/WeaponKnifeAttackState.cs | 34 +++ .../WeaponKnifeAttackState.cs.meta | 11 + .../WeaponKnife/WeaponKnifeIdleState.cs | 35 +++ .../WeaponKnife/WeaponKnifeIdleState.cs.meta | 11 + .../WeaponKnife/WeaponKnifeSeekState.cs | 41 ++++ .../WeaponKnife/WeaponKnifeSeekState.cs.meta | 11 + .../WeaponLance/WeaponLance.AttackState.cs | 31 --- .../WeaponLance.AttackState.cs.meta | 11 - .../WeaponLance.CheckInRangeState.cs | 48 ---- .../WeaponLance.CheckInRangeState.cs.meta | 11 - .../WeaponLance.CheckOutRangeState.cs | 38 --- .../WeaponLance.CheckOutRangeState.cs.meta | 11 - .../WeaponLance/WeaponLance.IdleState.cs | 32 --- .../WeaponLance/WeaponLance.IdleState.cs.meta | 11 - .../Weapon/WeaponLance/WeaponLance.cs | 181 ++------------ .../Weapon/WeaponLance/WeaponLanceAimState.cs | 51 ++++ .../WeaponLance/WeaponLanceAimState.cs.meta | 11 + .../WeaponLance/WeaponLanceAttackState.cs | 34 +++ .../WeaponLanceAttackState.cs.meta | 11 + .../WeaponLance/WeaponLanceIdleState.cs | 35 +++ .../WeaponLance/WeaponLanceIdleState.cs.meta | 11 + .../WeaponLance/WeaponLanceSeekState.cs | 41 ++++ .../WeaponLance/WeaponLanceSeekState.cs.meta | 11 + .../WeaponLightning.AttackState.cs | 31 --- .../WeaponLightning.AttackState.cs.meta | 11 - .../WeaponLightning.CheckInRangeState.cs | 48 ---- .../WeaponLightning.CheckInRangeState.cs.meta | 11 - .../WeaponLightning.CheckOutRangeState.cs | 38 --- ...WeaponLightning.CheckOutRangeState.cs.meta | 11 - .../WeaponLightning.IdleState.cs | 32 --- .../WeaponLightning.IdleState.cs.meta | 11 - .../Weapon/WeaponLightning/WeaponLightning.cs | 111 ++------- .../WeaponLightningAimState.cs | 51 ++++ .../WeaponLightningAimState.cs.meta | 11 + .../WeaponLightningAttackState.cs | 34 +++ .../WeaponLightningAttackState.cs.meta | 11 + .../WeaponLightningIdleState.cs | 35 +++ .../WeaponLightningIdleState.cs.meta | 11 + .../WeaponLightningSeekState.cs | 41 ++++ .../WeaponLightningSeekState.cs.meta | 11 + .../WeaponSlash/WeaponSlash.AttackState.cs | 30 --- .../WeaponSlash.AttackState.cs.meta | 3 - .../WeaponSlash.CheckInRangeState.cs | 47 ---- .../WeaponSlash.CheckInRangeState.cs.meta | 3 - .../WeaponSlash.CheckOutRangeState.cs | 37 --- .../WeaponSlash.CheckOutRangeState.cs.meta | 3 - .../WeaponSlash/WeaponSlash.IdleState.cs | 31 --- .../WeaponSlash/WeaponSlash.IdleState.cs.meta | 3 - .../Weapon/WeaponSlash/WeaponSlash.cs | 127 ++-------- .../Weapon/WeaponSlash/WeaponSlashAimState.cs | 51 ++++ .../WeaponSlash/WeaponSlashAimState.cs.meta | 11 + .../WeaponSlash/WeaponSlashAttackState.cs | 34 +++ .../WeaponSlashAttackState.cs.meta | 11 + .../WeaponSlash/WeaponSlashIdleState.cs | 35 +++ .../WeaponSlash/WeaponSlashIdleState.cs.meta | 11 + .../WeaponSlash/WeaponSlashSeekState.cs | 41 ++++ .../WeaponSlash/WeaponSlashSeekState.cs.meta | 11 + .../Runtime/Utility/EntityDataFactory.cs | 13 +- 数据表/Datas/WeaponConfig_武器配置.xlsx | Bin 11581 -> 11585 bytes 数据表/Datas/__enums__.xlsx | Bin 15157 -> 15370 bytes 115 files changed, 1431 insertions(+), 1905 deletions(-) delete mode 100644 Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponHandgunData.cs delete mode 100644 Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponKnifeData.cs delete mode 100644 Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLanceData.cs delete mode 100644 Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs delete mode 100644 Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs.meta delete mode 100644 Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs delete mode 100644 Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs.meta create mode 100644 Assets/GameMain/Scripts/Base/Gen/WeaponStateType.cs rename Assets/GameMain/Scripts/Base/{EntityData/Weapon/WeaponLanceData.cs.meta => Gen/WeaponStateType.cs.meta} (83%) create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs rename Assets/GameMain/Scripts/{Base/EntityData/Weapon/WeaponHandgunData.cs.meta => Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs.meta} (83%) create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateMachine.cs rename Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/{TargetSelector/HighestHealthTargetSelector.cs.meta => FSM/WeaponStateMachine.cs.meta} (83%) delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/HighestHealthTargetSelector.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs rename Assets/GameMain/Scripts/{Base/EntityData/Weapon/WeaponKnifeData.cs.meta => Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs.meta} (83%) create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs.meta diff --git a/Assets/GameMain/Scripts/Base/Definition/Constant/Constant.Layer.cs b/Assets/GameMain/Scripts/Base/Definition/Constant/Constant.Layer.cs index 8698f46..3eb5447 100644 --- a/Assets/GameMain/Scripts/Base/Definition/Constant/Constant.Layer.cs +++ b/Assets/GameMain/Scripts/Base/Definition/Constant/Constant.Layer.cs @@ -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; } } } diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponData.cs b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponData.cs index 5b6df7e..c799e46 100644 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponData.cs +++ b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponData.cs @@ -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 Parameters => Config.Parameters; - public IReadOnlyDictionary Parameters => _weaponConfig.Parameters; + public IReadOnlyList Modifiers => Config.Modifiers; - public IReadOnlyList 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; } } } + diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponHandgunData.cs b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponHandgunData.cs deleted file mode 100644 index deafdc5..0000000 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponHandgunData.cs +++ /dev/null @@ -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(); - } - } -} diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponKnifeData.cs b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponKnifeData.cs deleted file mode 100644 index 25d90f3..0000000 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponKnifeData.cs +++ /dev/null @@ -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) - }; - } - } -} diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLanceData.cs b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLanceData.cs deleted file mode 100644 index e247dc8..0000000 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLanceData.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using SepCore.Definition; - -namespace SepCore.Entity -{ - [Serializable] - public sealed class WeaponLanceParamsData - { - /// - /// 横向半宽,表示前戳矩形判定的一半宽度。 - /// - public float HitHalfWidth { get; set; } - - /// - /// 旧字段兼容,未配置 HitHalfWidth 时回退使用。 - /// - public float HitRadius { get; set; } - - /// - /// 前戳判定盒体的总高度。 - /// - public float HitHeight { get; set; } - - /// - /// 判定盒体中心相对战斗平面的高度偏移。 - /// - public float HitCenterYOffset { get; set; } - - /// - /// 前刺距离,同时驱动武器位移和命中长度。 - /// - public float PierceLength { get; set; } - - /// - /// 旧字段兼容,未配置 PierceLength 时回退使用。 - /// - public float ThrustDistance { get; set; } - - /// - /// 判定起点相对武器当前位置的前置偏移。 - /// - public float ForwardOffset { get; set; } - - /// - /// 追踪目标时的转向速度。 - /// - public float RotateSpeed { get; set; } - - /// - /// 向前突刺阶段耗时。 - /// - public float AttackDuration { get; set; } - - /// - /// 收枪返回阶段耗时。 - /// - 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) - }; - } - } -} diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs deleted file mode 100644 index eadda4c..0000000 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs +++ /dev/null @@ -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 - }; - } - } -} diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs.meta b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs.meta deleted file mode 100644 index 77f60c8..0000000 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLightningData.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 25b0006918fd46959c7f6b8ec1bbc8ab -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs deleted file mode 100644 index c38018e..0000000 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs +++ /dev/null @@ -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) - }; - } - } -} diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs.meta b/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs.meta deleted file mode 100644 index a111bea..0000000 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponSlashData.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 4aa6473525dc4c62aeee02ff562ec453 -timeCreated: 1771484661 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Base/Gen/WeaponStateType.cs b/Assets/GameMain/Scripts/Base/Gen/WeaponStateType.cs new file mode 100644 index 0000000..840ec7f --- /dev/null +++ b/Assets/GameMain/Scripts/Base/Gen/WeaponStateType.cs @@ -0,0 +1,42 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + + +namespace SepCore.Definition +{ + /// + /// 武器状态 + /// + public enum WeaponStateType : byte + { + /// + /// 无 + /// + None = 0, + /// + /// 攻击 + /// + Attack = 1, + /// + /// 待机 + /// + Idle = 2, + /// + /// 搜索 + /// + Seek = 3, + /// + /// 瞄准 + /// + Aim = 4, + } + +} + diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLanceData.cs.meta b/Assets/GameMain/Scripts/Base/Gen/WeaponStateType.cs.meta similarity index 83% rename from Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLanceData.cs.meta rename to Assets/GameMain/Scripts/Base/Gen/WeaponStateType.cs.meta index f16bacc..80d3ea6 100644 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponLanceData.cs.meta +++ b/Assets/GameMain/Scripts/Base/Gen/WeaponStateType.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4d1a821e8ee1a9a4b912b70b0a1616eb +guid: 1065e2570786f184c9553db06202a6a7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/GameMain/Scripts/Presentation/ItemTooltip/ItemTooltipContext.cs b/Assets/GameMain/Scripts/Presentation/ItemTooltip/ItemTooltipContext.cs index 217b9a6..15862e7 100644 --- a/Assets/GameMain/Scripts/Presentation/ItemTooltip/ItemTooltipContext.cs +++ b/Assets/GameMain/Scripts/Presentation/ItemTooltip/ItemTooltipContext.cs @@ -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); } diff --git a/Assets/GameMain/Scripts/Presentation/Shop/ShopController.BuildContext.cs b/Assets/GameMain/Scripts/Presentation/Shop/ShopController.BuildContext.cs index d7ec1f7..8fc12cb 100644 --- a/Assets/GameMain/Scripts/Presentation/Shop/ShopController.BuildContext.cs +++ b/Assets/GameMain/Scripts/Presentation/Shop/ShopController.BuildContext.cs @@ -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), diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs index 3a5f5ea..4f27cfd 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs @@ -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); diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs index df28e51..afac194 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs @@ -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); diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/EntityBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/EntityBase.cs index 1dcc733..0a1145d 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/EntityBase.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/EntityBase.cs @@ -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); diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM.meta new file mode 100644 index 0000000..6d1b743 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 299f47fcfb4de4c4ea5a78d7ed206291 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs new file mode 100644 index 0000000..c81c8ef --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs @@ -0,0 +1,17 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 武器状态基类。 + /// + 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(); + } +} diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponHandgunData.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs.meta similarity index 83% rename from Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponHandgunData.cs.meta rename to Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs.meta index 53b24f0..a67812e 100644 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponHandgunData.cs.meta +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateBase.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 574d105bf89bf7546a682ed407708034 +guid: 64f43677dac08f744848382cab8588fb MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateMachine.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateMachine.cs new file mode 100644 index 0000000..694cd7e --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateMachine.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 武器状态机。负责状态注册、切换与驱动。 + /// + public class WeaponStateMachine + { + private readonly WeaponBase _weapon; + private readonly Dictionary _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; + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/HighestHealthTargetSelector.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateMachine.cs.meta similarity index 83% rename from Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/HighestHealthTargetSelector.cs.meta rename to Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateMachine.cs.meta index 88b7756..48e3322 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/HighestHealthTargetSelector.cs.meta +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/FSM/WeaponStateMachine.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 54932952c9fa3fd4dbe6d21f2a484b4d +guid: d29897e3011246748bb0c939e54a0ca5 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/HighestHealthTargetSelector.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/HighestHealthTargetSelector.cs deleted file mode 100644 index 44f6f1d..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/HighestHealthTargetSelector.cs +++ /dev/null @@ -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 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(); - if (healthComponent == null) return false; - - health = healthComponent.CurrentHealth; - return true; - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs deleted file mode 100644 index 9af4ba1..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs +++ /dev/null @@ -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 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(); - if (statComponent == null) return false; - - var hpStat = statComponent.GetStat(StatType.MaxHealth); - if (hpStat == null) return false; - - health = hpStat.Value; - return true; - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs.meta deleted file mode 100644 index cba2a2d..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/LowestHealthTargetSelector.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 081d67a491f5792479306d88091168e3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs deleted file mode 100644 index 3b4ef8d..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SepCore.Entity -{ - public enum TargetSelectorType - { - Nearest = 0, - HighestHealth = 1, - LowestHealth = 2 - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs.meta deleted file mode 100644 index ea2704e..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/TargetSelector/TargetSelectorType.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4eb1e0b99139fdd4280c8616804a8f77 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs index 88df126..b662234 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs @@ -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, - } - /// /// 武器类。 /// 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 _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 _attackStatCallback; - private static readonly List 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(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(); + 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 GetCandidates() + protected IEnumerable 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(); } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs deleted file mode 100644 index 4e3dcac..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs +++ /dev/null @@ -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() { } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs.meta deleted file mode 100644 index 14646b2..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.AttackState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 4e72df067466464fa3be92bfd049dd0d -timeCreated: 1771579220 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs deleted file mode 100644 index ad8f9a6..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs +++ /dev/null @@ -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() { } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs.meta deleted file mode 100644 index d807587..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckInRangeState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: bfd76c0fb7284ceaa28f78c702f595da -timeCreated: 1771579231 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs deleted file mode 100644 index 99e9058..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs +++ /dev/null @@ -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() { } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs.meta deleted file mode 100644 index 45f7a6c..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.CheckOutRangeState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 3a9f936d3c8643f7bf632734548f800c -timeCreated: 1771579241 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs deleted file mode 100644 index 81bbaee..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs +++ /dev/null @@ -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() { } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs.meta deleted file mode 100644 index 85dfec6..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.IdleState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 9c5341944d1a467b8553d31bc3d81d5b -timeCreated: 1771579252 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.cs index 61bcb2d..482c1cf 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgun.cs @@ -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(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 } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs new file mode 100644 index 0000000..ec21025 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs @@ -0,0 +1,51 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 手枪的瞄准状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponKnifeData.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs.meta similarity index 83% rename from Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponKnifeData.cs.meta rename to Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs.meta index a087ed2..fefdc42 100644 --- a/Assets/GameMain/Scripts/Base/EntityData/Weapon/WeaponKnifeData.cs.meta +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAimState.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: da25d8e0669f0194d8f56f4283c67b38 +guid: 71e311baa75a23448a593f2abb05999c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs new file mode 100644 index 0000000..e0c0b5b --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs @@ -0,0 +1,34 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 手枪的攻击状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs.meta new file mode 100644 index 0000000..64fe420 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0513d1d0592c344b93c8df22f892ea0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs new file mode 100644 index 0000000..89cbf3a --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs @@ -0,0 +1,35 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 手枪的待机状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs.meta new file mode 100644 index 0000000..8a80571 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunIdleState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd6af23da5725ce4385e23b49b0d4811 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs new file mode 100644 index 0000000..51eed39 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs @@ -0,0 +1,41 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 手枪的搜寻状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs.meta new file mode 100644 index 0000000..e71f81e --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunSeekState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e7a9fee7c4ec5f498756d6e17b1259e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs deleted file mode 100644 index 834fa11..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs.meta deleted file mode 100644 index 177f872..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.AttackState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 417516ffe5574e0c998105fcaa073de7 -timeCreated: 1771578955 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs deleted file mode 100644 index 9c6fe94..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs.meta deleted file mode 100644 index 4eb0912..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckInRangeState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 59644093364644768c26261b24bdb820 -timeCreated: 1771578916 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs deleted file mode 100644 index bd7f76d..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs.meta deleted file mode 100644 index 520dce9..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.CheckOutRangeState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: d9e1a63869694e72a360afe29cc94cfb -timeCreated: 1771578994 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs deleted file mode 100644 index ff2bc3a..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs +++ /dev/null @@ -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() - { - } - } - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs.meta deleted file mode 100644 index 520eac7..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.IdleState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: b4d66ec44b064640aa95c6aa71ff3846 -timeCreated: 1771578662 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.cs index 7c065e7..632674c 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnife.cs @@ -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 _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(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) diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs new file mode 100644 index 0000000..38863e3 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs @@ -0,0 +1,51 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 小刀的瞄准状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs.meta new file mode 100644 index 0000000..bebb44f --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAimState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fb5d24a4e6deac4b8c64d1acc697adc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs new file mode 100644 index 0000000..999a18e --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs @@ -0,0 +1,34 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 小刀的攻击状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs.meta new file mode 100644 index 0000000..cab5688 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aad966d918f5cdb45912c0275826ef5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs new file mode 100644 index 0000000..0e7a470 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs @@ -0,0 +1,35 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 小刀的待机状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs.meta new file mode 100644 index 0000000..8e04f78 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeIdleState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 393f986a051df42409d687426bb213b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs new file mode 100644 index 0000000..361e27a --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs @@ -0,0 +1,41 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 小刀的搜寻状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs.meta new file mode 100644 index 0000000..7d6f23a --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeSeekState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac6681a70a0b3a6429a876bac61166cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs deleted file mode 100644 index 38e49a7..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs.meta deleted file mode 100644 index 2ad423b..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.AttackState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d9991ee189b733b4e9d40395357f5ef2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs deleted file mode 100644 index f549537..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs.meta deleted file mode 100644 index 3e6f575..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckInRangeState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 01edc8bb0ff46a14d8028e03af932b52 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs deleted file mode 100644 index 864fd97..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs.meta deleted file mode 100644 index 416d391..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.CheckOutRangeState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8009ad2c332a956438e3357ed5e30eb6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs deleted file mode 100644 index 9a55e5b..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs.meta deleted file mode 100644 index 4ad5fa9..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.IdleState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4c9e98f5381e579469dcdac7bf3e1e25 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.cs index 5415084..f07b92b 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLance.cs @@ -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 _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(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; } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs new file mode 100644 index 0000000..245a72a --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs @@ -0,0 +1,51 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 长枪的瞄准状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs.meta new file mode 100644 index 0000000..2a305a3 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAimState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b871ce786fd5a4b44832f9437a752880 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs new file mode 100644 index 0000000..6de10c1 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs @@ -0,0 +1,34 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 长枪的攻击状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs.meta new file mode 100644 index 0000000..b0a52eb --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b3529471ff522fa4dbbf7ca2005b9358 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs new file mode 100644 index 0000000..6e913de --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs @@ -0,0 +1,35 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 长枪的待机状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs.meta new file mode 100644 index 0000000..d8371cc --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceIdleState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a33cf2ffd5d4d54194406a6d9eb68a5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs new file mode 100644 index 0000000..f6a721d --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs @@ -0,0 +1,41 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 长枪的搜寻状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs.meta new file mode 100644 index 0000000..4d033ed --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceSeekState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 366f9a214870cfb4b90fc2682c74f127 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs deleted file mode 100644 index d82da22..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs.meta deleted file mode 100644 index 36aa280..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.AttackState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 25854832a7d3416dacab67bcfef2a2fa -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs deleted file mode 100644 index 4438a9d..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs.meta deleted file mode 100644 index 46164eb..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckInRangeState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 25404ad172434ba38609e797b6d96919 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs deleted file mode 100644 index c96b847..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs.meta deleted file mode 100644 index e9c3d49..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.CheckOutRangeState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f3aa479abb79491d83e8de76806faf7a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs deleted file mode 100644 index 44955c1..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs +++ /dev/null @@ -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() - { - } - } - } -} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs.meta deleted file mode 100644 index 70343bd..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.IdleState.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3464037c29774c3ea326dc93fe33551e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.cs index 69027d6..9748ca9 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightning.cs @@ -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 _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(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) diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs new file mode 100644 index 0000000..0d8c88a --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs @@ -0,0 +1,51 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 闪电的瞄准状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs.meta new file mode 100644 index 0000000..0cc235a --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAimState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 472efc8d8802b4941892df6907b98d03 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs new file mode 100644 index 0000000..8c51a07 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs @@ -0,0 +1,34 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 闪电的攻击状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs.meta new file mode 100644 index 0000000..0f14743 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d4b2a83cb82e1442826effabf6c9351 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs new file mode 100644 index 0000000..009c0e7 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs @@ -0,0 +1,35 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 闪电的待机状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs.meta new file mode 100644 index 0000000..ff54197 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningIdleState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f6fccbd2de0c494fa2f33e0fba04d4f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs new file mode 100644 index 0000000..8201aae --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs @@ -0,0 +1,41 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 闪电的搜寻状态。 + /// + 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() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs.meta new file mode 100644 index 0000000..1f06fff --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningSeekState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9fe264206ae0d0d4492b94b952d6d698 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs deleted file mode 100644 index bb848b9..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs +++ /dev/null @@ -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() - { - } - } - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs.meta deleted file mode 100644 index 89d8e6a..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.AttackState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 96b136ec326941a0821f66ddf750de29 -timeCreated: 1771579419 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs deleted file mode 100644 index c50059f..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs.meta deleted file mode 100644 index 5b75111..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckInRangeState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 57a936178b5a4ff2938df369b77631e6 -timeCreated: 1771579430 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs deleted file mode 100644 index e3e3a21..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs +++ /dev/null @@ -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() - { - } - } - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs.meta deleted file mode 100644 index 1cb2f25..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.CheckOutRangeState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 89988d7be9104b0f83a423d1de2398b1 -timeCreated: 1771579440 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs deleted file mode 100644 index 3189b71..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace SepCore.Entity -{ - public partial class WeaponSlash - { - private class IdleState : WeaponStateBase - { - private WeaponSlash _weapon; - public override WeaponStateType State => WeaponStateType.Idle; - public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash; - - public override void OnEnter() - { - } - - public override void OnUpdate(float elapseSeconds, float realElapseSeconds) - { - _weapon.Check(); - _weapon.RotateToOrigin(elapseSeconds); - - if (_weapon._target != null && _weapon._target.Available) - { - _weapon.TransitionTo(WeaponStateType.Check_OutRange); - } - } - - public override void OnLeave() - { - } - } - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs.meta deleted file mode 100644 index eeb31e1..0000000 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.IdleState.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: bec8a4a6cb974111bbd70072754f9fdb -timeCreated: 1771579452 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.cs index b98ceb6..96b61d0 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlash.cs @@ -8,51 +8,37 @@ using UnityGameFramework.Runtime; namespace SepCore.Entity { - public partial class WeaponSlash : WeaponBase + public class WeaponSlash : WeaponBase { #region Property - private WeaponSlashData _weaponData; - - private Quaternion _cachedRotation; - [SerializeField] private float _rotateSpeed = 4f; - private Sequence _attackSequence; private Vector3 _attackCenter; private Quaternion _cachedAttackRotation; private IWeaponAttackEffect _attackEffect; - [SerializeField] private float attackDuration = 0.4f; - [SerializeField] private float returnDuration = 0.4f; + [SerializeField] private float _attackDuration = 0.4f; + [SerializeField] private float _returnDuration = 0.4f; [SerializeField] private float _swingAngleScale = 1f; - [SerializeField] private LayerMask _hitMask = ~0; [SerializeField] private int _maxHitColliders = 32; private Collider[] _hitResults; private readonly HashSet _hitEntityIds = new(); - private float _attackRadius; - private float _attackRadiusSqr; - private float _sectorAngle; + public float SectorAngle => WeaponData.GetParamFloat(ParamType.SectorAngle); #endregion - - public override ImpactData GetImpactData() - { - return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat); - } - protected override void BuildStates() { - RegisterState(new IdleState()); - RegisterState(new CheckInRangeState()); - RegisterState(new CheckOutRangeState()); - RegisterState(new AttackState()); + RegisterState(new WeaponSlashIdleState()); + RegisterState(new WeaponSlashAimState()); + RegisterState(new WeaponSlashSeekState()); + RegisterState(new WeaponSlashAttackState()); } - protected override void Attack() + public override void Attack() { StopAttackTween(false); FaceTargetImmediately(); @@ -61,20 +47,20 @@ namespace SepCore.Entity _attackCenter = CachedTransform.position; _cachedAttackRotation = CachedTransform.rotation; - float swingAngle = Mathf.Clamp(_sectorAngle * Mathf.Max(0.1f, _swingAngleScale), 15f, 170f); + float swingAngle = Mathf.Clamp(SectorAngle * Mathf.Max(0.1f, _swingAngleScale), 15f, 170f); Quaternion startRotation = Quaternion.AngleAxis(-swingAngle * 0.5f, Vector3.up) * _cachedAttackRotation; Quaternion endRotation = Quaternion.AngleAxis(swingAngle * 0.5f, Vector3.up) * _cachedAttackRotation; CachedTransform.rotation = startRotation; _attackSequence = DOTween.Sequence(); _attackSequence.Append( - CachedTransform.DORotateQuaternion(endRotation, attackDuration).SetEase(Ease.OutSine)); - _attackSequence.InsertCallback(attackDuration * 0.5f, () => + CachedTransform.DORotateQuaternion(endRotation, _attackDuration).SetEase(Ease.OutSine)); + _attackSequence.InsertCallback(_attackDuration * 0.5f, () => { - _attackEffect?.Play(this, _attackCenter, _target, _attackRadius); + _attackEffect.Play(this, _attackCenter, _target, WeaponData.AttackRange); ApplySectorDamage(); }); - _attackSequence.Append(CachedTransform.DORotateQuaternion(_cachedAttackRotation, returnDuration) + _attackSequence.Append(CachedTransform.DORotateQuaternion(_cachedAttackRotation, _returnDuration) .SetEase(Ease.InSine)); _attackSequence.AppendCallback(() => { @@ -83,14 +69,10 @@ namespace SepCore.Entity }); } - protected override void Check() - { - _target = SelectTarget(_sqrRange); - } - private void ApplySectorDamage() { - if (_attackRadius <= 0f) return; + float attackRadius = WeaponData.AttackRange; + if (attackRadius <= 0f) return; Vector3 forward = CachedTransform.forward; forward.y = 0f; @@ -100,19 +82,19 @@ namespace SepCore.Entity } forward.Normalize(); - float halfAngle = _sectorAngle * 0.5f; - if (TryQueueSectorCollisionQuery(_attackCenter, _attackRadius, in forward, halfAngle, + float halfAngle = SectorAngle * 0.5f; + if (TryQueueSectorCollisionQuery(_attackCenter, attackRadius, in forward, halfAngle, Mathf.Max(1, _maxHitColliders))) { - _hitEntityIds.Clear(); return; } if (_hitResults == null || _hitResults.Length == 0) return; - int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, _attackRadius, _hitResults, _hitMask, + int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, attackRadius, _hitResults, _hitMask, QueryTriggerInteraction.Collide); + float attackRadiusSqr = attackRadius * attackRadius; _hitEntityIds.Clear(); for (int i = 0; i < hitCount; i++) { @@ -127,7 +109,7 @@ namespace SepCore.Entity toTarget.y = 0f; float sqrDistance = toTarget.sqrMagnitude; - if (sqrDistance <= Mathf.Epsilon || sqrDistance > _attackRadiusSqr) continue; + if (sqrDistance <= Mathf.Epsilon || sqrDistance > attackRadiusSqr) continue; float angle = Vector3.Angle(forward, toTarget.normalized); if (angle > halfAngle) continue; @@ -136,53 +118,11 @@ namespace SepCore.Entity } } - private void RotateToTarget(float elapseSeconds) - { - if (_target == null || !_target.Available) return; - - Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position; - directionToTarget.y = 0f; - if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return; - - Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up); - CachedTransform.rotation = - Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds); - } - - private void RotateToOrigin(float elapseSeconds) - { - CachedTransform.rotation = - Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds); - } - - private void FaceTargetImmediately() - { - if (_target == null || !_target.Available) return; - - Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position; - directionToTarget.y = 0f; - if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return; - - CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up); - } - #region Lifecycle protected override bool OnWeaponShow(object userData) { - _weaponData = RequireWeaponData(userData); - if (_weaponData == null) return false; - WeaponData = _weaponData; - - StartAttackCooldown(_weaponData.Cooldown); - _sqrRange = _weaponData.AttackRange * _weaponData.AttackRange; - _cachedRotation = CachedTransform.rotation; - - _attackRadius = Mathf.Max(0.1f, _weaponData.AttackRange); - _attackRadiusSqr = _attackRadius * _attackRadius; - - float configuredSectorAngle = _weaponData.ParamsData != null ? _weaponData.ParamsData.SectorAngle : 0f; - _sectorAngle = configuredSectorAngle > 0f ? Mathf.Clamp(configuredSectorAngle, 1f, 360f) : 90f; + _cachedAttackRotation = _cachedRotation; int capacity = Mathf.Max(1, _maxHitColliders); if (_hitResults == null || _hitResults.Length != capacity) @@ -191,37 +131,19 @@ namespace SepCore.Entity } _attackEffect = new SlashSectorAttackEffect(); - - if (_weaponData.OwnerCamp == CampType.Player) - { - gameObject.layer = LayerMask.NameToLayer("PlayerWeapon"); - _hitMask = LayerMask.GetMask("Enemy"); - } - else if (_weaponData.OwnerCamp == CampType.Enemy) - { - gameObject.layer = LayerMask.NameToLayer("EnemyWeapon"); - _hitMask = LayerMask.GetMask("Player"); - } - return true; } protected override void OnWeaponHide(object userData) { StopAttackTween(true); - CancelAttackCooldown(); _attackEffect = null; } - protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData) - { - BindAttackStatFromOwner(parentEntity); - } - protected override void OnWeaponDetach(EntityLogic parentEntity, object userData) { StopAttackTween(true); - ReleaseAttackStatSubscription(); + base.OnWeaponDetach(parentEntity, userData); } protected override void OnEnabledChanged(bool enabled) @@ -234,7 +156,6 @@ namespace SepCore.Entity #endregion - private void StopAttackTween(bool resetTransform) { if (_attackSequence != null) @@ -252,7 +173,5 @@ namespace SepCore.Entity _hitEntityIds.Clear(); } - - public float SectorAngle => _sectorAngle; } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs new file mode 100644 index 0000000..0d3ffd6 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs @@ -0,0 +1,51 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 斩击的瞄准状态。 + /// + public sealed class WeaponSlashAimState : WeaponStateBase + { + private WeaponSlash _weapon; + + public override WeaponStateType State => WeaponStateType.Aim; + + public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon; + + public override void OnEnter() + { + if (_weapon.CanAttack) + { + _weapon.TransitionTo(WeaponStateType.Attack); + } + } + + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + _weapon.Check(); + _weapon.RotateToTarget(elapseSeconds); + + if (_weapon.Target == null || !_weapon.Target.Available) + { + _weapon.TransitionTo(WeaponStateType.Idle); + return; + } + + if (!_weapon.IsInRange(_weapon.Target, _weapon.SqrRange)) + { + _weapon.TransitionTo(WeaponStateType.Seek); + return; + } + + if (_weapon.CanAttack) + { + _weapon.TransitionTo(WeaponStateType.Attack); + } + } + + public override void OnLeave() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs.meta new file mode 100644 index 0000000..43e843d --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAimState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7414da82794ead4da61b1999fc7e744 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs new file mode 100644 index 0000000..f8080e4 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs @@ -0,0 +1,34 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 斩击的攻击状态。 + /// + public sealed class WeaponSlashAttackState : WeaponStateBase + { + private WeaponSlash _weapon; + + public override WeaponStateType State => WeaponStateType.Attack; + + public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon; + + public override void OnEnter() + { + _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.Attack(); + } + + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + if (!_weapon.IsAttacking) + { + _weapon.TransitionTo(WeaponStateType.Aim); + } + } + + public override void OnLeave() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs.meta new file mode 100644 index 0000000..5818fed --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8eb95376ed449de4482bcdce19c7fbb7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs new file mode 100644 index 0000000..cb2db26 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs @@ -0,0 +1,35 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 斩击的待机状态。 + /// + public sealed class WeaponSlashIdleState : WeaponStateBase + { + private WeaponSlash _weapon; + + public override WeaponStateType State => WeaponStateType.Idle; + + public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon; + + public override void OnEnter() + { + } + + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + _weapon.Check(); + _weapon.RotateToOrigin(elapseSeconds); + + if (_weapon.Target != null && _weapon.Target.Available) + { + _weapon.TransitionTo(WeaponStateType.Seek); + } + } + + public override void OnLeave() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs.meta new file mode 100644 index 0000000..8b49adb --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashIdleState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 001c3370c7d371b40a0d7821281a14f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs new file mode 100644 index 0000000..c2f5a64 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs @@ -0,0 +1,41 @@ +using SepCore.Definition; + +namespace SepCore.Entity +{ + /// + /// 斩击的搜寻状态。 + /// + public sealed class WeaponSlashSeekState : WeaponStateBase + { + private WeaponSlash _weapon; + + public override WeaponStateType State => WeaponStateType.Seek; + + public override void OnInit(WeaponBase weapon) => _weapon = (WeaponSlash)weapon; + + public override void OnEnter() + { + } + + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + _weapon.Check(); + _weapon.RotateToTarget(elapseSeconds); + + if (_weapon.Target == null || !_weapon.Target.Available) + { + _weapon.TransitionTo(WeaponStateType.Idle); + return; + } + + if (_weapon.IsInRange(_weapon.Target, _weapon.SqrRange)) + { + _weapon.TransitionTo(WeaponStateType.Aim); + } + } + + public override void OnLeave() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs.meta b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs.meta new file mode 100644 index 0000000..7045ee5 --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashSeekState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f296d8dc7c2e6a459556c44496a4619 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/Utility/EntityDataFactory.cs b/Assets/GameMain/Scripts/Runtime/Utility/EntityDataFactory.cs index 42fecae..655e8ee 100644 --- a/Assets/GameMain/Scripts/Runtime/Utility/EntityDataFactory.cs +++ b/Assets/GameMain/Scripts/Runtime/Utility/EntityDataFactory.cs @@ -20,21 +20,12 @@ namespace SepCore.CustomUtility public static WeaponData Create(int entityId, WeaponType weaponType, int ownerId, CampType ownerCamp) { var config = GameEntry.Luban.Get((int)weaponType); - return weaponType switch - { - WeaponType.Handgun => new WeaponHandgunData(entityId, config, ownerId, ownerCamp), - WeaponType.Knife => new WeaponKnifeData(entityId, config, ownerId, ownerCamp), - WeaponType.Axe => new WeaponSlashData(entityId, config, ownerId, ownerCamp), - WeaponType.Lightning => new WeaponLightningData(entityId, config, ownerId, ownerCamp), - WeaponType.Lance => new WeaponLanceData(entityId, config, ownerId, ownerCamp), - _ => throw new NotImplementedException($"Unsupported weapon type '{weaponType}'.") - }; + return new WeaponData(entityId, config, ownerId, ownerCamp); } public static WeaponData Create(int entityId, WeaponConfig config, int ownerId, CampType ownerCamp) { - WeaponType type = config.WeaponType; - return Create(entityId, type, ownerId, ownerCamp); + return new WeaponData(entityId, config, ownerId, ownerCamp); } } } diff --git a/数据表/Datas/WeaponConfig_武器配置.xlsx b/数据表/Datas/WeaponConfig_武器配置.xlsx index 1e629a86352ee61b7ce8dfb6f8bc3053e52e7d6d..3073538d7f2ce10b187bdbf574979f114d072e7a 100644 GIT binary patch delta 3927 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 delta 3918 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 diff --git a/数据表/Datas/__enums__.xlsx b/数据表/Datas/__enums__.xlsx index 48ed9b5ac4ee67f20875b101f53db17e93f10e48..c821c303334860032cc2947347315061c9055660 100644 GIT binary patch delta 10790 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 Y|GmL~gEBDRK}0EJf*5cWy#J{G3n0)pod5s; delta 10488 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 z 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