725 lines
16 KiB
C#
725 lines
16 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using TMPro;
|
|
using UnityEngine.Events;
|
|
|
|
public class CardSpawnTest : MonoBehaviour
|
|
{
|
|
[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 Start Cards")]
|
|
public Transform playerCardPos1;
|
|
public Transform playerCardPos2;
|
|
|
|
[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("Dealer Card UI")]
|
|
public Image[] dealerCardImages;
|
|
public Sprite dealerCardBackSprite;
|
|
public Sprite[] cardSprites;
|
|
|
|
[Header("Win Mark UI")]
|
|
public GameObject[] playerWinMarks;
|
|
public GameObject[] dealerWinMarks;
|
|
|
|
[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>();
|
|
|
|
private List<string> playerCards = new List<string>();
|
|
private List<string> dealerCards = new List<string>();
|
|
|
|
private Dictionary<string, Sprite> cardSpriteMap = new Dictionary<string, Sprite>();
|
|
|
|
private int playerHitIndex = 0;
|
|
private int dealerHitIndex = 0;
|
|
|
|
private int playerWinCount = 0;
|
|
private int dealerWinCount = 0;
|
|
|
|
private GameObject dealerHiddenCard;
|
|
|
|
private bool isPlayerTurn = false;
|
|
private bool isGameOver = true;
|
|
private bool isMatchOver = false;
|
|
private bool hasMatchStarted = false;
|
|
private bool isEndingMatch = false;
|
|
|
|
void Awake()
|
|
{
|
|
BuildCardSpriteMap();
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
PrepareBeforeStart();
|
|
}
|
|
|
|
void BuildCardSpriteMap()
|
|
{
|
|
cardSpriteMap.Clear();
|
|
|
|
foreach (Sprite sprite in cardSprites)
|
|
{
|
|
if (sprite == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string key = NormalizeCardName(sprite.name);
|
|
|
|
if (!cardSpriteMap.ContainsKey(key))
|
|
{
|
|
cardSpriteMap.Add(key, sprite);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
HideDealerCardUI();
|
|
|
|
if (resultText != null)
|
|
{
|
|
resultText.gameObject.SetActive(false);
|
|
}
|
|
|
|
if (playerScoreText != null)
|
|
{
|
|
playerScoreText.text = "플레이어: 0";
|
|
}
|
|
|
|
if (dealerScoreText != null)
|
|
{
|
|
dealerScoreText.text = "선장: ?";
|
|
}
|
|
|
|
if (hitButton != null)
|
|
{
|
|
hitButton.interactable = false;
|
|
}
|
|
|
|
if (standButton != null)
|
|
{
|
|
standButton.interactable = false;
|
|
}
|
|
|
|
Debug.Log("블랙잭 준비 완료. 플레이어가 앉기를 기다리는 중.");
|
|
}
|
|
|
|
public void StartMatch()
|
|
{
|
|
if (hasMatchStarted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
hasMatchStarted = true;
|
|
|
|
playerWinCount = 0;
|
|
dealerWinCount = 0;
|
|
isMatchOver = false;
|
|
isEndingMatch = false;
|
|
|
|
HideAllWinMarks();
|
|
StartRound();
|
|
|
|
Debug.Log("블랙잭 승부 시작");
|
|
}
|
|
|
|
void StartRound()
|
|
{
|
|
ClearSpawnedCards();
|
|
HideDealerCardUI();
|
|
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("새 라운드 시작");
|
|
Debug.Log("플레이어 점수: " + CalculateScore(playerCards));
|
|
}
|
|
|
|
void BuildDeck()
|
|
{
|
|
deck.Clear();
|
|
|
|
foreach (GameObject card in cardPrefabs)
|
|
{
|
|
if (card != null)
|
|
{
|
|
deck.Add(card);
|
|
}
|
|
}
|
|
|
|
Debug.Log("덱 카드 수: " + deck.Count);
|
|
}
|
|
|
|
GameObject DrawRandomCard()
|
|
{
|
|
if (deck.Count == 0)
|
|
{
|
|
Debug.LogWarning("덱이 비어 있습니다.");
|
|
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);
|
|
|
|
int dealerCardIndex = dealerCards.Count - 1;
|
|
UpdateDealerCardUI(dealerCardIndex, prefab.name, faceDown);
|
|
|
|
return SpawnCard(prefab, pos, faceDown);
|
|
}
|
|
|
|
public void Hit()
|
|
{
|
|
if (isGameOver || isMatchOver)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!isPlayerTurn)
|
|
{
|
|
Debug.Log("이미 멈췄습니다. 카드를 더 받을 수 없습니다.");
|
|
return;
|
|
}
|
|
|
|
if (playerHitIndex >= playerHitPositions.Length)
|
|
{
|
|
Debug.Log("플레이어 카드 위치가 더 이상 없습니다.");
|
|
return;
|
|
}
|
|
|
|
DealPlayerCard(playerHitPositions[playerHitIndex]);
|
|
playerHitIndex++;
|
|
|
|
UpdateScoreUI(false);
|
|
|
|
int playerScore = CalculateScore(playerCards);
|
|
Debug.Log("카드 받은 후 플레이어 점수: " + playerScore);
|
|
|
|
if (playerScore > 21)
|
|
{
|
|
Debug.Log("플레이어 버스트! 이번 라운드는 선장 승리.");
|
|
EndRound(-1, "버스트!\n선장 승리!");
|
|
}
|
|
}
|
|
|
|
public void Stand()
|
|
{
|
|
if (isGameOver || isMatchOver)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!isPlayerTurn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
isPlayerTurn = false;
|
|
|
|
if (dealerHiddenCard != null)
|
|
{
|
|
dealerHiddenCard.transform.rotation = Quaternion.Euler(frontRotation);
|
|
}
|
|
|
|
RevealDealerCardsUI();
|
|
UpdateScoreUI(true);
|
|
|
|
Debug.Log("스탠드: 선장의 숨겨진 카드 공개.");
|
|
Debug.Log("선장 점수: " + CalculateScore(dealerCards));
|
|
|
|
DealerTurn();
|
|
}
|
|
|
|
void DealerTurn()
|
|
{
|
|
while (CalculateScore(dealerCards) < 17)
|
|
{
|
|
if (dealerHitIndex >= dealerHitPositions.Length)
|
|
{
|
|
Debug.Log("선장 카드 위치가 더 이상 없습니다.");
|
|
break;
|
|
}
|
|
|
|
DealDealerCard(dealerHitPositions[dealerHitIndex], false);
|
|
dealerHitIndex++;
|
|
|
|
UpdateScoreUI(true);
|
|
|
|
Debug.Log("선장이 카드를 받았습니다. 선장 점수: " + CalculateScore(dealerCards));
|
|
}
|
|
|
|
JudgeResult();
|
|
}
|
|
|
|
void JudgeResult()
|
|
{
|
|
int playerScore = CalculateScore(playerCards);
|
|
int dealerScore = CalculateScore(dealerCards);
|
|
|
|
Debug.Log("최종 플레이어 점수: " + playerScore);
|
|
Debug.Log("최종 선장 점수: " + dealerScore);
|
|
|
|
if (dealerScore > 21)
|
|
{
|
|
EndRound(1, "선장 버스트!\n플레이어 승리!");
|
|
}
|
|
else if (playerScore > dealerScore)
|
|
{
|
|
EndRound(1, "플레이어 승리!");
|
|
}
|
|
else if (playerScore < dealerScore)
|
|
{
|
|
EndRound(-1, "선장 승리!");
|
|
}
|
|
else
|
|
{
|
|
EndRound(0, "무승부!\n승점 없음");
|
|
}
|
|
}
|
|
|
|
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("최종 결과\n플레이어 승리!");
|
|
Debug.Log("최종 결과: 플레이어 승리");
|
|
|
|
StartCoroutine(EndMatchAfterDelay());
|
|
return;
|
|
}
|
|
|
|
if (dealerWinCount >= targetWinCount)
|
|
{
|
|
isMatchOver = true;
|
|
ShowResult("최종 결과\n선장 승리!\n다시 도전!");
|
|
Debug.Log("최종 결과: 선장 승리. 승부를 다시 시작합니다.");
|
|
|
|
StartCoroutine(RestartMatchAfterLose());
|
|
return;
|
|
}
|
|
|
|
StartCoroutine(StartNextRoundAfterDelay());
|
|
}
|
|
|
|
IEnumerator StartNextRoundAfterDelay()
|
|
{
|
|
yield return new WaitForSeconds(nextRoundDelay);
|
|
|
|
if (!isMatchOver)
|
|
{
|
|
StartRound();
|
|
}
|
|
}
|
|
|
|
IEnumerator EndMatchAfterDelay()
|
|
{
|
|
if (isEndingMatch)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
isEndingMatch = true;
|
|
|
|
yield return new WaitForSeconds(matchEndDelay);
|
|
|
|
onMatchEnded?.Invoke();
|
|
}
|
|
|
|
IEnumerator RestartMatchAfterLose()
|
|
{
|
|
if (isEndingMatch)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
isEndingMatch = true;
|
|
|
|
yield return new WaitForSeconds(matchEndDelay);
|
|
|
|
playerWinCount = 0;
|
|
dealerWinCount = 0;
|
|
|
|
isMatchOver = false;
|
|
isGameOver = true;
|
|
isPlayerTurn = false;
|
|
isEndingMatch = false;
|
|
|
|
HideAllWinMarks();
|
|
StartRound();
|
|
|
|
Debug.Log("패배 후 블랙잭을 다시 시작합니다.");
|
|
}
|
|
|
|
void UpdateScoreUI(bool showDealerFullScore)
|
|
{
|
|
int playerScore = CalculateScore(playerCards);
|
|
|
|
if (playerScoreText != null)
|
|
{
|
|
playerScoreText.text = "플레이어: " + playerScore;
|
|
}
|
|
|
|
if (dealerScoreText != null)
|
|
{
|
|
if (showDealerFullScore)
|
|
{
|
|
dealerScoreText.text = "선장: " + CalculateScore(dealerCards);
|
|
}
|
|
else
|
|
{
|
|
dealerScoreText.text = "선장: ?";
|
|
}
|
|
}
|
|
}
|
|
|
|
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("카드 값을 찾을 수 없습니다: " + 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("카드 프리팹 또는 위치가 비어 있습니다.");
|
|
return null;
|
|
}
|
|
|
|
GameObject card = Instantiate(prefab, pos.position, Quaternion.identity);
|
|
spawnedCards.Add(card);
|
|
|
|
card.transform.localScale = prefab.transform.localScale * cardScale;
|
|
|
|
if (faceDown)
|
|
{
|
|
card.transform.rotation = Quaternion.Euler(backRotation);
|
|
}
|
|
else
|
|
{
|
|
card.transform.rotation = Quaternion.Euler(frontRotation);
|
|
}
|
|
|
|
return card;
|
|
}
|
|
|
|
void HideDealerCardUI()
|
|
{
|
|
if (dealerCardImages == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (Image image in dealerCardImages)
|
|
{
|
|
if (image == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
image.sprite = null;
|
|
image.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
void UpdateDealerCardUI(int index, string cardName, bool faceDown)
|
|
{
|
|
if (dealerCardImages == null || index < 0 || index >= dealerCardImages.Length)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Image targetImage = dealerCardImages[index];
|
|
|
|
if (targetImage == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
targetImage.gameObject.SetActive(true);
|
|
|
|
if (faceDown)
|
|
{
|
|
targetImage.sprite = dealerCardBackSprite;
|
|
return;
|
|
}
|
|
|
|
targetImage.sprite = GetCardSprite(cardName);
|
|
}
|
|
|
|
void RevealDealerCardsUI()
|
|
{
|
|
for (int i = 0; i < dealerCards.Count; i++)
|
|
{
|
|
UpdateDealerCardUI(i, dealerCards[i], false);
|
|
}
|
|
}
|
|
|
|
Sprite GetCardSprite(string cardName)
|
|
{
|
|
string key = NormalizeCardName(cardName);
|
|
|
|
if (cardSpriteMap.TryGetValue(key, out Sprite sprite))
|
|
{
|
|
return sprite;
|
|
}
|
|
|
|
Debug.LogWarning("카드 UI 이미지를 찾을 수 없습니다: " + cardName);
|
|
return null;
|
|
}
|
|
|
|
string NormalizeCardName(string cardName)
|
|
{
|
|
if (string.IsNullOrEmpty(cardName))
|
|
{
|
|
return "";
|
|
}
|
|
|
|
return cardName
|
|
.Replace("(Clone)", "")
|
|
.Replace(" ", "")
|
|
.Replace("_", "")
|
|
.Replace("-", "")
|
|
.Trim()
|
|
.ToLower();
|
|
}
|
|
} |