165 lines
5.8 KiB
C#
165 lines
5.8 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using DG.Tweening;
|
|
|
|
namespace CryingSnow.FarmingIsland
|
|
{
|
|
[RequireComponent(typeof(Animator))]
|
|
public class AnimalController : MonoBehaviour, IProp
|
|
{
|
|
[Tooltip("The speed at which the animal moves while wandering.")]
|
|
[SerializeField] private float movingSpeed = 1f;
|
|
|
|
[Tooltip("The rotation speed of the animal in degrees per second.")]
|
|
[SerializeField] private float turningSpeed = 360f;
|
|
|
|
[Tooltip("The base offset of the animal's NavMeshAgent from the ground.")]
|
|
[SerializeField] private float baseOffset = 0f;
|
|
|
|
[Tooltip("The radius within which the animal will wander.")]
|
|
[SerializeField] private float wanderRadius = 4f;
|
|
|
|
[Tooltip("Possible animation triggers that the animal may randomly perform.")]
|
|
[SerializeField] private string[] actionTriggers;
|
|
|
|
public Location Location => Location.None; // Represents the location type; not used here.
|
|
|
|
private NavMeshAgent agent; // The NavMeshAgent that controls the animal's movement.
|
|
private Animator animator; // Animator component controlling the animal's animations.
|
|
|
|
void Awake()
|
|
{
|
|
animator = GetComponent<Animator>();
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
void IProp.Animate(bool instant, System.Action onFinished)
|
|
{
|
|
gameObject.SetActive(true);
|
|
StartCoroutine(Initialize(instant));
|
|
}
|
|
|
|
IEnumerator Initialize(bool instant)
|
|
{
|
|
if (instant)
|
|
{
|
|
// Wait until the NavMesh is ready before creating the agent.
|
|
yield return new WaitUntil(() => IslandManager.Instance.HasNavMesh);
|
|
CreateAgent();
|
|
}
|
|
else
|
|
{
|
|
// Scale in the animal smoothly using a bounce effect.
|
|
transform.localScale = Vector3.zero;
|
|
yield return transform.DOScale(Vector3.one, 1f)
|
|
.SetEase(Ease.OutBounce)
|
|
.WaitForCompletion();
|
|
|
|
CreateAgent();
|
|
}
|
|
|
|
// Start the wandering behavior after initialization.
|
|
StartCoroutine(Wander());
|
|
}
|
|
|
|
private void CreateAgent()
|
|
{
|
|
agent = gameObject.AddComponent<NavMeshAgent>();
|
|
agent.baseOffset = baseOffset;
|
|
agent.speed = movingSpeed;
|
|
agent.angularSpeed = turningSpeed;
|
|
agent.stoppingDistance = 0.1f;
|
|
agent.autoBraking = false;
|
|
agent.obstacleAvoidanceType = ObstacleAvoidanceType.NoObstacleAvoidance;
|
|
}
|
|
|
|
IEnumerator Wander()
|
|
{
|
|
Vector3 destination = Vector3.zero;
|
|
yield return new WaitUntil(() => GetRandomPoint(out destination)); // Find a random destination within range.
|
|
agent.SetDestination(destination);
|
|
animator.SetBool("IsMoving", true);
|
|
|
|
yield return new WaitUntil(() => HasArrived());
|
|
animator.SetBool("IsMoving", false);
|
|
|
|
// Wait for a random amount of time before deciding the next action.
|
|
yield return new WaitForSeconds(Random.Range(1f, 5f));
|
|
DecideNextAction();
|
|
}
|
|
|
|
// Attempts to find a valid random point within the wander radius for the animal to move to.
|
|
private bool GetRandomPoint(out Vector3 result)
|
|
{
|
|
var center = transform.position;
|
|
// 50% chance to wander between islands.
|
|
if (Random.value < 0.5f) center = IslandManager.Instance.GetRandomIsland().GetPosition();
|
|
|
|
// Try to find a valid point within the NavMesh.
|
|
for (int i = 0; i < 30; i++)
|
|
{
|
|
Vector3 randomPoint = center + Random.insideUnitSphere * wanderRadius;
|
|
|
|
NavMeshHit hit;
|
|
if (NavMesh.SamplePosition(randomPoint, out hit, 1.0f, NavMesh.AllAreas))
|
|
{
|
|
result = hit.position;
|
|
return true;
|
|
}
|
|
}
|
|
result = Vector3.zero;
|
|
return false;
|
|
}
|
|
|
|
// Checks if the NavMeshAgent has arrived at its destination.
|
|
private bool HasArrived()
|
|
{
|
|
if (!agent.pathPending)
|
|
{
|
|
if (agent.remainingDistance <= agent.stoppingDistance)
|
|
{
|
|
if (!agent.hasPath || agent.velocity.sqrMagnitude == 0f)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
IEnumerator DoRandomAction()
|
|
{
|
|
int randomIndex = Random.Range(0, actionTriggers.Length);
|
|
string randomAction = actionTriggers[randomIndex];
|
|
animator.SetTrigger(randomAction); // Trigger the random animation.
|
|
|
|
yield return new WaitForSeconds(0.3f); // Wait for the animation transition.
|
|
yield return new WaitForSeconds(animator.GetCurrentAnimatorStateInfo(0).length); // Wait for the animation to finish.
|
|
|
|
DecideNextAction();
|
|
}
|
|
|
|
public void DecideNextAction()
|
|
{
|
|
int nextAction = Random.Range(0, 2);
|
|
|
|
if (nextAction == 0) StartCoroutine(Wander()); // 50% chance to wander again.
|
|
else StartCoroutine(DoRandomAction()); // 50% chance to perform a random action.
|
|
}
|
|
|
|
public void InteruptMovement()
|
|
{
|
|
StopAllCoroutines(); // Stop any current wandering or actions.
|
|
animator.SetBool("IsMoving", false);
|
|
agent.SetDestination(transform.position); // Halt the NavMeshAgent's movement.
|
|
}
|
|
|
|
public void SetAnimatorBool(string parameter, bool value)
|
|
{
|
|
animator.SetBool(parameter, value);
|
|
}
|
|
}
|
|
}
|