ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unity 2D] Fly Bird 스코어 누적 count 하기 (+ 간단 버그 수정)
    Unity 2D 2023. 3. 25. 21:49

    저번 글에서 Bird가 파이프에 부딪히면 동작을 멈추도록 했다.

    이번에는 Bird가 파이프를 통과할 때 1점씩 점수를 획득하도록 만들어보자 ..

     

    그 전에 저번 글에서 Bird가 멈추게 했는데, 정작 파이프 반복 생성을 막는 걸 놓쳤었다.

    그리고 오류 한 가지 또 놓침 ㅠㅠ

    Bird가 초반 Ready 상태일 때도 파이프가 계속 생성되고 있다.

    그거 먼저 잡고 가야겠다..,

    아주 간단하다. (간단한 걸 놓쳐버림~)

     

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SpawnPoint : MonoBehaviour
    {
        // 파이프 프리팹
        public GameObject pipePrefab;
        // 스폰 시간
        [SerializeField] float spawnTime = 1;
        // 타이머
        private float countdown = 0;
    
        // 랜덤하게 나올 스폰 타임
        [SerializeField] float spawnMaxTime = 1.1f;
        [SerializeField] float spawnMinTime = 0.9f;
        // 랜덤하게 나올 스폰 위치
        [SerializeField] float spawnMaxY = 2.3f;
        [SerializeField] float spawnMinY = -1.3f;
     
        void Update()
        {
            // Death 상태이거나 Start 전이면 프리팹 생성을 멈춘다 
            if(GameManager.isStart == false || GameManager.isDeath == true)
            {
                return;
            }
            // 타이머 구현
            countdown += Time.deltaTime;
            if (countdown >= spawnTime)
            {
                SpawnPipe();
                countdown = 0;
                // 시간을 랜덤하게 스폰하기
                spawnTime = Random.Range(spawnMinTime, spawnMaxTime);
            }
        }
    
        void SpawnPipe()
        {
            // 위치를 랜덤하게 스폰하기
            float spawnY = transform.position.y + Random.Range(spawnMinY, spawnMaxY);
            Vector3 randomPosition = new Vector3(transform.position.x, spawnY, transform.position.z);
            // 파이프 프리팹 생성 
            Instantiate(pipePrefab, randomPosition, Quaternion.identity);
        }
    }

     

    그냥 조건에 맞게 return 시켜주면 된다.

     

    Ready 상태에서는 파이프가 생성되지 않는 걸 확인할 수 있다.
    게임이 끝났을 때 파이프가 생성되지 않는 걸 확인할 수 있다.

     

    이제 파이프를 통과할 때 1점씩 얻을 수 있도록 스코어를 누적시키자.

    UI를 활용해서 만들 예정이다.

    이 게임의 UI를 관리할 캔버스를 생성할 것이다.

    (모든 UI가 화면에 나타나기 위해서는 무조건 캔버스에서 나타내야 한다.)

     

     

    하이라키창에서 우클릭 → UI → Canvas 클릭

     

    빈 오브젝트로 PlayUI 라고 UI 폴더를 따로 나눴다.

    (나중에 안 헷갈리기 위해서 미리미리 폴더를 정리하자.)

    숫자를 입력하기 위해서 TextMeshPro를 사용한다.

    하이라키창 우클릭 → UI → Text - TextMeshPro 클릭

     

     

    텍스트의 위치를 설정할 수 있다.
    텍스트에 다양한 효과를 줄 수 있다.

    개인적으로 UI는 결국 개인 센스 감각에 따라 달라진라고 생각한다.

    물론 어느 정도 스킬과 지식은 당연히 가지고 있어야 하지만.. ^_ㅠ

     

    나도 일단 대충 꾸며보았다... . .. . .. 

    위 숫자 00는 누적될 스코어가 보여질 곳이다.

     

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    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;
        }
    }

    GameManager에서는 보통 score, life, bool 형 등 큼직한 것들을 관리한다.

    score도 마찬가지로 Static을 사용했기 때문에 0으로 꼭 초기화한다.

     

     

    using UnityEngine;
    using TMPro;
    
    public class PlayUI : MonoBehaviour
    {
        public TextMeshProUGUI scoreText;
        void Update()
        {
            scoreText.text = GameManager.score.ToString();
        }
    }

    PlayUI 스크립트를 만들고 text 를 누적해서 받을 변수를 선언한다.

    인스펙터 창에서 자기 자신을 드래그하여 넣어준다.

    (텍스트를 필드 선언할 때 using TMPro; 를 꼭 추가해줘야 한다.)

    그리고 GameManager에서 선언한 score를 string 형식으로 받는다.

     

     

    void GetScore()
        {
            GameManager.score++;
        }
    
        void OnTriggerEnter2D(Collider2D coll)
        {
            if(coll.gameObject.tag == "Score")
            {
                GameManager.isDeath = false;
                GetScore();
            }
        }

    그리고 Player 스크립트로 돌아가서 Bird가 파이프를 통과했을 때 

    GameManager의 score가 1씩 누적 추가되도록 메서드 GetScore를 만들어서 작성하고

    Trigger가 호출될 때 GetScore 메서드가 실행되도록 구현했다.

     

     

    점수가 1씩 올라가는 것을 확인할 수 있다.

     

     


    전체 코드

    더보기

    SpawnPoint.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SpawnPoint : MonoBehaviour
    {
        // 파이프 프리팹
        public GameObject pipePrefab;
        // 스폰 시간
        [SerializeField] float spawnTime = 1;
        // 타이머
        private float countdown = 0;
    
        // 랜덤하게 나올 스폰 타임
        [SerializeField] float spawnMaxTime = 1.1f;
        [SerializeField] float spawnMinTime = 0.9f;
        // 랜덤하게 나올 스폰 위치
        [SerializeField] float spawnMaxY = 2.3f;
        [SerializeField] float spawnMinY = -1.3f;
     
        void Update()
        {
            // Death 상태이거나 Start 전이면 프리팹 생성을 멈춘다 
            if(GameManager.isStart == false || GameManager.isDeath == true)
            {
                return;
            }
            // 타이머 구현
            countdown += Time.deltaTime;
            if (countdown >= spawnTime)
            {
                SpawnPipe();
                countdown = 0;
                // 시간을 랜덤하게 스폰하기
                spawnTime = Random.Range(spawnMinTime, spawnMaxTime);
            }
        }
    
        void SpawnPipe()
        {
            // 위치를 랜덤하게 스폰하기
            float spawnY = transform.position.y + Random.Range(spawnMinY, spawnMaxY);
            Vector3 randomPosition = new Vector3(transform.position.x, spawnY, transform.position.z);
            // 파이프 프리팹 생성 
            Instantiate(pipePrefab, randomPosition, Quaternion.identity);
        }
    }

     

     

     

    GameManager.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    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;
        }
    }

     

     

     

    PlayUI.cs

    using UnityEngine;
    using TMPro;
    
    public class PlayUI : MonoBehaviour
    {
        public TextMeshProUGUI scoreText;
        void Update()
        {
            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;
    
        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 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")
            {
                GameManager.isDeath = true;
            }
        }
    }
Designed by Tistory.