using System.Linq; using BITFALL.Guns.States; using BITFALL.Player.Movement; using UnityEngine; using BITKit; using BITKit.Entities; using BITKit.Entities.Melee; using UnityEngine.InputSystem; using BITKit.StateMachine; using Unity.Mathematics; using UnityEngine.InputSystem.Interactions; #if UNITY_EDITOR using UnityEditor; #endif namespace BITFALL.Guns { public interface IGunState : IState { } [System.Serializable] public abstract class GunState : IGunState { public BITGun root; private IEntityMovement _entityMovement; public bool Enabled { get;set; } public virtual void Initialize() { _entityMovement = root.Entity.Get(); _entityMovement.OnStateChanged += OnMovementStateChanged; } public virtual void OnStateEntry(IState old) { } public virtual void OnStateExit(IState old, IState newState) { } public virtual void OnStateUpdate(float deltaTime) { } public virtual void OnMovementStateChanged(IEntityMovementState old, IEntityMovementState newState) { } public virtual void AnimationEvent(string name) { } } [System.Serializable] public class GunStateMachine : MonoStateMachine { } public class BITGun : BITEquipBase { // 常数引用 public const string _Movement = "Movement"; public const string _Run = "Run"; public const string _Sprint = "Sprint"; public const string _Aim = "Aim"; public const string _Interactive = "Interactive"; public const string _Equip = "Equip"; public const string _Fire = "Fire"; public const string _Reload = "Reload"; public const string _Melee = "Melee"; public const string _Climb = "Climb"; //简单设置 [Header(Constant.Header.Settings)] [SerializeField] private Vector3 bulletInitialOffset; [SerializeField] private int initialAimFov = 60; [SerializeField] private SpringEulerAngle recoilSpring=new(); // 输入系统 [Header(Constant.Header.Input)] public InputActionReference fireAction; public InputActionReference aimAction; public InputActionReference reloadAction; public InputActionReference meleeAction; public InputActionGroup actionGroup = new(); // 依赖注入 [Header(Constant.Header.Providers)] [SerializeReference, SubclassSelector] private IMeleeService meleeService; // 引用组件 [Header(Constant.Header.Components)] [SerializeField] private Renderer[] rendererComponents; [SerializeField] private VFXPlayer vfxPlayer; [SerializeField] private LocationAdditive locationAdditive; // 引用预制体 [Header(Constant.Header.Prefabs)] [SerializeField] internal AssetableGun assetable; [Header(Constant.Header.Reference)] // 内部变量burst [Header(Constant.Header.InternalVariables)] public ExpectState expectFiring; public ExpectState expectAiming; internal readonly IntervalUpdate fireInterval = new(0.32f); internal readonly IntervalUpdate burstFireInterval = new(0.1f); internal int burstFireCount; private IEntityMovement _movement; private IPlayerMovement _playerMovement; private static readonly int IsGrounded = Animator.StringToHash("IsGrounded"); #region 接口实现 public override string AddressablePath => assetable.AddressablePath; #endregion public override void OnAwake() { base.OnAwake(); actionGroup.RegisterCallback(fireAction, OnFire); actionGroup.RegisterCallback(aimAction, OnAim); actionGroup.RegisterCallback(reloadAction, OnReload); actionGroup.RegisterCallback(meleeAction, OnMelee); _movement = entity.Get(); _playerMovement = entity.Get(); _movement.OnStateChanged += OnMovementStateChanged; _movement.OnCommand += OnMovementCommand; } private void OnMovementCommand(object obj) { switch (obj) { case OnPlayerJumpCommand: animator.Play("Jump"); break; } } private void OnMelee(InputAction.CallbackContext obj) { switch (CurrentState) { case Equip: case Melee: return; } TransitionState(); } private void OnReload(InputAction.CallbackContext obj) { if (obj.JustPressed() is false) return; switch (CurrentState) { case Equip: case Melee: return; } TransitionState(); } private void OnMovementStateChanged(IEntityMovementState arg1, IEntityMovementState arg2) { foreach (var x in StateDictionary.Values) { x.OnMovementStateChanged(arg1,arg2); } } public override void OnStart() { base.OnStart(); foreach (var x in rendererComponents) { x.enabled = false; } } public override void Entry() { base.Entry(); var animName = animator.animator.GetCurrentAnimatorStateInfo(0).shortNameHash; animator.animator.Play(animName,-1,0); animator.animator.enabled = true; actionGroup.allowInput.AddElement(this); foreach (var x in rendererComponents) { x.enabled = true; } expectFiring.Reset(); Enabled = true; fireInterval.Interval = 1f / assetable.FireMode.FireRate; fireInterval.Reset(); if (assetable.FireMode is BurstFireMode burstFireMode) { burstFireInterval.Interval = burstFireMode.BurstFireInterval; burstFireInterval.Reset(); } TransitionState(StateDictionary.First().Value); } public override void Exit() { actionGroup.allowInput.RemoveElement(this); foreach (var x in rendererComponents) { x.enabled = false; } expectFiring.Reset(); animator.animator.enabled = false; } public override void OnUpdate(float deltaTime) { CurrentState?.OnStateUpdate(deltaTime); switch (assetable.FireMode) { case AutoFireMode: expectFiring.shouldBe = fireAction.action.IsPressed(); break; case SemiFireMode: expectFiring.shouldBe = fireAction.action.WasPressedThisFrame(); break; case BurstFireMode when expectFiring.being: expectFiring.shouldBe = fireAction.action.WasPressedThisFrame(); if(burstFireInterval.AllowUpdate) { Fire(); } break; } animator.animator.SetBool(IsGrounded,_movement.IsGrounded); recoilSpring.Update(deltaTime,default); locationAdditive.AddEuler(recoilSpring.value); } public override void AnimationEvent(string eventName) { CurrentState?.AnimationEvent(eventName); switch (eventName) { case "Melee": meleeService.Melee(new MeleeCommand { PlayerId = entity.Id, Position = transform.position, Range = 1 }); break; } } public void Fire() { //如果启用了指针则不开火 if(BITAppForUnity.AllowCursor) { return; } //播放射击动画 animator.Play(BITGun._Fire); //调用BulletManager生成子弹 var _transform = transform; var rotation = _transform.rotation; BulletService.Spawn(new SpawnBullet { initiator = entity.Id, pos = (_transform.position+rotation * bulletInitialOffset).Fix(), rot = rotation, forward = _transform.forward.Fix(), initialDamage = 32, }); //播放枪口MuzzleFlash vfxPlayer.Execute(); //开火模式逻辑判断 switch (assetable.FireMode) { case AutoFireMode: break; case SemiFireMode: //如果是半自动开火,则取消射击 expectFiring.Reset(); break; case BurstFireMode burstFireMode: burstFireCount++; expectFiring.being = true; if(burstFireCount > burstFireMode.BurstRound) { burstFireCount = 0; expectFiring.Reset(); } break; } if (assetable.TryGetProperty(out var _recoil)) { var _newRecoil = new Vector3 { x = _recoil.Recoil.x, y = _recoil.Recoil.y.Random(), z = _recoil.Recoil.z.Random() }; recoilSpring.value = _newRecoil; _playerMovement.AddViewEuler(new float2(_newRecoil.x,_newRecoil.y)); } } private void OnFire(InputAction.CallbackContext context) { } private void OnAim(InputAction.CallbackContext context) { expectAiming.shouldBe = context.ReadValueAsButton(); } } #if UNITY_EDITOR [CustomEditor(typeof(BITGun))] public class BITGunInspector:BITInspector{} #endif }