블랙잭 NPC 코인 및 AI Navigation 기능 추가
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class HookWalkToTable : MonoBehaviour
|
||||
{
|
||||
@@ -8,8 +9,9 @@ public class HookWalkToTable : MonoBehaviour
|
||||
public Transform tableFrontPoint;
|
||||
public Transform chairFrontPoint;
|
||||
|
||||
[Header("Sit Point")]
|
||||
[Header("Pose Points")]
|
||||
public Transform sitPoint;
|
||||
public Transform standPoint;
|
||||
|
||||
[Header("Move Setting")]
|
||||
public float arriveBuffer = 0.15f;
|
||||
@@ -18,22 +20,57 @@ public class HookWalkToTable : MonoBehaviour
|
||||
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("Test")]
|
||||
public Key testStartKey = Key.T;
|
||||
[Header("Events")]
|
||||
public UnityEvent onHookSeated;
|
||||
|
||||
private NavMeshAgent agent;
|
||||
|
||||
private int step = 0;
|
||||
private bool isMoving = false;
|
||||
private bool hasArrived = 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()
|
||||
{
|
||||
@@ -50,9 +87,10 @@ void Start()
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetBool(walkBoolName, false);
|
||||
animator.ResetTrigger(sitTriggerName);
|
||||
animator.ResetTrigger(standTriggerName);
|
||||
}
|
||||
|
||||
// Inspector에 Collider를 안 넣어도 자동으로 후크 자식 콜라이더들을 찾아줌
|
||||
if (hookColliders == null || hookColliders.Length == 0)
|
||||
{
|
||||
hookColliders = GetComponentsInChildren<Collider>();
|
||||
@@ -61,12 +99,6 @@ void Start()
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 테스트용: T 누르면 출발
|
||||
if (Keyboard.current != null && Keyboard.current[testStartKey].wasPressedThisFrame)
|
||||
{
|
||||
StartWalking();
|
||||
}
|
||||
|
||||
if (!isMoving || agent == null)
|
||||
{
|
||||
return;
|
||||
@@ -85,6 +117,26 @@ void Update()
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!isWaving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keepLookingAtTargetWhileWaving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (lookTarget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LookAtTargetContinuously();
|
||||
}
|
||||
|
||||
public void StartWalking()
|
||||
{
|
||||
if (agent == null)
|
||||
@@ -99,23 +151,52 @@ public void StartWalking()
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMoving || hasArrived || isSitting)
|
||||
if (hasStarted || isMoving || isSitting || isStanding || isWaving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hasStarted = true;
|
||||
step = 0;
|
||||
hasArrived = false;
|
||||
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);
|
||||
}
|
||||
|
||||
agent.enabled = true;
|
||||
agent.isStopped = false;
|
||||
|
||||
MoveTo(tableFrontPoint);
|
||||
|
||||
Debug.Log("Hook starts walking to table front point.");
|
||||
@@ -125,7 +206,7 @@ void MoveTo(Transform target)
|
||||
{
|
||||
NavMeshHit hit;
|
||||
|
||||
if (NavMesh.SamplePosition(target.position, out hit, 1.5f, NavMesh.AllAreas))
|
||||
if (NavMesh.SamplePosition(target.position, out hit, 2f, NavMesh.AllAreas))
|
||||
{
|
||||
currentDestination = hit.position;
|
||||
agent.SetDestination(currentDestination);
|
||||
@@ -150,17 +231,15 @@ void GoNextStep()
|
||||
}
|
||||
else
|
||||
{
|
||||
ArriveAndSit();
|
||||
StartSitAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
void ArriveAndSit()
|
||||
void StartSitAnimation()
|
||||
{
|
||||
isMoving = false;
|
||||
hasArrived = true;
|
||||
isSitting = true;
|
||||
|
||||
// 1. NavMeshAgent 먼저 끄기
|
||||
if (agent != null)
|
||||
{
|
||||
agent.isStopped = true;
|
||||
@@ -168,32 +247,303 @@ void ArriveAndSit()
|
||||
agent.enabled = false;
|
||||
}
|
||||
|
||||
// 2. 후크 몸 Collider 끄기
|
||||
// 의자/테이블 콜라이더에 밀려서 위로 올라가는 문제 방지
|
||||
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();
|
||||
}
|
||||
|
||||
// 3. 실제 앉을 위치로 강제 이동
|
||||
if (sitPoint != null)
|
||||
Debug.Log("Hook is now sitting.");
|
||||
|
||||
if (!hookSeatedEventCalled)
|
||||
{
|
||||
transform.position = sitPoint.position;
|
||||
transform.rotation = sitPoint.rotation;
|
||||
}
|
||||
else if (chairFrontPoint != null)
|
||||
{
|
||||
transform.rotation = chairFrontPoint.rotation;
|
||||
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.");
|
||||
}
|
||||
|
||||
// 4. 앉는 애니메이션 실행
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetBool(walkBoolName, false);
|
||||
animator.SetTrigger(sitTriggerName);
|
||||
animator.ResetTrigger(sitTriggerName);
|
||||
animator.ResetTrigger(standTriggerName);
|
||||
animator.SetTrigger(standTriggerName);
|
||||
}
|
||||
|
||||
Debug.Log("Hook moved to sit point and started sitting.");
|
||||
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()
|
||||
@@ -212,19 +562,39 @@ void DisableHookColliders()
|
||||
}
|
||||
}
|
||||
|
||||
void StopWalking()
|
||||
void EnableHookColliders()
|
||||
{
|
||||
isMoving = false;
|
||||
|
||||
if (agent != null && agent.enabled)
|
||||
if (hookColliders == null || hookColliders.Length == 0)
|
||||
{
|
||||
agent.isStopped = true;
|
||||
agent.ResetPath();
|
||||
return;
|
||||
}
|
||||
|
||||
if (animator != null)
|
||||
foreach (Collider col in hookColliders)
|
||||
{
|
||||
animator.SetBool(walkBoolName, false);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user