Korokke 2022. 3. 26. 22:32

FPS에서 절대 빠지면 안 되는 총쏘기를 구현해 보겠다.

 

왼쪽 버튼을 눌렀을 때 총알이 나가도록 하고 싶다.

하지만 실제로 총알이 나가는 건 아니고, 메인 카메라가 바라보는 방향에 닿은 곳의 정보를 가져와서 총알 자국을 남기는 것이다.

 

이때 사용할 것이 Raycast이다.

Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);

먼저 ray에 Ray가 시작되는 지점과 앞으로 나아갈 방향을 정한다. 카메라가 바라보는 정면 방향으로 총을 쏠 것이기 때문에 시작 지점에 Camera.main.transform.position 나아갈 방향에는 Camera.main.transform.forwad를 넣는다.

Ray를 만들었으니 이제 쏴야되는데 그것은 Physics.Raycast가 담당한다. 하지만 이걸 그냥 사용하면 true랑 false만 반환하기 때문에 닿은 대상의 정보를 담을 RaycastHit 변수 역시 필요하다. 

out키워드는 해당 함수를 이쪽 변수에 반환해주세요 라는 뜻으로 이해하면 편할 것 같다.

void Update()
    {
        // 마우스 왼쪽 버튼을 누르면
        if (Input.GetButtonDown("Fire1"))
        {
            // 카메라의 위치에서 카메라의 앞방향으로 나아가는 빛을 만든다
            Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);
            // 닿은 대상의 정보를 저장할 RaycastHit hitInfo 변수를 만들어 주고
            RaycastHit hitInfo;
            // 만약 무엇인가에 닿았다면 그 정보를 hitInfo에 담는다
            if(Physics.Raycast(ray, out hitInfo))
            {
                print(hitInfo.transform.name);
            }
        }
    }

위 코드에서 hitInfo.gameobject.name이 아니라 transform.name을 썼는데 만약 .gameobject가 없다면 바로 다음에 해볼 것은 transform으로 찾는 것이다. ※transform은 gameobject의 분신같은 존재! 

 

좌클릭을 했을 때 정상적으로 오브젝트의 이름이 나오는 것을 확인할 수 있다. 

 

이제 총알 자국을 Ray가 부딪힌 곳에 나타내고 싶다. 

public GameObject bImpactFactory;

먼저 총알 자국을 담을 gameobject를 선언해주고(1), Instantiate(bImpactFactory)로 생성(2)한 것을 bImpact 변수에 담고(3) 위치를 hitInfo.point(4)로 했다.  

void Update()
    {
        // 마우스 왼쪽 버튼을 누르면
        if (Input.GetButtonDown("Fire1"))
        {
            // 카메라의 위치에서 카메라의 앞방향으로 나아가는 빛을 만든다
            Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);
            // 닿은 대상의 정보를 저장할 RaycastHit hitInfo 변수를 만들어 주고
            RaycastHit hitInfo;
            // 만약 무엇인가에 닿았다면 그 정보를 hitInfo에 담는다
            if(Physics.Raycast(ray, out hitInfo))
            {
                print(hitInfo.transform.name);

                // 총알자국을 부딪힌 곳에 남김
                GameObject bImpact = Instantiate(bImpactFactory);
                bImpact.transform.position = hitInfo.point;
            }
        }
    }

hitInfo.transform.position으로 안 하고, hitInfo.point로 한 이유는...

transform.position 은 해당 오브젝트의 정중앙을 뜻한다. 예를 들어, 힘들게 상대방의 머리에 조준해서 쏴도 상대의 정중앙인 명치에 맞게 되는 현상을 볼 수 있다. point는 정확히 Ray가 닿은 지점이기 때문에 머리를 쏘면 머리, 다리를 쏘면 다리에 총알을 박아 넣을 수 있다.  

 

 

잘 되는지 실험... 뭔가 이상하다 땅에 쏘면 잘 되는데 벽에 쏴도 총알 이펙트가 위로 튄다. 왜냐면 가져온 총알 이펙트는 자신의 foward 방향으로만 튀기 때문이다.  

bImpact.transform.forward = hitInfo.normal;

그래서 총알 이펙트의 forward를 hitInfo의 normal (수직방향)으로 설정하면 정상적으로 작동한다. 

 

 

crosshair 이미지를 넣고 플레이 해보니 정상작동 하는 것을 확인