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(); 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(); } } 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; } }