172 lines
5.6 KiB
C#
172 lines
5.6 KiB
C#
using Characters.Enemy.States;
|
|
using Koptilnya.StateMachine;
|
|
using Mirror;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using UnityEngine.Animations.Rigging;
|
|
using UnityEngine.Serialization;
|
|
|
|
namespace Characters.Enemy
|
|
{
|
|
public class Enemy : Pawn
|
|
{
|
|
public NavMeshAgent agent;
|
|
public Animator animator;
|
|
public Rig aimRig;
|
|
public AudioSource audioSource;
|
|
public AudioSource footstepAudioSource;
|
|
public ParticleSystem hitVFX;
|
|
public Transform aimTransform;
|
|
public float aggressionDistance = 5f;
|
|
|
|
[SyncVar] public EnemyState state = EnemyState.Idle;
|
|
[SyncVar, HideInInspector] public float aimRigWeight;
|
|
|
|
public readonly StateMachine<EnemyState> stateMachine = new StateMachine<EnemyState>();
|
|
|
|
[FormerlySerializedAs("target")] public Transform targetTransform;
|
|
public NetworkAnimator networkAnimator;
|
|
|
|
public bool HasTarget => targetTransform != null;
|
|
public float DistanceToTarget => Vector3.Distance(targetTransform.transform.position, transform.position);
|
|
|
|
private CustomNetworkManager _networkManager;
|
|
private NetworkGameManager _networkGameManager;
|
|
private float _targetAimRigWeight;
|
|
private float _stepCycle;
|
|
private AudioClip _footstepClip;
|
|
private Vector3 _footstepPrevPosition;
|
|
private NavMeshPath _navMeshPath;
|
|
|
|
private const float StepDelay = 0.12f;
|
|
private const float RunSpeed = 4.974f;
|
|
private static readonly int SpeedAnimHash = Animator.StringToHash("speed");
|
|
|
|
void Start()
|
|
{
|
|
_networkManager = NetworkManager.singleton.GetComponent<CustomNetworkManager>();
|
|
_networkGameManager = NetworkGameManager.singleton;
|
|
_footstepClip = Resources.Load<AudioClip>("Audio/EnemySounds/Steps/monsterStep1");
|
|
_navMeshPath = new();
|
|
|
|
networkAnimator = GetComponent<NetworkAnimator>();
|
|
|
|
stateMachine.Add(new IdleState(this));
|
|
stateMachine.Add(new PatrolState(this));
|
|
stateMachine.Add(new ChaseState(this));
|
|
stateMachine.Add(new StunnedState(this));
|
|
stateMachine.Add(new AttackState(this));
|
|
|
|
stateMachine.SetCurrentState(state);
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
Debug.DrawLine(transform.position, transform.forward * aggressionDistance, Color.green, 0f);
|
|
|
|
if (isClient)
|
|
{
|
|
Footsteps();
|
|
}
|
|
|
|
if (isServer)
|
|
{
|
|
agent.speed = _speedMul * RunSpeed;
|
|
animator.SetFloat(SpeedAnimHash, agent.velocity.magnitude);
|
|
}
|
|
|
|
aimRigWeight = isServer ? Mathf.Lerp(aimRigWeight, _targetAimRigWeight, Time.deltaTime) : aimRigWeight;
|
|
aimRig.weight = aimRigWeight;
|
|
|
|
stateMachine.Update();
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
stateMachine.FixedUpdate();
|
|
}
|
|
|
|
public void SetAimRigWeight(float value)
|
|
{
|
|
_targetAimRigWeight = value;
|
|
}
|
|
|
|
void Footsteps()
|
|
{
|
|
float velocity = ((_footstepPrevPosition - transform.position) / Time.deltaTime).magnitude;
|
|
float clampedVelocity = Mathf.Clamp(RunSpeed / velocity + 1, 1, RunSpeed);
|
|
|
|
if (Time.time > _stepCycle && velocity > 0)
|
|
{
|
|
footstepAudioSource.pitch = 1f + Random.Range(-0.1f, 0.1f);
|
|
footstepAudioSource.PlayOneShot(_footstepClip);
|
|
|
|
_stepCycle = Time.time + (clampedVelocity * StepDelay);
|
|
}
|
|
|
|
_footstepPrevPosition = transform.position;
|
|
}
|
|
|
|
[ClientRpc]
|
|
public void RpcKillTarget(Transform target)
|
|
{
|
|
Interactions interactions = target.GetComponent<Interactions>();
|
|
|
|
if (interactions != null) interactions.DropProp();
|
|
|
|
target.GetComponent<Pawn>().Die();
|
|
}
|
|
|
|
[ServerCallback]
|
|
public override void TakeDamage()
|
|
{
|
|
base.TakeDamage();
|
|
|
|
ChangeState(EnemyState.Stunned);
|
|
}
|
|
|
|
[Server]
|
|
public void ChangeState(EnemyState newState)
|
|
{
|
|
state = newState;
|
|
|
|
RpcChangeState(newState);
|
|
}
|
|
|
|
[ClientRpc]
|
|
private void RpcChangeState(EnemyState newState)
|
|
{
|
|
stateMachine.SetCurrentState(newState);
|
|
}
|
|
|
|
public Transform GetClosestTarget()
|
|
{
|
|
if (!agent.isOnNavMesh) return null;
|
|
|
|
var alivePlayers = _networkManager.alive;
|
|
|
|
if (HasTarget && alivePlayers.Count == 1 && alivePlayers[0].transform == targetTransform)
|
|
{
|
|
return targetTransform;
|
|
}
|
|
|
|
Transform closestTarget = null;
|
|
|
|
var sortDistance = float.MaxValue;
|
|
foreach (var player in _networkManager.alive)
|
|
{
|
|
var targetPosition = player.transform.position;
|
|
var distance = Vector3.Distance(targetPosition, transform.position);
|
|
var canReachTarget = agent.CalculatePath(targetPosition, _navMeshPath) && _navMeshPath.status == NavMeshPathStatus.PathComplete;
|
|
|
|
if (distance <= aggressionDistance && distance < sortDistance && canReachTarget)
|
|
{
|
|
sortDistance = distance;
|
|
closestTarget = player.transform;
|
|
}
|
|
}
|
|
|
|
return closestTarget;
|
|
}
|
|
}
|
|
} |