본문 바로가기
Unity 강의/Unity Course(2) - 절대강좌! 유니티

[Unity Course 2] 09. 레이캐스트 활용

by 첨부엉. 2024. 6. 26.
반응형
위키북스 출판사 이재현 저자님의 '절대강좌! 유니티' 책을 참고하여 필기한 내용입니다.

 

대부분의 fps게임은 총알이 날라가지 않고 발사와 동시에 적에게 명중하는 것을 알 수 있다.

레이캐스트

: 눈에 보이지 않는 광선을 쏘아서 해당 광선에 맞은 물체가 적인지 여부를 판단한 뒤 여러 가지 후처리를 하는 방식

비단 발사 로직 뿐만 아니라 감지 센서 역할과 클릭 후 이동, 또는 회전하는데도 활용됨

 

DrawRay

: 레이캐스트는 씬 뷰에서 시각적으로 표시되지 않기때문에 개발할 때는 DrawRay 함수를 이용하여 시각적으로 표시한다.

FireCtrl함수에서 스크립트 Update함수를 다음과 같이 추가한다.

private void Update()
{
    // Ray를 시각적으로 표시하기 위해 사용
    Debug.DrawRay(firePos.position, firePos.forward * 10.0f, Color.green);

    // 마우스 왼쪽 버튼을 클릭했을 때 Fire함수 호출
    if (Input.GetMouseButtonDown(0)){
        Fire();
    }
}

 

레이캐스트가 어떤 객체를 검출하기 위해서는 객체가 하나 이상의 Collider 컴포넌트를 갖고 있어야함

***Collider를 갖고 있는 모든 객체에 레이케스트가 반응함

 

모든 레이캐스트가 반응하기 때문에 특정 레이어만 검출하는 것이 물리 엔진의 부하를 줄여줌

 

Raycast, RaycastHit

ray에 맞아서 사망하는 결과를 확인하기 위해서 Instatiate 함수로 총알을 생성하는 로직은 주석처리

 

FireCtrl.cs 수정

using System.Collections;
using UnityEngine;

[RequireComponent(typeof(AudioSource))]
public class FireCtrl : MonoBehaviour
{
    // 총알 프리팹
    public GameObject bullet;
    // 총알 발사 좌표
    public Transform firePos;
    // 총소리에 사용할 오디오 음원
    public AudioClip fireSfx;

    // AudioSource 컴포넌트를 저장할 변수
    private new AudioSource audio;
    // Muzzle Flash 의 MeshRenderer 컴포넌트
    private MeshRenderer muzzleFlash;

    // Raycast 결괏값을 저장하기 위한 구조체 선언
    private RaycastHit hit;

    private void Start()
    {
        ...생략
    }

    private void Update()
    {
        // Ray를 시각적으로 표시하기 위해 사용
        Debug.DrawRay(firePos.position, firePos.forward * 10.0f, Color.green);

        // 마우스 왼쪽 버튼을 클릭했을 때 Fire함수 호출
        if (Input.GetMouseButtonDown(0)){
            Fire();

            // Ray 발사
            if(Physics.Raycast(firePos.position,
                                firePos.forward,
                                out hit,
                                10.0f,
                                1<<6 ))
            {
                Debug.Log($"Hit={hit.transform.name}");
            }
        }
    }

    void Fire()
    {
        // Bullet 프리팹을 동적으로 생성(생성할 객체, 위치, 회전)
		// Instantiate(bullet, firePos.position, firePos.rotation);
		// 총소리 발생
		audio.PlayOneShot(fireSfx, 1.0f);
		// 총구 화염 효과 코루틴 함수 호출
		StartCoroutine(ShowMuzzleFlash());

    }

    IEnumerator ShowMuzzleFlash()
    {
        ...생략
    }
}

 

RaycastHit 구조체의 주요 속성

속성 설명
collider 맞은 게임오브젝트의 Collider 반환
transform 맞은 게임오브젝트의 transform 반환
point 맞은 위치의 월드 좌푯값 반환(Vector3)
distance 발사 위치와 맞은 위치 사이의 거리
normal Ray가 맞은 표면의 법선 벡터

 

raycast 함수의 인자는 총 16가지의 사용법이 있음

우리가 사용한 인자는 아래와 같음

Physics.Raycast(발사원점, 발사방향, out 결괏값, 광선거리, 검출할_레이어)

 

Ray를 투사하여 조건에 맞는 객체에 닿으면 true 값을 반환함

 

이제 총알을 발사하지 않고 레이를 발사하기 때문에 MonsterCtrl에서 OnCollision Enter함수는 돌아가지 않게 된다. 

따라서 OnCollisionEnter 함수를 다음과 같이 변경하고 OnDamage함수를 추가함

private void OnCollisionEnter(Collision coll)
{
    if(coll.collider.CompareTag("BULLET"))
    {
        // 충돌한 총알을 삭제
        Destroy(coll.gameObject);
    }
}

// 레이캐스트를 사용해 데미지를 입히는 로직
public void OnDamage(Vector3 pos, Vector3 normal)
{
    // 피격 애니메이션 실행
    anim.SetTrigger(hashHit);

    // 총알의 충돌 지점의 법선 벡터
    Quaternion rot = Quaternion.LookRotation(normal);

    // 혈흔 효과를 생성하는 함수 호출
    ShowBloodEffect(pos, rot);

    // 몬스터의 hp 차감
    hp -= 30;
    if (hp <= 0)
    {
        state = State.DIE;
        // 몬스터가 사망했을 때 50점을 추가
        GameManager.instance.DisplayScore(50);
    }
}

 

이제 FireCtrl.cs에서 OnDamage함수를 호출한다.

private void Update()
{
    // Ray를 시각적으로 표시하기 위해 사용
    Debug.DrawRay(firePos.position, firePos.forward * 10.0f, Color.green);

    // 마우스 왼쪽 버튼을 클릭했을 때 Fire함수 호출
    if (Input.GetMouseButtonDown(0)){
        Fire();

        // Ray 발사
        if(Physics.Raycast(firePos.position,
                            firePos.forward,
                            out hit,
                            10.0f,
                            1<<6 ))
        {
            Debug.Log($"Hit={hit.transform.name}");
            hit.transform.GetComponent<MonsterCtrl>()?.OnDamage(hit.point, hit.normal);
        }
    }
}

void Fire()
{
    // Bullet 프리팹을 동적으로 생성(생성할 객체, 위치, 회전)
    Instantiate(bullet, firePos.position, firePos.rotation);
    // 총소리 발생
    audio.PlayOneShot(fireSfx, 1.0f);
    // 총구 화염 효과 코루틴 함수 호출
    StartCoroutine(ShowMuzzleFlash());
}

 

이제 총알은 시각적인 효과이기 때문에 아까 주석처리한 Instantiate() 주석을 해제한다.

 

반응형