vampire-like/Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponKnife.cs

334 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using Components;
using CustomComponent;
using Definition.DataStruct;
using Definition.Enum;
using DG.Tweening;
using Entity.EntityData;
using Game.Utility;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public class WeaponKnife : WeaponBase
{
private WeaponKnifeData _weaponData;
private float _currAttackTimer = 0f;
private EnemyManagerComponent _enemy;
private EntityBase _target;
private float _sqrRange;
private Collider _collider;
private Rigidbody _rigidbody;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 2f;
private Dictionary<WeaponStateType, WeaponStateBase> _weaponStates;
private StatProperty _attackStat = new();
private Action<StatModifier, bool> _attackStatCallback;
[SerializeField] private float attackDuration = 0.15f; // 戳刺持续时间
[SerializeField] private float returnDuration = 0.2f; // 回归持续时间
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, _attackStat);
}
private void InitStates()
{
_weaponStates = new Dictionary<WeaponStateType, WeaponStateBase>
{
{ WeaponStateType.Idle, new IdleState() },
{ WeaponStateType.Check_InRange, new CheckInRangeState() },
{ WeaponStateType.Check_OutRange, new CheckOutRangeState() },
{ WeaponStateType.Attack, new AttackState() }
};
foreach (var state in _weaponStates.Values)
{
state.OnInit(this);
}
}
#region FSM Methods
protected override void Attack()
{
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * _weaponData.AttackRange;
_isAttacking = true;
_collider.enabled = true;
_rigidbody.detectCollisions = true;
Transform parentTransform = CachedTransform.parent;
CachedTransform.SetParent(null);
var sequence = DOTween.Sequence();
sequence.Append(CachedTransform.DOMove(targetPos, attackDuration).SetEase(Ease.OutQuad));
sequence.AppendCallback(() =>
{
_collider.enabled = false;
_rigidbody.detectCollisions = false;
CachedTransform.SetParent(parentTransform);
});
sequence.Append(CachedTransform.DOLocalMove(Vector3.zero, returnDuration / 2f).SetEase(Ease.InQuad));
sequence.AppendCallback(() => { _isAttacking = false; });
}
protected override void Check()
{
_target = null;
float minSqrMagnitude = float.MaxValue;
var enemies = _enemy.Enemies;
foreach (var enemy in enemies)
{
if (enemy == null || !enemy.Available) continue;
float sqrMagnitude = AIUtility.GetSqrMagnitude(this, enemy);
if (minSqrMagnitude < sqrMagnitude) continue;
_target = enemy;
minSqrMagnitude = sqrMagnitude;
}
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = (_target.CachedTransform.position - transform.position).normalized;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, Vector3.up);
var quaternion = Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
CachedTransform.rotation = quaternion;
}
private void RotateToOrigin(float elapseSeconds)
{
var quaternion = Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
CachedTransform.rotation = quaternion;
}
protected override void TransitionTo(WeaponStateType state)
{
_currentState?.OnLeave();
_currentState = _weaponStates[state];
_currentState.OnEnter();
}
#endregion
#region FSM
protected override void OnShow(object userData)
{
base.OnShow(userData);
_weaponData = userData as WeaponKnifeData;
if (_weaponData == null)
{
Log.Error("WeaponKnife data is invalid.");
return;
}
WeaponData = _weaponData;
_collider = GetComponent<Collider>();
_rigidbody = GetComponent<Rigidbody>();
_currAttackTimer = 0;
_enemy = GameEntry.EnemyManager;
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = transform.rotation;
_collider.enabled = false;
_rigidbody.detectCollisions = false;
if (_weaponData.OwnerCamp == CampType.Player)
this.gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
if (_weaponData.OwnerCamp == CampType.Enemy)
this.gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
InitStates();
TransitionTo(WeaponStateType.Idle);
}
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
{
base.OnAttachTo(parentEntity, parentTransform, userData);
if (parentEntity is Player player)
{
var statComponent = player.GetComponent<StatComponent>();
if (statComponent != null)
{
this._attackStat = statComponent.GetStat(StatType.Attack);
this._attackStatCallback = (modifier, b) => statComponent.UpdateStat(_attackStat, modifier, b);
statComponent.Subscribe(StatType.Attack, this._attackStatCallback);
}
}
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (!_isEnabled) return;
Log.Debug($"current state: {_currentState}");
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
}
protected override void OnDetachFrom(EntityLogic parentEntity, object userData)
{
base.OnDetachFrom(parentEntity, userData);
if (parentEntity is Player player)
{
var statComponent = player.GetComponent<StatComponent>();
if (statComponent != null)
{
statComponent.Unsubscribe(StatType.Attack, this._attackStatCallback);
}
}
this._attackStat = null;
this._attackStatCallback = null;
}
#endregion
private 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()
{
}
}
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 (AIUtility.GetSqrMagnitude(_weapon._target, _weapon) < _weapon._sqrRange)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
return;
}
}
public override void OnLeave()
{
}
}
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()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (AIUtility.GetSqrMagnitude(_weapon._target, _weapon) >= _weapon._sqrRange)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
return;
}
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
return;
}
}
public override void OnLeave()
{
_weapon._currAttackTimer = 0;
}
}
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.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (!_weapon._isAttacking)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave()
{
}
}
}
}