336 lines
11 KiB
C#
336 lines
11 KiB
C#
using NUnit.Framework;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.AI;
|
||
using UnityStandardAssets.Characters.ThirdPerson;
|
||
|
||
public class StatueBehaviour : MonoBehaviour
|
||
{
|
||
#region Visibility Variables
|
||
[SerializeField] private float _viewRadius;
|
||
[SerializeField] private float _viewAngle;
|
||
|
||
public LayerMask playerMask;
|
||
public LayerMask obstacleMask;
|
||
#endregion
|
||
|
||
public Animator statueAnimator;
|
||
public NavMeshAgent statueAI;
|
||
[SerializeField] ThirdPersonCharacter thirdPerson;
|
||
[SerializeField] Transform _player;
|
||
[SerializeField] public Transform _statue;
|
||
//[SerializeField] private List<SkinnedMeshRenderer> _statueSkinnedMeshRenderer;
|
||
|
||
[SerializeField] private float _stMovementSpeed;
|
||
[SerializeField] float killRange = 0.5f; // Example kill range; adjust as necessary
|
||
|
||
#region VariantsForPatrol
|
||
[SerializeField] private List<Transform> patrolPoints; // List of patrol points
|
||
private int _currentPatrolIndex = 0; // Current patrol point index
|
||
private bool _canChangePoint = true; // Flag to control when to move to the next point
|
||
private bool _forceToFreezeStatue;
|
||
#endregion
|
||
|
||
Coroutine waitBeforeChaseCoroutine;
|
||
|
||
public StatueDetection statueDetection;
|
||
|
||
public enum StatueState
|
||
{
|
||
Spawn,
|
||
Patrol,
|
||
Chase,
|
||
Freeze,
|
||
Kill
|
||
}
|
||
|
||
public StatueState state = StatueState.Spawn;
|
||
|
||
private void Start()
|
||
{
|
||
statueAI = GetComponent<NavMeshAgent>();
|
||
_player = PlayerManager.GetInstance().playerGameObj.transform;
|
||
|
||
//statueDetection = _player.GetComponent<StatueDetection>();
|
||
|
||
// Προσθήκη του αγάλματος στη λίστα του StatueDetection
|
||
if (statueDetection != null)
|
||
{
|
||
if (!statueDetection.statues.Contains(this.transform))
|
||
{
|
||
statueDetection.statues.Add(this.transform);
|
||
}
|
||
}
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
UpdateStatueBehaviour();
|
||
}
|
||
|
||
void UpdateStatueBehaviour()
|
||
{
|
||
UpdateStatueMovement(); // Update movement while patrolling
|
||
#region Statue's animations handling
|
||
float speed = statueAI.velocity.magnitude;
|
||
// Map NavMeshAgent's speed to blend tree parameter
|
||
float normalizedSpeed = speed / statueAI.speed; // Adjust the divisor to match your maximum NavMeshAgent speed
|
||
statueAnimator.SetFloat("speedPercent", normalizedSpeed);
|
||
#endregion
|
||
|
||
switch (state)
|
||
{
|
||
case StatueState.Spawn:
|
||
if (!statueAI.isActiveAndEnabled)
|
||
{
|
||
this.gameObject.SetActive(true);
|
||
}
|
||
else
|
||
{
|
||
state = StatueState.Patrol;
|
||
}
|
||
break;
|
||
case StatueState.Patrol:
|
||
|
||
Patrol(patrolPoints);
|
||
|
||
if (statueAI.remainingDistance > statueAI.stoppingDistance)
|
||
{
|
||
UpdateStatueMovement(); // Update movement while patrolling
|
||
}
|
||
|
||
if (CanSeePlayer())
|
||
{
|
||
waitBeforeChaseCoroutine = StartCoroutine(WaitBeforeChase()); // Start the coroutine before chasing
|
||
}
|
||
|
||
break;
|
||
case StatueState.Chase:
|
||
CheckKillCondition();
|
||
|
||
if (statueDetection != null && statueDetection.CheckStatuesInView())
|
||
{
|
||
// Αν ο παίκτης κοιτάζει κάποιο άγαλμα (συμπεριλαμβανομένου αυτού)
|
||
state = StatueState.Freeze;
|
||
statueAI.isStopped = true; // Σταμάτημα κίνησης του αγάλματος
|
||
statueAnimator.SetBool("isFrozen", true); // Έναρξη animation παγώματος
|
||
if (waitBeforeChaseCoroutine != null)
|
||
{
|
||
StopCoroutine(waitBeforeChaseCoroutine);
|
||
waitBeforeChaseCoroutine = null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Συνέχιση καταδίωξης
|
||
ChasePlayer();
|
||
}
|
||
break;
|
||
case StatueState.Freeze:
|
||
// Check if player has stopped looking
|
||
if (_forceToFreezeStatue == false)
|
||
{
|
||
if (statueDetection != null && !statueDetection.CheckStatuesInView())
|
||
{
|
||
state = StatueState.Chase; // Resume chase if player looks away
|
||
}
|
||
}
|
||
break;
|
||
case StatueState.Kill:
|
||
// Logic for killing the player (not implemented)
|
||
break;
|
||
}
|
||
}
|
||
|
||
public void ForceToFreezeStatue()
|
||
{
|
||
_forceToFreezeStatue = true;
|
||
// Αν ο παίκτης κοιτάζει κάποιο άγαλμα (συμπεριλαμβανομένου αυτού)
|
||
state = StatueState.Freeze;
|
||
statueAI.isStopped = true; // Σταμάτημα κίνησης του αγάλματος
|
||
statueAI.SetDestination(transform.position);
|
||
statueAnimator.SetBool("isFrozen", true); // Έναρξη animation παγώματος
|
||
if (waitBeforeChaseCoroutine != null)
|
||
{
|
||
StopCoroutine(waitBeforeChaseCoroutine);
|
||
waitBeforeChaseCoroutine = null;
|
||
}
|
||
}
|
||
|
||
public void UnfreezeForcedFreezedStatue()
|
||
{
|
||
_forceToFreezeStatue = false;
|
||
}
|
||
|
||
void UpdateStatueMovement()
|
||
{
|
||
//If agent has not reached the desired destination :
|
||
if (statueAI.remainingDistance > statueAI.stoppingDistance)
|
||
{
|
||
thirdPerson.Move(statueAI.desiredVelocity, false, false);
|
||
}
|
||
else // If agent has reached the desired destination :
|
||
{
|
||
thirdPerson.Move(Vector3.zero, false, false); //Agent stops moving.
|
||
}
|
||
}
|
||
|
||
#region Field of view
|
||
public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal) // This calculate the angle of the viewpoint
|
||
{
|
||
if (!angleIsGlobal)
|
||
{
|
||
angleInDegrees += transform.eulerAngles.y;
|
||
}
|
||
return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
|
||
}
|
||
|
||
// This method is checking if pplayer is inside the view angle and callculates is there isnt any obstacle between the player and Medea
|
||
public bool CanSeePlayer()
|
||
{
|
||
Collider[] targetInViewRadius = Physics.OverlapSphere(transform.position, _viewRadius, playerMask);
|
||
|
||
if (targetInViewRadius.Length > 0)
|
||
{
|
||
_player = targetInViewRadius[0].transform;
|
||
Vector3 dirToTarget = (_player.position - transform.position).normalized;
|
||
|
||
if (Vector3.Angle(transform.forward, dirToTarget) < _viewAngle / 2)
|
||
{
|
||
float distToTarget = Vector3.Distance(transform.position, _player.position);
|
||
|
||
if (!Physics.Raycast(transform.position, dirToTarget, distToTarget, obstacleMask))
|
||
{
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
private void OnDrawGizmos()
|
||
{
|
||
#region GizmosForView
|
||
Gizmos.color = Color.yellow;
|
||
Gizmos.DrawWireSphere(transform.position, _viewRadius);
|
||
Vector3 viewAngleA = DirFromAngle(-_viewAngle / 2, false);
|
||
Vector3 viewAngleB = DirFromAngle(_viewAngle / 2, false);
|
||
Gizmos.DrawLine(transform.position, transform.position + viewAngleA * _viewRadius);
|
||
Gizmos.DrawLine(transform.position, transform.position + viewAngleB * _viewRadius);
|
||
Gizmos.DrawLine(transform.position, _player.position);
|
||
#endregion
|
||
}
|
||
#endregion
|
||
|
||
#region PatrolBehaviour
|
||
void Patrol(List<Transform> points)
|
||
{
|
||
if (_canChangePoint)
|
||
{
|
||
// Check if the statue has reached the current destination point
|
||
if (!statueAI.pathPending && statueAI.remainingDistance < 0.25f)
|
||
{
|
||
// Trigger the "looking around" animation
|
||
statueAnimator.SetBool("IsLookingAround", true);
|
||
|
||
// Stop the agent movement while waiting
|
||
statueAI.isStopped = true;
|
||
|
||
// Move to the next patrol point
|
||
statueAI.destination = points[_currentPatrolIndex].position;
|
||
|
||
// Update the patrol index to the next point
|
||
_currentPatrolIndex = (_currentPatrolIndex + 1) % points.Count;
|
||
|
||
// Set a flag to wait before moving to the next point
|
||
_canChangePoint = false;
|
||
|
||
// Start coroutine to wait before moving again
|
||
StartCoroutine(WaitForNextPatrolPoint());
|
||
}
|
||
}
|
||
}
|
||
|
||
// Coroutine to wait for a few seconds before moving to the next patrol point
|
||
IEnumerator WaitForNextPatrolPoint()
|
||
{
|
||
yield return new WaitForSeconds(2f); // Wait time can be adjusted
|
||
statueAnimator.SetBool("IsLookingAround", false); // Reset the animation
|
||
statueAI.isStopped = false; // Resume movement
|
||
_canChangePoint = true; // Allow changing to the next point
|
||
}
|
||
#endregion
|
||
|
||
#region ChaseBehaviour
|
||
void ChasePlayer()
|
||
{
|
||
statueAI.isStopped = false;
|
||
statueAnimator.SetBool("IsLookingAround", false); // Stop patrol animation
|
||
|
||
// Set destination to the player's position
|
||
statueAI.SetDestination(_player.position);
|
||
}
|
||
#endregion
|
||
|
||
#region WaitBeforeChase
|
||
IEnumerator WaitBeforeChase()
|
||
{
|
||
state = StatueState.Freeze; // Temporarily freeze before chasing
|
||
statueAI.isStopped = true; // Stop movement
|
||
|
||
yield return new WaitForSeconds(2f); // Wait for 2 seconds
|
||
|
||
statueAI.isStopped = false; // Resume movement
|
||
state = StatueState.Chase; // Switch to chase state
|
||
}
|
||
#endregion
|
||
|
||
#region Killing player
|
||
|
||
private void CheckKillCondition()
|
||
{
|
||
//float killRange = 0.5f; // Example kill range; adjust as necessary
|
||
float distanceToPlayer = Vector3.Distance(transform.position, _player.position);
|
||
|
||
if (distanceToPlayer <= killRange)
|
||
{
|
||
KillPlayer(); // Call kill logic if within range
|
||
}
|
||
}
|
||
|
||
private void KillPlayer()
|
||
{
|
||
// Disable the statue's AI to stop its behavior
|
||
statueAI.enabled = false; // Disable the statue's AI (if needed)
|
||
|
||
// Disable the statue's mesh renderers
|
||
DisableMedeaMeshRenderers(); // Call your method to disable mesh renderers
|
||
|
||
// Trigger the player death action (Low Sanity)
|
||
PlayerDeath.GetInstance().LowSanityGhostKillsPlayer();
|
||
|
||
// Optional: Log the killing action for debugging
|
||
Debug.Log("I killed him");
|
||
}
|
||
|
||
// Method to disable mesh renderers of the statue
|
||
private void DisableMedeaMeshRenderers()
|
||
{
|
||
// Assuming you have a list of SkinnedMeshRenderers
|
||
SkinnedMeshRenderer[] meshRenderers = GetComponentsInChildren<SkinnedMeshRenderer>();
|
||
|
||
foreach (var renderer in meshRenderers)
|
||
{
|
||
renderer.enabled = false; // Disable the mesh renderer
|
||
}
|
||
}
|
||
#endregion
|
||
}
|