[압량(Amnyang)] #9. 2D 게임 배경 간단히 배치 및 카메라 추적 이동 구현하기

2022. 2. 13. 15:35Projects/Amnyang

 

 

 

이제 이 프로토타입 개발 프로젝트도 여정의 끝이 조금씩 보이기 시작하는 듯하다. 물론 아직 다음 스테이지 내부 구현도 해야 하고, 게임 시작 전 서사 또한 간단하게 구현해야 하지만 그건 그 때 생각하자.

 

디자이너 님께서 고생을 정말 많이 해주신다. 원래라면 여러 디자이너 분들이 각 파트별로 담당하시게 될 텐데, 그걸 혼자서 처리하고 계시니.... 그런 디자이너 님의 노고가 들어간 배경 및 배경 오브젝트들이 거의 다 완성이 되었다.

 

배경 이미지들(뒷산 배경 등)은 아직 진행 중이시고, 길가에 있는 배경 오브젝트(전봇대 등)들은 완성이 되었다. 그래서 그것들부터 우선적으로 배치를 하려고 했다.

 

 

 

1. 배경 배치 전, 실수한 부분을 깨닫다.

 

그런데, 초보 개발자들의 모임이라서 그런지 실수를 하고 말았다. 개발 시작 전에 단위(Unit) 개념을 공부하지 않고, 기준 단위 또한 정하지 않았었다....

Unity에서 유닛(Unit)이라는 길이 단위가 존재한다. 물리 연산은 1 유닛 당 1m로 계산하기 때문에 물리가 많이 들어간 게임은 이 점을 숙지해야 한다고 한다.

 

1 유닛

 

또한, 스프라이트(Sprite)에서 PPU(Pixels Per Unit)이란 것도 설정할 수 있다.

 

PPU

 

PPU(Pixels Per Unit)는 1 유닛(Unit) 안에 배치될 픽셀의 개수. 즉, 1 유닛에 표현되어질 픽셀의 개수

PPU가 100 이라면, "100 X 100" 사이즈의 스프라이트가 1 유닛 크기에 딱 맞게 된다.

 

100 * 100 스프라이트를 PPU 100으로 했을 경우 ( 1 유닛에 딱 맞다.)

 

100 * 100 스프라이트의 PPU를 10으로 하면, (100 / 10) * (100 / 10) = 100 유닛을 차지하게 된다.

 

100 * 100 스프라이트를 PPU 1으로 했을 경우

 

즉, Unity의 유닛(Unit) 단위에 맞출 때 이 PPU를 통해 조절하면, 트랜스폼에서 별도의 스케일 조정없이 편하고 정확하게 크기 및 거리 계산이 된다는 걸 뒤늦게 깨달아버렸다.

 

받아온 스프라이트의 픽셀 수는 정해져있는데, 스케일 값을 늘려버리면 넓은 공간에 상대적으로 적은 픽셀이 존재하게 되므로 화질이 떨어져 보일 수 밖에 없을 것이다. (이미지를 확대하면 깨져보이는 것과 비슷한 것이 아닐까 싶다.)

 

기획상에서 주인공 캐릭터의 키가 1.2m이다. 64 * 128 (대략적인 수치) 정도 크기의 스프라이트라면 도트 그래픽인 것 마냥 표현이 될 것 같으니, 좀 더 많은 픽셀 수로 표현을 해야 하겠지? 256 * 512 이면 될까?...

 

그리고 PPU를 500으로 설정하면, 1 유닛을 1m로 간주한다했으니, 1m를 조금 넘는 크기를 가지게 될 것이다. 비교할 수 있는 기준(캐릭터) 길이가 생겼으니, 배경 건물이나 오브젝트들의 크기 또한 이미지 크기 대비 PPU를 잘 조절하여 실질적인 크기 셋팅이 가능할 것이라고 생각된다.

 

또한, PPU를 사용하여 조절하는 것이 카메라 사이즈 계산을 정확하게 할 수 있으므로 유용하다. 

 

 

🔗Unity Documention에 의하면,

 

"가급적 트랜스폼의 스케일 값을 조정하지 않을 것을 권고하며, 현실 크기로 모델(여기서는 이미지)을 만들어 트랜스폼의 스케일 값을 별도로 조정하지 않도록 사용하는 것이 이상적"

 

 

특히나, 물리 시뮬레이션이 중요한 오브젝트라면 더더욱 그럴 것이라 생각된다. 위에서도 말했지만, Unity 물리 엔진은 기본적으로 월드 공간에서의 한 유닛을 1m에 해당한다고 간주한다.

 

만약 물리 시뮬레이션을 적용할 오브젝트가 굉장히 클 경우, "슬로우 모션"으로 진행되는 것처럼 보일 수 있다.

물리 시뮬레이션은 물리 엔진에 기반하여 실제로 정확히 잘 동작하고 있으나, 물리 적용 기본 단위에 비해 오브젝트의 크기가 지나치게 크기 때문에 그렇게 보이는 것이다.

 

 

물론 현재 내가 진행 중인 프로젝트의 게임은 물리 연산이 많이 들어가진 않는다. 하지만, 나중에 정확한 물리 연산이 많이 들어가는 게임을 개발할 때, 이런 부분을 몰랐다면 많이 고생했을 것 같다는 생각이 들었다.

 

초보라서 몰라 고려를 못했던 내용이 맞다. 현재 진행하고 있는 프로젝트는 트랜스폼의 스케일로 상대적인 크기 조정을 했다. 우리가 몰라서 말씀을 못 드렸기 때문에 디자이너 님 또한 이런 부분을 고려하지 못하고, 캔버스 크기를 2의 제곱수로 하지 않았을 것이다. (메모리 효율을 위해 2의 제곱 수로 설정하는 것이 좋다고 한다.)

 

처음부터 전부 다 수정하기에는 PSD, PSB 파일이 유니티에서 이미지 크기가 나오지 않아서, 전부 일일히 리소스마다 물어봐야 하는데 그것보다는 그냥 이대로 진행하는 게 나을 것 같았다.

 

 

배경 및 배경 오브젝트 설치하기 (대략적인 설치일 뿐 완성은 아님)

 

우선 이런 식으로 배치는 해놨다.

 

공포 게임이다 보니, 전역 조명 세기를 낮게 해놨는데 너무 안 보여서 잠시 올려서 캡쳐했다. 뒷 배경(산, 나무)는 아직 작업 중이라고 하셔서 검은 바탕으로 일단 해놨다.

 

 

2. 카메라가 주인공 캐릭터를 추적하여 따라가게 만들기

 

Unity에서 제공하는 시네머신(Cinemachine)을 사용하면 영화같은 카메라 촬영을 할 수 있다. 하지만, 시네머신 공부는 못 했고 스크립트로 구현해보는 게 좀 더 프로그래밍적으로 얻을 게 많을 것 같아서 도전했다. 🔗Dev GomDol 님의 영상을 참고했다. 근데, 사실상 따라치고 알고리즘 이해하는 과정을 거친 게 다다..ㅎㅎㅎ

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraManager : MonoBehaviour
{

    public Transform followingTarget;
    public float moveSpeed;
    public Vector2 canMoveAreaCenter;       // 카메라 이동 가능영역 중심
    public Vector2 canMoveAreaSize;         // 카메라 이동 가능영역 크기

    private float cameraHalfWidth;          // 카메라의 월드 공간에서의 가로 길이
    private float cameraHalfHeight;         // 카메라의 월드 공간에서의 세로 길이



    void Awake()
    {
        cameraHalfHeight = Camera.main.orthographicSize;
        cameraHalfWidth = Screen.width * cameraHalfHeight / Screen.height;

    }

    void LateUpdate()                   /* 카메라 이동은 LateUpdate()에서 처리 */
    {
        FollowTarget();
    }


    private void OnDrawGizmos()         /* 카메라가 타겟에 따라 이동이 가능한 영역을 표시 */
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireCube(canMoveAreaCenter, canMoveAreaSize);
    }


    private void FollowTarget()
    {
        Vector3 targetPosition = new Vector3(followingTarget.position.x,
                                             this.transform.position.y,
                                             this.transform.position.z);

        this.transform.position = Vector3.Lerp(this.transform.position,
                                               targetPosition,
                                               moveSpeed * Time.deltaTime);

        float restrictionAreaX = canMoveAreaSize.x * 0.5f - cameraHalfWidth;
        float clampX = Mathf.Clamp(this.transform.position.x,
                                   -restrictionAreaX + canMoveAreaCenter.x,
                                   restrictionAreaX + canMoveAreaCenter.x);

        float restrictionAreaY = canMoveAreaSize.y * 0.5f - cameraHalfHeight;
        float clampY = Mathf.Clamp(this.transform.position.y,
                                   -restrictionAreaY + canMoveAreaCenter.y,
                                   restrictionAreaY + canMoveAreaCenter.y);


        this.transform.position = new Vector3(clampX, clampY, this.transform.position.z);
    }
}

 

카메라가 이동할 수 있는 영역을 먼저 설정하고, 그 밖을 넘어가려고 하면 카메라 이동이 더 이상 안 되도록 했다.

자세한 알고리즘은 위에 걸어둔 링크 영상에 있다.

 

빨간색 영역 내부에서만 카메라 이동이 가능하다.

 

생각하고, 이것저것 찾아본 건 많은데 구현한 건 별로 없다.....ㅎㅎㅎ 프로젝트 마감일이 다가오는데...

 

 

3. 카메라 추적 이동 및 제한구역 설정 완료

 

 

728x90
반응형