레트로의 유니티 게임 프로그래밍 에센스 - 7.3

Cosmos·2023년 4월 6일
0

학습 매체 : 책

책이름 : 레트로의 유니티 게임 프로그래밍 에센스

저자 : 이제민


본 내용은 해당 강의 내용을 공부하면서 정리한 글입니다.


7.3 탄알 충돌 처리


  • Bullet 스크립트에 플레이어와의 충돌을 감지하는 기능을 작성하자. 그전에 먼저 유니티에서 게임 오브젝트 간의 충돌을 어떻게 감지하는지 알아보자.

7.3.1 충돌 이벤트 메서드

  • 콜라이더를 가진 게임 오브젝트 A와 B가 서로 충돌한 상황을 가정해보자. 이때 게임 오브젝트 A와 B 모두 자신이 충돌했다는 사실을 모른다.

  • 게임 오브젝트는 자신이 충돌한 사실을 스스로 알 수 없다. 그 대신 충돌했음을 알려주는 메시지가 A와 B에 보내진다.

  • 충돌 메시지를 통해 게임 오브젝트와 해당 게임 오브젝트에 추가된 컴포넌트들은 충돌 사실을 알게 되고 충돌에 대응하는 메서드를 실행한다.

  • 충돌 종류에 따라 OnTriggerEnter 혹은 OnCollisionEnter 메시지를 받는다.

  • 오브젝트가 충돌 메시지에 대응하려면 충돌 메시지와 같은 이름으로 메서드를 작성하면 된다.

  • 또한 충돌 메시지에는 충돌한 상대방 게임 오브젝트에 대한 정보도 함께 첨부된다. 따라서 충돌한 상대방 게임 오브젝트가 어떠한 오브젝트인지 충돌 메시지를 통해 알 수 있으며, 어떤 대응을 해야 하는지도 결정할 수 있다.

  • 결론적으로, 메시지 기반 방식 덕분에 우리는 충돌을 어떻게 감지할까 고민하지 않아도 된다. 단지 충돌했을 때 무엇을 실행할지만 결정하면 된다.

리지드바디 컴포넌트와 충돌 메시지

  • 위에서 언급한 충돌 메시지를 발생시키는 것은 리지드바디 컴포넌트이다. 따라서 이벤트 메서드를 사용하려면 서로 충돌 중인 게임 오브젝트 중에서 최소 하나의 게임 오브젝트는 리지드바디 컴포넌트를 가지고 있어야 한다.
  • 충돌 메시지에 대응하는 메서드를 충돌 이벤트 메서드라고 부른다. 충돌 종류에 따라 충돌 이벤트 메서드 중 알맞은 것을 선택하여 구현한다.

OnCollision 계열 : 일반 충돌

  • 일반적인 콜라이더를 가진 두 게임 오브젝트가 충돌할 때 자동으로 실행된다. 충돌한 두 콜라이더는 서로 통과하지 않고 밀어낸다.
  • OnCollisionEnter(Collisoin collision) : 충돌한 순간

  • OnCollisionStay(Collisoin collision) : 충돌하는 동안

  • OnCollisionExit(Collisoin collision) : 충돌했다가 분리되는 순간

  • OnCollision 계열 메서드가 실행될 때는 메서드 입력으로 충돌 관련 정보가 Collision 타입으로 들어온다.

  • Collision 타입은 충돌 관련 정보를 담아두는 단순한 정보 컨테이너이다. 따라서 입력으로 들어온 collision을 통해 충돌한 상대방 게임 오브젝트, 충돌 지점, 충돌 표면의 방향 등을 알 수 있다.


OnTrigger 계열 : 트리거 충돌

  • 충돌한 두 게임 오브젝트의 콜라이더 중 최소 하나가 트리거 콜라이더라면 자동으로 실행된다. 이 경우 두 게임 오브젝트가 충돌했을 때 서로 그대로 통과한다.
  • OnTriggerEnter(Collider other) : 충돌한 순간

  • OnTriggerStay(Collider other) : 충돌하는 동안

  • OnTriggerExit(Collider other) : 충돌했다가 분리되는 순간

  • OnTrigger 계열의 메서드가 실행될 때는 메서드 입력으로 충돌한 상대방 게임 오브젝트의 콜라이더 컴포넌트가 Collider 타입으로 들어온다.

  • 여기서 Collisoin이 아닌 Collider 타입을 입력받는 이유는 트리거 충돌에는 상세한 충돌 정보가 필요 없기 때문이다.

  • 트리거 충돌은 일반적인 충돌과 달리 서로 밀어내지 않고 그대로 통과한다. 따라서 물리적인 반발력이나 정확한 충돌 지점, 충격량 등이 존재하지 않으므로 충돌한 상대방 게임 오브젝트(의 콜라이더 컴포넌트)를 곧장 받는다.

OnTrigger 계열의 메서드는 자신이 트리거 콜라이더가 아니어도 실행된다.

  • 충돌한 두 콜라이더 중 하나 이상이 트리거 콜라이더일 때 양쪽 모두에서 OnTrigger 계열의 메서드가 실행된다.
  • 따라서 자신의 콜라이더가 일반 콜라이더라고 해도 충돌한 상대방 게임 오브젝트의 콜라이더가 트리거 콜라이더면 자신과 상대방 모두 OnCollision이 아닌 OnTrigger 계열의 충돌 이벤트 메서드가 실행된다.

7.3.2 탄알에 충돌 감지 구현

  • Bullet 게임 오브젝트는 Is Trigger가 체크된 트리거 콜라이더를 가지고 있다.

  • 따라서 Bullet 스크립트에 충돌 이벤트 메서드로 OnTriggerEnter( )를 작성해야 한다.

  • OnTriggerEnter( ) 메서드는 아래 처리를 구현한다.

1. 충돌한 상대방 게임 오브젝트가 플레이어인지 체크

2. 상대방 게임 오브젝트가 플레이어면 해당 게임 오브젝트의 PlayerController 컴포넌트의 Die( ) 메서드 실행

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float speed = 8f;
    private Rigidbody bulletRigidbody;

    void Start()
    {
        bulletRigidbody= GetComponent<Rigidbody>();
        bulletRigidbody.velocity = transform.forward * speed;

        Destroy(gameObject, 3f);
    }
    void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
        {
            PlayerController playerController = other.GetComponent<PlayerController>();

            if (playerController != null)
            {
                playerController.Die();
            }
        }
    }
}
  • OnTriggerEnter( )의 입력으로 들어오는 other는 충돌한 상대방 게임 오브젝트의 콜라이더 컴포넌트이다.

  • other를 통해 충돌한 상대방 게임 오브젝트의 태그가 Player인지 검사한다.

if (other.tag == "Player")
  • 게임 오브젝트나 컴포넌트는 tag라는 변수를 제공하여 자신의 게임 오브젝트의 태그가 무엇인지 알려준다.

  • 6장에서 Player 게임 오브젝트를 식별할 수 있도록 Player 게임 오브젝트에 Player 태그를 할당했다.

  • 따라서 충돌한 상대방 게임 오브젝트의 태그 other.tag가 "Player"과 일치하면 상대방 게임 오브젝트는 Player 게임 오브젝트이다.

  • 다음으로, if 문 블록이 실행되고 상대방 게임 오브젝트로부터 PlayerController 컴포넌트를 가져온다. 어떤 게임 오브젝트의 컴포넌트를 찾아 가져오려면 GetComponent( ) 메서드를 실행한다.

PlayerController playerController = other.GetComponent<PlayerController>();

if (playerController != null)
{
	playerController.Die();
}
  • other.GetComponent<PlayerController>();가 실행되면 other의 게임 오브젝트에 추가된 PlayerController 컴포넌트를 찾아서 가져온다.

  • 가져온 PlayerController 타입의 컴포넌트를 playerController 변수에 할당했다.

  • 이제 playerController를 통해 PlayerController 컴포넌트의 Die( ) 메서드를 실행하면 상대방 플레이어가 죽게 된다.

if (playerController != null)
{
	playerController.Die();
}
  • playerController.Die();를 실행하기 전에 if (playerController != null)를 검사했다.

  • 따라서 playerController가 null이 아닌(!=) 경우에만 playerController.Die();가 실행된다.

  • 이것은 충돌한 상대방 게임 오브젝트가 PlayerController 컴포넌트를 가지고 있지 않은 경우를 대비한 것이다.

  • other의 게임 오브젝트에 PlayerController 컴포넌트가 없으면 playerController에 null이 할당된다. null은 변수에 어떠한 참조도 할당되지 못한 상태를 나타내는 값이다.

  • null이 할당된 playerController는 어떠한 실체도 가리키지 않는다. 그 상태에서 playerController.Die();를 실행하면 에러가 난다. 존재하지 않는 실체를 사용하는 것은 불가능하기 때문이다.

  • 우리는 6장에서 Player 게임 오브젝트에 분명히 PlayerController 스크립트를 컴포넌트로 추가했다. 하지만, 실수를 대비하여 코드를 견고하게 만드는 것은 좋은 일이다.


7.3.3. 완성된 전체 Bullet 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float speed = 8f;
    private Rigidbody bulletRigidbody;

    void Start()
    {
        bulletRigidbody= GetComponent<Rigidbody>();
        bulletRigidbody.velocity = transform.forward * speed;

        Destroy(gameObject, 3f);
    }
    void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
        {
            PlayerController playerController = other.GetComponent<PlayerController>();

            if (playerController != null)
            {
                playerController.Die();
            }
        }
    }
}
  • 코드를 제대로 작성했다면 저장하고 유니티 에디터로 돌아가자

7.3.4. Bullet 게임 오브젝트 완성

  • 프로젝트 창의 Bullet 스크립트를 하이어라키 창의 Bullet으로 드래그&드롭

  • 하이어라키 창의 Bullet 게임 오브젝트 선택 > 인스펙터 창 상단의 Overrides > Apply All 클릭

  • 프리팹과 연동된 게임 오브젝트 Apply All 버튼을 누르면 해당 게임 오브젝트의 변경 사항이 프리팹에 반영된다.

  • 반대로, Revert All 버튼을 누르면 게임 오브젝트의 변경 사항이 취소되고 연동된 프리팹과 같은 모습으로 되돌아간다.

  • Bullet 프리팹에 Bullet 게임 오브젝트의 변경 사항을 적용했으니 씬에 있는 Bullet 게임 오브젝트는 지우겠다. 다음부터는 코드로 Bullet 프리팹에서 실시간으로 Bullet 게임 오브젝트를 생성할 것이기 때문이다.

프리팹 편집 모드

  • 프리팹은 일반적으로 다음 두 가지 방법으로 편집할 수 있다.

1. 인스턴스를 통한 프리팹 편집

2. 프리팹 편집 모드 사용

  • 첫 번째 방법은 프리팹으로부터 생성한 게임 오브젝트의 변경 사항을 프리팹에 반영하는 것으로, 우리가 Bullet 프리팹을 만들 때 사용한 방법이다.
  • 새로운 변경 사항이 생기면 해당 게임 오브젝트를 선택했을 때 인스펙터 창에서 다음과 같이 변경 사항을 비교할 수 있다.

  • 위 그림에서 프리팹에 추가되거나 변경된 필드굵은 문자로 표시되고, 해당 필드 왼쪽 모서리에 파란 실선이 표시된다.
  • 또한 제거된 컴포넌트는 -(Removed) 표시가 붙는다. 반대로 새로 추가된 컴포넌트에는 + 표시가 붙는다.
  • 이렇게 원본 프리팹(프리팹 소스)과 복제본 게임 오브젝트 사이의 변경 사항들은 추적되고 있다. 그리고 변경 사항들은 인스펙터 창 상단의 Overrides 버튼을 눌러 오버라이드 드롭다운 창을 띄워 한번에 확인할 수 있다.
  • 오버라이드 드롭다운 창에서 Revert All 버튼을 누르면 현재 게임 오브젝트의 변경 사항이 취소되고 원본 프리팹과 같은 모습으로 되돌아간다. 반대로 Apply All 버튼을 누르면 현재 게임 오브젝트의 변경 사항이 원본 프리팹에 반영된다.
  • 만약 변경 사항들을 개별적으로 자세히 비교하거나 일부만 반영하고 싶다면 오버라이드 드롭다운 창에서 개별 컴포넌트 또는 게임 오브젝트를 클릭한다. 그러면 해당 항목에 대한 변경 사항을 확인할 수 있는 비교 창이 열린다.

  • 비교 창에서 Revert 또는 Apply를 통해 해당 변경 사항만 골라서 취소하거나 원본 프리팹에 반영할 수 있다.
  • 위와 같이 프리팹으로부터 생성한 복제본 게임 오브젝트를 편집하는 방법과 달리 프리팹 편집 모드에서 프리팹을 직접 변경할 수도 있다.
  • 이 경우 프로젝트 창에서 프리팹을 더블 클릭하거나 하이어라키 창에서 프리팹으로부터 생성된 게임 오브젝트 옆에 있는 > 표시를 클릭하면 된다.
  • 그러면 대응하는 프리팹을 편집할 수 있는 전용 씬이 열리고 편집할 프리팹이 씬 창과 하이어라키 창에 표시된다.
  • 경우에 따라서는 직전까지 열어두었던 씬과 게임 오브젝트들이 씬 창에 회색빛으로 어둡게 처리되어 프리팹 편집 모드에 함께 표시될 수 있다.

  • 프리팹 편집 모드
  • 모드에서 프리팹을 수정한 다음에는 씬 창 우측 상단의 Save 버튼을 누르거나 [Ctrl + S]를 눌러 수정 사항을 저장할 수 있다. 만약 씬 창 우측 상단의 Auto Save가 체크된 상태라면 수시로 프리팹 수정 사항이 자동 저장된다.
  • 프리팹 편집모드를 나가고 싶다면 하이어리키 창에서 프리팹 이름 옆에 있는 < 버튼을 클릭하면 된다.

다음 강의에서 계속~

profile
게임 개발을 목적으로 공부하고 있는 대학생입니다.

0개의 댓글