블랙잭 카드 및 이동 기능 수정
This commit is contained in:
@@ -1,38 +1,487 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
public class CardSpawnTest : MonoBehaviour
|
||||
{
|
||||
[Header("Card Prefab")]
|
||||
public GameObject cardPrefab;
|
||||
[Header("Card Prefabs")]
|
||||
public GameObject[] cardPrefabs;
|
||||
|
||||
[Header("Card Setting")]
|
||||
public float cardScale = 2f;
|
||||
|
||||
public Vector3 frontRotation = new Vector3(-90f, 0f, 90f);
|
||||
public Vector3 backRotation = new Vector3(90f, 0f, 90f);
|
||||
|
||||
[Header("Player Cards")]
|
||||
[Header("Player Start Cards")]
|
||||
public Transform playerCardPos1;
|
||||
public Transform playerCardPos2;
|
||||
|
||||
[Header("Dealer Cards")]
|
||||
[Header("Dealer Start Cards")]
|
||||
public Transform dealerOpenCardPos;
|
||||
public Transform dealerHiddenCardPos;
|
||||
|
||||
[Header("Player Hit Card Positions")]
|
||||
public Transform[] playerHitPositions;
|
||||
|
||||
[Header("Dealer Hit Card Positions")]
|
||||
public Transform[] dealerHitPositions;
|
||||
|
||||
[Header("UI Buttons")]
|
||||
public Button hitButton;
|
||||
public Button standButton;
|
||||
|
||||
[Header("Score UI")]
|
||||
public TMP_Text playerScoreText;
|
||||
public TMP_Text dealerScoreText;
|
||||
public TMP_Text resultText;
|
||||
|
||||
[Header("Win Mark UI")]
|
||||
public GameObject[] playerWinMarks;
|
||||
public GameObject[] dealerWinMarks;
|
||||
|
||||
[Header("Match Setting")]
|
||||
public int targetWinCount = 3;
|
||||
public float nextRoundDelay = 2f;
|
||||
|
||||
private List<GameObject> deck = new List<GameObject>();
|
||||
private List<GameObject> spawnedCards = new List<GameObject>();
|
||||
|
||||
private List<string> playerCards = new List<string>();
|
||||
private List<string> dealerCards = new List<string>();
|
||||
|
||||
private int playerHitIndex = 0;
|
||||
private int dealerHitIndex = 0;
|
||||
|
||||
private int playerWinCount = 0;
|
||||
private int dealerWinCount = 0;
|
||||
|
||||
private GameObject dealerHiddenCard;
|
||||
|
||||
private bool isPlayerTurn = true;
|
||||
private bool isGameOver = false;
|
||||
private bool isMatchOver = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
SpawnCard(playerCardPos1, false);
|
||||
SpawnCard(playerCardPos2, false);
|
||||
|
||||
SpawnCard(dealerOpenCardPos, false);
|
||||
SpawnCard(dealerHiddenCardPos, true);
|
||||
StartMatch();
|
||||
}
|
||||
|
||||
void SpawnCard(Transform pos, bool faceDown)
|
||||
void StartMatch()
|
||||
{
|
||||
GameObject card = Instantiate(cardPrefab, pos.position, Quaternion.identity);
|
||||
playerWinCount = 0;
|
||||
dealerWinCount = 0;
|
||||
isMatchOver = false;
|
||||
|
||||
card.transform.localScale = cardPrefab.transform.localScale * cardScale;
|
||||
HideAllWinMarks();
|
||||
StartRound();
|
||||
}
|
||||
|
||||
void StartRound()
|
||||
{
|
||||
ClearSpawnedCards();
|
||||
BuildDeck();
|
||||
|
||||
isPlayerTurn = true;
|
||||
isGameOver = false;
|
||||
|
||||
playerHitIndex = 0;
|
||||
dealerHitIndex = 0;
|
||||
|
||||
playerCards.Clear();
|
||||
dealerCards.Clear();
|
||||
|
||||
if (resultText != null)
|
||||
{
|
||||
resultText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (hitButton != null)
|
||||
{
|
||||
hitButton.interactable = true;
|
||||
}
|
||||
|
||||
if (standButton != null)
|
||||
{
|
||||
standButton.interactable = true;
|
||||
}
|
||||
|
||||
DealPlayerCard(playerCardPos1);
|
||||
DealPlayerCard(playerCardPos2);
|
||||
|
||||
DealDealerCard(dealerOpenCardPos, false);
|
||||
dealerHiddenCard = DealDealerCard(dealerHiddenCardPos, true);
|
||||
|
||||
UpdateScoreUI(false);
|
||||
|
||||
Debug.Log("New Round Start");
|
||||
Debug.Log("Player Score: " + CalculateScore(playerCards));
|
||||
Debug.Log("Dealer Open Card Score: " + GetCardValue(dealerCards[0]));
|
||||
}
|
||||
|
||||
void BuildDeck()
|
||||
{
|
||||
deck.Clear();
|
||||
|
||||
foreach (GameObject card in cardPrefabs)
|
||||
{
|
||||
if (card != null)
|
||||
{
|
||||
deck.Add(card);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("Deck Count: " + deck.Count);
|
||||
}
|
||||
|
||||
GameObject DrawRandomCard()
|
||||
{
|
||||
if (deck.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("Deck is empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = Random.Range(0, deck.Count);
|
||||
GameObject selectedCard = deck[index];
|
||||
|
||||
deck.RemoveAt(index);
|
||||
|
||||
return selectedCard;
|
||||
}
|
||||
|
||||
void DealPlayerCard(Transform pos)
|
||||
{
|
||||
GameObject prefab = DrawRandomCard();
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerCards.Add(prefab.name);
|
||||
SpawnCard(prefab, pos, false);
|
||||
}
|
||||
|
||||
GameObject DealDealerCard(Transform pos, bool faceDown)
|
||||
{
|
||||
GameObject prefab = DrawRandomCard();
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
dealerCards.Add(prefab.name);
|
||||
return SpawnCard(prefab, pos, faceDown);
|
||||
}
|
||||
|
||||
public void Hit()
|
||||
{
|
||||
if (isGameOver || isMatchOver)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlayerTurn)
|
||||
{
|
||||
Debug.Log("You already stood. Hit is disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerHitIndex >= playerHitPositions.Length)
|
||||
{
|
||||
Debug.Log("No more player card positions.");
|
||||
return;
|
||||
}
|
||||
|
||||
DealPlayerCard(playerHitPositions[playerHitIndex]);
|
||||
playerHitIndex++;
|
||||
|
||||
UpdateScoreUI(false);
|
||||
|
||||
int playerScore = CalculateScore(playerCards);
|
||||
Debug.Log("Player Score After Hit: " + playerScore);
|
||||
|
||||
if (playerScore > 21)
|
||||
{
|
||||
Debug.Log("Player Bust! Dealer wins this round.");
|
||||
EndRound(-1, "Player Bust!\nDealer Wins Round!");
|
||||
}
|
||||
}
|
||||
|
||||
public void Stand()
|
||||
{
|
||||
if (isGameOver || isMatchOver)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlayerTurn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isPlayerTurn = false;
|
||||
|
||||
if (dealerHiddenCard != null)
|
||||
{
|
||||
dealerHiddenCard.transform.rotation = Quaternion.Euler(frontRotation);
|
||||
}
|
||||
|
||||
UpdateScoreUI(true);
|
||||
|
||||
Debug.Log("Stand: Dealer hidden card opened.");
|
||||
Debug.Log("Dealer Score: " + CalculateScore(dealerCards));
|
||||
|
||||
DealerTurn();
|
||||
}
|
||||
|
||||
void DealerTurn()
|
||||
{
|
||||
while (CalculateScore(dealerCards) < 17)
|
||||
{
|
||||
if (dealerHitIndex >= dealerHitPositions.Length)
|
||||
{
|
||||
Debug.Log("No more dealer card positions.");
|
||||
break;
|
||||
}
|
||||
|
||||
DealDealerCard(dealerHitPositions[dealerHitIndex], false);
|
||||
dealerHitIndex++;
|
||||
|
||||
UpdateScoreUI(true);
|
||||
|
||||
Debug.Log("Dealer draws a card. Dealer Score: " + CalculateScore(dealerCards));
|
||||
}
|
||||
|
||||
JudgeResult();
|
||||
}
|
||||
|
||||
void JudgeResult()
|
||||
{
|
||||
int playerScore = CalculateScore(playerCards);
|
||||
int dealerScore = CalculateScore(dealerCards);
|
||||
|
||||
Debug.Log("Final Player Score: " + playerScore);
|
||||
Debug.Log("Final Dealer Score: " + dealerScore);
|
||||
|
||||
if (dealerScore > 21)
|
||||
{
|
||||
EndRound(1, "Dealer Bust!\nPlayer Wins Round!");
|
||||
}
|
||||
else if (playerScore > dealerScore)
|
||||
{
|
||||
EndRound(1, "Player Wins Round!");
|
||||
}
|
||||
else if (playerScore < dealerScore)
|
||||
{
|
||||
EndRound(-1, "Dealer Wins Round!");
|
||||
}
|
||||
else
|
||||
{
|
||||
EndRound(0, "Draw!\nNo Score");
|
||||
}
|
||||
}
|
||||
|
||||
void EndRound(int winner, string message)
|
||||
{
|
||||
isGameOver = true;
|
||||
isPlayerTurn = false;
|
||||
|
||||
if (hitButton != null)
|
||||
{
|
||||
hitButton.interactable = false;
|
||||
}
|
||||
|
||||
if (standButton != null)
|
||||
{
|
||||
standButton.interactable = false;
|
||||
}
|
||||
|
||||
if (winner == 1)
|
||||
{
|
||||
if (playerWinCount < playerWinMarks.Length)
|
||||
{
|
||||
playerWinMarks[playerWinCount].SetActive(true);
|
||||
}
|
||||
|
||||
playerWinCount++;
|
||||
}
|
||||
else if (winner == -1)
|
||||
{
|
||||
if (dealerWinCount < dealerWinMarks.Length)
|
||||
{
|
||||
dealerWinMarks[dealerWinCount].SetActive(true);
|
||||
}
|
||||
|
||||
dealerWinCount++;
|
||||
}
|
||||
|
||||
ShowResult(message);
|
||||
|
||||
if (playerWinCount >= targetWinCount)
|
||||
{
|
||||
isMatchOver = true;
|
||||
ShowResult("Final Result\nPlayer Wins!");
|
||||
Debug.Log("Final Result: Player Wins!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dealerWinCount >= targetWinCount)
|
||||
{
|
||||
isMatchOver = true;
|
||||
ShowResult("Final Result\nDealer Wins!");
|
||||
Debug.Log("Final Result: Dealer Wins!");
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(StartNextRoundAfterDelay());
|
||||
}
|
||||
|
||||
IEnumerator StartNextRoundAfterDelay()
|
||||
{
|
||||
yield return new WaitForSeconds(nextRoundDelay);
|
||||
|
||||
if (!isMatchOver)
|
||||
{
|
||||
StartRound();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateScoreUI(bool showDealerFullScore)
|
||||
{
|
||||
int playerScore = CalculateScore(playerCards);
|
||||
|
||||
if (playerScoreText != null)
|
||||
{
|
||||
playerScoreText.text = "Player: " + playerScore;
|
||||
}
|
||||
|
||||
if (dealerScoreText != null)
|
||||
{
|
||||
if (showDealerFullScore)
|
||||
{
|
||||
dealerScoreText.text = "Dealer: " + CalculateScore(dealerCards);
|
||||
}
|
||||
else
|
||||
{
|
||||
dealerScoreText.text = "Dealer: ?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShowResult(string message)
|
||||
{
|
||||
if (resultText != null)
|
||||
{
|
||||
resultText.gameObject.SetActive(true);
|
||||
resultText.text = message;
|
||||
}
|
||||
}
|
||||
|
||||
void HideAllWinMarks()
|
||||
{
|
||||
foreach (GameObject mark in playerWinMarks)
|
||||
{
|
||||
if (mark != null)
|
||||
{
|
||||
mark.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GameObject mark in dealerWinMarks)
|
||||
{
|
||||
if (mark != null)
|
||||
{
|
||||
mark.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearSpawnedCards()
|
||||
{
|
||||
foreach (GameObject card in spawnedCards)
|
||||
{
|
||||
if (card != null)
|
||||
{
|
||||
Destroy(card);
|
||||
}
|
||||
}
|
||||
|
||||
spawnedCards.Clear();
|
||||
}
|
||||
|
||||
int CalculateScore(List<string> cards)
|
||||
{
|
||||
int total = 0;
|
||||
int aceCount = 0;
|
||||
|
||||
foreach (string cardName in cards)
|
||||
{
|
||||
int value = GetCardValue(cardName);
|
||||
|
||||
if (IsAce(cardName))
|
||||
{
|
||||
aceCount++;
|
||||
}
|
||||
|
||||
total += value;
|
||||
}
|
||||
|
||||
while (total > 21 && aceCount > 0)
|
||||
{
|
||||
total -= 10;
|
||||
aceCount--;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
int GetCardValue(string cardName)
|
||||
{
|
||||
string lowerName = cardName.ToLower();
|
||||
|
||||
if (lowerName.Contains("ace"))
|
||||
{
|
||||
return 11;
|
||||
}
|
||||
|
||||
if (lowerName.Contains("jack") || lowerName.Contains("queen") || lowerName.Contains("king"))
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
Match match = Regex.Match(cardName, @"\d+");
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
return int.Parse(match.Value);
|
||||
}
|
||||
|
||||
Debug.LogWarning("Card value not found: " + cardName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IsAce(string cardName)
|
||||
{
|
||||
return cardName.ToLower().Contains("ace");
|
||||
}
|
||||
|
||||
GameObject SpawnCard(GameObject prefab, Transform pos, bool faceDown)
|
||||
{
|
||||
if (prefab == null || pos == null)
|
||||
{
|
||||
Debug.LogWarning("Card prefab or position is missing.");
|
||||
return null;
|
||||
}
|
||||
|
||||
GameObject card = Instantiate(prefab, pos.position, Quaternion.identity);
|
||||
spawnedCards.Add(card);
|
||||
|
||||
card.transform.localScale = prefab.transform.localScale * cardScale;
|
||||
|
||||
if (faceDown)
|
||||
{
|
||||
@@ -42,5 +491,7 @@ void SpawnCard(Transform pos, bool faceDown)
|
||||
{
|
||||
card.transform.rotation = Quaternion.Euler(frontRotation);
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
}
|
||||
230
Assets/02_Scripts/Blackjack/HookWalkToTable.cs
Normal file
230
Assets/02_Scripts/Blackjack/HookWalkToTable.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public class HookWalkToTable : MonoBehaviour
|
||||
{
|
||||
[Header("Path Points")]
|
||||
public Transform tableFrontPoint;
|
||||
public Transform chairFrontPoint;
|
||||
|
||||
[Header("Sit Point")]
|
||||
public Transform sitPoint;
|
||||
|
||||
[Header("Move Setting")]
|
||||
public float arriveBuffer = 0.15f;
|
||||
|
||||
[Header("Animator")]
|
||||
public Animator animator;
|
||||
public string walkBoolName = "isWalking";
|
||||
public string sitTriggerName = "sitTrigger";
|
||||
|
||||
[Header("Collision")]
|
||||
public bool disableCollidersWhenSitting = true;
|
||||
public Collider[] hookColliders;
|
||||
|
||||
[Header("Test")]
|
||||
public Key testStartKey = Key.T;
|
||||
|
||||
private NavMeshAgent agent;
|
||||
|
||||
private int step = 0;
|
||||
private bool isMoving = false;
|
||||
private bool hasArrived = false;
|
||||
private bool isSitting = false;
|
||||
|
||||
private Vector3 currentDestination;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Inspector에 Collider를 안 넣어도 자동으로 후크 자식 콜라이더들을 찾아줌
|
||||
if (hookColliders == null || hookColliders.Length == 0)
|
||||
{
|
||||
hookColliders = GetComponentsInChildren<Collider>();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 테스트용: T 누르면 출발
|
||||
if (Keyboard.current != null && Keyboard.current[testStartKey].wasPressedThisFrame)
|
||||
{
|
||||
StartWalking();
|
||||
}
|
||||
|
||||
if (!isMoving || agent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (agent.pathPending)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float distance = Vector3.Distance(transform.position, currentDestination);
|
||||
|
||||
if (distance <= agent.stoppingDistance + arriveBuffer)
|
||||
{
|
||||
GoNextStep();
|
||||
}
|
||||
}
|
||||
|
||||
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 (isMoving || hasArrived || isSitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
step = 0;
|
||||
hasArrived = false;
|
||||
isMoving = true;
|
||||
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetBool(walkBoolName, true);
|
||||
}
|
||||
|
||||
agent.enabled = true;
|
||||
agent.isStopped = false;
|
||||
|
||||
MoveTo(tableFrontPoint);
|
||||
|
||||
Debug.Log("Hook starts walking to table front point.");
|
||||
}
|
||||
|
||||
void MoveTo(Transform target)
|
||||
{
|
||||
NavMeshHit hit;
|
||||
|
||||
if (NavMesh.SamplePosition(target.position, out hit, 1.5f, 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
|
||||
{
|
||||
ArriveAndSit();
|
||||
}
|
||||
}
|
||||
|
||||
void ArriveAndSit()
|
||||
{
|
||||
isMoving = false;
|
||||
hasArrived = true;
|
||||
isSitting = true;
|
||||
|
||||
// 1. NavMeshAgent 먼저 끄기
|
||||
if (agent != null)
|
||||
{
|
||||
agent.isStopped = true;
|
||||
agent.ResetPath();
|
||||
agent.enabled = false;
|
||||
}
|
||||
|
||||
// 2. 후크 몸 Collider 끄기
|
||||
// 의자/테이블 콜라이더에 밀려서 위로 올라가는 문제 방지
|
||||
if (disableCollidersWhenSitting)
|
||||
{
|
||||
DisableHookColliders();
|
||||
}
|
||||
|
||||
// 3. 실제 앉을 위치로 강제 이동
|
||||
if (sitPoint != null)
|
||||
{
|
||||
transform.position = sitPoint.position;
|
||||
transform.rotation = sitPoint.rotation;
|
||||
}
|
||||
else if (chairFrontPoint != null)
|
||||
{
|
||||
transform.rotation = chairFrontPoint.rotation;
|
||||
}
|
||||
|
||||
// 4. 앉는 애니메이션 실행
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetBool(walkBoolName, false);
|
||||
animator.SetTrigger(sitTriggerName);
|
||||
}
|
||||
|
||||
Debug.Log("Hook moved to sit point and started sitting.");
|
||||
}
|
||||
|
||||
void DisableHookColliders()
|
||||
{
|
||||
if (hookColliders == null || hookColliders.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Collider col in hookColliders)
|
||||
{
|
||||
if (col != null)
|
||||
{
|
||||
col.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StopWalking()
|
||||
{
|
||||
isMoving = false;
|
||||
|
||||
if (agent != null && agent.enabled)
|
||||
{
|
||||
agent.isStopped = true;
|
||||
agent.ResetPath();
|
||||
}
|
||||
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetBool(walkBoolName, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Blackjack/HookWalkToTable.cs.meta
Normal file
2
Assets/02_Scripts/Blackjack/HookWalkToTable.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39cf77b883021d64ba8f144f4a7ee177
|
||||
Reference in New Issue
Block a user