BITFALL/Assets/Artists/Scripts/Equip/BITGun.cs

342 lines
11 KiB
C#

using System;
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 Cysharp.Threading.Tasks;
using Unity.Mathematics;
using UnityEditorInternal;
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;
[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<GunState> { }
public class BITGun : BITEquipBase<GunState>
{
//简单设置
[Header(Constant.Header.Settings)]
[SerializeField] private Vector3 bulletInitialOffset;
[SerializeField] private SpringEulerAngle recoilSpring=new();
// 输入系统
[Header(Constant.Header.Input)]
public InputActionReference fireAction;
public InputActionReference aimAction;
public InputActionReference reloadAction;
public InputActionReference meleeAction;
[Header(Constant.Header.HotFix)]
[SerializeField] private Transform cameraView;
// 引用组件
[Header(Constant.Header.Components)]
[SerializeField] private LocationAdditive locationAdditive;
// 引用预制体
[Header(Constant.Header.Prefabs)]
[SerializeField] internal AssetableGun assetable;
[Header(Constant.Header.Reference)]
// 内部变量burst
[Header(Constant.Header.InternalVariables)]
public ExpectState<bool> expectFiring;
public ExpectState<bool> 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;
private static readonly int IsGrounded = Animator.StringToHash("IsGrounded");
private AssetableGun _gun=>item as AssetableGun;
private bool isHolstered;
#region
public override string AddressablePath => assetable.AddressablePath;
#endregion
public override void OnAwake()
{
base.OnAwake();
inputActionGroup.RegisterCallback(fireAction, OnFire);
inputActionGroup.RegisterCallback(aimAction, OnAim);
inputActionGroup.RegisterCallback(reloadAction, OnReload);
inputActionGroup.RegisterCallback(meleeAction, OnMelee);
_movement.OnStateChanged += OnMovementStateChanged;
_movement.OnCommand += OnMovementCommand;
animator[0].onStateExit += (state) =>
{
isHolstered = state is BITConstant.Player.Holster;
};
}
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<Melee>();
}
private void OnReload(InputAction.CallbackContext obj)
{
if (obj.JustPressed() is false) return;
switch (CurrentState)
{
case Equip:
case Melee:
return;
}
TransitionState<Reload>();
}
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 / assetable.FireMode.FireRate;
fireInterval.Reset();
if (assetable.FireMode is BurstFireMode burstFireMode)
{
burstFireInterval.Interval = burstFireMode.BurstFireInterval;
burstFireInterval.Reset();
}
TransitionState<Equip>();
return UniTask.CompletedTask;
}
public override async UniTask ExitAsync()
{
TransitionState<Holster>();
inputActionGroup.allowInput.RemoveElement(this);
expectFiring.Reset();
cameraView.localPosition = default;
try
{
while (_health.IsAlive && isHolstered is false)
{
destroyCancellationToken.ThrowIfCancellationRequested();
await UniTask.NextFrame();
}
destroyCancellationToken.ThrowIfCancellationRequested();
await base.ExitAsync();
}
catch (OperationCanceledException)
{
}
}
public override void OnUpdate(float deltaTime)
{
UpdateState(deltaTime);
switch (assetable.FireMode)
{
case AutoFireMode:
break;
case SemiFireMode:
break;
case BurstFireMode when expectFiring.being:
expectFiring.shouldBe = fireAction.action.WasPressedThisFrame();
if(burstFireInterval.AllowUpdate)
{
Fire();
}
break;
}
animator.animator.SetBool(IsGrounded,_movement.IsGrounded);
animator.animator.SetFloat(BITConstant.Player.SqrMagnitude,_movement.LocomotionBasedVelocity.sqrMagnitude);
recoilSpring.Update(deltaTime,default);
locationAdditive.AddEuler(recoilSpring.value);
}
public override void AnimationEvent(string eventName)
{
if(IsEntered is false) return;
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(BITAppForUnity.AllowCursor)
{
return;
}
//播放射击动画
UnityEntity.Invoke(Constant.Animation.Play, BITConstant.Player.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 = _gun.InitialDamage,
InitialForce = _gun.InitialBulletForce,
});
//开火模式逻辑判断
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<IRecoil>(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)
{
switch (assetable.FireMode)
{
case AutoFireMode :
switch (context)
{
case {interaction:TapInteraction , started:true}:
expectFiring.shouldBe = true;
break;
case {interaction:TapInteraction , performed:true}:
case {interaction:HoldInteraction , canceled:true}:
expectFiring.shouldBe = false;
break;
}
break;
case SemiFireMode:
switch (context)
{
case { interaction: TapInteraction, started: true }:
expectFiring.shouldBe = true;
break;
}
break;
}
}
private void OnAim(InputAction.CallbackContext context)
{
expectAiming.shouldBe = context.ReadValueAsButton();
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(BITGun))]
public class BITGunInspector:BITInspector<BITGun>{}
#endif
}