Unity3D/유니티 공부

[Unity / C#] 가위바위보 프로그래밍.

상연 2022. 4. 24. 21:12

목차

    C#을 사용한 가위바위보 구현

    요구사항

    • 숫자키(1~3)로 가위(1) 바위(2) 보(3)를 선택한다.
    • SpaceBar를 누르면 선택한 키(가위/바위/보)가 확정된다.
    • 컴퓨터는 랜덤으로 가위 바위 보를 선택한다.
    • 유저와 컴퓨터의 패를 비교하여 승부를 한다.
    • 승부결과를 알린다. (유저기준)

     

    전체 코드

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class RSP : MonoBehaviour
    {
        enum Hands
        {
            none = 0,
            Rock = 1,
            Scisscors = 2,
            Paper = 3,
        }
    
        Hands user_hand = Hands.Rock; // 유저가 낼 손
        Hands com_hand = Hands.Rock; // 컴퓨터가 낼 손
    
    
        //1. 유저(Input) - 자신이 낼 손패값을 입력한다.
        void InputUserHand()
        {
            if (Input.GetKeyDown(KeyCode.Alpha1)) 
            { 
                user_hand = Hands.Rock;
                Debug.Log(user_hand);
            }
            else if (Input.GetKeyDown(KeyCode.Alpha2))
            {
                user_hand = Hands.Scisscors;
                Debug.Log(user_hand);
            }
            else if (Input.GetKeyDown(KeyCode.Alpha3)) 
            { 
                user_hand = Hands.Paper;
                Debug.Log(user_hand);
            }
            else if (Input.GetKeyDown(KeyCode.Space))
            {
                Debug.Log($"사용자가 다음에 낼 손을 결정했습니다. {user_hand}");
                SetComHand();
            }
        }
    
        void SetComHand()
        {
            int rand_com = Random.Range(1, 4); //1이상 4미만의 '정수'를 랜덤하게 반환한다.
    
            switch (rand_com)
            {
                case (int)Hands.Rock:
                    com_hand = Hands.Rock;
                    break;
                case (int)Hands.Scisscors:
                    com_hand = Hands.Scisscors;
                    break;
                case (int)Hands.Paper:
                    com_hand = Hands.Paper;
                    break;
            }
            Debug.Log($"컴퓨터가 다음에 낼 손을 결정했습니다. {com_hand}");
            fight_switch();
        }
    
        // 3. 유저와 컴퓨터 간의 손패 비교 - 1
        void fight_if()
        {
            string result = "";
            //조건문으로 하는 법
            if (user_hand == com_hand)
            {
                result = "Draw";
            }
            else if (user_hand == Hands.Rock)
            {
                result = (com_hand == Hands.Paper) ? "Lose" : "Win";
            }
            else if (user_hand == Hands.Scisscors)
            {
                result = (com_hand == Hands.Rock) ? "Lose" : "Win";
            }
            else if (user_hand == Hands.Paper)
            {
                result = (com_hand == Hands.Scisscors) ? "Lose" : "Win";
            }
    
            user_hand = Hands.Rock;
            Debug.Log(result);
        }
    
        // 3. 유저와 컴퓨터 간의 손패 비교 - 2
        void fight_switch()
        {
            string result = "";
            switch (user_hand)
            {
                case Hands.Rock:
                    if (com_hand == Hands.Rock) result = "Draw";
                    else if (com_hand == Hands.Scisscors) result = "Win";
                    else if (com_hand == Hands.Paper) result = "Lose";
                    break;
    
                case Hands.Scisscors:
                    if (com_hand == Hands.Rock) result = "Lose";
                    else if (com_hand == Hands.Scisscors) result = "Draw";
                    else if (com_hand == Hands.Paper) result = "Win";
                    break;
    
                case Hands.Paper:
                    if (com_hand == Hands.Rock) result = "Win";
                    else if (com_hand == Hands.Scisscors) result = "Lose";
                    else if (com_hand == Hands.Paper) result = "Draw";
                    break;
            }
    
            user_hand = Hands.Rock;
            Debug.Log(result);
        }
    
        void Update()
        {
            InputUserHand();
        }
    }

    코드 설명

       enum Hands
        {
            none = 0,
            Rock = 1,
            Scisscors = 2,
            Paper = 3,
        }

    enum 을 사용한다.
    enum을 사용하면 상수 숫자들을 보다 의미있는 단어로 표현가능하다.


    현재 이 프로그램은 매우 간단하여 1, 2, 3이 각각 어느 의미를 표현하는지 기억하고 이해하기는 매우 쉽지만

    프로그램의 규모가 커질수록 숫자와 역할의 매칭을 머리속으로 하기란 불가능에 가까우며 유지보수를 매우 어렵게 만든다.
    따라서 위와같이 enum을 사용하여 상수 숫자들을 우리가 원하는 의미의 단어이름과 매핑해준다.

    Hands user_hand = Hands.Rock; // 유저가 낼 손
    Hands com_hand = Hands.Rock; // 컴퓨터가 낼 손

    변수는 위와같이 두 개면 충분하다.
    각 변수는 처음에 만든 Hands enum타입의 데이터를 저장한다. (추후 추가 설명)

    • 실행 흐름
        1. 유저(Input) - 자신이 낼 손패값을 입력한다.
        • (1~3번으로 고르며, SpaceBar를 통해 입력을 확정한다.다른 값은 무시하며 기본 손패는 주먹이라고 가정한다)
        1. 컴퓨터의 손패를 랜덤으로 정해준다.
        1. 유저와 컴퓨터의 손패를 비교하여 승패 출력
        1. 다시 1번으로

    프로그램이 진행될 순서는 위와 같으며, 구현을 위해서 각 흐름당 하나의 함수를 만들어보자.

    1번함수 - 유저(Input)

    void InputUserHand()
        {
            if (Input.GetKeyDown(KeyCode.Alpha1)) 
            { 
                user_hand = Hands.Rock;
                Debug.Log(user_hand);
            }
            else if (Input.GetKeyDown(KeyCode.Alpha2))
            {
                user_hand = Hands.Scisscors;
                Debug.Log(user_hand);
            }
            else if (Input.GetKeyDown(KeyCode.Alpha3)) 
            { 
                user_hand = Hands.Paper;
                Debug.Log(user_hand);
            }
            else if (Input.GetKeyDown(KeyCode.Space))
            {
                Debug.Log($"사용자가 다음에 낼 손을 결정했습니다. {user_hand}");
                SetComHand();
            }
       }

    조건문이 많아서, 길어보이지만 내용은 간단하다.
    Input.GetKeyDown 함수를 활용하여 사용자로부터 입력을 받고 입력에 맞는 값을 저장해주는 역할을 한다.

    이 함수를 통해 익혀야 할 것

      1. 사용자로부터 KeyBoard 입력에 대한 처리
        • Input.GetKeyDown() 함수의 사용법
          • Input.GetKeyDown은 눌렀을때
          • Input.GetKeyUp은 눌렀다가 뗀 순간
          • Input.GetKey 는 누르는동안
      1. enum 변수에 값을 대입하는 방법.
        • user_hand = Hands.Rock;

    여기서 Hands 데이터 타입의 user_hand 변수에는 위와같이 Hands Enum 안에 있는 각 요소값들을 넣어줄 수 있다.

    Hands데이터 타입의 user_hand 변수에 Hands의 요소 중 하나인 Rock이라는 값을 넣어주겠다.
    우선 이해가 안간다면 일단은 그렇구나 라고 하고 넘어가자
    나중에 Class라던지 다양한 Collection을 배우고 사용하다보면 자연스럽게 알게 될 내용이다.

    enum에 대해 더 자세히 알고 싶다면

     

    C# 열거형 enum - C# 프로그래밍 배우기 (Learn C# Programming)

    C# enum (열거형) C#의 키워드 enum은 열거형 상수(constant)를 표현하기 위한 것으로 이를 이용하면 상수 숫자들을 보다 의미있는 단어들로 표현할 수 있어서 프로그램을 읽기 쉽게 해준다. enum의 각

    www.csharpstudy.com

     

    2번함수 - 컴퓨터의 손패를 랜덤으로 정해준다.

    void SetComHand()
        {
            int rand_com = Random.Range(1, 4); //1이상 4미만의 '정수'를 랜덤하게 반환한다.
    
            switch (rand_com)
            {
                case (int)Hands.Rock:
                    com_hand = Hands.Rock;
                    break;
                case (int)Hands.Scisscors:
                    com_hand = Hands.Scisscors;
                    break;
                case (int)Hands.Paper:
                    com_hand = Hands.Paper;
                    break;
            }
        }

    이 함수를 통해 익혀야 할 것.

      1. Random 클래스를 활용한 Random 값 사용
        • Random.Range(a, b) 이와같이 사용하면?
          a 이상 b 미만 의 정수값을 반환한다.
          이 외에도 많은 Random 클래스의 함수가 있으니, 찾아서 사용하면 좋다.
      1. enum 의 요소가 가지고 있는 정수형 값 사용법.(형변환)

    아까 우리가 enum을 선언할때 각 요소는 정수형 값을 지정받았다.
    ex) Rock = 1
    애초에 enum을 사용하는 이유가 상수 숫자를 의미있게 '표현'하는것이었으므로
    언제든 그 '표현'된것으로부터 숫자를 꺼내 올 수도 있어야 한다.

    그 숫자를 꺼내오는 방법은(int) 형변환을 해 주는것
    Hands.Rock 은 원래 숫자 1을 표현(갖고)하고 있는것이기 때문에
    그 앞에 정수로 형변환을 해 주는(int)를 붙여주면 1이 된다.

    따라서
    Case(int)Hands.Rock:Case 1 : 과 동일하며 (Hands.Rock 이 정수 1을 표현하기에)
    위의 Switch 문이 하는일은
    Random으로 정해진 1~3의 값을 Hands와 매핑하여 컴퓨터에게 랜덤한 손을 쥐어주는것.

    3번함수 - 유저와 컴퓨터 간의 손패 비교

    유저와 컴퓨터 각각 손패를 결정했으면 서로 가위바위보 시켜야한다.
    시키는 방법에 있어서 조건문을 사용해서 하는방법과 Switch를 사용해서 하는방법
    두 가지를 생각해보았는데, 둘 중 편한대로 사용하면 된다.

    조건문을 사용하는 경우

    void fight_if()
        {
            string result = "";
            //조건문으로 하는 법
            if (user_hand == com_hand)
            {
                result = "Draw";
            }
            else if (user_hand == Hands.Rock)
            {
                result = (com_hand == Hands.Paper) ? "Lose" : "Win";
            }
            else if (user_hand == Hands.Scisscors)
            {
                result = (com_hand == Hands.Rock) ? "Lose" : "Win";
            }
            else if (user_hand == Hands.Paper)
            {
                result = (com_hand == Hands.Scisscors) ? "Lose" : "Win";
            }
    
            user_hand = Hands.Rock;
            Debug.Log(result);
    
        }

    우선 유저와 컴퓨터의 손패가 같은지(비겼는지) 확인한다.
    만약 다르다면, 컴퓨터가 들고있는 손패는 유저입장에서 두 가지 경우다.
    (유저가) 이겼거나, 패배하거나.

    따라서 이 경우 삼항연산자를 사용하면 좀 더 간단히 표현가능하다.

    else if (user_hand == Hands.Rock)
            {
                result = (com_hand == Hands.Paper) ? "Lose" : "Win";
            }

    유저가 Hands.Rock, 즉 주먹이라면
    이미 위에서 같은 경우는 배제 되었으므로 컴퓨터가 가진 패는 가위 혹은 .
    만약 컴퓨터가 라는 조건이 이면 유저는 LOSE며, 거짓이면 WIN이다.

    Switch를 사용하는 경우

    void fight_switch()
        {
            string result = "";
            switch (user_hand)
            {
                case Hands.Rock:
                    if (com_hand == Hands.Rock) result = "Draw";
                    else if (com_hand == Hands.Scisscors) result = "Win";
                    else if (com_hand == Hands.Paper) result = "Lose";
                    break;
    
                case Hands.Scisscors:
                    if (com_hand == Hands.Rock) result = "Lose";
                    else if (com_hand == Hands.Scisscors) result = "Draw";
                    else if (com_hand == Hands.Paper) result = "Win";
                    break;
    
                case Hands.Paper:
                    if (com_hand == Hands.Rock) result = "Win";
                    else if (com_hand == Hands.Scisscors) result = "Lose";
                    else if (com_hand == Hands.Paper) result = "Draw";
                    break;
            }
    
            user_hand = Hands.Rock;
            Debug.Log(result);
        }

    enum을 Switch 문으로 사용하여 가위바위보를 하는 방법이다.
    user_hand를 Switch 조건으로 넣어서 가위/바위/보의 Case에 대해 조건문으로 처리를한다.

    함수의 연결

    이제 가위바위보를 구현하기 위해 필요한 변수와 함수를 모두 만들었다.
    이제 각 함수를 연결만 해주면 완성된다.

    그런데 어떻게 연결할까?

    우선 1번함수에 대해 생각해보자.
    사용자로부터 입력을 받아야한다. 그런데, 입력을 1회만 받는지, 아니면 계속해서 받아야 하는지?

    계속해서 받아야한다. 그렇기 때문에 이 1번함수는 Update() 함수에 들어가게 된다.

    그 다음 2번함수에 대해 생각해보자.
    컴퓨터의 랜덤패를 정하는 함수인데, 사실 이 함수는 어느 위치에 들어가도 무방하다.
    가위바위보를 하기 전에만 실행하면 되는데

    개인적으로는 유저가 1번함수에서 Space를 눌렀을때 실행되는게 좋다고 생각했다.
    Space를 누르면 유저의 손패가 결정되는 동시에 컴퓨터의 손패도 결정이 되는것이기 때문이다.

    따라서 2번 함수는 1번함수 내에 Space 입력을 받는 조건문 안에 위치하게된다.

    마지막으로 가위바위보를 하는 함수이다.

    가위바위보를 하기 위해서는 어떻게 되어야 할까?
    양 측의 손패가 결정되어있어야한다
    따라서 양 측의 손패가 결정되는 순간인데
    우리의 경우 현재 유저의 손패 결정 -> 컴퓨터 손패 결정
    이러한 흐름으로 진행이 되고 있으므로 컴퓨터 손패가 결정된 직후에 가위바위보 게임을 진행시키면 될 것이다.
    따라서 2번 함수의 마지막에 3번 함수를 실행하면 가위바위보가 구현이 된다.

    실행 결과

     

    추가

    위와 같은 가위바위보 구현을 유니티의 UI와 연결하면

    이렇게 만들수 있다.

    [UI와 연결했을시의 코드]

    더보기
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using Random = UnityEngine.Random;
    
    public class RPS : MonoBehaviour
    {
        enum hands
        {
            none = 0,
            Rock = 1,
            Scissors = 2,
            Paper = 3
        }
    
        enum results
        {
            idle = 0,
            Win = 1,
            Lose = 2,
            Draw = 3
        }
        GameObject Obj_User;
        GameObject Obj_Com;
    
        Text Text_result;
    
    
        hands userHand; // User가 제시할 손패
        hands comHand; // 컴퓨터가 제시할 손패
    
        Sprite[] Image_hand;
        String[] Msg_result = { "", "WIN", "LOSE", "DRAW" };
    
        Action KeyAction = null;
        Action Game = null;
    
        bool flagGame;
        results result;
    
        private void Awake()
        {
            init();
        }
    
        private void Update()
        {
            if (KeyAction != null) KeyAction.Invoke();
            if (flagGame == true) Game.Invoke();
    
        }
    
        void init()
        {
            KeyAction -= OnKeyBoard;
            KeyAction += OnKeyBoard;
    
            Game -= OnGame;
            Game += OnGame;
    
            flagGame = false;
    
            Image_hand = Resources.LoadAll<Sprite>("Image/Hand");
            
            Obj_User = GameObject.Find("UI/Panel/User/Hand");
            
            Obj_Com = GameObject.Find("UI/Panel/Com/Hand");
    
            Text_result = GameObject.Find("UI/Panel/Result").GetComponent<Text>();
            setIdle();
        }
    
        void setIdle()
        {
            userHand = hands.none;
            comHand = hands.none;
            Obj_User.GetComponent<Image>().sprite = Image_hand[(int)hands.none];
            Obj_Com.GetComponent<Image>().sprite = Image_hand[(int)hands.none];
            Text_result.text = Msg_result[(int)results.idle];
        }
    
        void OnKeyBoard()
        {
            if(Input.GetKeyDown(KeyCode.Alpha1))
            {
                userHand = hands.Rock;
                Debug.Log($"User's hands is a {userHand}");
            }
            else if(Input.GetKeyDown(KeyCode.Alpha2))
            {
                userHand = hands.Scissors;
                Debug.Log($"User's hands is a {userHand}");
            }
            else if(Input.GetKeyDown(KeyCode.Alpha3))
            {
                userHand = hands.Paper;
                Debug.Log($"User's hands is a {userHand}");
            }
            else if(Input.GetKeyDown(KeyCode.Space))
            {
                Debug.Log("결정");
                flagGame = true;
            }
            Obj_User.GetComponent<Image>().sprite = Image_hand[(int)userHand];
        }
    
        void SetComHand()
        {
            int tmp = Random.Range(1, 4);
            switch(tmp)
            {
                case 1:
                    comHand = hands.Rock;
                    break;
                case 2:
                    comHand = hands.Scissors;
                    break;
                case 3:
                    comHand = hands.Paper;
                    break;
            }
            Obj_Com.GetComponent<Image>().sprite = Image_hand[(int)comHand];
        }
    
        void OnGame()
        {
            flagGame = false;
            SetComHand();
            Debug.Log($"Computer's hand is a {comHand}");
            if (userHand == comHand) result = results.Draw;
            else
            {
                if(userHand == hands.Rock)
                {
                    result = comHand == hands.Paper ? results.Lose : results.Win;
                }
                else if(userHand == hands.Scissors)
                {
                    result = comHand == hands.Rock ? results.Lose : results.Win;
                }
                else if(userHand == hands.Paper)
                {
                    result = comHand == hands.Scissors ? results.Lose : results.Win;
                }
            }
    
            StartCoroutine(endGame());
        }
    
        IEnumerator endGame()
        {
            KeyAction -= OnKeyBoard;
            Text_result.text = Msg_result[(int)result];
            for(int i=0; i<3; i++)
            {
                yield return new WaitForSeconds(1.0f);
            }
            KeyAction += OnKeyBoard;
            setIdle();
        }
    }