블랙잭 NPC 코인 및 AI Navigation 기능 추가

This commit is contained in:
dldydtn9755-crypto
2026-06-19 15:24:19 +09:00
parent 67c2b2c179
commit 3d0432ebd4
795 changed files with 49564 additions and 55 deletions

View File

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