본문 바로가기
공부/유니티

유니티3D 공부(Part5) 가위바위보 게임 만들기

by 라이티아 2024. 10. 3.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Janken : MonoBehaviour
{
    public enum voice{
        start,
        pon,
        goo,
        choki,
        par,
        win,
        loose,
        draw
    }

    const int JANKEN = 0;
    const int GOO = 1;
    const int CHOKI = 2;
    const int PAR = 3;
    const int DRAW = 4;
    const int WIN = 5;
    const int LOOSE = 6;

    private bool _flgJanken;
    private int _modeJanken;

    public AudioClip [] voice_janken = new AudioClip[8];

    private Animator _animator;
    private AudioSource _audioSource;

    private int _myHand;
    private int _unityHand;
    private int _flagResult;

    private float _waitTime;

    public GUIStyle [] btu = new GUIStyle[4]; // 1 바위 2 찌 3 빠

    void Start()
    {
        _animator = GetComponent<Animator>();
        _audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        _animator.SetBool("Win", false);
        _animator.SetBool("Loose", false);
        if (_flgJanken == true)
        {
            switch (_modeJanken)
            {
                case 0: // 시작
                    UnityChanAction(JANKEN);
                    _modeJanken++;
                    break;
                case 1: // 플레이어 입력 대기
                    break;
                case 2: // 판정
                    _flagResult = -1;
                    _unityHand = Random.Range(GOO, PAR+1);
                    UnityChanAction(_unityHand);

                    if (_myHand == _unityHand)
                    {
                        _flagResult = DRAW;
                    }
                    else
                    {
                        switch (_unityHand)
                        {
                            case GOO:
                                if (_myHand == PAR) _flagResult = LOOSE;
                                break;
                            case CHOKI:
                                if (_myHand == GOO) _flagResult = LOOSE;
                                break;
                            case PAR:
                                if (_myHand == CHOKI) _flagResult = LOOSE;
                                break;
                        }
                        if (_flagResult != LOOSE) _flagResult = WIN;
                    }
                    _modeJanken++;
                    break;
                case 3: // 결과
                    _waitTime += Time.deltaTime;

                    if (_waitTime > 1.5)
                    {
                        UnityChanAction(_flagResult);
                        _waitTime = 0;
                        _modeJanken++;
                    }
                    break;
                case 4:
                    _flgJanken = false;
                    _modeJanken = 0;
                    break;
            }
        }
    }
    void OnGUI()
    {
        if (_flgJanken == false)
        {
            if (GUI.Button(new Rect(10, Screen.height - 110, 100, 100), "가위바위보", btu[3]))
            {
                _flgJanken = true;
            }
        }
        if (_modeJanken == 1)
        {
            if(GUI.Button (new Rect(Screen.width / 2 - 120, 400, 100, 100), "바위", btu[0]))
            {
                _myHand = GOO;
                _modeJanken++;
            }
            if(GUI.Button (new Rect(Screen.width / 2, 400, 100, 100), "가위", btu[1]))
            {
                _myHand = CHOKI;
                _modeJanken++;
            }
            if(GUI.Button (new Rect(Screen.width / 2 + 120, 400, 100, 100), "보", btu[2]))
            {
                _myHand = PAR;
                _modeJanken++;
            }
        }
    }
    void UnityChanAction(int action)
    {
        switch (action)
        {
            case JANKEN:
                //animator.SetBool("Janken", true);
                _audioSource.clip = voice_janken[(int)voice.start];
                break;
            case GOO:
                //_animator.SetBool("Goo", true);
                _audioSource.clip = voice_janken[(int)voice.goo];
                break;
            case CHOKI:
                //_animator.SetBool("CHOKI", true);
                _audioSource.clip = voice_janken[(int)voice.choki];
                break;
            case PAR:
                //_animator.SetBool("Par", true);
                _audioSource.clip = voice_janken[(int)voice.par];
                break;
            case DRAW:
                //_animator.SetBool("Aiko", true);
                _audioSource.clip = voice_janken[(int)voice.draw];
                break;
            case WIN:
                _animator.SetBool("Win", true);
                _audioSource.clip = voice_janken[(int)voice.win];
                break;
            case LOOSE:
                _animator.SetBool("Loose", true);
                _audioSource.clip = voice_janken[(int)voice.loose];
                break;
        }
        _audioSource.Play();
    }
}

이번 글은 완성된 프로젝트를 보면서 설명하는 글이 되었다

 

 

최종 완성본이다

 

천천히 코드를 설명하겠다

 

    public enum voice{
        start,
        pon,
        goo,
        choki,
        par,
        win,
        loose,
        draw
    }

    public AudioClip [] voice_janken = new AudioClip[8];

    const int JANKEN = 0;
    const int GOO = 1;
    const int CHOKI = 2;
    const int PAR = 3;
    const int DRAW = 4;
    const int WIN = 5;
    const int LOOSE = 6;

    private bool _flgJanken;
    private int _modeJanken;

    private Animator _animator;
    private AudioSource _audioSource;

    private int _myHand;
    private int _unityHand;
    private int _flagResult;

    private float _waitTime;

    public GUIStyle [] btu = new GUIStyle[4]; // 1 바위 2 찌 3 빠

변수들을 관리하는 시작줄들이다

public enum voice

첫 열거형 voice는 보이스를 재생할때 사용된다 만약 이를 사용하지 않을시 AudioClip을 하나하나 손수 넣는 대참사가 날 수 있으니, 사용법을 알아두면 좋다

public AudioClip [] voice_janken = new AudioClip[8];

유니티짱의 보이스를 담아두는 audioclip배열이다

이를 나중에 voice_janken[(int)voice.원하는보이스 이름]으로 사용하게 된다

    const int JANKEN = 0;
    const int GOO = 1;
    const int CHOKI = 2;
    const int PAR = 3;
    const int DRAW = 4;
    const int WIN = 5;
    const int LOOSE = 6;

각각 유니티짱의 보이스 재생등에 상태를 확인하는 용도의 값들이다

위에서 설명한 열거형을 사용하지 않을시 끔찍한 가독성을 보여준다

이후 Swich문에서 사용된다

 

    private bool _flgJanken;
    private int _modeJanken;

첫 flag는 게임 시작을 확인하는 용도의 flag이다

그 다음 mode는 게임의 진행 단계를 나타낸다

    private int _myHand;
    private int _unityHand;
    private int _flagResult;

    private float _waitTime;

hand 변수 2개의 경우 유니티짱과 플레이어의 조작을 확인하는 용도의 value이다

flagresult는 두 hand변수를 비교하여 승리, 패배, 비김을 확인하는 용도이다

 

waittime의경우 다음 게임을 진행하기 위한 대기 타임이다
    
public GUIStyle [] btu = new GUIStyle[4]; // 1 바위 2 찌 3 빠

마지막 GUI의 경우 버튼의 이미지를 담기 위한 배열이다

    void Update()
    {
        _animator.SetBool("Win", false);
        _animator.SetBool("Loose", false);
        if (_flgJanken == true)
        {
            switch (_modeJanken)
            {
                case 0: // 시작
                    UnityChanAction(JANKEN);
                    _modeJanken++;
                    break;
                case 1: // 플레이어 입력 대기
                    break;
                case 2: // 판정
                    _flagResult = -1;
                    _unityHand = Random.Range(GOO, PAR+1);
                    UnityChanAction(_unityHand);

                    if (_myHand == _unityHand)
                    {
                        _flagResult = DRAW;
                    }
                    else
                    {
                        switch (_unityHand)
                        {
                            case GOO:
                                if (_myHand == PAR) _flagResult = LOOSE;
                                break;
                            case CHOKI:
                                if (_myHand == GOO) _flagResult = LOOSE;
                                break;
                            case PAR:
                                if (_myHand == CHOKI) _flagResult = LOOSE;
                                break;
                        }
                        if (_flagResult != LOOSE) _flagResult = WIN;
                    }
                    _modeJanken++;
                    break;
                case 3: // 결과
                    _waitTime += Time.deltaTime;

                    if (_waitTime > 1.5)
                    {
                        UnityChanAction(_flagResult);
                        _waitTime = 0;
                        _modeJanken++;
                    }
                    break;
                case 4:
                    _flgJanken = false;
                    _modeJanken = 0;
                    break;
            }
        }
    }

대망의 update 부분이다

 

시작부터 애니메이터의 값을 초기화 하고 있는데

 

 

기본적으로 이런 형태를 하고 있다

 

즉, win과 lose가 false일시 wait애니메이션 상태를 유지하는 것이다

 

그후, if문에서 _flgJanken이 true가 되면 게임을 시작한다

이때 이 bool값을 true로 만들어 주는 버튼은 

void OnGUI()

함수에 존재하는 OnGUI이다

OnGUI는 Update와 같이 매 프레임 자동 갱신되는 함수이며, 말 그대로 GUI를 표현하기 위한 메서드이다

 

이때 첫 if문이

        if (_flgJanken == false)
        {
            if (GUI.Button(new Rect(10, Screen.height - 110, 100, 100), "가위바위보", btu[3]))
            {
                _flgJanken = true;
            }
        }

인데 즉, 게임이 시작중이 아니라면 시작버튼을 구현하고 있는 것이다.

이렇게 구현된 버튼을 누를시 _flgJanken이 true가 되어서 update에서 게임을 시작하게 된다

이렇게 update에서 case0을 벗어날시

case1에 다다르게 되는데 

                case 1: // 플레이어 입력 대기
                    break;

겉보기에도 아무것도 없어 보인다...

        if (_modeJanken == 1)
        {
            if(GUI.Button (new Rect(Screen.width / 2 - 120, 400, 100, 100), "바위", btu[0]))
            {
                _myHand = GOO;
                _modeJanken++;
            }
            if(GUI.Button (new Rect(Screen.width / 2, 400, 100, 100), "가위", btu[1]))
            {
                _myHand = CHOKI;
                _modeJanken++;
            }
            if(GUI.Button (new Rect(Screen.width / 2 + 120, 400, 100, 100), "보", btu[2]))
            {
                _myHand = PAR;
                _modeJanken++;
            }
        }

왜냐하면 이때 사용되는 버튼들이 OnGUI에 구현되어 있기 때문이다

시작버튼을 누르면 묵찌빠 버튼 3개가 등장하게 되어 있다

이때 GUI.(class) (new (도형)(vector3 좌표값), (string text), (gui image))의 형태의 메서드를 사용한다

그래서 각각의 버튼을 누를시, 플레이어의 hand를 저장한뒤 다음 단계로 넘어간다

                case 2: // 판정
                    _flagResult = -1;
                    _unityHand = Random.Range(GOO, PAR+1);
                    UnityChanAction(_unityHand);

                    if (_myHand == _unityHand)
                    {
                        _flagResult = DRAW;
                    }
                    else
                    {
                        switch (_unityHand)
                        {
                            case GOO:
                                if (_myHand == PAR) _flagResult = LOOSE;
                                break;
                            case CHOKI:
                                if (_myHand == GOO) _flagResult = LOOSE;
                                break;
                            case PAR:
                                if (_myHand == CHOKI) _flagResult = LOOSE;
                                break;
                        }
                        if (_flagResult != LOOSE) _flagResult = WIN;
                    }
                    _modeJanken++;
                    break;

판정은 간단하다, Random으로 유니티짱의 hand를 결정한뒤, 바로 승리 / 패배 / 무승부를 가리고 다음단계로 넘어간다

                case 3: // 결과
                    _waitTime += Time.deltaTime;

                    if (_waitTime > 1.5)
                    {
                        UnityChanAction(_flagResult);
                        _waitTime = 0;
                        _modeJanken++;
                    }
                    break;

case 2에서 얻은결과를 가지고 유니티짱이 반응할 시간을 제공한다

                case 4:
                    _flgJanken = false;
                    _modeJanken = 0;
                    break;

그후 다시 첫 if문으로 돌아가도록 만들어낸다

 

중간 중간에 존재하는 

void UnityChanAction(int action)

의 경우

    void UnityChanAction(int action)
    {
        switch (action)
        {
            case JANKEN:
                //animator.SetBool("Janken", true);
                _audioSource.clip = voice_janken[(int)voice.start];
                break;
            case GOO:
                //_animator.SetBool("Goo", true);
                _audioSource.clip = voice_janken[(int)voice.goo];
                break;
            case CHOKI:
                //_animator.SetBool("CHOKI", true);
                _audioSource.clip = voice_janken[(int)voice.choki];
                break;
            case PAR:
                //_animator.SetBool("Par", true);
                _audioSource.clip = voice_janken[(int)voice.par];
                break;
            case DRAW:
                //_animator.SetBool("Aiko", true);
                _audioSource.clip = voice_janken[(int)voice.draw];
                break;
            case WIN:
                _animator.SetBool("Win", true);
                _audioSource.clip = voice_janken[(int)voice.win];
                break;
            case LOOSE:
                _animator.SetBool("Loose", true);
                _audioSource.clip = voice_janken[(int)voice.loose];
                break;
        }
        _audioSource.Play();
    }

의 형태로, 유니티짱의 애니메이션, 보이스 재생을 담당한다