본문 바로가기
Unity/Basic

[Unity] 3인칭 카메라 회전, 줌 인/아웃 구현

by Blemish 2021. 6. 28.

Goal

  • 카메라 회전, 줌 인/아웃을 구현할 수 있다.

 

Unity에서 3인칭 시점 카메라 사용방법에 대해서 알아보자.

 

 

1. 게임 오브젝트 구현

오브젝트를 중심으로 회전하는 카메라를 구현하기 위해서는 먼저 타깃에 해당하는 오브젝트를 생성한 다음 카메라를 오브젝트에 Child화 한다.

게임뷰

우선 Capsule, Plane 게임오브젝트를 추가한다. 그리고 카메라의 타깃이 될 수 있는 빈 오브젝트(CameraPoint)를 추가한 다음 메인 카메라(Main Camera)를 Child화 한다.

인스펙터

 

 

2. 스크립트 구현

우선 Assest에 Script 폴더를 구현한 다음 ThirdPersonCam 스크립트를 추가

  • 카메라 회전
public class ThirdPersonCam : MonoBehaviour
{
    public Transform follow;
    Vector2 m_Input;

    void Rotate()
    {
        if (Input.GetMouseButton(0))
        {
            m_Input.x = Input.GetAxis("Mouse X");
            m_Input.y = Input.GetAxis("Mouse Y");

            if (m_Input.magnitude != 0)
            {
                Quaternion q = follow.rotation;
                q.eulerAngles = new Vector3(q.eulerAngles.x + m_Input.y * m_Speed, q.eulerAngles.y + m_Input.x * m_Speed, q.eulerAngles.z);
                follow.rotation = q;

            }
        }
    }
    
    public void LateUpdate()
    {
        Rotate();
    }
}

 우선 follow 변수에 CameraPoint 오브젝트를 저장한다. 그리고 게임 뷰에서 사용자가 마우스로 클릭한 좌표값을 확인하기 위해서 Input.GetAxis를 이용하여 x, y 좌표값을 받은 다음 m_Input에 저장한다. m_Input 벡터의 길이가 0이 아닐 때 변수 follow에 해당하는 오브젝트를 회전시킨다. 즉, 카메라의 부모 오브젝트 CameraPoint(변수 follow)가 회전하게 되면 카메라도 같이 회전하게 된다. 

 

q.eulerAngles = new Vector3(q.eulerAngles.x + m_Input.y * m_Speed, q.eulerAngles.y + m_Input.x * m_Speed, q.eulerAngles.z);

 

m_Input의 x, y는 회전 축을 의미하기 때문에 x값에는 m_Input.y의 값을 더하고 y값에는 m_Input.x 값을 더한다.

 

  • 카메라 줌
    void Zoom()
    {
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (scroll != 0)
        {
            Transform cam = Camera.main.transform;
            if (CheckRay(cam, scroll))
            {
                Vector3 targetDist = cam.transform.position - follow.transform.position;
                targetDist = Vector3.Normalize(targetDist);
                Camera.main.transform.position -= (targetDist * scroll * m_Zoom);
            }
        }

        Camera.main.transform.LookAt(follow.transform);
    }

마우스 휠을 이용하여 카메라 줌 인/아웃을 하기 위해서는 먼저 마우스 스크롤 값을 받아와야 한다. 따라서 Input.GetAxis("Mouse ScrollWheel")을 이용하여 휠 값을 받아온다. 카메라의 현재 위치와 타깃의 위치 사이의 거리 값을 구한 다음 정규화한 후 마우스 휠의 값과 줌 속도 값을 곱하여 위치를 조절하면 된다.

 

    bool CheckRay(Transform cam, float scroll)
    {
        if (Physics.Raycast(cam.position, transform.forward, out m_Hit, m_MaxRayDist))
        {
            Debug.DrawRay(cam.position, transform.forward * m_Hit.distance, Color.red);
            cam.position += new Vector3(0, 0, m_Hit.point.z);
            return false;
        }

        return true;
    }

줌 인(In)할 때 카메라가 타겟을 뚫고 들어가는 경우가 있기 때문에 Raycast를 활용하여 타깃을 뚫고 가지 못하도록 하였다.

원래는 줌 인을 할 때 Min 값을 설정하면 되지만 조금 색다르게 구현해봤다. 

 

3. 전체 소스

using UnityEngine;

public class ThirdPersonCam : MonoBehaviour
{
    public Transform follow;
    [SerializeField] float m_Speed;
    [SerializeField] float m_MaxRayDist = 1;
    [SerializeField] float m_Zoom = 3f;
    RaycastHit m_Hit;
    Vector2 m_Input;

    void Start()
    {
    }

    void Rotate()
    {
        if (Input.GetMouseButton(0))
        {
            m_Input.x = Input.GetAxis("Mouse X");
            m_Input.y = Input.GetAxis("Mouse Y");

            if (m_Input.magnitude != 0)
            {
                Quaternion q = follow.rotation;
                q.eulerAngles = new Vector3(q.eulerAngles.x + m_Input.y * m_Speed, q.eulerAngles.y + m_Input.x * m_Speed, q.eulerAngles.z);
                follow.rotation = q;

            }
        }
    }

    void Zoom()
    {
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (scroll != 0)
        {
            Transform cam = Camera.main.transform;
            if (CheckRay(cam, scroll))
            {
                Vector3 targetDist = cam.transform.position - follow.transform.position;
                targetDist = Vector3.Normalize(targetDist);
                Camera.main.transform.position -= (targetDist * scroll * m_Zoom);
            }
        }

        Camera.main.transform.LookAt(follow.transform);
    }

    public void LateUpdate()
    {
        Rotate();
        Zoom();
    }

    bool CheckRay(Transform cam, float scroll)
    {
        if (Physics.Raycast(cam.position, transform.forward, out m_Hit, m_MaxRayDist))
        {
            Debug.Log("hit point : " + m_Hit.point + ", distance : " + m_Hit.distance + ", name : " + m_Hit.collider.name);
            Debug.DrawRay(cam.position, transform.forward * m_Hit.distance, Color.red);
            cam.position += new Vector3(0, 0, m_Hit.point.z);
            return false;
        }

        return true;
    }
}

 

댓글