366 lines
11 KiB
C#
366 lines
11 KiB
C#
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.AI;
|
||
|
using Mirror;
|
||
|
using UnityEngine.Animations.Rigging;
|
||
|
using Player;
|
||
|
|
||
|
public partial class EnemyController : Pawn
|
||
|
{
|
||
|
public bool spawnCube = false;
|
||
|
public NavMeshAgent controller;
|
||
|
public Animator animator;
|
||
|
public Rig targetLayer;
|
||
|
public AudioSource audioSource;
|
||
|
public ParticleSystem hitVFX;
|
||
|
|
||
|
public bool hasPath;
|
||
|
|
||
|
public GameObject propPrefab;
|
||
|
|
||
|
public Transform aimTarget;
|
||
|
public float aggressionDistance = 5f;
|
||
|
public float _stepTiming = 0.15f;
|
||
|
CustomNetworkManager networkManager;
|
||
|
NetworkGameManager networkGameManager;
|
||
|
|
||
|
private Transform _target = null;
|
||
|
private List<Pawn> players;
|
||
|
|
||
|
private Vector3 _position;
|
||
|
|
||
|
private float _stepCycle;
|
||
|
private float _runSpeed = 4.974f; //_walkSpeed = 0.299f
|
||
|
private float targetLayerWeight;
|
||
|
|
||
|
private int _currenPointId;
|
||
|
|
||
|
[SyncVar]
|
||
|
private float layerWeight;
|
||
|
private float _remainingDistance;
|
||
|
|
||
|
NavMeshTriangulation navMeshData;
|
||
|
|
||
|
[SyncVar(hook = nameof(OnStateChanged))]
|
||
|
private ENEMY_STATE state = ENEMY_STATE.PATROL;
|
||
|
|
||
|
|
||
|
private AudioClip skrClip, agressiveClip;
|
||
|
void Start()
|
||
|
{
|
||
|
// Time.timeScale = 10;
|
||
|
skrClip = Resources.Load<AudioClip>("Audio/EnemySounds/woodSkr");
|
||
|
agressiveClip = Resources.Load<AudioClip>("Audio/EnemySounds/FX/agressive2");
|
||
|
|
||
|
networkManager = NetworkManager.singleton.GetComponent<CustomNetworkManager>();
|
||
|
networkGameManager = NetworkGameManager.singleton;
|
||
|
players = networkManager.alive;
|
||
|
|
||
|
if (!isServer) return;
|
||
|
|
||
|
this.OnDamage += TakeDamageAnimation;
|
||
|
|
||
|
navMeshData = NavMesh.CalculateTriangulation();
|
||
|
|
||
|
if (spawnCube)
|
||
|
{
|
||
|
Vector3 randLoc = navMeshData.vertices[navMeshData.indices[Random.Range(0, navMeshData.indices.Length - 3)]];
|
||
|
GameObject gameObject = Instantiate(propPrefab, randLoc + Vector3.up * 1, Quaternion.identity);
|
||
|
gameObject.GetComponent<PropBehaviour>().Original = true;
|
||
|
NetworkServer.Spawn(gameObject);
|
||
|
}
|
||
|
|
||
|
navMeshPath = new NavMeshPath();
|
||
|
}
|
||
|
|
||
|
public NavMeshPath navMeshPath;
|
||
|
public bool isKilling = false;
|
||
|
|
||
|
void Update()
|
||
|
{
|
||
|
controller.speed = _speedMul * _runSpeed;
|
||
|
|
||
|
CalculateFootstepTriggers();
|
||
|
|
||
|
layerWeight = isServer ? Mathf.Lerp(layerWeight, targetLayerWeight, Time.deltaTime) : layerWeight;
|
||
|
|
||
|
targetLayer.weight = layerWeight;
|
||
|
|
||
|
if (!isServer) return;
|
||
|
|
||
|
animator.SetFloat("speed", controller.velocity.magnitude);
|
||
|
|
||
|
_remainingDistance = controller.GetPathRemainingDistance();
|
||
|
hasPath = controller.pathStatus != NavMeshPathStatus.PathComplete;
|
||
|
|
||
|
// Есть таргет?
|
||
|
if (_target != null) // Да
|
||
|
{
|
||
|
controller.destination = _target.position;
|
||
|
|
||
|
float distance = Vector3.Distance(_target.transform.position, transform.position);
|
||
|
|
||
|
if (distance < aggressionDistance) // (_remainingDistance < aggressionDistance && _remainingDistance > 0)
|
||
|
{
|
||
|
aimTarget.position = _target.position + _target.up * 1.2f;
|
||
|
targetLayerWeight = 1f;
|
||
|
|
||
|
// Debug.Log(_remainingDistance);
|
||
|
|
||
|
// Если подошли к нему
|
||
|
if (distance < 1f)
|
||
|
{
|
||
|
// Debug.Log("da");
|
||
|
|
||
|
// _target.GetComponent<Interactions>().DropProp();
|
||
|
if (isKilling == false)
|
||
|
{
|
||
|
RpcKillPlayer(_target);
|
||
|
animator.SetTrigger("attack");
|
||
|
|
||
|
StartCoroutine(AfterKillTimer());
|
||
|
}
|
||
|
|
||
|
isKilling = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_target = null;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//if (controller.GetPathRemainingDistance() <= 0f) // Нет mojet doiti
|
||
|
targetLayerWeight = 0f;
|
||
|
// Ищем ближайшего игрока
|
||
|
foreach (var player in players)
|
||
|
{
|
||
|
float distance = Vector3.Distance(player.transform.position, transform.position);
|
||
|
|
||
|
// Debug.Log(CalculateNewPath(player.transform));
|
||
|
|
||
|
if (distance <= aggressionDistance && CalculateNewPath(player.transform)) // && _remainingDistance > 0
|
||
|
{
|
||
|
//if (_remainingDistance < aggressionDistance && controller.GetPathRemainingDistance() >= 0)
|
||
|
_target = player.transform;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_target == null && _remainingDistance <= 1.5f) //!controller.pathPending ||
|
||
|
{
|
||
|
SetRandomDestination();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if (_target == null)
|
||
|
// {
|
||
|
// float pointDistance = Vector3.Distance(_patrolPoints[_currenPointId].position, transform.position);
|
||
|
|
||
|
// if (pointDistance < 2)
|
||
|
// {
|
||
|
// _currenPointId = Random.Range(0, _patrolPoints.Length);
|
||
|
// }
|
||
|
|
||
|
// controller.destination = _patrolPoints[_currenPointId].position;
|
||
|
|
||
|
// foreach (var player in players)
|
||
|
// {
|
||
|
// float distance = Vector3.Distance(player.transform.position, transform.position);
|
||
|
|
||
|
// if (distance < aggressionDistance)
|
||
|
// {
|
||
|
// state = ENEMY_STATE.ROTATE;
|
||
|
|
||
|
// _target = player.transform;
|
||
|
// targetLayerWeight = 1f;
|
||
|
|
||
|
// break;
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
// else
|
||
|
// {
|
||
|
// if (layerWeight >= 0.9f && hasPath)
|
||
|
// {
|
||
|
// if (state == ENEMY_STATE.ROTATE)
|
||
|
// {
|
||
|
// state = ENEMY_STATE.CHASE;
|
||
|
// }
|
||
|
|
||
|
// controller.destination = _target.position;
|
||
|
// }
|
||
|
|
||
|
// if (controller.velocity.magnitude <= 1 && !hasPath)
|
||
|
// {
|
||
|
// _target = null;
|
||
|
// state = ENEMY_STATE.PATROL;
|
||
|
// }
|
||
|
|
||
|
// target.position = _target.position + _target.up * 1.2f;
|
||
|
|
||
|
// var distance = Vector3.Distance(_target.transform.position, transform.position);
|
||
|
|
||
|
// if (distance <= 1)
|
||
|
// {
|
||
|
// _target.GetComponent<Pawn>().Die();
|
||
|
// }
|
||
|
|
||
|
// if (distance >= aggressionDistance)
|
||
|
// {
|
||
|
// state = ENEMY_STATE.PATROL;
|
||
|
|
||
|
// _target = null;
|
||
|
// targetLayerWeight = 0f;
|
||
|
// }
|
||
|
// }
|
||
|
}
|
||
|
|
||
|
bool CalculateNewPath(Transform target)
|
||
|
{
|
||
|
controller.CalculatePath(target.position, navMeshPath);
|
||
|
|
||
|
if (navMeshPath.status != NavMeshPathStatus.PathComplete)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[ClientRpc]
|
||
|
void RpcKillPlayer(Transform target)
|
||
|
{
|
||
|
Interactions interactions = target.GetComponent<Interactions>();
|
||
|
|
||
|
if (interactions != null) interactions.DropProp();
|
||
|
|
||
|
target.GetComponent<Pawn>().Die();
|
||
|
}
|
||
|
|
||
|
IEnumerator AfterKillTimer()
|
||
|
{
|
||
|
yield return new WaitForSeconds(5);
|
||
|
isKilling = false;
|
||
|
_target = null;
|
||
|
aimTarget.position = transform.position;
|
||
|
|
||
|
SetRandomDestination();
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
void SetRandomDestination()
|
||
|
{
|
||
|
var targetPoint = GetRandomLocation();
|
||
|
// var targetPoint = navMeshData.vertices[2000];
|
||
|
//_patrolPoints[Random.Range(0, _patrolPoints.Length - 1)].position;
|
||
|
// hasPath = controller.SetDestination(targetPoint);
|
||
|
controller.destination = targetPoint;
|
||
|
// hasPath = controller.hasPath;
|
||
|
}
|
||
|
void OnStateChanged(ENEMY_STATE oldState, ENEMY_STATE newState)
|
||
|
{
|
||
|
audioSource.Stop();
|
||
|
|
||
|
switch (newState)
|
||
|
{
|
||
|
case ENEMY_STATE.PATROL:
|
||
|
audioSource.reverbZoneMix = 1f;
|
||
|
break;
|
||
|
|
||
|
case ENEMY_STATE.ROTATE:
|
||
|
audioSource.clip = skrClip;
|
||
|
audioSource.Play();
|
||
|
break;
|
||
|
|
||
|
case ENEMY_STATE.CHASE:
|
||
|
// audioSource.clip = agressiveClip;
|
||
|
// audioSource.reverbZoneMix = 0f;
|
||
|
// audioSource.Play();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private List<Vector3> _points = new List<Vector3>();
|
||
|
|
||
|
Vector3 GetRandomLocation()
|
||
|
{
|
||
|
// // NavMeshTriangulation navMeshData = NavMesh.CalculateTriangulation();
|
||
|
|
||
|
// int maxIndices = navMeshData.indices.Length - 3;
|
||
|
// // Pick the first indice of a random triangle in the nav mesh
|
||
|
// int firstVertexSelected = Random.Range(0, maxIndices);
|
||
|
// int secondVertexSelected = Random.Range(0, maxIndices);
|
||
|
// //Spawn on Verticies
|
||
|
// Vector3 point = navMeshData.vertices[navMeshData.indices[firstVertexSelected]];
|
||
|
|
||
|
// Vector3 firstVertexPosition = navMeshData.vertices[navMeshData.indices[firstVertexSelected]];
|
||
|
// Vector3 secondVertexPosition = navMeshData.vertices[navMeshData.indices[secondVertexSelected]];
|
||
|
// //Eliminate points that share a similar X or Z position to stop spawining in square grid line formations
|
||
|
// if ((int)firstVertexPosition.x == (int)secondVertexPosition.x ||
|
||
|
// (int)firstVertexPosition.z == (int)secondVertexPosition.z
|
||
|
// )
|
||
|
// {
|
||
|
// point = GetRandomLocation(); //Re-Roll a position - I'm not happy with this recursion it could be better
|
||
|
// }
|
||
|
// else
|
||
|
// {
|
||
|
// // Select a random point on it
|
||
|
// point = Vector3.Lerp(
|
||
|
// firstVertexPosition,
|
||
|
// secondVertexPosition, //[t + 1]],
|
||
|
// Random.Range(0.05f, 0.95f) // Not using Random.value as clumps form around Verticies
|
||
|
// );
|
||
|
// }
|
||
|
// //Vector3.Lerp(point, navMeshData.vertices[navMeshData.indices[t + 2]], Random.value); //Made Obsolete
|
||
|
|
||
|
// return point;
|
||
|
var point = navMeshData.vertices[navMeshData.indices[Random.Range(0, navMeshData.indices.Length - 3)]];
|
||
|
_points.Add(point);
|
||
|
return point;
|
||
|
}
|
||
|
|
||
|
void OnDrawGizmos()
|
||
|
{
|
||
|
foreach (var point in _points)
|
||
|
{
|
||
|
Gizmos.DrawSphere(point, 1f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CalculateFootstepTriggers()
|
||
|
{
|
||
|
float _velocity = ((_position - transform.position) / Time.deltaTime).magnitude;
|
||
|
// Debug.Log(_controllerVelocity);
|
||
|
float clampedVelocity = Mathf.Clamp(_runSpeed / _velocity + 1, 1, _runSpeed);
|
||
|
|
||
|
if (Time.time > _stepCycle && _velocity > 0)
|
||
|
{
|
||
|
CallFootstepClip();
|
||
|
|
||
|
_stepCycle = Time.time + (clampedVelocity * _stepTiming);
|
||
|
}
|
||
|
|
||
|
_position = transform.position;
|
||
|
}
|
||
|
|
||
|
public void CallFootstepClip()
|
||
|
{
|
||
|
audioSource.pitch = 1f + Random.Range(-0.1f, 0.1f);
|
||
|
audioSource.PlayOneShot(Resources.Load<AudioClip>("Audio/EnemySounds/Steps/monsterStep1"));
|
||
|
}
|
||
|
|
||
|
void TakeDamageAnimation(Pawn self)
|
||
|
{
|
||
|
animator.SetTrigger("damage");
|
||
|
|
||
|
hitVFX.Stop();
|
||
|
hitVFX.Play();
|
||
|
}
|
||
|
}
|