블랙잭 NPC 코인 및 AI Navigation 기능 추가
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class CardSpawnTest : MonoBehaviour
|
||||
{
|
||||
@@ -45,6 +46,10 @@ public class CardSpawnTest : MonoBehaviour
|
||||
[Header("Match Setting")]
|
||||
public int targetWinCount = 3;
|
||||
public float nextRoundDelay = 2f;
|
||||
public float matchEndDelay = 3f;
|
||||
|
||||
[Header("Match End Event")]
|
||||
public UnityEvent onMatchEnded;
|
||||
|
||||
private List<GameObject> deck = new List<GameObject>();
|
||||
private List<GameObject> spawnedCards = new List<GameObject>();
|
||||
@@ -60,23 +65,84 @@ public class CardSpawnTest : MonoBehaviour
|
||||
|
||||
private GameObject dealerHiddenCard;
|
||||
|
||||
private bool isPlayerTurn = true;
|
||||
private bool isGameOver = false;
|
||||
private bool isPlayerTurn = false;
|
||||
private bool isGameOver = true;
|
||||
private bool isMatchOver = false;
|
||||
private bool hasMatchStarted = false;
|
||||
private bool isEndingMatch = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
StartMatch();
|
||||
PrepareBeforeStart();
|
||||
}
|
||||
|
||||
void StartMatch()
|
||||
void PrepareBeforeStart()
|
||||
{
|
||||
ClearSpawnedCards();
|
||||
|
||||
playerCards.Clear();
|
||||
dealerCards.Clear();
|
||||
|
||||
playerHitIndex = 0;
|
||||
dealerHitIndex = 0;
|
||||
|
||||
playerWinCount = 0;
|
||||
dealerWinCount = 0;
|
||||
|
||||
isPlayerTurn = false;
|
||||
isGameOver = true;
|
||||
isMatchOver = false;
|
||||
hasMatchStarted = false;
|
||||
isEndingMatch = false;
|
||||
|
||||
HideAllWinMarks();
|
||||
|
||||
if (resultText != null)
|
||||
{
|
||||
resultText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (playerScoreText != null)
|
||||
{
|
||||
playerScoreText.text = "Player: 0";
|
||||
}
|
||||
|
||||
if (dealerScoreText != null)
|
||||
{
|
||||
dealerScoreText.text = "Dealer: ?";
|
||||
}
|
||||
|
||||
if (hitButton != null)
|
||||
{
|
||||
hitButton.interactable = false;
|
||||
}
|
||||
|
||||
if (standButton != null)
|
||||
{
|
||||
standButton.interactable = false;
|
||||
}
|
||||
|
||||
Debug.Log("Blackjack ready. Waiting for player seated.");
|
||||
}
|
||||
|
||||
public void StartMatch()
|
||||
{
|
||||
if (hasMatchStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hasMatchStarted = true;
|
||||
|
||||
playerWinCount = 0;
|
||||
dealerWinCount = 0;
|
||||
isMatchOver = false;
|
||||
isEndingMatch = false;
|
||||
|
||||
HideAllWinMarks();
|
||||
StartRound();
|
||||
|
||||
Debug.Log("Blackjack Match Start");
|
||||
}
|
||||
|
||||
void StartRound()
|
||||
@@ -118,7 +184,11 @@ void StartRound()
|
||||
|
||||
Debug.Log("New Round Start");
|
||||
Debug.Log("Player Score: " + CalculateScore(playerCards));
|
||||
Debug.Log("Dealer Open Card Score: " + GetCardValue(dealerCards[0]));
|
||||
|
||||
if (dealerCards.Count > 0)
|
||||
{
|
||||
Debug.Log("Dealer Open Card Score: " + GetCardValue(dealerCards[0]));
|
||||
}
|
||||
}
|
||||
|
||||
void BuildDeck()
|
||||
@@ -327,6 +397,8 @@ void EndRound(int winner, string message)
|
||||
isMatchOver = true;
|
||||
ShowResult("Final Result\nPlayer Wins!");
|
||||
Debug.Log("Final Result: Player Wins!");
|
||||
|
||||
StartCoroutine(EndMatchAfterDelay());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -335,6 +407,8 @@ void EndRound(int winner, string message)
|
||||
isMatchOver = true;
|
||||
ShowResult("Final Result\nDealer Wins!");
|
||||
Debug.Log("Final Result: Dealer Wins!");
|
||||
|
||||
StartCoroutine(EndMatchAfterDelay());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -351,6 +425,20 @@ IEnumerator StartNextRoundAfterDelay()
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator EndMatchAfterDelay()
|
||||
{
|
||||
if (isEndingMatch)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
isEndingMatch = true;
|
||||
|
||||
yield return new WaitForSeconds(matchEndDelay);
|
||||
|
||||
onMatchEnded?.Invoke();
|
||||
}
|
||||
|
||||
void UpdateScoreUI(bool showDealerFullScore)
|
||||
{
|
||||
int playerScore = CalculateScore(playerCards);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
217
Assets/02_Scripts/Blackjack/MinionRouteMover.cs
Normal file
217
Assets/02_Scripts/Blackjack/MinionRouteMover.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
public class MinionRouteMover : MonoBehaviour
|
||||
{
|
||||
[Header("Route Points")]
|
||||
public Transform[] routePoints;
|
||||
|
||||
[Header("Move Type")]
|
||||
public bool pingPong = true;
|
||||
public bool loop = true;
|
||||
|
||||
[Header("Move Setting")]
|
||||
public float arriveDistance = 0.3f;
|
||||
public float waitTimeAtPoint = 0.5f;
|
||||
|
||||
[Header("Random Setting")]
|
||||
public bool randomStartDelay = true;
|
||||
public float maxStartDelay = 2f;
|
||||
|
||||
public bool randomSpeed = true;
|
||||
public float minSpeed = 1.8f;
|
||||
public float maxSpeed = 3.0f;
|
||||
|
||||
public bool randomAnimationSpeed = true;
|
||||
public float minAnimSpeed = 0.9f;
|
||||
public float maxAnimSpeed = 1.15f;
|
||||
|
||||
private NavMeshAgent agent;
|
||||
private Animator animator;
|
||||
|
||||
private int currentIndex = 0;
|
||||
private int direction = 1;
|
||||
|
||||
private bool started = false;
|
||||
private bool waiting = false;
|
||||
|
||||
private float startTimer = 0f;
|
||||
private float waitTimer = 0f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
agent = GetComponent<NavMeshAgent>();
|
||||
animator = GetComponent<Animator>();
|
||||
|
||||
if (agent == null)
|
||||
{
|
||||
Debug.LogWarning(name + " : NavMeshAgent ¾øÀ½");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (routePoints == null || routePoints.Length == 0)
|
||||
{
|
||||
Debug.LogWarning(name + " : Route Points ¾øÀ½");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (randomSpeed)
|
||||
{
|
||||
agent.speed = Random.Range(minSpeed, maxSpeed);
|
||||
}
|
||||
|
||||
if (randomAnimationSpeed && animator != null)
|
||||
{
|
||||
animator.speed = Random.Range(minAnimSpeed, maxAnimSpeed);
|
||||
}
|
||||
|
||||
if (randomStartDelay)
|
||||
{
|
||||
startTimer = Random.Range(0f, maxStartDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
startTimer = 0f;
|
||||
}
|
||||
|
||||
agent.isStopped = true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
startTimer -= Time.deltaTime;
|
||||
|
||||
if (startTimer <= 0f)
|
||||
{
|
||||
started = true;
|
||||
MoveToCurrentPoint();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (waiting)
|
||||
{
|
||||
waitTimer -= Time.deltaTime;
|
||||
|
||||
if (waitTimer <= 0f)
|
||||
{
|
||||
waiting = false;
|
||||
GoNextPoint();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (agent.pathPending)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (agent.remainingDistance <= arriveDistance)
|
||||
{
|
||||
StartWait();
|
||||
}
|
||||
}
|
||||
|
||||
void MoveToCurrentPoint()
|
||||
{
|
||||
if (routePoints[currentIndex] == null)
|
||||
{
|
||||
GoNextPoint();
|
||||
return;
|
||||
}
|
||||
|
||||
NavMeshHit hit;
|
||||
|
||||
if (NavMesh.SamplePosition(routePoints[currentIndex].position, out hit, 2f, NavMesh.AllAreas))
|
||||
{
|
||||
agent.isStopped = false;
|
||||
agent.SetDestination(hit.position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning(name + " : Route Point°¡ NavMesh ±Ùó¿¡ ¾øÀ½ - " + routePoints[currentIndex].name);
|
||||
GoNextPoint();
|
||||
}
|
||||
}
|
||||
|
||||
void StartWait()
|
||||
{
|
||||
waiting = true;
|
||||
waitTimer = waitTimeAtPoint;
|
||||
agent.isStopped = true;
|
||||
}
|
||||
|
||||
void GoNextPoint()
|
||||
{
|
||||
if (routePoints.Length <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pingPong)
|
||||
{
|
||||
if (currentIndex >= routePoints.Length - 1)
|
||||
{
|
||||
direction = -1;
|
||||
}
|
||||
else if (currentIndex <= 0)
|
||||
{
|
||||
direction = 1;
|
||||
}
|
||||
|
||||
currentIndex += direction;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentIndex++;
|
||||
|
||||
if (currentIndex >= routePoints.Length)
|
||||
{
|
||||
if (loop)
|
||||
{
|
||||
currentIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
agent.isStopped = true;
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MoveToCurrentPoint();
|
||||
}
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (routePoints == null || routePoints.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gizmos.color = Color.yellow;
|
||||
|
||||
for (int i = 0; i < routePoints.Length - 1; i++)
|
||||
{
|
||||
if (routePoints[i] != null && routePoints[i + 1] != null)
|
||||
{
|
||||
Gizmos.DrawLine(routePoints[i].position, routePoints[i + 1].position);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pingPong && loop)
|
||||
{
|
||||
if (routePoints[0] != null && routePoints[routePoints.Length - 1] != null)
|
||||
{
|
||||
Gizmos.DrawLine(routePoints[routePoints.Length - 1].position, routePoints[0].position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Blackjack/MinionRouteMover.cs.meta
Normal file
2
Assets/02_Scripts/Blackjack/MinionRouteMover.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dd34cc6a8ba55842b3d23db81e4e84d
|
||||
266
Assets/02_Scripts/Blackjack/PlayerChairSeat.cs
Normal file
266
Assets/02_Scripts/Blackjack/PlayerChairSeat.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class PlayerChairSeat : MonoBehaviour
|
||||
{
|
||||
[Header("XR Player")]
|
||||
public Transform xrOrigin;
|
||||
public Camera xrCamera;
|
||||
|
||||
[Header("Seat Point")]
|
||||
public Transform seatHeadPoint;
|
||||
|
||||
[Header("Release Point")]
|
||||
public Transform releasePoint;
|
||||
public bool moveToReleasePointOnRelease = true;
|
||||
|
||||
[Header("VR Interaction Check")]
|
||||
public bool useDistanceCheck = true;
|
||||
public float sitDistance = 2f;
|
||||
|
||||
[Header("Lock While Seated")]
|
||||
public bool lockXROriginWhileSeated = true;
|
||||
|
||||
[Header("Release Move Check")]
|
||||
public bool hideUIWhenPlayerMovesAfterRelease = true;
|
||||
public float moveDetectDistance = 0.15f;
|
||||
|
||||
[Header("Disable Movement While Seated")]
|
||||
public Behaviour[] disableWhileSeated;
|
||||
|
||||
[Header("Seat Interaction Zone")]
|
||||
public GameObject[] disableObjectsAfterSeated;
|
||||
public Collider[] disableCollidersAfterSeated;
|
||||
public Behaviour[] disableBehavioursAfterSeated;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent onSeated;
|
||||
public UnityEvent onReleased;
|
||||
public UnityEvent onMovedAfterRelease;
|
||||
|
||||
private bool isSeated = false;
|
||||
private bool isReleased = false;
|
||||
private bool movedAfterReleaseEventCalled = false;
|
||||
|
||||
private CharacterController characterController;
|
||||
|
||||
private Vector3 lockedOriginPosition;
|
||||
private Quaternion lockedOriginRotation;
|
||||
|
||||
private Vector3 releaseOriginPosition;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (xrOrigin != null)
|
||||
{
|
||||
characterController = xrOrigin.GetComponent<CharacterController>();
|
||||
}
|
||||
|
||||
SetSeatInteractionZoneActive(true);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
CheckMoveAfterRelease();
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!isSeated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lockXROriginWhileSeated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (xrOrigin == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
xrOrigin.position = lockedOriginPosition;
|
||||
xrOrigin.rotation = lockedOriginRotation;
|
||||
}
|
||||
|
||||
public void SitDown()
|
||||
{
|
||||
if (isSeated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (xrOrigin == null || xrCamera == null || seatHeadPoint == null)
|
||||
{
|
||||
Debug.LogWarning("XR Origin / XR Camera / Seat Head Point ¿¬°á ¾È µÊ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (useDistanceCheck)
|
||||
{
|
||||
float distance = Vector3.Distance(xrCamera.transform.position, seatHeadPoint.position);
|
||||
|
||||
if (distance > sitDistance)
|
||||
{
|
||||
Debug.Log("Too far from chair to sit.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isSeated = true;
|
||||
isReleased = false;
|
||||
movedAfterReleaseEventCalled = false;
|
||||
|
||||
foreach (Behaviour behaviour in disableWhileSeated)
|
||||
{
|
||||
if (behaviour != null)
|
||||
{
|
||||
behaviour.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (characterController != null)
|
||||
{
|
||||
characterController.enabled = false;
|
||||
}
|
||||
|
||||
float cameraYaw = xrCamera.transform.eulerAngles.y;
|
||||
float targetYaw = seatHeadPoint.eulerAngles.y;
|
||||
float yawOffset = targetYaw - cameraYaw;
|
||||
|
||||
xrOrigin.RotateAround(xrCamera.transform.position, Vector3.up, yawOffset);
|
||||
|
||||
Vector3 offset = seatHeadPoint.position - xrCamera.transform.position;
|
||||
xrOrigin.position += offset;
|
||||
|
||||
lockedOriginPosition = xrOrigin.position;
|
||||
lockedOriginRotation = xrOrigin.rotation;
|
||||
|
||||
if (characterController != null)
|
||||
{
|
||||
characterController.enabled = true;
|
||||
}
|
||||
|
||||
SetSeatInteractionZoneActive(false);
|
||||
|
||||
Debug.Log("Player seated and locked.");
|
||||
|
||||
onSeated?.Invoke();
|
||||
}
|
||||
|
||||
public void ReleaseSeat()
|
||||
{
|
||||
if (!isSeated)
|
||||
{
|
||||
Debug.Log("ReleaseSeat called, but player is not seated.");
|
||||
return;
|
||||
}
|
||||
|
||||
isSeated = false;
|
||||
isReleased = true;
|
||||
movedAfterReleaseEventCalled = false;
|
||||
|
||||
if (characterController != null)
|
||||
{
|
||||
characterController.enabled = false;
|
||||
}
|
||||
|
||||
if (moveToReleasePointOnRelease && releasePoint != null && xrOrigin != null)
|
||||
{
|
||||
xrOrigin.position = releasePoint.position;
|
||||
xrOrigin.rotation = releasePoint.rotation;
|
||||
}
|
||||
|
||||
if (characterController != null)
|
||||
{
|
||||
characterController.enabled = true;
|
||||
}
|
||||
|
||||
if (xrOrigin != null)
|
||||
{
|
||||
releaseOriginPosition = xrOrigin.position;
|
||||
}
|
||||
|
||||
foreach (Behaviour behaviour in disableWhileSeated)
|
||||
{
|
||||
if (behaviour != null)
|
||||
{
|
||||
behaviour.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
SetSeatInteractionZoneActive(true);
|
||||
|
||||
Debug.Log("Player released from seat.");
|
||||
|
||||
onReleased?.Invoke();
|
||||
}
|
||||
|
||||
void SetSeatInteractionZoneActive(bool active)
|
||||
{
|
||||
foreach (GameObject obj in disableObjectsAfterSeated)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
obj.SetActive(active);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Collider col in disableCollidersAfterSeated)
|
||||
{
|
||||
if (col != null)
|
||||
{
|
||||
col.enabled = active;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Behaviour behaviour in disableBehavioursAfterSeated)
|
||||
{
|
||||
if (behaviour != null)
|
||||
{
|
||||
behaviour.enabled = active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckMoveAfterRelease()
|
||||
{
|
||||
if (!hideUIWhenPlayerMovesAfterRelease)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isReleased)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (movedAfterReleaseEventCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (xrOrigin == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float distance = Vector3.Distance(xrOrigin.position, releaseOriginPosition);
|
||||
|
||||
if (distance >= moveDetectDistance)
|
||||
{
|
||||
movedAfterReleaseEventCalled = true;
|
||||
|
||||
Debug.Log("Player moved after release. Hide game UI.");
|
||||
|
||||
onMovedAfterRelease?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSeated()
|
||||
{
|
||||
return isSeated;
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Blackjack/PlayerChairSeat.cs.meta
Normal file
2
Assets/02_Scripts/Blackjack/PlayerChairSeat.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 546c359d1abbbe84d9aa316ab145fa4e
|
||||
73
Assets/02_Scripts/Managers/CoinManager.cs
Normal file
73
Assets/02_Scripts/Managers/CoinManager.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
|
||||
public class CoinManager : MonoBehaviour
|
||||
{
|
||||
public static CoinManager Instance;
|
||||
|
||||
[Header("Coin Data")]
|
||||
public int currentCoins = 0;
|
||||
|
||||
[Header("UI")]
|
||||
public TMP_Text coinText;
|
||||
|
||||
[Header("Scene Setting")]
|
||||
public bool dontDestroyOnLoad = true;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
|
||||
if (dontDestroyOnLoad)
|
||||
{
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
UpdateCoinUI();
|
||||
}
|
||||
|
||||
public void AddCoin(int amount)
|
||||
{
|
||||
currentCoins += amount;
|
||||
UpdateCoinUI();
|
||||
|
||||
Debug.Log("Coin Added: " + amount + " / Total Coin: " + currentCoins);
|
||||
}
|
||||
|
||||
public bool SpendCoin(int amount)
|
||||
{
|
||||
if (currentCoins < amount)
|
||||
{
|
||||
Debug.Log("Not enough coins. Current: " + currentCoins + " / Need: " + amount);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentCoins -= amount;
|
||||
UpdateCoinUI();
|
||||
|
||||
Debug.Log("Coin Spent: " + amount + " / Total Coin: " + currentCoins);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetCoinCount()
|
||||
{
|
||||
return currentCoins;
|
||||
}
|
||||
|
||||
void UpdateCoinUI()
|
||||
{
|
||||
if (coinText != null)
|
||||
{
|
||||
coinText.text = "Coin: " + currentCoins;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Managers/CoinManager.cs.meta
Normal file
2
Assets/02_Scripts/Managers/CoinManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3730fd3e77bda8849aedc74946d2254a
|
||||
145
Assets/02_Scripts/Managers/CoinPickup.cs
Normal file
145
Assets/02_Scripts/Managers/CoinPickup.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Interaction.Toolkit;
|
||||
using UnityEngine.XR.Interaction.Toolkit.Interactables;
|
||||
|
||||
public class CoinPickup : MonoBehaviour
|
||||
{
|
||||
[Header("Coin Setting")]
|
||||
public int coinValue = 1;
|
||||
|
||||
[Header("Visual Spin")]
|
||||
public Transform visualRoot;
|
||||
public bool rotate = true;
|
||||
public float rotateSpeed = 180f;
|
||||
|
||||
[Tooltip("게임 코인처럼 세로로 빙글빙글 돌 때 보통 Y축")]
|
||||
public Vector3 rotateAxis = Vector3.up;
|
||||
|
||||
[Header("Float Motion")]
|
||||
public bool floatMotion = true;
|
||||
public float floatHeight = 0.08f;
|
||||
public float floatSpeed = 2f;
|
||||
|
||||
[Header("Sound")]
|
||||
public AudioSource pickupSound;
|
||||
|
||||
private bool isCollected = false;
|
||||
private Vector3 startPosition;
|
||||
private XRSimpleInteractable simpleInteractable;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
simpleInteractable = GetComponent<XRSimpleInteractable>();
|
||||
|
||||
if (simpleInteractable != null)
|
||||
{
|
||||
simpleInteractable.selectEntered.AddListener(OnSelected);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("XRSimpleInteractable not found on coin.");
|
||||
}
|
||||
|
||||
if (visualRoot == null)
|
||||
{
|
||||
visualRoot = transform;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (simpleInteractable != null)
|
||||
{
|
||||
simpleInteractable.selectEntered.RemoveListener(OnSelected);
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
startPosition = transform.position;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (isCollected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (rotate && visualRoot != null)
|
||||
{
|
||||
visualRoot.Rotate(rotateAxis.normalized, rotateSpeed * Time.deltaTime, Space.Self);
|
||||
}
|
||||
|
||||
if (floatMotion)
|
||||
{
|
||||
float yOffset = Mathf.Sin(Time.time * floatSpeed) * floatHeight;
|
||||
transform.position = startPosition + new Vector3(0f, yOffset, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelected(SelectEnterEventArgs args)
|
||||
{
|
||||
Debug.Log("Coin selected by XR Simple Interactable.");
|
||||
CollectCoin();
|
||||
}
|
||||
|
||||
public void CollectCoin()
|
||||
{
|
||||
if (isCollected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isCollected = true;
|
||||
|
||||
Debug.Log("CollectCoin called.");
|
||||
|
||||
if (CoinManager.Instance != null)
|
||||
{
|
||||
CoinManager.Instance.AddCoin(coinValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("CoinManager not found.");
|
||||
}
|
||||
|
||||
HideCoinImmediately();
|
||||
|
||||
if (pickupSound != null && pickupSound.clip != null)
|
||||
{
|
||||
pickupSound.transform.parent = null;
|
||||
pickupSound.Play();
|
||||
Destroy(pickupSound.gameObject, pickupSound.clip.length);
|
||||
}
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
void HideCoinImmediately()
|
||||
{
|
||||
MeshRenderer[] renderers = GetComponentsInChildren<MeshRenderer>();
|
||||
foreach (MeshRenderer renderer in renderers)
|
||||
{
|
||||
renderer.enabled = false;
|
||||
}
|
||||
|
||||
Collider[] colliders = GetComponentsInChildren<Collider>();
|
||||
foreach (Collider collider in colliders)
|
||||
{
|
||||
collider.enabled = false;
|
||||
}
|
||||
|
||||
Rigidbody rb = GetComponent<Rigidbody>();
|
||||
if (rb != null)
|
||||
{
|
||||
rb.isKinematic = true;
|
||||
rb.useGravity = false;
|
||||
}
|
||||
|
||||
if (simpleInteractable != null)
|
||||
{
|
||||
simpleInteractable.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Managers/CoinPickup.cs.meta
Normal file
2
Assets/02_Scripts/Managers/CoinPickup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ccf1d0aa82d6794785d3b8d3318a8a6
|
||||
Reference in New Issue
Block a user