Galaxy Card/1-1. 시스템 개발 #인게임

(9) 상점기능#6 (아이템 구매처리 구현)

DOlpa_GB 2025. 6. 8. 03:42

사실 지금은 문제가 다 해결된 상황에서 글을 쓰고 있는거지만

정말 어이없는 이유로 거의 3일내내 해결이 안됐던 오류가 있었고 그걸 알아내느라 여태 글을 못썻다...

 

사실 이 오류를 발견하고 알아내는 과정까지 쓰는게 가장 좋긴하겠지만

잘 안풀리고 있을땐 언제 해결이 될지도 모르는데 이것까지 쓸 기분이 아니랄까..ㅋㅋ

암튼 해결했으니 다행이다 

 

이번엔 아이템 드래그도 되고, 생성도 되고, 교체도 되니 본격적으로 아이템 구매를 하도록 만들어보겠다

 

 

원래는 조커와 아이템이 보관되는 장소이지만 구매하려는 아이템을 드래그하는 순간

저 두 장소를 덮는 새로운 상자가 생기며 이곳이 구매하는 영역임을 알려주며 동시에 가격도 다시 한번 상기시켜준다

 

 

 

그래서 구매를 확정지을 수 있는 공간을 BuyZone으로 만들어준뒤

public class ShowBuyUseZone : MonoBehaviour
{
    
    public GameObject BuyZone;
    private Tween scaleTween;
    private Tween pulseTween;
    private const float ANIM_DURATION = 0.4f; // 기본 애니메이션 지속 시간 (늘림)
    private const float PULSE_DURATION = 1.2f; // 맥박 애니메이션 주기 (늘림)
    private bool isDraggingValidItem = false;
    
    // BuyZone을 활성화할 태그 목록
    private readonly HashSet<string> validTags = new HashSet<string>
    {
        "Joker",
        "ItemPack",
        "Planet",
        "Taro",
        "Voucher"
    };

    private void Awake()
    {
        if (BuyZone != null)
        {
            // 초기 상태 설정 (작은 크기로 시작)
            BuyZone.transform.localScale = Vector3.zero;
            BuyZone.SetActive(false);
        }
    }

 

만약 선택한(드래그한) 아이템의 태그가 조커,아이템팩,행성,타로,바우처 중 하나라면 

BuyZone을 활성화하고 아니면 비활성화 하도록 만들어주었다

 

물론 활성화/비활성화 애니메이션은 DOTween으로 부드럽게 구현!

그래서 위 코드는 ShowBuyUseZone라는 빈 오브젝트를 만들어 거기에 부착해주었고

 

using UnityEngine;

public class BuyZone : MonoBehaviour
{
    // BuyZone과 충돌했을 때 확인할 태그 목록
    private readonly string[] validTags = 
    {
        "Joker",
        "ItemPack",
        "Planet",
        "Taro",
        "Voucher"
    };

    private void OnTriggerEnter2D(Collider2D other)
    {
        // 충돌한 오브젝트의 태그가 유효한 태그 목록에 있는지 확인
        if (System.Array.Exists(validTags, tag => other.CompareTag(tag)))
        {
            Debug.Log("구매 요청!");
        }
    }
}

 

BuyZone이라는 새로운 스크립트를 만들어 여기에 만약 5개의 태그 중 하나를 가진 오브젝트가 BuyZone과의

충돌을 감지한다면 "구매 요청!" 이라는 로그를 띄우도록 하였다

 

 

 

로그가 성공적으로 뜨는것을 확인할 수 있다

그리고 이걸 못해서 거의 3일내내 온갖 개고생을 했는데 원인은 위에 쓴것처럼 스크립트를 두개로 나누어 

관리하지 않았던것...

 

사실 너무 기본적인 내용이라 어떻게 오류를 알아냈나 설명할것도 없다

충돌판정을 해야하는데 ShowBuyUseZone라는 빈 오브젝트에서 계속 의미없는 충돌판정을 한것

 

왜 충돌판정이 안되는건가 싶어 실험용으로 만들어놓은 오브젝트들...이제 넘어가도록 하자

 

private void Update()
    {
        // 현재 드래그 중인 오브젝트 찾기
        var dragItems = FindObjectsOfType<DragItem>();
        var draggingItem = dragItems.FirstOrDefault(item => item.isDrag);
        
        if (draggingItem != null && Itemcost != null)
        {
            // 드래그 중인 오브젝트의 태그가 유효한지 확인
            if (validTags.Contains(draggingItem.tag))
            {
                // JokerStat 컴포넌트 가져오기
                var jokerStat = draggingItem.GetComponent<JokerStat>();
                if (jokerStat != null)
                {
                    // 가격 표시 (예: "$100")
                    Itemcost.text = "($" + jokerStat.price.ToString() + ")";
                    return;
                }
            }
        }

 

그리고 Itemcost라는 텍스트 오브젝트를 public으로 추가한뒤 드래그할때마다 해당 아이템의 가격을 

실시간으로 보여주도록 해주었다

 

이런식으로. 3원짜리 아이템을 선택하면 구매 ($3) 7원짜리는 ($7) 이라고 뜨게 된다

 

그리고 하나 더. 위에 떡하니 가격을 알려주는데 굳이 드래그할때 가격태그가 한번 더 붙어 있어야하나 싶다

 

 

 

그래서 가만히 있을땐 가격태그 활성화

드래그할땐 가격태그를 비활성화 해주었다. 코드는 너무 간단하니 생략

 

이제 진짜 핵심이자 구매처리의 완성. 

구매존에서 드래그를 떼는 순간 구매가능한 돈이 있는지 확인하고 돈이 빠지면서

조커존에 자동 배치가 되어야한다.... 한번 해보자

 

우선 구매가 이루어지는 과정을 적어보고 시작하자

 

1. 구매존에 드래그

2. 드래그 뗌

3. 현재돈 - 아이템 가격 >= 0 인지 확인

4. 맞다면 구매확정, 아니라면 구매실패

5. 구매확정시 조커존에 자동배치, 실패시 원래자리로 복귀

 

  private void OnMouseUp()
    {
        if (isDragging)
        {
            // 구매 영역 안에서 드롭한 경우
            if (isInBuyZone)
            {
                GameManager gameManager = FindObjectOfType<GameManager>();
                // 현재 드래그 중인 오브젝트의 JokerStat 컴포넌트 가져오기
                JokerStat jokerStat = GetComponent<JokerStat>();
                
                if (gameManager != null)
                {
                    if (jokerStat != null)
                    {
                        // 현재 오브젝트의 JokerStat에서 가격 가져오기
                        Debug.Log($"보유 금액: ${gameManager.money}\n아이템 가격: ${jokerStat.price}");
                    }
                    else
                    {
                        Debug.Log("JokerStat 컴포넌트를 찾을 수 없습니다!");
                    }
                }
                else
                {
                    Debug.Log("GameManager를 찾을 수 없습니다!");
                }
            }

 

우선 구매존에서 드래그를 떼는 순간 현재 가지고 있는 돈을 알 수 있는 GameManager와

아이템의 가격을 알 수 있는 JokerStat을 가져와 클릭을 떼는 순간 보유금액과 아이템 가격을 로그에 출력하도록 하였다

 

 

잘 뜨는 것이 보인다. 이제 이 두가지를 알았으니 

 

3. 현재돈 - 아이템 가격 >= 0 인지 확인

4. 맞다면 구매확정, 아니라면 구매실패

 

3,4번을 해줄 차례

 

 if (gameManager.money - jokerStat.price >= 0)
                        {
                            Debug.Log($"구매확정!\n보유 금액: ${gameManager.money}\n아이템 가격: ${jokerStat.price}");
                        }
                        else
                        {
                            Debug.Log($"구매실패!\n보유 금액: ${gameManager.money}\n아이템 가격: ${jokerStat.price}");
                        }

 

아주 간단한 if 로직

 

 

보유금액보다 아이템 가격이 같거나 작으면 확정, 더 크면 실패로 이어진다

 

 

5. 구매확정시 조커존에 자동배치, 실패시 원래자리로 복귀

 

 

여기서부터가 가장 중요한 부분이다

이걸 하기전에 우선 실제 게임처럼 아이템을 구매할 시 현재 보유중인 돈이 아이템 가격만큼 깎이게 해보겠다

 

    public void BuyItem(int price)
    {
        money -= price;
        UpdateUI();
    }

    public void SellItem(int price)
    {
        money += price;
        UpdateUI();
    }

    public void UpdateUI()
    {
        handCountText.text = handcount.ToString();
        trashCountText.text = trashcount.ToString();
        moneyText.text = "$" + money.ToString("N0");
        AnteText.text = ante + "/8";
        RoundText.text = round.ToString();
        GoalPointText.text = GoalPoint.ToString("N0");
    }

 

우선 현재 보유중인 돈을 관리하는 GameManager에 BuyItem과 SellItem 메서드를 추가해줬다

당연히 더하거나 빠지는 순간 변경된 돈값이 반영돼야 하기에 UpdateUI도 추가해주었고

 

                        if (gameManager.money - jokerStat.price >= 0)
                        {
                            gameManager.BuyItem(jokerStat.price);
                            Debug.Log($"구매확정!\n보유 금액: ${gameManager.money}\n아이템 가격: ${jokerStat.price}");
                        }
                        else
                        {
                            Debug.Log($"구매실패!\n보유 금액: ${gameManager.money}\n아이템 가격: ${jokerStat.price}");
                        }

 

그리고 아이템을 구매했는데 구매가 확정이라면 BuyItem 메서드에 아이템 가격을 매개변수로 넣어주었다

 

 

 

이제 구매확정이 될때마다 현재 보유중인 돈이 아이템 가격만큼 줄어들고

만약 아이템 가격이 더 크다면 구매실패가 뜨는것을 확인할 수 있다

 

이제 진짜 구매된 아이템 존으로 이동하는것까지 구현해보자

 

 if (gameManager.money - jokerStat.price >= 0)
                        {
                            gameManager.BuyItem(jokerStat.price);
                            Debug.Log($"구매확정!\n보유 금액: ${gameManager.money}\n아이템 가격: ${jokerStat.price}");
                            
                            // 구매 성공 시 JokerZone으로 이동
                            GameObject jokerZone = GameObject.FindGameObjectWithTag("JokerZone");
                            if (jokerZone != null)
                            {
                                // 드래그 중지 및 위치 고정
                                isDragging = false;
                                isDrag = false;
                                
                                // JokerZone의 자식으로 설정
                                transform.SetParent(jokerZone.transform);
                                
                                // BoxCollider2D 가져오기
                                var boxCollider = jokerZone.GetComponent<BoxCollider2D>();
                                if (boxCollider != null)
                                {
                                    // 태그를 "BuyJoker"로 변경 (먼저 태그를 변경해야 함)
                                    gameObject.tag = "BuyJoker";
                                    
                                    // JokerZone 내의 모든 BuyJoker 태그를 가진 오브젝트 찾기 (자식 오브젝트 중에서)
                                    var buyJokers = new List<GameObject>();
                                    foreach (Transform child in jokerZone.transform)
                                    {
                                        if (child.CompareTag("BuyJoker"))
                                        {
                                            buyJokers.Add(child.gameObject);
                                        }
                                    }
                                    
                                    int jokerCount = buyJokers.Count;
                                    
                                    // BoxCollider2D의 경계 가져오기
                                    Bounds bounds = boxCollider.bounds;
                                    float width = bounds.size.x * 0.8f; // 80%만 사용하도록 조정 (경계선 여유 공간 확보)
                                    float startX = bounds.center.x - (width / 2); // 중앙을 기준으로 좌우로 퍼지도록
                                    
                                    // 아이템이 1개일 때는 가운데, 2개 이상일 때는 균등 간격으로 배치
                                    if (jokerCount == 1)
                                    {
                                        // 1개: 가운데
                                        transform.position = new Vector3(bounds.center.x, bounds.center.y, transform.position.z);
                                    }
                                    else if (jokerCount > 1)
                                    {
                                        // 2개 이상: 균등 간격으로 배치
                                        float spacing = width / (jokerCount - 1);
                                        
                                        // 모든 BuyJoker 오브젝트 재배치
                                        for (int i = 0; i < jokerCount; i++)
                                        {
                                            float xPos = startX + (spacing * i);
                                            buyJokers[i].transform.position = new Vector3(
                                                xPos, 
                                                bounds.center.y, 
                                                buyJokers[i].transform.position.z);
                                        }
                                    }
                                }
                                
                                // 드래그 비활성화
                                enabled = false;
                                return; 
                            }

 

우선 아이템 구매 확정시 조커존의 너비, 즉 box collider2D의 너비를 구한다

 

그리고 콜라이더의 너비를 파악하여 5등분을 한뒤

구매한 오브젝트가

1개면 ( - - O - - )

2개면 ( - O - O - )

3개면 ( O - O - O )

4개면 ( O O O O )

5개면 ( O O O O O )

 

이런식으로 배치가 되게 하며 구매가 된 아이템은 구매가 안된 아이템과 구별을 위해

태그를 "BuyJoker"로 변경해주었다. 

많은 부분의 기능이 태그로 감별하여 이루어지므로 태그만 바꿔줘도 구매한 조커처리가 될것이다

 

 

 

구현성공! 

 

1개면 ( - - O - - )

2개면 ( - O - O - )

3개면 ( O - O - O )

4개면 ( O O O O )

5개면 ( O O O O O )

 

처럼 잘 배치가 되는것을 확인할 수 있다

지금은 시스템 구현이라 그렇게 이뻐보이진 않지만 보기좋게 다듬고 더 디테일한 수정은 다음글에서 해보도록 하겠다

반응형