본문 바로가기
유니티

[유니티] #5 수박 게임 만들기 (업그레이드)

by 인디제이 2025. 3. 13.

 

업그레이드 버튼

 

게임에서 특정 기능을 업그레이드할 때, 버튼을 클릭하여 업그레이드를 구매하는 시스템이 필요하다.

 

using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class UpgradeButton : MonoBehaviour
{
    [Header("References")]
    public Button button;
    [SerializeField] private GameObject limitPanel;
    [SerializeField] private GameObject purchasedPanel;
    [SerializeField] private LevelManager _levelManager;
    [SerializeField] private UpgradeManager _upgradeManager;

    [Header("Text")]
    [SerializeField] private TextMeshProUGUI warningText;

    [Header("Settings")]
    public int cost; // 업그레이드 비용
    public int limitLevel; // 업그레이드를 구매할 수 있는 최소 레벨

    [HideInInspector]
    public bool purchased = false; // 업그레이드 구매 여부

    private void Start()
    {
        warningText.gameObject.SetActive(false);
    }

    // 업그레이드 버튼 클릭 시 호출되는 함수
    public void TryPurchaseUpgrade()
    {
        SaveManager _saveManager = SaveManager.Instance;
        _upgradeManager?.ButtonSound();

        if (_levelManager.CurrentLevel < limitLevel)
        {
            ShowWarningText("레벨이");
            return;
        }

        if (_saveManager.CurrentGold < cost)
        {
            ShowWarningText("골드가");
            return;
        }

        // 업그레이드 성공
        CompleteUpgrade();
    }

    // 업그레이드를 완료하는 함수
    private void CompleteUpgrade()
    {
        purchased = true;
        button.interactable = false;     // 버튼 비활성화
        transform.SetAsLastSibling();    // 버튼을 content의 맨 아래로 이동
        purchasedPanel?.SetActive(true); // 구매 완료 패널 활성화
    }

    // 경고 메시지를 화면에 표시하는 함수
    private void ShowWarningText(string text)
    {
        if (warningText == null) return;
        warningText.text = $"{text} 부족합니다.";
        warningText.gameObject.SetActive(true);
        warningText.alpha = 1f;
        warningText.transform.localPosition = Vector3.zero;

        // 기존 애니메이션 중단
        warningText.DOKill();

        // 텍스트를 위로 이동시키면서 서서히 사라지게 함
        warningText.transform.DOLocalMoveY(30, 2f);
        warningText.DOFade(0, 2f).OnComplete(() => warningText.gameObject.SetActive(false));
    }

    // DOTween 애니메이션을 정리하는 함수
    private void OnDestroy()
    {
        DOTween.KillAll();
    }
}

 

업그레이드 버튼 클릭 이벤트 (TryPurchaseUpgrade 함수)

 

이 함수는 업그레이드 버튼을 클릭했을 때 호출된다.

  1.  SaveManager의 인스턴스를 가져와서 캐싱 (코드의 일관성과 미비한 성능 최적화)
  2. UpgradeManager의 ButtonSound() 함수를 호출하여 버튼 클릭 소리 재생
  3. 플레이어 레벨이 limitLevel보다 낮으면 ShowWarningText("레벨이")를 호출하여 경고 메세지 표시하고 종료
  4. 플레이어 골드가 cost보다 적으면 ShowWarningText("골드가")를 호출하여 경고 메세지 표시하고 종료
  5. 위의 조건을 통과하면 CompleteUpgrade()를 호출하여 업그레이드 완료

 

 

업그레이드 완료 처리 (CompleteUpgrade 함수)

 

이 함수는 업그레이드가 성공적으로 이루어졌을 때 실행된다.

 

 

  • purchased 변수를 true로 변경하여 구매 완료 상태 저장 (추후 업그레이드 정보 세이브)
  • 버튼을 비활성화(button.interactable = false)하여 더 이상 클릭할 수 없도록 변경
  • transform.SetAsLastSibling()을 호출하여 버튼을 UI의 맨 아래로 이동
  • purchasePanel을 활성화하여 "구매 완료" 상태 표시

 

 

경고 메세지 표시 (ShowWarningText 함수)

 

이 함수는 플레이어가 골드나 레벨이 부족할 때 경고 메세지를 띄우는 역할을 한다.

 

  1. warningText의 텍스트를 "[무엇]이 부족합니다." 형태로 설정
  2. warningText를 활성화하고, 위치 초기화
  3. DoKill()을 호출하여 기존 애니메이션이 있으면 중단
  4. DoLocalMoveY(30, 2f)를 호출하여 텍스트를 2초 동안 30px 위로 이동
  5. DoFade(0, 2f)를 호출하여 2초 동안 텍스트를 서서히 사라지는 효과
  6. OnComplete를 통해 애니메이션 종료 후 warningText 비활성화

 

일부 게임에서 버튼을 여러 번 클릭했을 때처럼 텍스트가 계속 다단식으로 쌓이지는 않는다.

그 기능은 아마 오브젝트 풀링을 통해 UI 메세지를 미리 생성해둔게 아닌가 싶다.

 

 

DoTween 정리 (OnDestory 함수)

 

이 함수는 게임 오브젝트가 삭제될 때 DoTween의 모든 애니메이션을 정리한다.

 

DoTween 애니메이션이 정리되지 않으면 버튼이 사라졌는데도 애니메이션이 계속 실행되거나,

새로운 씬으로 이동했는데 이전 씬에서 실행되던 애니메이션이 남아있는 등 버그가 발생한다.

 

Tools → Demigiant → DoTween Utility Panel → Preferences에 Safe Mode가 체크 되어있다면

객체가 파괴됐을 때도 경고 메세지만 나올 뿐 별 문제가 발생하지는 않지만,

DoTween 함수를 썼을 경우 꼭 DoTween.KillAll() 함수를 호출해주고 있다.

 

 

 

업그레이드 구현

 

업그레이드를 숫자가 아닌 타입으로 관리하기 위해 enum을 사용했다.

 

public enum UpgradeType
{
    LUCKY_GOLD,
    SHAKE_BOX,
    EASY_REQUEST,
    FRUIT_PREVIEW,
    GUIDE_LINE,
    BLUEBERRY_REMOVE
}

public class UpgradeButton : MonoBehaviour
{
    public UpgradeType upgradeType; // 업그레이드 유형
    public int cost; // 업그레이드 비용
    
    public void TryPurchaseUpgrade()
    {
        SaveManager _saveManager = SaveManager.Instance;
        
        ...
        
        // 업그레이드 정보 저장
        _saveManager.SaveUpgradePurchased(upgradeType, true);
    }
}

 

Enum형

 

Enum을 사용하면 UpgradeType.LUCKY_GOLD 처럼 직관적인 코드 작성이 가능하다.

 

클래스 밖에 enum을 선언하면, 해당 enum을 전역적으로 접근할 수 있다.

enum이 여러 클래스에서 공통으로 사용될 때 좋은 방법이다.

 

클래스 안에 enum을 선언하면, 클래스 내부에서만 접근할 수 있다.

외부에서 사용하려면 UpgradeButton.UpgradeType.LUCKY_GOLD 처럼 사용해야 한다.

 

 

업그레이드 버튼

 

업그레이드 버튼마다 타입(upgradeType)을 인스펙터에서 설정했다.

TryPurchaseUpgrade() 가 호출되면 업그레이드를 구매하고, SaveManager에 저장한다.

 

 

public class SaveManager : SingleTon<SaveManager>
{
    private bool[] upgradePurchased = new bool[Enum.GetValues(typeof(UpgradeType)).Length];
    
    public bool[] UpgradePurchased
    {
        get => upgradePurchased;
        private set
        {
            upgradePurchased = value;
        }
    }

    // 업그레이드 구매 여부 저장
    public void SaveUpgradePurchased(UpgradeType type, bool purchased)
    {
        int index = (int)type; // enum을 int로 변환하여 배열 인덱스로 사용
        upgradePurchased[index] = purchased;
        Debug.Log($"[SaveManager] {type} 업그레이드 구매 완료");

        SaveData(); // 데이터 저장
    }
}

 

SaveManager
  • upgradePurchased[]: 업그레이드 구매 여부를 저장하는 배열
  • UpgradePurchased 프로퍼티: 업그레이드 상태를 읽을 수 있지만, 외부에서 직접 수정하지 못하도록 보호
  • SaveUpgradePurchased(): 구매한 업그레이드 저장

 

Enum.GetValues()

 

enum은 숫자 배열이 아니기 때문에 UpgradeType.Length를 직접 가져올 수 없다.

Enum.GetValues(typeof(UpgradeType)).Length를 사용하면 자동으로 전체 업그레이드 개수를 가져 올 수 있다.

추후 업그레이드를 추가할 때, 숫자를 직접 변경하지 않아도 자동으로 크기가 조절된다.

 

 

public class NPCRequest : MonoBehaviour
{
    [Header("Settings")]
    public int requestAmount; // 요청 개수
    private bool isEasyRequestUnlocked = false; // 업그레이드 여부

    private void Start()
    {
        if (SaveManager.Instance.UpgradePurchased[(int)UpgradeType.EASY_REQUEST])
        {
            isEasyRequestUnlocked = true;
        }
    }

    public void SetupRequest()
    {
        // 업그레이드 했으면 1, 아니면 1 ~ 2
        requestAmount = isEasyRequestUnlocked ? 1 : Random.Range(1, 3); 
    }
}

 

업그레이드 적용 방식
  1.  Start() 에서 SaveManager를 통해 업그레이드 구매 여부 확인
  2. 업그레이드가 적용되었다면 isEasyRequestUnlocked = true 설정
  3. SetupRequest() 에서 삼항 연산자를 사용하여 주문 개수를 조정

 

삼항 연산자를 사용하면 if-else 문을 쓰지 않고 간단하게 조건에 따라 값을 설정할 수 있다.

기존 코드에 추가가 간편하다.