20230104_카메라 이동

2024. 1. 4. 22:35IT/TIL

오늘의 TIL은 게임에서의 카메라 이동과 관련된 내용인데,

 

실제로 구현하려는 내용은 카메라가 플레이어를 쫓아가면서, 일정 위치 이상으로는 가지 않는 것이다.

 

즉, 카메라가 플레이어를 쫓아가지만, 일정 범위 내에서만 움직이게 만드는 방법을 구현한 내용이다.

 

 

2D 플랫포머 게임을 만드는 과정에서, 카메라가 내가 만든 맵 이상으로 비추게되면

 

아래 이미지처럼 어색하게 보일 수 있는데

 

맵의 끝 부분(도착점이 있는 부분)이라면 이 부분 이상으로 오른쪽으로 가지 않는 것이 좀 더 부드럽게 보일 것이다.

 

따라서, 이 부분을 제한하기위해 작업을 진행하면,

 

원래는 왼쪽의 이미지처럼 플레이어의 상속되게 Main Camera를 넣어줬었는데

 

 

오른쪽 이미지처럼 새롭게 카메라 오브젝트를 만들고 여기에 스크립트를 연결하여

 

플레이어를 부드럽게 따라가는 카메라를 만들 수 있으며 동시에 원했던 카메라의 이동을 제한하게 만들 수 있다.

 

스크립트를 적어보면 아래와 같다.

이를 나눠서 살펴보면,

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    public Transform playerTransform;
    private Vector3 cameraPosition = new Vector3(0, 0, -10);

    public float cameraMoveSpeed = 5f;

    public Vector2 center;
    public Vector2 mapSize;

    private float cameraHeight;
    private float cameraWidth;

    private void Start()
    {
        cameraHeight = Camera.main.orthographicSize;
        cameraWidth = cameraHeight * Screen.width / Screen.height;

    }

    private void FixedUpdate()
    {
        CameraMovement();
    }

    private void CameraMovement()
    {
        transform.position = Vector3.Lerp(transform.position, playerTransform.position + cameraPosition, Time.deltaTime * cameraMoveSpeed);

        float lx = mapSize.x * 0.5f - cameraWidth;
        float ly = mapSize.y * 0.5f - cameraHeight;

        float clampX = Mathf.Clamp(transform.position.x, -lx + center.x, lx + center.x);
        float clampY = Mathf.Clamp(transform.position.y, -ly + center.y, ly + center.y);

        transform.position = new Vector3(clampX, clampY, -10f);
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireCube(center, mapSize);
    }
}

 

 

    public Transform playerTransform;
    private Vector3 cameraPosition = new Vector3(0, 0, -10);

    public float cameraMoveSpeed = 5f;

    public Vector2 center;
    public Vector2 mapSize;

    private float cameraHeight;
    private float cameraWidth;

 

필요한 데이터들을 선언하는 부분이다.

 

playerTransform은 public으로 선언하여 유니티 상에서 직접 연결하는 방식으로 했으며,

cameraPosition은 카메라 움직임의 Vector3를 계산하는 부분에서 z값을 -10하여 카메라의 위치를 조정하기 위한 값이다.

 

cameraMoveSpeed는 카메라가 플레이어를 따라가는 속도로, 이 속도 값을 조절하여 부드럽게 움직임을 조정할 수 있다.

 

center와 mapSize는 맵과 관련된 값으로, center는 맵의 중앙을, mapSize는 맵의 크기를 설정하는 것으로,

이 값을 조절하여 카메라의 움직임을 제한할 수 있다.

 

cameraHeight와 cameraWidth는 main camera의 크기를 조정하는 값으로,

이는 카메라의 Size에 기반하여 표시되는 크기를 조정해준다.

 

    private void Start()
    {
        cameraHeight = Camera.main.orthographicSize;
        cameraWidth = cameraHeight * Screen.width / Screen.height;

    }

 

Start에서 cameraHeight와 cameraWidth를 main 카메라의 설정에서 가져온다.

 

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireCube(center, mapSize);
    }

 

OnDrawGizmoa는 카메라의 크기를 확인할 수 있게 표시해주는 것으로

아래의 이미지와 같이 빨간색의 사각형으로 center와 mapSize를 조정하는데 도움을 주는

맵 구현 시에 도움을 주는 것으로, 실제 게임에는 표시되지 않는다.

 

이를 통하여 center와 mapSize를 조정하여 카메라의 각 끝을 맞출 수 있게 된다.

 

이후에 가장 중요한 CameraMovement 함수를 살펴보면 아래와 같다.

(이 CameraMovement는 FixedUpdate를 통해 실행되므로, Update와 달리 고정된 시간마다 작동한다는 특징을 갖게 된다)

    private void CameraMovement()
    {
        transform.position = Vector3.Lerp(transform.position, playerTransform.position + cameraPosition, Time.deltaTime * cameraMoveSpeed);

        float lx = mapSize.x * 0.5f - cameraWidth;
        float ly = mapSize.y * 0.5f - cameraHeight;

        float clampX = Mathf.Clamp(transform.position.x, -lx + center.x, lx + center.x);
        float clampY = Mathf.Clamp(transform.position.y, -ly + center.y, ly + center.y);

        transform.position = new Vector3(clampX, clampY, -10f);
    }

 

이제 함수를 살펴보면,

 

transform.position = Vector3.Lerp(transform.position, playerTransform.position + cameraPosition, Time.deltaTime * cameraMoveSpeed);

 

transform.position은 카메라의 위치로 이를 Vector3.Lerp를 이용하여 부드럽게 이동시키는데

 

Vector3를 사용하는 것은 카메라가 z = -10의 위치에서 촬영해야하므로 Vector2가 아닌 Vector3를 사용한다.

 

Vector3.Lerp 함수는 Vector3.Lerp(위치1, 위치2, 0 ~ 1 사이의 수)를 사용하는데,

 

0에 가까울수록 위치1에 가깝고, 1에 가까울수록 위치2에 가까워진다.

 

따라서 위처럼 사용하면, 지금 위치에서 플레이어의 위치로 이동하는데,

 

cameraMoveSpeed를 deltaTime을 곱한 만큼의 속도로 움직이므로,

 

현재 카메라의 위치를 (0, 0, -10)이라고 하고, 플레이어의 위치를 (10, 0, 0)으로 한다면,

 

카메라의 위치가

0.1f초 후에는 (0 + 10 * 0.1 * 5, 0, -10) -> (5, 0, -10) 이 되고

0.2f초 후에는 (5 + 5 * 0.1 * 5, 0, -10) -> (7.5, 0, -10) 이 된다.

0.3f초 후에는 (7.5 + 2.5 * 0.1 * 5, 0, -10) -> (8.75, 0, -10) 이 된다.

 

즉, 카메라의 위치가 직선으로 움직이는 것이 아닌,

 

매 fixedUpdate당 현재 위치와 카메라의 위치의 차이의 반만큼 이동하게 된다.

 

그림으로 나타내면 아래와 같은데,

0.1f마다 카메라와 플레이어의 거리가 반만큼 줄어주는 효과를 주는 것이 이 Lerf 함수이다.

 

이 방법을 사용하여 카메라를 부드럽게 이동시키고, 아래의

transform.position = new Vector3(clampX, clampY, -10f) 를 이용하여 카메라의 위치를 제한하는데,

 

Mathf.Clamp 함수는 Mathf.Clam(transform.position.x, -lx + center.x, lx + center.x)로 표현할 수 있는데

이는 transform.position.x 의 값을 -lx + center.x 에서 lx.center.x 의 범위로 제한한다는 뜻이다.

 

여기서 맵 크기에 따라서 카메라의 범위가 달라지기 때문에 lx와 center를 사용하는데,

 

위에서 설정한 Gizmos를 이용하여 mapSize와 center를 지정해주었는데,

 

lx와 ly의 경우 mapSize에 0.5f를 곱해주고 카메라의 크기만큼을 빼주는데, 이를 그림으로 표현하면 아래와 같다.

 

0.5f를 곱해준 mapSize.x에서 cameraWidth를 빼준 값이 내가 제한하고 싶은 카메라의 범위가 되므로,

 

clampX를 이 범위로 지정함으로써 카메라의 위치를 제한할 수 있게 된다.

 

다시 말해서 float clampX = Mathf.Clamp(transform.position.x, -lx + center.x, lx + center.x)로 지정함으로써

 

clampX라는 값은 transform.position.x의 값을 가지고 -(lx - center.x)에서 (lx + center.x)의 사이에 값을 넣어주므로

 

값이 -(lx - center.x)보다 작더라도 -(lx - center.x)로, (lx + center.x)보다 크더라도 (lx + center.x)로 정해준다.

 

이를 transfor.position = new Vector3(clampX, clampY, -10f)로 함으로써

 

transform.position = Vector3.Lerp()를 이용하여 변경한 위치값을 제한된 영역 안으로 넣어주는 것이다.

 

이를 통해서 카메라를 부드럽게 이동시키면서 범위를 제한시킬 수 있게되었다.

'IT > TIL' 카테고리의 다른 글

20240108_맵의 상호작용 물체 만들기  (0) 2024.01.08
20240105_함정 만들기  (0) 2024.01.08
20240103_맵 만들기  (0) 2024.01.04
20240102_유니티 미니맵  (0) 2024.01.02
20231229_Queue, Stack  (0) 2023.12.29