GameDev/Cubidom

Cubidom 카메라 컨트롤

SMNNMN 2026. 3. 16. 00:42

저번에 만든 카메라는 타겟을 기준으로 3인칭 회전이 가능했었다. 

이번엔 클릭한 큐브를 타겟으로 변경하고, 카메라가 그 카메라를 기준으로 회전가능하며, 카메라의 확대, 축소를 만들 것이다.

 

 

타겟 변경

가장 먼저 만든 코드는 타겟을 바꾸는 코드로 우클릭으로 큐브를 클릭했을 때, 타겟을 변경하고 그를 알리는 코드를 작성했다. 

    void ChengeTarget()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // 카메라로 부터 클릭한 월드좌표까지 광선
            if (Physics.Raycast(ray, out hit, Mathf.Infinity, LayerMask.GetMask("Cube"))) // 광선에 맞은 충돌체를 담는다.
            {
                target = hit.transform;
                StartCoroutine(IChangeTarget());
                Debug.Log("타겟 변경");
            }
        }
    }

Raycast로 큐브임을 확인하고, 타겟을 변경한다.

이때 바뀐 타겟으로 이동하는 코드는 코루틴에서 작성했다.

 

이 코루틴함수에서는 타겟이 변경되었을 때, 바뀐 타겟을 기준으로 카메라위치를 재정의하는 기능을 한다. 

    IEnumerator IChangeTarget()
    {
        isMoveing = true;
        float t = 0;
        Vector3 start = transform.position;
        Vector3 end = target.position + offset;
        while(t <= 1)
        {
            transform.position = Vector3.Lerp(start,end,t);
            t += Time.deltaTime * cameraMoveSpeed;
            yield return null;
        }
        transform.position = target.position + offset;

        isMoveing = false;

        yield return null;
    }

주요 코드로는 Vector.Lerp를 사용해서 움직임을 나타냈다. 

start와 end 그리고 t라는 시간개념으로 Lerp를 만들었다. t에는 Time.deltaTime  * 계수를 곱해서 t가 1이 되면 도착되는 즉, 실행되는 움직임은 1초로 구현된다.(계수가 1초 기준하에) 

isMoving의 경우 타겟변경으로 인한 이동중 다른 타겟으로 변경할 수 없게하는 bool형 변수이다. 

 

 

Move 함수의 경우 추가 조건과, UI에 관해 추가로 구현했다. 

    void Move()
    {
        if(Input.GetMouseButton(1) && !isMoving)
        {
            UIManager.Instance.InActiveSeleteBlock();
            InputController.Instance.CancelSelete();

            yaw += Input.GetAxis("Mouse X") * Time.deltaTime * sensitivityX;
            pitch -= Input.GetAxis("Mouse Y") * Time.deltaTime * sensitivityY;

            pitch = Mathf.Clamp(pitch, -30, 80);

            Quaternion rotation = Quaternion.Euler(pitch, yaw, 0);
            offset = rotation * new Vector3(0,0,-distance);

            transform.position = target.position + offset;
            transform.rotation = rotation;
        }
    }

UIManager의 접근과 InputController의 접근은 현재 생성UI창이 활성화 된 상태에서 우클릭으로 화면을 회전하게 되면 UI창이 사라지게끔 구현했다. 

 

이 코드는 처음 우클릭을 할때, 카메라의 회전을 처음 보여지는 카메라의 회전과 같게끔 해주었다. 

    void Start()
    {
        pitch = transform.rotation.eulerAngles.x;
        yaw = transform.rotation.eulerAngles.y;
    }

 

 

카메라 확대, 축소

    void Scroll()
    {
        zoom -= Input.GetAxis("Mouse ScrollWheel") * zoomSensitivity;
        zoom = Mathf.Clamp(zoom, zoomIn, zoomOut);
        Debug.Log(zoom);
        Camera.main.orthographicSize = baseZoom + zoom;
    }

스크롤의 경우 zoom이라는 현재 마우스 스크롤값을 가진다. 

Input.GetAxis("Mouse ScrollWhell")를 통해서 마우스 스크롤 입력값을 빼준다. (스크롤을 올리면 확대가 돼야하고, 그러기 위해서는 카메라의 orthographic 크기가 작아져야 하기때문)

이후 기존 카메라 값인 baseZoom과 zoom 값을 더해서 카메라의 직교 크기를 정해준다. 

 

 

완성본

큐브를 클릭하면 카메라가 이동하며, 줌인-아웃을 할수 있다.

 

 

원래는 Cinemachine사용하려고 했는데, 아직 잘 활용 못하겠어서 일단 코드로 필요한 부분만 작성했다.

또 시간이 되게 많이 걸린 문제가 카메라가 직교방식에서 카메라의 ptich가 일정 수치 이상 내려가면 아랫면(땅)밑에 있는 스카이 박스를 보여주면서 이상한 화면이 구성됐는데, 이를 해결하기 위해서 카메라가 화면에 비추는 ViewportToWorldPoint를 사용해서 땅 이하로는 안보이게 하려고 했다. 하지만 그렇게 하면 직교라는 방식때문에 카메라가 높이있는 큐브들의 아랫면을 비추지 못하기 때문에 씬 화면 바탕렌더링 색을 단색-화이트로 해서 구현했다. 

내 생각에는 직교라는 방식에서는 구현이 안되거나 매우 어려운 문제인 것 같다.