언어/C#

[C#] 유니티 리듬게임 만들기(상점)

떼이로 2022. 6. 4. 20:11

- 유튜브 '케이디' 님의 리듬게임 영상을 공부하면서 진행하였습니다.

https://www.youtube.com/watch?v=u3vUdu57-jY&list=PLUZ5gNInsv_MCnum4bOQRI72LdGkIY3tY&ab_channel=%EC%BC%80%EC%9D%B4%EB%94%94 

 

영상 모두 진행한 후 Chapter 19 이후부터 추가적인 요소를 혼자 힘으로 만들어 보는 중

 

▶상점 구현하기

간단한 뽑기 상점겸 인벤토리 역활을 하는 STORE 메뉴 구현

◆ 필수 기능

1. 뽑기 버튼을 눌렀을 때 자신의 코인이 N만큼 차감

2. 뽑기 버튼을 눌렀을 때 랜덤하게 스킨 중 하나 획득

3. 자신이 뽑은 스킨은 보유 중 표시

4. 보유 중인 스킨을 누르면 해당 스킨으로 변경

5. Back버튼 눌렀을 때 메인화면으로 이동

6. 기본 스킨은 처음 부터 사용가능

 

 

 

 

 

 

 

 

 

 

 

 

▷StoreMenu 구현

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class StoreMenu : MonoBehaviour
{
    [SerializeField] Text txtCoin = null;
    [SerializeField] GameObject goMainUI = null;
    [SerializeField] GameObject goWarning = null;
    [SerializeField] GameObject goArm = null;
    [SerializeField] GameObject showResult = null;
    [SerializeField] GameObject[] slot = null;
    [SerializeField] Animator warnningAnimator = null;
    [SerializeField] Animator armAnimator = null;
    [SerializeField] Animator resultAnimator = null;
    [SerializeField] Image skinResult = null;
    [SerializeField] Text changeSkin = null;


    string warn = "Warn";
    int r;

    PlayerController thePlayer;
    DatabaseManager theDatabase;
  
    
    void OnEnable()
    {
    	//AudioManager에서 Store BGM라면 Loop = true
        AudioManager.instance.StopBGM();
        AudioManager.instance.PlayBGM("Store");
        if (theDatabase == null)
        {
            theDatabase = FindObjectOfType<DatabaseManager>();
            thePlayer = FindObjectOfType<PlayerController>();
        }
		//DB에서 받아온 자신의 코인
        txtCoin.text = string.Format("{0:#,##0}", theDatabase.coin);
        SetInvent();
    }
    
	//인벤토리 세팅
    public void SetInvent()
    {
        for (int i = 0; i < theDatabase.invent.Length; i++)
        {
            if (theDatabase.invent[i] == true) 
                slot[i].SetActive(true);
        }
    }
	
    //Skin 버튼 클릭 이벤트
    public void BtnSkin0()
    {
        if (theDatabase.invent[0])
        {
            thePlayer.SetMat(0);
            changeSkin.text = "스킨(빨강)이 변경되었습니다.";
            AudioManager.instance.PlaySFX("Change");
            goArm.SetActive(true);
            armEffect();
        }
    }
    public void BtnSkin1()
    {
        if (theDatabase.invent[1])
        {
            thePlayer.SetMat(1);
            changeSkin.text = "스킨(노랑)이 변경되었습니다.";
            AudioManager.instance.PlaySFX("Change");
            goArm.SetActive(true);
            armEffect();
        }
    }
    public void BtnSkin2()
    {
        if (theDatabase.invent[2])
        {
            thePlayer.SetMat(2);
            changeSkin.text = "스킨(초록)이 변경되었습니다.";
            AudioManager.instance.PlaySFX("Change");
            goArm.SetActive(true);
            armEffect();
        }
    }
    public void BtnSkin3()
    {
        if (theDatabase.invent[3])
        {
            thePlayer.SetMat(3);
            changeSkin.text = "스킨(파랑)이 변경되었습니다.";
            AudioManager.instance.PlaySFX("Change");
            goArm.SetActive(true);
            armEffect();
        }
    }
    public void BtnSkin4()
    {
        if (theDatabase.invent[4])
        {
            thePlayer.SetMat(4);
            changeSkin.text = "스킨(그레이)이 변경되었습니다.";
            AudioManager.instance.PlaySFX("Change");
            goArm.SetActive(true);
            armEffect();
        }
    }
    public void BtnSkin5()
    {
        if (theDatabase.invent[5])
        {
            thePlayer.SetMat(5);
            changeSkin.text = "스킨(검정)이 변경되었습니다.";
            AudioManager.instance.PlaySFX("Change");
            goArm.SetActive(true);
            armEffect();
        }
    }

    public void BtnSkin6()
    {
            thePlayer.SetMat(6);
            changeSkin.text = "기본스킨으로 변경되었습니다.";
            AudioManager.instance.PlaySFX("Change");
            goArm.SetActive(true);
            armEffect();
    }
	
    //Back버튼 이벤트
    public void BtnMain()
    {
        goMainUI.SetActive(true);
        this.gameObject.SetActive(false);
        AudioManager.instance.StopBGM();
    }
    
	//상점 이팩트
    public void WarnnigEffect()
    {
        warnningAnimator.SetTrigger(warn);
    }
    public void armEffect()
    {
        armAnimator.SetTrigger("Arm");
    }

    public void resultEffect()
    {
        resultAnimator.SetTrigger("Show");
    }
    
    //뽑기버튼 이벤트
    public void BtnBuy()
    {
        if (theDatabase.coin >= 1) {
            AudioManager.instance.PlaySFX("Buy");
             r = Random.Range(0, 6);
            theDatabase.coin -= 1;
            theDatabase.SaveCoin();
            txtCoin.text = string.Format("{0:#,##0}", theDatabase.coin);
            theDatabase.invent[r] = true;
            theDatabase.SaveInvent();
            SetInvent();
            showResult.SetActive(true);
            ShowResult();
        }
        else
        {
            AudioManager.instance.PlaySFX("Err");
            goWarning.SetActive(true);
            WarnnigEffect();
        }
    }
    
    //뽑기 결과 
    public void ShowResult()
    {
        resultEffect();
        switch(r){
            case 0: 
                skinResult.color = Color.red; 
                break;
            case 1:
                skinResult.color = Color.yellow;
                break;
            case 2:
                skinResult.color = Color.green;
                break;
            case 3:
                skinResult.color = Color.blue;
                break;
            case 4:
                skinResult.color = Color.gray;
                break;
            case 5:
            //검은색일경우 화면에 안보여서 보일만큼 어둠게 조절
                skinResult.color = new Color(0.3f, 0.3f, 0.3f); 
                break;
            default : break;

        }
    }
	//다이얼로그 창 X버튼 이벤트
    public void BtnX()
    {
        goWarning.SetActive(false);
        goArm.SetActive(false);
        showResult.SetActive(false);
    }
}

 ▷필요 요소(PlayerController)

public class PlayerController : MonoBehaviour
{
	public Color basic;
    public Color[] currentSkin = { Color.red, Color.yellow, Color.green, Color.blue, Color.gray, Color.black};

    //setMat
    public void SetMat(int c_num)
    {
        if (c_num == 6)
            realCube.GetComponent<Renderer>().material.color = basic;
        else
            realCube.GetComponent<Renderer>().material.color = currentSkin[c_num];
        theDatabase.skin = c_num;
        theDatabase.SaveSkin();
    }

Playcontroller내에 따로 스킨배열을 생성하여 선택만 해주었음. (배열을 StoreManu Class로 옮겨서 사용해도 상관없을 듯)

◐기본 스킨 설정시 에러발생

처음 기본스킨을 핑크로 해놔서 다시 선택할 때,

public Color basic = new Color(//대충 핑크색 RGB) 로 받았더니 게임 시작시 공이 새 하얗게 변함.

 

이것도 무슨 레어스킨같긴 한데.. 암튼 목적과 달라서 해결 방법을 찾아봄 무슨 HSV값도해보고 RGB에서 /255f 방법도 써봤는데 

그대로임... 

 

◑ 해결방법

그렇게 찾아보던 중.. 컬러코드를 이용해서 스크립트에 색상을 받아오는 방법을 발견 위 코드처럼

Color basic 선언해놓고 void start() 내에

ColorUtility.TryParseHtmlString("#F480FF", out basic);

 

이렇게 작성해주면 색상코드를 맞는 형태로 basic에 넣어 줌.

특정 색상값 받을때는 이게더 쉬우면서 확실한것 같네  

 

 

 

 

 

 

 

 ▷필요 요소(DataBase)

public class DatabaseManager : MonoBehaviour
{
    public int coin = 100; //실험용으로 100 넣어 둠
    public int skin;
    public bool[] invent;
    
      void Start()
    {
        LoadCoin();
        LoadSkin();
        LoadInvent();
    }
    
    //적용된 스킨 저장
     public void SaveSkin()
    {
        PlayerPrefs.SetInt("Skin", skin);
    }
    
	//인벤토리에 들어있는 데이터 저장
    public void SaveInvent()
    {
        for (int i = 0; i < invent.Length; i++)
        {
            PlayerPrefs.SetInt("HasSkin" + i, System.Convert.ToInt32(invent[i]));
        }
    }
    
	//인벤토리 불러오기
    public void LoadInvent()
    {
        if (PlayerPrefs.HasKey("HasSkin0"))
        {
            for (int i = 0; i < invent.Length; i++)
            {
                invent[i] = System.Convert.ToBoolean(PlayerPrefs.GetInt("HasSkin" + i));
            }
        }
    }
    
    //스킨 불러오기
    public void LoadSkin()
    {
        if (PlayerPrefs.HasKey("Skin"))
        {
            skin = PlayerPrefs.GetInt("Skin");
            
        }
    }
    
    //코인저장
    public void SaveCoin()
    {
        PlayerPrefs.SetInt("Coin", coin);
    }
    
    //코인불러오기
    public void LoadCoin()
    {
        if (PlayerPrefs.HasKey("Coin"))
        {
            coin = PlayerPrefs.GetInt("Coin");
        }
    }

유니티랑 C#이 처음 써보는거라 그냥  Coin이나 Skin은 Score저장 방법이랑 비슷하게 진행함 

◆ 막힌부분

문제는 인벤토리였음 bool형의 스킨배열을 만들고 획득하면 그 스킨에 맞는 인덱스를 true로 바꿔주기로 했는데

PlayerPrefs.GetBool <이런거 없어서.. 한참 고민하다가  bool형을 int로 받을 수있게만 해주면 될 것 같아

ToBoolean을 사용해 봄 (효과는 굉장하였다) 아무 문제없이 생각한데로 동작 함

 

▷필요 요소(Result)

   public class Result : MonoBehaviour
{
   public void ShowResult()
    {
		int t_coin = (t_currentScore / 50);
        theDatabase.coin += t_coin;
        theDatabase.SaveCoin();

코인은 결과창에서 점수 나누기 50한만큼 Database에 누적해서 Save해줬음

 

간단하지만 처음 유니티랑 C#을 건드려본 나에게는 너무 힘들었다... 다음에는 장애물을 만들어 볼 예정