Files
WhaleAdventure_VR/Assets/02_Scripts/Blackjack/HookWalkToTable.cs
2026-06-19 15:24:19 +09:00

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