ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unity 2D] Fly Bird 충돌체와 충돌했을 때 Death 처리하기
    Unity 2D 2023. 3. 25. 20:27

    bird가 파이프와 땅 혹은 하늘에 부딪혔을 때 Death 처리가 되는 것을 구현할 거다.

     

     

     

    그 전에 살펴보면.. 이미 통과한 파이프가 삭제되지 않고 그대로 남겨져 있는 걸 볼 수 있다.

    하이라키 창에 Clone이 계속 쌓이면 프로그램이 쓸데없이 느려지고 무거워질 것이다.

    이 파이프 먼저 삭제시켜보자.

     

    빈 오브젝트 PipeKiller를 생성하고 Box Collider2D를 추가해서 충돌체를 감지할 위치를 설정했다.

    설정할 때  ground와 닿게 되면 ground도 삭제되니까 닿지 않게 설정해준다.

    Collider는 두 충돌체 중 한 오브젝트 이상은 무조건 Rigidbody 컴포넌트가 있어야 구현이 된다.

    PipeKiller가 그나마 가장 간단한 오브젝트이기 때문에 여기에 Rigidbody2D 기능을 붙여준다.

    실행해보면 파이프킬러가 중력에 의해 밑으로 떨어지는데,

    리지드바디의 gravity scale (중력의 크기 조정 기능)을 0으로 설정하면 그 자리 그대로 고정되어 있는 것을 확인할 수 있다.

    혹은 리지드바디의 Body Type을 Kinematic으로 변경해준다. 

    Kinematic물리적인 힘에 반응하지 않는다는 의미를 가지고 있다.

    (밑에 링크 달아놓을 콜라이더 내용 중 정적 콜라이더랑 비슷한 맥락의 의미인듯 하다.)

    이 오브젝트 역시 카메라 시선을 따라다녀야 하기 때문에 메인 카메라의 자식 오브젝트로 둔다.

    그리고 스크립트를 만들었다.

     

    using UnityEngine;
    
    public class PipeKiller : MonoBehaviour
    {
        private void OnTriggerEnter2D(Collision2D coll)
        {
            Destroy(coll.gameObject);
        }
    }

    OnTriggerEnter2D 메서드를 사용했는데 그 이유는

    충돌 이벤트 보다는 충돌 감지가 목적이기 때문이다.

    (Trigger 함수 사용했으니까 collider 기능 중 is trigger 가 활성화 되어야 한다.. 필수..)

    충돌하는 모든 파이프는 모두 삭제할 것이기 때문에 Tag을 이용하지 않아도 된다.

    부딪히는 모든 오브젝트를 Destroy 처리한다.

     

    Trigger과 Collider에 관한 글은 따로 적어놨으니 이 글을 참고하면 될듯,,

    https://sdgogood.tistory.com/18

     

    이미 통과한 파이프가 PipeKiller와 닿으면 Destroy 되는 걸 확인할 수 있다.

     

    이번엔 bird가 파이프와 땅 혹은 하늘에 부딪혔을 때 Death 처리가 되게 만들 거다.

    충돌체마다 구현되는 것이 다르다.

    Pipe와 Ground와 SkyWall에 부딪히면 Death 처리를 하고,

    Pipe 사이를 통과하면 score를 획득하게 처리할 것이다.

     

    저번 글에 Tag을 미리 설정해놨다.

    Bird가 통과할 Pipe 오브젝트에만 Score Tag을 설정했다.
    Bird가 부딪혔을 때 Death처리할 pipe 자식 오브젝트들은 Pipe로 설정했었다.

     

    이제 Start와 Death 등 게임 전체를 관리해주는 게임 매니저를 만든다.

    빈 오브젝트에 GameManager을 생성하고, GameManager 스크립트를 추가한다.

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class GameManager : MonoBehaviour
    {
        // 플레이어 죽음 체크하기 
        public static bool isDeath = false;
        // 플레이어 시작 체크하기
        public static bool isStart = false;
        void Start()
        {
            isDeath = false;
            isStart = false;
        }
    }

     

    public static 을 사용해서 다른 스크립트에서 쉽게 호출할 수 있게 했다.

    static 선언한 변수는 이전 데이터가 그대로 넘어올 위험이 있기 때문에 

    Start 함수에서 초기화 해주는 습관을 들인다.

     

    이왕 게임 매니저를 쓰는 거..  

    Player 스크립트에서 앞서 사용했던 isStart도 게임 매니저 스크립트로 옮긴다.

    그리고 게임 매니저에서 isStart와 isDeath를 받아왔다.

    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()
        {
            // 스페이스바 혹은 마우스 좌버튼을 눌렀을 때
            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()
        {
            // Bird가 앞으로 이동하는 것 구현하기
            transform.Translate(Vector3.right * moveSpeed * Time.deltaTime, Space.World);
        }
    
        void OnTriggerEnter2D(Collider2D coll)
        {
            if(coll.gameObject.tag == "Score")
            {
                GameManager.isDeath = false;
            }
        }
    
        void OnCollisionEnter2D(Collision2D coll)
        {
            if(coll.gameObject.tag == "Pipe" || coll.gameObject.tag == "Ground")
            {
                GameManager.isDeath = true;
            }
        }
    }

     

     

    Score 택을 가진 충돌체를 통과하면 (is trigger 활성화) 죽음은 false가 되고,

    Pipe 혹은 Ground 택을 가진 충돌체와 부딪히면 죽음은 true가 된다.

     

    이제 Death == true; 일 때 bird가 활성화되지 않도록 구현해본다.

     

    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 MoveBird()
        {
            if(GameManager.isDeath == true)
            {
                return;
            }
            // Bird가 앞으로 이동하는 것 구현하기
            transform.Translate(Vector3.right * moveSpeed * Time.deltaTime, Space.World);
        }

    bird가 Ground나 Pipe 택에 부딪혔을 때 움직이지 않도록 했다.

    씬을 실행해보면 파이프를 통과하면 계속 게임이 진행되고 

    다른 곳에 부딪히면 Bird가 그대로 멈춰버리는 것을 확인할 수 있다.

     

     


    전체 코드

    더보기

    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 OnTriggerEnter2D(Collider2D coll)
        {
            if(coll.gameObject.tag == "Score")
            {
                GameManager.isDeath = false;
            }
        }
    
        void OnCollisionEnter2D(Collision2D coll)
        {
            if(coll.gameObject.tag == "Pipe" || coll.gameObject.tag == "Ground")
            {
                GameManager.isDeath = true;
            }
        }
    }

     

     

     

    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;
        void Start()
        {
            isDeath = false;
            isStart = false;
        }
    }

     

     

     

    PipeKiller.cs

    using UnityEngine;
    
    public class PipeKiller : MonoBehaviour
    {
        void OnTriggerEnter2D(Collider2D coll)
        {
            Destroy(coll.gameObject);
        }
    }
Designed by Tistory.