using System; using System.Linq; using BITFALL.Guns.States; using BITFALL.Player.Equip; using BITFALL.Player.Movement; using UnityEngine; using BITKit; using BITKit.Entities; using BITKit.Entities.Melee; using UnityEngine.InputSystem; using BITKit.StateMachine; using Cysharp.Threading.Tasks; using Unity.Mathematics; using UnityEngine.InputSystem.Interactions; #if UNITY_EDITOR #endif namespace BITFALL.Guns { public interface IGunState : IState { } [System.Serializable] public abstract class GunState : IGunState { public BITGun root; [Inject] protected IEntityMovement _entityMovement; public bool Enabled { get;set; } public virtual void Initialize() { root.Entity.Inject(this); _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 { //简单设置 [Header(Constant.Header.Settings)] [SerializeField] private Vector3 bulletInitialOffset; [SerializeField] private SpringEulerAngle recoilSpring=new(); [SerializeField] private float recoilPositionWeight=1; [Header(Constant.Header.Gameobjects)] [SerializeField] private Transform initialSight; [SerializeField] private Optional newSight; [Header(nameof(Optional))] [SerializeField] private Optional breathingAdditive; // 输入系统 [Header(Constant.Header.Input)] [SerializeField] internal InputActionReference fireAction; [SerializeField] internal InputActionReference aimAction; [SerializeField] internal InputActionReference reloadAction; [SerializeField] internal InputActionReference meleeAction; [SerializeField] internal InputActionReference steadyAimAction; [Header(Constant.Header.HotFix)] [SerializeField] private Transform cameraView; // 引用组件 [Header(Constant.Header.Components)] [SerializeField] private LocationAdditive locationAdditive; [SerializeField] private LocationAdditive selfViewAdditive; // 引用预制体 [Header(Constant.Header.Prefabs)] // 内部变量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; [Inject] private IEntityMovement _movement; [Inject] private IPlayerMovement _playerMovement; [Inject] private IHealth _health; [Inject] private IEquipService _equipService; private static readonly int IsGrounded = Animator.StringToHash(BITConstant.Player.IsGrounded); private static readonly int IsCrouched = Animator.StringToHash(BITConstant.Player.IsCrouched); private AssetableGun _gun=>item as AssetableGun; private bool isHolstered; private bool isSteadyAim; public bool RequireBolt { get; set; } #region 接口实现 public override string AddressablePath => _gun.AddressablePath; #endregion public override void OnAwake() { base.OnAwake(); inputActionGroup.RegisterCallback(fireAction, OnFire); inputActionGroup.RegisterCallback(aimAction, OnAim); inputActionGroup.RegisterCallback(reloadAction, OnReload); inputActionGroup.RegisterCallback(meleeAction, OnMelee); if (breathingAdditive.Allow) inputActionGroup.RegisterCallback(steadyAimAction, OnSteadyAim); _movement.OnStateChanged += OnMovementStateChanged; _movement.OnCommand += OnMovementCommand; animator[0].onStateExit += (state) => { isHolstered = state is BITConstant.Player.Holster; }; BITAppForUnity.AllowCursor.AddListener(OnAllowCursor); destroyCancellationToken.Register(() => { BITAppForUnity.AllowCursor.RemoveListener(OnAllowCursor); }); } private void OnAllowCursor(bool allow) { if (!allow) return; expectFiring.Reset(); expectAiming.Reset(); } private void OnSteadyAim(InputAction.CallbackContext obj) { switch (obj) { case { interaction: PressInteraction, performed: true } when CurrentState is Aim: isSteadyAim = true; break; case { interaction: PressInteraction, canceled: true }: isSteadyAim = false; break; } } 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 UniTask EntryAsync() { base.EntryAsync(); isHolstered = false; inputActionGroup.allowInput.AddElement(this); expectFiring.Reset(); Enabled = true; fireInterval.Interval = 1f / _gun.FireMode.FireRate; fireInterval.Reset(); if (_gun.FireMode is BurstFireMode burstFireMode) { burstFireInterval.Interval = burstFireMode.BurstFireInterval; burstFireInterval.Reset(); } TransitionState(); return UniTask.CompletedTask; } public override async UniTask ExitAsync() { TransitionState(); _equipService.AllowAttack = false; inputActionGroup.allowInput.RemoveElement(this); expectFiring.Reset(); cameraView.localPosition = default; try { while (_health.IsAlive && isHolstered is false) { destroyCancellationToken.ThrowIfCancellationRequested(); _equipService.Zoom.Value = Mathf.MoveTowards(_equipService.Zoom.Value,0,Time.deltaTime); await UniTask.NextFrame(); } _equipService.Zoom.Allow = false; destroyCancellationToken.ThrowIfCancellationRequested(); _equipService.Stable = 1; await base.ExitAsync(); } catch (OperationCanceledException) { } } public override void OnUpdate(float deltaTime) { UpdateState(deltaTime); switch (_gun.FireMode) { case AutoFireMode: break; case SemiFireMode: break; case BurstFireMode when expectFiring.being: expectFiring.shouldBe = fireAction.action.WasPressedThisFrame(); if(burstFireInterval.AllowUpdate) { switch (AnimationProperties.TryGetValue(BITConstant.Player.AllowFire, out var allowFire)) { case false: case true when allowFire >0.9f: Fire(); break; } } break; } animator.animator.SetBool(IsGrounded,_movement.IsGrounded); animator.animator.SetBool(IsCrouched,_movement.CurrentState is IPlayerCrouchState or IPlayerSlideState); animator.animator.SetFloat(BITConstant.Player.SqrMagnitude,_movement.LocomotionBasedVelocity.sqrMagnitude); recoilSpring.Update(deltaTime,default); locationAdditive.AddEuler(recoilSpring.value); if (selfViewAdditive) { var positionWeight = recoilPositionWeight; if (AnimationProperties.TryGetValue(BITConstant.Player.Aim, out var aim)) { positionWeight *= Mathf.Lerp(2, 1, aim); } selfViewAdditive.AddEuler(recoilSpring.value); selfViewAdditive.AddPosition( new Vector3( recoilSpring.value.y, -recoilSpring.value.x, recoilSpring.value.z ) * positionWeight ); } if(AnimationProperties.TryGetValue(BITConstant.Player.Aim, out var _aim)) { _equipService.Zoom.Allow = CurrentState is Aim; _equipService.Zoom.Value =Mathf.Lerp(1,_gun.InitialAimZoom, _aim); _equipService.AllowScope = _aim > 0.86f && _gun.IsScopeAim; if (breathingAdditive.Allow) { var breatheFrequency = 0.5f; // 呼吸的频率,值越大呼吸越快 var breatheAmplitude = 0.005f; // 呼吸的幅度,即准星上下移动的最大距离 if (_playerMovement.Stamina <= 8) { breatheFrequency = 8; breatheAmplitude = 0.1f; } var breatheOffset = Mathf.Sin(Time.time * breatheFrequency) * breatheAmplitude; if (isSteadyAim) { if (_playerMovement.Stamina > 0) { _playerMovement.Stamina -= 16 * deltaTime; breatheOffset *= 0.16f; } else { isSteadyAim = false; } } else { breatheOffset *= _aim; } _playerMovement.AddViewEuler(new float2(breatheOffset, breatheOffset)); } if (newSight.Allow) { transform.localPosition=Vector3.Lerp(default, newSight.Value.position-initialSight.position, _aim); } else { transform.localPosition = default; } } if (AnimationProperties.TryGetValue(BITConstant.Player.Stable, out var stable)) { _equipService.Stable = stable; } if(AnimationProperties.TryGetValue(BITConstant.Player.AllowFire, out var _allowFire)) { _equipService.AllowAttack = _allowFire > 0.9f; } AllowRendering.SetDisableElements(64564,_equipService.AllowScope); expectAiming.shouldBe = inputActionGroup.GetAction(aimAction).IsPressed(); } public override void AnimationEvent(string eventName) { if(IsEntered is false) return; base.AnimationEvent(eventName); CurrentState?.AnimationEvent(eventName); // switch (eventName) // { // case "Melee": // meleeService.Melee(new MeleeCommand // { // PlayerId = Entity.Id, // Position = Transform.position, // Force = Transform.forward * 128, // Range = 1 // }); // break; // } } public void Fire() { if (RequireBolt) return; //播放射击动画 UnityEntity.Invoke(Constant.Animation.Play, BITConstant.Player.Fire); if (_gun.BuckShot.Allow) { InternalAddRecoil(); for (int i = 0; i < _gun.BuckShot.Value; i++) { InternalFire(); } } else { InternalFire(); InternalAddRecoil(); } //开火模式逻辑判断 switch (_gun.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 (_gun.FireMode is SemiFireMode {RequireBoltAction:true}) { RequireBolt = true; } } private void InternalFire() { //调用BulletManager生成子弹 var _transform = transform; var rotation = _transform.rotation; var random = UnityEngine.Random.insideUnitCircle; if (_gun.TryGetProperty(out var spread)) { random *= spread.Spread; } if(AnimationProperties.TryGetValue(BITConstant.Player.Aim, out var aim)) { random *= Mathf.Lerp(_gun.InitialHipFireSpread,1,aim); } random *= recoilSpring.value.GetLength(); BulletService.Spawn(new SpawnBullet { Initiator = Entity.Id, Position = (_transform.position+rotation * bulletInitialOffset), Rotation = rotation, Forward = rotation * (Vector3.forward + Vector3.up * random.y + Vector3.right * random.x), InitialDamage = _gun.InitialDamage, StartSpeed = _gun.InitialBulletSpeed, InitialForce = _gun.InitialBulletForce, }); } private void InternalAddRecoil() { if (_gun.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) { //如果启用了指针则不开火 if(BITAppForUnity.AllowCursor) { expectFiring.Reset(); return; } switch (_gun.FireMode) { case AutoFireMode : switch (context) { case {interaction:PressInteraction , started:true}: expectFiring.shouldBe = true; break; case {interaction:PressInteraction , canceled:true}: expectFiring.shouldBe = false; break; } break; case SemiFireMode: switch (context) { case { interaction: PressInteraction, started: true } when fireInterval.AllowUpdateWithoutReset && RequireBolt is false: expectFiring.shouldBe = true; break; } break; } } private void OnAim(InputAction.CallbackContext context) { //如果启用了指针则不开火 if(BITAppForUnity.AllowCursor) { expectAiming.Reset(); return; } //expectAiming.shouldBe = context.ReadValueAsButton(); } } // #if UNITY_EDITOR // [CustomEditor(typeof(BITGun))] // public class BITGunInspector:BITInspector{} // #endif }