600 lines
14 KiB
C#
600 lines
14 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using UnityEngine.Events;
|
|
|
|
public class HookWalkToTable : MonoBehaviour
|
|
{
|
|
[Header("Path Points")]
|
|
public Transform tableFrontPoint;
|
|
public Transform chairFrontPoint;
|
|
|
|
[Header("Pose Points")]
|
|
public Transform sitPoint;
|
|
public Transform standPoint;
|
|
|
|
[Header("Move Setting")]
|
|
public float arriveBuffer = 0.15f;
|
|
|
|
[Header("Animator")]
|
|
public Animator animator;
|
|
public string walkBoolName = "isWalking";
|
|
public string sitTriggerName = "sitTrigger";
|
|
public string standTriggerName = "standTrig";
|
|
|
|
[Header("Sit Correction")]
|
|
public bool correctToSitPointAfterSitAnim = true;
|
|
public string sitAnimationStateName = "Stand To Sit 0";
|
|
public float sitAnimFallbackDuration = 2f;
|
|
public float sitCorrectionDuration = 0.45f;
|
|
|
|
[Header("Auto Stand")]
|
|
public bool autoStandAfterSit = false;
|
|
public float sitStayDuration = 5f;
|
|
|
|
[Header("Stand Correction")]
|
|
public bool moveToStandPointBeforeStandAnim = true;
|
|
public string standAnimationStateName = "Sit To Stand 0";
|
|
public float standAnimFallbackDuration = 2f;
|
|
public float standCorrectionDuration = 0.45f;
|
|
|
|
[Header("Wave After Stand")]
|
|
public bool waveAfterStand = true;
|
|
public Transform lookTarget;
|
|
public string waveStateName = "Waving";
|
|
public float lookAtTargetDuration = 0.4f;
|
|
public float waveCrossFadeDuration = 0.15f;
|
|
|
|
[Header("Keep Looking While Waving")]
|
|
public bool keepLookingAtTargetWhileWaving = true;
|
|
public float lookFollowSpeed = 5f;
|
|
public float lookYawOffset = 0f;
|
|
|
|
[Header("Collision")]
|
|
public bool disableCollidersWhenSitting = true;
|
|
public bool enableCollidersBeforeStand = true;
|
|
public Collider[] hookColliders;
|
|
|
|
[Header("Events")]
|
|
public UnityEvent onHookSeated;
|
|
|
|
private NavMeshAgent agent;
|
|
|
|
private int step = 0;
|
|
private bool isMoving = false;
|
|
private bool isSitting = false;
|
|
private bool isStanding = false;
|
|
private bool isWaving = false;
|
|
private bool hasStarted = false;
|
|
private bool hookSeatedEventCalled = false;
|
|
|
|
private Vector3 currentDestination;
|
|
private Coroutine sitRoutine;
|
|
private Coroutine standRoutine;
|
|
|
|
void Start()
|
|
{
|
|
agent = GetComponent<NavMeshAgent>();
|
|
|
|
if (agent != null)
|
|
{
|
|
agent.isStopped = true;
|
|
agent.ResetPath();
|
|
agent.stoppingDistance = 0.05f;
|
|
agent.autoBraking = true;
|
|
}
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool(walkBoolName, false);
|
|
animator.ResetTrigger(sitTriggerName);
|
|
animator.ResetTrigger(standTriggerName);
|
|
}
|
|
|
|
if (hookColliders == null || hookColliders.Length == 0)
|
|
{
|
|
hookColliders = GetComponentsInChildren<Collider>();
|
|
}
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (!isMoving || agent == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (agent.pathPending)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float distance = Vector3.Distance(transform.position, currentDestination);
|
|
|
|
if (distance <= agent.stoppingDistance + arriveBuffer)
|
|
{
|
|
GoNextStep();
|
|
}
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
if (!isWaving)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!keepLookingAtTargetWhileWaving)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (lookTarget == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LookAtTargetContinuously();
|
|
}
|
|
|
|
public void StartWalking()
|
|
{
|
|
if (agent == null)
|
|
{
|
|
Debug.LogWarning("NavMeshAgent is missing.");
|
|
return;
|
|
}
|
|
|
|
if (tableFrontPoint == null || chairFrontPoint == null)
|
|
{
|
|
Debug.LogWarning("TableFrontPoint or ChairFrontPoint is missing.");
|
|
return;
|
|
}
|
|
|
|
if (hasStarted || isMoving || isSitting || isStanding || isWaving)
|
|
{
|
|
return;
|
|
}
|
|
|
|
hasStarted = true;
|
|
step = 0;
|
|
isMoving = true;
|
|
isSitting = false;
|
|
isStanding = false;
|
|
isWaving = false;
|
|
hookSeatedEventCalled = false;
|
|
|
|
EnableHookColliders();
|
|
|
|
if (sitRoutine != null)
|
|
{
|
|
StopCoroutine(sitRoutine);
|
|
sitRoutine = null;
|
|
}
|
|
|
|
if (standRoutine != null)
|
|
{
|
|
StopCoroutine(standRoutine);
|
|
standRoutine = null;
|
|
}
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool(walkBoolName, false);
|
|
animator.ResetTrigger(sitTriggerName);
|
|
animator.ResetTrigger(standTriggerName);
|
|
}
|
|
|
|
if (!agent.enabled)
|
|
{
|
|
agent.enabled = true;
|
|
}
|
|
|
|
agent.isStopped = false;
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool(walkBoolName, true);
|
|
}
|
|
|
|
MoveTo(tableFrontPoint);
|
|
|
|
Debug.Log("Hook starts walking to table front point.");
|
|
}
|
|
|
|
void MoveTo(Transform target)
|
|
{
|
|
NavMeshHit hit;
|
|
|
|
if (NavMesh.SamplePosition(target.position, out hit, 2f, NavMesh.AllAreas))
|
|
{
|
|
currentDestination = hit.position;
|
|
agent.SetDestination(currentDestination);
|
|
|
|
Debug.Log("Destination set: " + target.name);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("Target is not near NavMesh: " + target.name);
|
|
StopWalking();
|
|
}
|
|
}
|
|
|
|
void GoNextStep()
|
|
{
|
|
step++;
|
|
|
|
if (step == 1)
|
|
{
|
|
MoveTo(chairFrontPoint);
|
|
Debug.Log("Hook moves to chair front point.");
|
|
}
|
|
else
|
|
{
|
|
StartSitAnimation();
|
|
}
|
|
}
|
|
|
|
void StartSitAnimation()
|
|
{
|
|
isMoving = false;
|
|
isSitting = true;
|
|
|
|
if (agent != null)
|
|
{
|
|
agent.isStopped = true;
|
|
agent.ResetPath();
|
|
agent.enabled = false;
|
|
}
|
|
|
|
if (chairFrontPoint != null)
|
|
{
|
|
transform.rotation = chairFrontPoint.rotation;
|
|
}
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool(walkBoolName, false);
|
|
animator.ResetTrigger(standTriggerName);
|
|
animator.ResetTrigger(sitTriggerName);
|
|
animator.SetTrigger(sitTriggerName);
|
|
}
|
|
|
|
Debug.Log("Hook started sit animation.");
|
|
|
|
sitRoutine = StartCoroutine(SitRoutine());
|
|
}
|
|
|
|
IEnumerator SitRoutine()
|
|
{
|
|
yield return StartCoroutine(WaitAnimatorStateEnd(sitAnimationStateName, sitAnimFallbackDuration));
|
|
|
|
if (correctToSitPointAfterSitAnim && sitPoint != null)
|
|
{
|
|
yield return StartCoroutine(SmoothMoveTo(sitPoint, sitCorrectionDuration));
|
|
Debug.Log("Hook corrected to sit point.");
|
|
}
|
|
|
|
if (disableCollidersWhenSitting)
|
|
{
|
|
DisableHookColliders();
|
|
}
|
|
|
|
Debug.Log("Hook is now sitting.");
|
|
|
|
if (!hookSeatedEventCalled)
|
|
{
|
|
hookSeatedEventCalled = true;
|
|
onHookSeated?.Invoke();
|
|
}
|
|
|
|
if (autoStandAfterSit)
|
|
{
|
|
yield return new WaitForSeconds(sitStayDuration);
|
|
StandUp();
|
|
}
|
|
|
|
sitRoutine = null;
|
|
}
|
|
|
|
public void StandUp()
|
|
{
|
|
if (!isSitting || isStanding)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (sitRoutine != null)
|
|
{
|
|
StopCoroutine(sitRoutine);
|
|
sitRoutine = null;
|
|
}
|
|
|
|
if (standRoutine != null)
|
|
{
|
|
StopCoroutine(standRoutine);
|
|
standRoutine = null;
|
|
}
|
|
|
|
standRoutine = StartCoroutine(StandRoutine());
|
|
}
|
|
|
|
IEnumerator StandRoutine()
|
|
{
|
|
isStanding = true;
|
|
isSitting = false;
|
|
|
|
if (enableCollidersBeforeStand)
|
|
{
|
|
EnableHookColliders();
|
|
}
|
|
|
|
if (moveToStandPointBeforeStandAnim && standPoint != null)
|
|
{
|
|
yield return StartCoroutine(SmoothMoveTo(standPoint, standCorrectionDuration));
|
|
Debug.Log("Hook moved to stand point before stand animation.");
|
|
}
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool(walkBoolName, false);
|
|
animator.ResetTrigger(sitTriggerName);
|
|
animator.ResetTrigger(standTriggerName);
|
|
animator.SetTrigger(standTriggerName);
|
|
}
|
|
|
|
Debug.Log("Hook started stand animation.");
|
|
|
|
yield return StartCoroutine(WaitAnimatorStateEnd(standAnimationStateName, standAnimFallbackDuration));
|
|
|
|
isStanding = false;
|
|
|
|
Debug.Log("Hook finished standing.");
|
|
|
|
if (waveAfterStand)
|
|
{
|
|
yield return StartCoroutine(StartWaveLoop());
|
|
}
|
|
|
|
hasStarted = false;
|
|
standRoutine = null;
|
|
}
|
|
|
|
IEnumerator StartWaveLoop()
|
|
{
|
|
isWaving = true;
|
|
|
|
if (lookTarget != null)
|
|
{
|
|
yield return StartCoroutine(SmoothLookAtTarget(lookTarget, lookAtTargetDuration));
|
|
Debug.Log("Hook looked at player.");
|
|
}
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool(walkBoolName, false);
|
|
animator.ResetTrigger(sitTriggerName);
|
|
animator.ResetTrigger(standTriggerName);
|
|
|
|
animator.CrossFade(waveStateName, waveCrossFadeDuration, 0);
|
|
|
|
Debug.Log("Hook forced Waving state: " + waveStateName);
|
|
}
|
|
}
|
|
|
|
void LookAtTargetContinuously()
|
|
{
|
|
Vector3 direction = lookTarget.position - transform.position;
|
|
direction.y = 0f;
|
|
|
|
if (direction.sqrMagnitude < 0.001f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Quaternion targetRotation = Quaternion.LookRotation(direction);
|
|
targetRotation *= Quaternion.Euler(0f, lookYawOffset, 0f);
|
|
|
|
transform.rotation = Quaternion.Slerp(
|
|
transform.rotation,
|
|
targetRotation,
|
|
Time.deltaTime * lookFollowSpeed
|
|
);
|
|
}
|
|
|
|
IEnumerator SmoothLookAtTarget(Transform target, float duration)
|
|
{
|
|
if (target == null)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
Vector3 direction = target.position - transform.position;
|
|
direction.y = 0f;
|
|
|
|
if (direction.sqrMagnitude < 0.001f)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
Quaternion startRotation = transform.rotation;
|
|
Quaternion targetRotation = Quaternion.LookRotation(direction);
|
|
targetRotation *= Quaternion.Euler(0f, lookYawOffset, 0f);
|
|
|
|
if (duration <= 0f)
|
|
{
|
|
transform.rotation = targetRotation;
|
|
yield break;
|
|
}
|
|
|
|
float elapsed = 0f;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
|
|
float t = elapsed / duration;
|
|
t = Mathf.Clamp01(t);
|
|
t = Mathf.SmoothStep(0f, 1f, t);
|
|
|
|
transform.rotation = Quaternion.Slerp(startRotation, targetRotation, t);
|
|
|
|
yield return null;
|
|
}
|
|
|
|
transform.rotation = targetRotation;
|
|
}
|
|
|
|
IEnumerator WaitAnimatorStateEnd(string stateName, float fallbackDuration)
|
|
{
|
|
if (animator == null)
|
|
{
|
|
yield return new WaitForSeconds(fallbackDuration);
|
|
yield break;
|
|
}
|
|
|
|
yield return null;
|
|
|
|
float timer = 0f;
|
|
float timeout = 2f;
|
|
|
|
while (!animator.GetCurrentAnimatorStateInfo(0).IsName(stateName))
|
|
{
|
|
timer += Time.deltaTime;
|
|
|
|
if (timer >= timeout)
|
|
{
|
|
Debug.LogWarning(stateName + " state not found. Fallback wait used.");
|
|
yield return new WaitForSeconds(fallbackDuration);
|
|
yield break;
|
|
}
|
|
|
|
yield return null;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
|
|
|
|
if (!stateInfo.IsName(stateName))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (stateInfo.normalizedTime >= 0.98f)
|
|
{
|
|
break;
|
|
}
|
|
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
IEnumerator SmoothMoveTo(Transform target, float duration)
|
|
{
|
|
if (target == null)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
if (duration <= 0f)
|
|
{
|
|
transform.position = target.position;
|
|
transform.rotation = target.rotation;
|
|
yield break;
|
|
}
|
|
|
|
Vector3 startPosition = transform.position;
|
|
Quaternion startRotation = transform.rotation;
|
|
|
|
Vector3 targetPosition = target.position;
|
|
Quaternion targetRotation = target.rotation;
|
|
|
|
float elapsed = 0f;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
|
|
float t = elapsed / duration;
|
|
t = Mathf.Clamp01(t);
|
|
t = Mathf.SmoothStep(0f, 1f, t);
|
|
|
|
transform.position = Vector3.Lerp(startPosition, targetPosition, t);
|
|
transform.rotation = Quaternion.Slerp(startRotation, targetRotation, t);
|
|
|
|
yield return null;
|
|
}
|
|
|
|
transform.position = targetPosition;
|
|
transform.rotation = targetRotation;
|
|
}
|
|
|
|
void StopWalking()
|
|
{
|
|
isMoving = false;
|
|
|
|
if (agent != null && agent.enabled)
|
|
{
|
|
agent.isStopped = true;
|
|
agent.ResetPath();
|
|
}
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool(walkBoolName, false);
|
|
}
|
|
}
|
|
|
|
void DisableHookColliders()
|
|
{
|
|
if (hookColliders == null || hookColliders.Length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (Collider col in hookColliders)
|
|
{
|
|
if (col != null)
|
|
{
|
|
col.enabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnableHookColliders()
|
|
{
|
|
if (hookColliders == null || hookColliders.Length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (Collider col in hookColliders)
|
|
{
|
|
if (col != null)
|
|
{
|
|
col.enabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsSitting()
|
|
{
|
|
return isSitting;
|
|
}
|
|
|
|
public bool IsStanding()
|
|
{
|
|
return isStanding;
|
|
}
|
|
|
|
public bool IsWaving()
|
|
{
|
|
return isWaving;
|
|
}
|
|
|
|
public bool HasStarted()
|
|
{
|
|
return hasStarted;
|
|
}
|
|
} |