기본적인 설계를 그림으로 한다

핵심은
카드를 실제로 게임에 그려주는 비주얼 드로어
카드를 사용하는 효과를 관리하는 전용 매니저
그 사이에 플레이어와 적 객체의 관리이며
이때 서로 책임 분리를 해야 할 것 같다

생각해보니 책임 분리가 과하게 들어간 것 같다
"카드"에 대한 책임만 집중해서 처리하면 될 것 같다
그럼 책임이 핵심 기능 2개로 나뉘게 되는데
- 카드를 화면에 그리는 기능
- 카드를 사용했을때 처리에 대한 기능
단순 무식하게 처리할려면 update로 연결해서 처리하면 되는데
너무 단순하고 무식하게 처리하는 방식이라 꺼려진다

일단 horizontal layout을 이용하여 카드가 모일 장소를 만들어 준 뒤
안쪽에서 사용될 카드에 대해서 정의한다
using UnityEngine;
public class Card : MonoBehaviour
{
[Header("Card ID")]
[SerializeField]
private int _cardID = -1; // 초기값, -1인 상태로 사용되면 예외처리 핸들링
/// <summary>
/// 카드의 id를 세팅 = card manager에서 call해서 사용
/// </summary>
/// <param name="id"></param>
public void SetCardID(int id)
{
_cardID = id;
}
}
이 컴포넌트를 가진 오브젝트를 어떻게 핸드에 넣을지를 생각해야 한다
public event System.Action<int> OnDrawCard;
player쪽에 델리게이트를 만들고
using UnityEngine;
public class CardManager : MonoBehaviour
{
[Header("card area")]
[SerializeField]
private Transform _handArea;
[Header("card prefab")]
[SerializeField]
private GameObject _card;
[Header("draw Deck")]
[SerializeField]
private Deck _drawDeck;
[Header("player object")]
[SerializeField]
private Player _player;
void Start()
{
_player.OnDrawCard += InstanceCard;
}
public void InstanceCard(int cardID)
{
GameObject card = Instantiate(_card, _handArea);
}
}
이를 카드 매니저에서 카드 생성과 연결해 구독한다
public void InstanceCard(int cardID)
{
GameObject card = Instantiate(_card, _handArea);
card.GetComponent<Card>().SetCardID(cardID);
}
생성시 id세팅을 동시에 해준다
더보기
결론: 패턴은 적절합니다. 다만 현재 구현은 핵심 이벤트가 발행되지 않아 UI가 갱신되지 않습니다. 아래만 고치면 안정적입니다.
- 이벤트 발행 지점: DrawCard()에서 카드 한 장을 핸드에 넣은 직후 OnDrawCard?.Invoke(cardId)를 호출하십시오. 지금은 구독만 있고 발행이 없습니다.
- 구독/해제 수명:
- CardManager.Start()에서 구독했다면 OnDestroy()에서 반드시 -=로 해제하십시오.
- Player도 OnDestroy()에서 TurnManager.Instance.OnTurnChanged -= TurnChanged 해제하십시오.
- 이벤트 의미 명확화: 이벤트명을 OnCardAddedToHand로 바꾸고 “핸드에 실제 반영 완료” 시점에만 쏘십시오. UI와 모델의 순서를 일치시켜 경쟁 상태를 막습니다.
- 페이로드 개선: int 하나보다 cardId와 소스/목적 덱, 드로우 인덱스 등을 담은 전용 args(예: CardDrawnArgs)로 확장 여지를 두십시오. 여러 장 드로우 시 OnCardsDrawn(IReadOnlyList<int>)도 고려하십시오.
- 핸드 한도 처리: 핸드가 가득 찬 상태에서는 드로우를 막거나 버림 처리 규칙을 적용하고, 그 결과를 이벤트로 알리십시오.
- 빈 드로우 덱 처리: 비었을 때 무덤 셔플→드로우 보충 로직을 먼저 실행한 뒤 드로우를 재시도하십시오. 실패 시에는 “실패 이벤트”를 별도로 발행하십시오.
- 중복 인스턴스 방지: 씬 리로드나 프리팹 재활성화 시 중복 구독이 일어나지 않도록 Awake/OnEnable에서 구독하고 OnDisable에서 해제하는 패턴을 지키십시오.
- 책임 분리: 카드 생성은 CardManager가 전담하고, Player는 상태 변경과 이벤트 발행만 담당하십시오. Deck은 순수 데이터 조작만 맡기십시오.
- 디버그 로그: 단순 로그 대신 이벤트 기반으로 상태를 추적하고, 필요 시 마지막 드로우 결과를 기록해 재현성을 높이십시오.
요약: 이벤트 기반 구조는 맞습니다. “드로우 → 핸드 반영 → 이벤트 발행”의 순서 보장, 구독 해제, 빈 덱·핸드 한도 예외 처리만 추가하면 됩니다.
private void DrawCard(int n)
{
for (int i = 0; i < n; i++)
{
if (!_drawDeck.IsDeckEmpty())
{
int drawCardId = _drawDeck.DrawCard();
_handDeck.SetCardToTop(drawCardId);
OnDrawCard.Invoke(drawCardId);
}
else
{
Debug.Log("draw deck is empty");
}
}
}
invoke가 빠져 있어서 추가해 준다
public void SetCardID(int id)
{
_cardID = id;
transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = _cardID.ToString();
}
확인용으로 card cs에 Tmpro text를 붙여준다

카드 오브젝트를 프리팹화 해준 뒤, 사용한다
'개발일지 > 게임개발' 카테고리의 다른 글
| Project_DT - 카드 사용 구현 (0) | 2025.09.30 |
|---|---|
| Project_DT - 카드 드래그 가이드 개발 (2) | 2025.09.25 |
| Project_DT - 플레이어 드로우 기능 (0) | 2025.09.23 |
| Project_DT - 덱 기능 설계, 구현 (0) | 2025.09.23 |
| Project_DT - 구글 시트 parsing 기능 만들기 (0) | 2025.09.19 |