ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unity 2D] Fly Bird 게임오버 UI 구현하기 (Best Score 저장, Button 구현)
    Unity 2D 2023. 3. 26. 15:22

     

    Bird가 Death처리 됐을 때 GameOver 판넬이 나오면서

    베스트 스코어와 현재 스코어가 같이 판넬 안에서 나타나고

    버튼을 만들어서 다시 게임을 플레이 할 수 있도록 한다.

     

     

    빈 오브젝트를 생성해서 GameOverUI 폴더를 만든다.

    하이라키창 우클릭 → UI → Panel

     

     

    이런 판넬이 하나 생긴다.

    이게 바로 GameOver UI의 바탕(Background)이 된다.

     

    파란 점을 마우스 클릭한 상태로 alt 키 혹은 opt 키를 클릭한다.

    누른 상태로 원하는 크기를 조정하면

    비율에 맞게 크기가 자동으로 맞춰져 편하게 설정할 수 있다.

    파란 점 클릭한 상태로 → alt키 혹은 opt키 누른 상태로 → 크기 조정

     

    실제 게임 씬에서는 이렇게 보인다.

    이제 또 UI를 역량껏 꾸며보겠다..

     

    일단 나는 이렇게 꾸밈..

     

    그리고 꿀팁 하나

    폰트 같은 걸 쓰다 보면 텍스트마다 효과가 동기화 되는 경우가 있는데

    이렇게 폰트를 복사해서 이름을 지정하고 텍스트마다 다른 폰트를 사용하면

    텍스트 각각 효과를 다르게 줄 수 있다.

     

     

    이제 버튼을 만들어보자.

    하이라키창 우클릭 → UI → Button 클릭 (TextMeshPro가 자동 생성됨)

     

    버튼을 두 개 만들고 텍스트도 넣었다.

     

    폴더 정리는 보기 편하게 정리했다.

    UI 폴더

    - 배경과 게임오버 텍스트

    - 스코어 텍스트

    - 버튼

     

    사실 근데 그냥 자기 스타일대로 정리하면 된다.

    새로운 스크립트를 만든다.

     

     

    using UnityEngine;
    using TMPro;
    
    public class GameOverUI : MonoBehaviour
    {
        public TextMeshProUGUI bestScoreText;
        public TextMeshProUGUI scoreText;
        
        // UI가 SetActive 할 때 호출되는 함수 
        private void OnEnable()
        {
            int bestScore = PlayerPrefs.GetInt("BestScore", 0);
            if(GameManager.score > bestScore)
            {
                PlayerPrefs.SetInt("BestScore", GameManager.score);
                bestScore = GameManager.score;
            }
            bestScoreText.text = bestScore.ToString();
            scoreText.text = GameManager.score.ToString();
        }
    }

    스코어를 경신했을 때 새로 저장할 변수, 현재 스코어 를 저장할 변수를 선언하고

    인스펙터 창으로 숫자 텍스트 오브젝트를 드래그한다.

    GameOver UI는 게임이 멈췄을 때 비활성화 상태였다가 활성화 되면서 딱 한 번만 나타나기 때문에

    OnEnable 함수를 사용했다. 딱 한 번만 호출된다는 특징이 있다.

     

    PlayerPrefs.GetInt("BestScore", 0);

    → key 값인 Best Score 을 0 값으로 불러온다. 이 값은 int형 변수 bestScore에 저장이 된다.

     

    이때 만약 스코어가 Best Score (초기값인 0) 보다 크다면 

    PlayerPrefs.SetInt("BestScore", GameManager.score);

    → key값인 Best Score는 GameManager.score 로 저장이 된다.

    그리고 int형 변수인 bestScore에 경신된 score 값을 새로 저장한다.

     

    경신한 경우가 아니라면 best Score 값은 그대로 int형 변수인 bestScore에 저장된 값이 되고

    현재 스코어 값도 GameManager.score 의 값이다.

     

     

    플레이어 스크립트로 돌아가서 게임오버일 때 UI가 나타나도록 구현해보자.

    public class Player : MonoBehaviour
    {
        // Jump 구현
        Rigidbody2D rb;
        [SerializeField] float Speed = 5f;
        private bool keyJump = false;
    
        // Rotate 구현
        [SerializeField] private float upRotate = 5f;
        [SerializeField] private float downRotate = -5f;
        private Vector3 birdRotation;
    
        // Move 구현
        [SerializeField] private float moveSpeed = 5f;
    
        // Ready 구현
        [SerializeField] private float readyPower = 0.3f;
    
        // 게임오버UI
        public GameObject GameOverUI;
    }

    필드 선언부에 GameOverUI 를 선언하고 인스펙터 창에 오브젝트를 드래그한다.

     

    {
    	void ShowDeathUI()
        {
            GameManager.isDeath = true;
            GameOverUI.SetActive(true);
        }
        
        void OnCollisionEnter2D(Collision2D coll)
        {
            if(coll.gameObject.tag == "Pipe" || coll.gameObject.tag == "Ground")
            {
                ShowDeathUI();
            }
        }
    }

    ShowDeathUI 메서드로 따로 빼서 죽음 처리하는 곳에 넣었다.

     

     

    스코어와 UI가 제대로 적용이 된 걸 확인할 수 있다.

    버튼 구현도 해보자.

    Retry 버튼을 눌렀을 때 게임이 다시 시작되고

    Menu 버튼을 눌렀을 때 메인화면으로 돌아가게 할 건데

    메뉴화면은 맨 마지막에 할 거라서 그냥 Debug로 체크만 하도록 한다.

    게임매니저에 메서드를 만든다.

     

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class GameManager : MonoBehaviour
    {
        // 플레이어 죽음 체크하기 
        public static bool isDeath = false;
        // 플레이어 시작 체크하기
        public static bool isStart = false;
        // 스코어
        public static int score = 0;
        void Start()
        {
            isDeath = false;
            isStart = false;
            score = 0;
        }
    
        public void Retry()
        {
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        }
    
        public void Menu()
        {
            Debug.Log("메인메뉴");
        }
    }

    씬을 관리하는 것이기 때문에 네임스페이스에

    uning UnityEngine.SceneManagement; 도 잊지 않고 입력해야 한다.

    public void 를 사용해서 인스펙터 창에서 button의 함수로써 연결해줄 것이다.

    GetActiveScene().name현재 씬의 이름을 뜻한다.

    SceneManager.LoadScene(SceneManager.GetActiveScene().name); 

    → 씬매니저를 통해 현재 씬을 다시 로드한다.

     

     

    먼저 GameOverUI 오브젝트를 비활성화 시킨다.

    Retry버튼을 누르면 인스펙터 창에 Button기능이 있다.

    On Click()에서 클릭했을 때 호출할 함수를 선택한다.

    On Click() 에서 + 버튼 클릭

     

    아까 Retry메서드와 Menu메서드를 구현해놨던 GameManager 스크립트가 들어있는

    게임매니저 오브젝트를 드래그해서 놓는다.

    No Function → GameManager 클릭

     

    Retry버튼에서는 Retry메서드를 선택한다. (현재 씬 다시 로드)

    Menu버튼에서는 Menu메서드를 선택하면 된다. ( Debug.Log("메인메뉴"); )

     

     

    게임씬에서 Menu버튼을 클릭할 때 나오는 값이다.

    게임 씬에서 제대로 작동하는 걸 확인할 수 있다.

     

     


    전체 코드

    더보기

    GameOverUI.cs

    using UnityEngine;
    using TMPro;
    
    public class GameOverUI : MonoBehaviour
    {
        public TextMeshProUGUI bestScoreText;
        public TextMeshProUGUI scoreText;
        
        private void OnEnable()
        {
            int bestScore = PlayerPrefs.GetInt("BestScore", 0);
            if(GameManager.score > bestScore)
            {
                PlayerPrefs.SetInt("BestScore", GameManager.score);
                bestScore = GameManager.score;
            }
            bestScoreText.text = bestScore.ToString();
            scoreText.text = GameManager.score.ToString();
        }
    }

     

     

     

     Player.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Player : MonoBehaviour
    {
        // Jump 구현
        Rigidbody2D rb;
        [SerializeField] float Speed = 5f;
        private bool keyJump = false;
    
        // Rotate 구현
        [SerializeField] private float upRotate = 5f;
        [SerializeField] private float downRotate = -5f;
        private Vector3 birdRotation;
    
        // Move 구현
        [SerializeField] private float moveSpeed = 5f;
    
        // Ready 구현
        [SerializeField] private float readyPower = 0.3f;
    
        // 게임오버UI
        public GameObject GameOverUI;
    
        void Start()
        {   // RigidBody2D 컴포넌트를 rb라는 변수를 통해 가져온다
            rb = this.GetComponent<Rigidbody2D>();
        }
    
        void Update()
        {
            
            InputBird();
    
            if(GameManager.isStart == false)
            {
                ReadyBird();
                return;
            }
            
            RotateBird();
            MoveBird();
        }
    
        private void FixedUpdate()
        {
            // 게임 물리 연산은 FixedUpdate 메서드 안에서 진행한다 
            // 입력받은 Jump값이 true라면
            if (keyJump == true)
            {
                // JumpBird메서드 받기
                JumpBird();
                // 그리고 다시 keyJump값은 false로 만들어준다
                keyJump = false;
            }
        }
    
        void ReadyBird()
        {
            if (rb.velocity.y < 0)
            {
                rb.velocity = Vector2.up * readyPower;
            }
        }
    
        void JumpBird()
        {   // Rigidbody의 velocity 는 Speed 만큼 올라간다 
            rb.velocity = Vector3.up * Speed;
        }
    
        void InputBird()
        {
            if(GameManager.isDeath == true)
            {
                return;
            }
            // 스페이스바 혹은 마우스 좌버튼을 눌렀을 때
            keyJump |= Input.GetKeyDown(KeyCode.Space);
            keyJump |= Input.GetMouseButtonDown(0);
    
            // Bird가 시작했을 때 내려오지 않는 버그 수정
            if(GameManager.isStart == false && keyJump == true)
            {
                GameManager.isStart = true;
            }
        }
    
        void RotateBird()
        {
            // upRotate와 downRotate를 누적해서 저장할 변수 degree 초기화
            float degree = 0;
            if(rb.velocity.y > 0)
            {
                // upRotate 누적
                degree = upRotate;
            }
            else if(rb.velocity.y < 0)
            {
                // downRotate 누적
                degree = downRotate;
            }
    
            // birdRotation을 오일러각으로 변환하여 최댓값 30, 최솟값 -90을 받는다 
            birdRotation = new Vector3(0, 0, Mathf.Clamp((birdRotation.z + degree), -90, 30));
            transform.eulerAngles = birdRotation;
        }
    
        void MoveBird()
        {
            if(GameManager.isDeath == true)
            {
                return;
            }
            // Bird가 앞으로 이동하는 것 구현하기
            transform.Translate(Vector3.right * moveSpeed * Time.deltaTime, Space.World);
        }
    
        void GetScore()
        {
            GameManager.score++;
        }
    
        void ShowDeathUI()
        {
            GameManager.isDeath = true;
            GameOverUI.SetActive(true);
        }
    
        void OnTriggerEnter2D(Collider2D coll)
        {
            if(coll.gameObject.tag == "Score")
            {
                GameManager.isDeath = false;
                GetScore();
            }
        }
    
        void OnCollisionEnter2D(Collision2D coll)
        {
            if(coll.gameObject.tag == "Pipe" || coll.gameObject.tag == "Ground")
            {
                ShowDeathUI();
            }
        }
    }

     

     

     

    GameManager.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class GameManager : MonoBehaviour
    {
        // 플레이어 죽음 체크하기 
        public static bool isDeath = false;
        // 플레이어 시작 체크하기
        public static bool isStart = false;
        // 스코어
        public static int score = 0;
        void Start()
        {
            isDeath = false;
            isStart = false;
            score = 0;
        }
    
        public void Retry()
        {
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        }
    
        public void Menu()
        {
            Debug.Log("메인메뉴");
        }
    }

     

     

     

     

Designed by Tistory.