286 lines
9.0 KiB
C#
286 lines
9.0 KiB
C#
using System.Linq;
|
|
using BITFALL.Guns.States;
|
|
using UnityEngine;
|
|
using BITKit;
|
|
using BITKit.Entities;
|
|
using BITKit.Entities.Melee;
|
|
using UnityEngine.InputSystem;
|
|
using BITKit.StateMachine;
|
|
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<IEntityMovement>();
|
|
_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>
|
|
{
|
|
// 常数引用
|
|
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;
|
|
|
|
// 输入系统
|
|
[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)]
|
|
public Renderer[] rendererComponents;
|
|
public VFXPlayer vfxPlayer;
|
|
|
|
// 引用预制体
|
|
[Header(Constant.Header.Prefabs)]
|
|
public 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;
|
|
|
|
private SpringEulerAngle positionSpring=new();
|
|
private SpringEulerAngle recoilSpring=new();
|
|
|
|
#region 接口实现
|
|
public override string AddressablePath => assetable.AdressablePath;
|
|
#endregion
|
|
|
|
public override void OnAwake()
|
|
{
|
|
base.OnAwake();
|
|
actionGroup.RegisterCallback(fireAction, OnFire);
|
|
actionGroup.RegisterCallback(aimAction, OnAim);
|
|
actionGroup.RegisterCallback(reloadAction, OnReload);
|
|
actionGroup.RegisterCallback(meleeAction, OnMelee);
|
|
entity.Get<IEntityMovement>().OnStateChanged += OnMovementStateChanged;
|
|
}
|
|
|
|
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 void OnStart()
|
|
{
|
|
base.OnStart();
|
|
foreach (var x in rendererComponents)
|
|
{
|
|
x.enabled = false;
|
|
}
|
|
}
|
|
public override void Entry()
|
|
{
|
|
base.Entry();
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
// .value = new Vector3(Random.Range(-gunSpec.recoil.x, 0), gunSpec.recoil.y.Random(), 0);
|
|
// sprintPos.value -= new Vector3(gunSpec.recoil.z.Random(), 0, gunSpec.recoil.z);
|
|
}
|
|
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<BITGun>{}
|
|
#endif
|
|
} |