2D Pixel Perfect 카메라 세팅과 해상도 대응 기법

2D Pixel Perfect 카메라 세팅과 해상도 대응 기법

2D 픽셀 아트가 흐려지거나 흔들리지 않도록 카메라와 스프라이트를 정렬하는 방법을 설명합니다. 기준 해상도와 종횡비가 다른 화면을 안정적으로 처리하는 실전 설정도 함께 다룹니다.

Pixel Perfect가 필요한 이유

픽셀 아트는 한 픽셀이 화면의 정수 단위 픽셀에 정확히 대응할 때 가장 선명하게 보인다. 카메라 위치, 스프라이트 위치, 확대 배율 중 하나라도 소수점 단위로 어긋나면 텍스처가 인접한 화면 픽셀 사이에 걸친다. 이때 필터링 설정과 관계없이 가장자리가 흐려지거나 이동 중 미세하게 떨려 보일 수 있다.

Pixel Perfect의 목표는 단순히 화면을 크게 만드는 것이 아니다. 월드 좌표와 카메라 투영 결과를 일정한 픽셀 격자에 맞추고 지원하지 않는 해상도에서는 규칙적인 여백 또는 크롭을 선택하는 일이다.

먼저 정할 값: PPU와 기준 해상도

설정은 다음 세 값을 먼저 고정하면 이해하기 쉽다.

  • PPU(Pixels Per Unit): 월드 유닛 1개에 대응하는 원본 텍스처 픽셀 수
  • 기준 해상도(Reference Resolution): 게임 화면이 의도한 내부 렌더링 해상도
  • 목표 종횡비: 기준 해상도가 만드는 가로세로 비율

예를 들어 16×16 타일을 월드에서 1유닛 크기로 쓰려면 PPU를 16으로 정할 수 있다. 320×180을 기준 해상도로 선택하면 16:9 화면에서 1픽셀 단위의 화면 구성이 쉽고 1920×1080에서는 정확히 6배 확대된다.

직교 카메라의 세로 화면 범위는 다음 관계를 가진다.

Orthographic Size=기준 세로 픽셀2×PPU\text{Orthographic Size} = \frac{\text{기준 세로 픽셀}}{2 \times \text{PPU}}

따라서 PPU가 16이고 기준 세로 해상도가 180이라면 Orthographic Size는 5.625다. 이 수치 자체가 소수인 것은 문제가 아니다. 중요한 것은 최종 투영이 정수 픽셀 격자에 맞는지다.

기준 해상도 320x180과 PPU 16의 관계를 보여 주는 2D 카메라 구성 예시

Unity Pixel Perfect Camera 설정

Unity에서는 2D Pixel Perfect 패키지의 Pixel Perfect Camera 컴포넌트를 사용하는 방법이 가장 간단하다. Universal Render Pipeline의 2D Renderer를 사용하든 기본 렌더링 경로를 사용하든 핵심 설정 원칙은 같다.

카메라에 컴포넌트를 추가한 뒤 다음 항목을 프로젝트 기준으로 맞춘다.

항목권장 기준의미
Assets Pixels Per Unit스프라이트 Import Settings의 PPU카메라와 에셋의 픽셀 밀도를 일치시킨다.
Reference Resolution예: 320×180내부 픽셀 격자의 기준 크기다.
Crop Frame X/Y화면 비율 대응 정책에 따라 선택남는 영역을 검은 여백으로 처리한다.
Upscale Render Texture저해상도 렌더링이 필요할 때 활성화기준 해상도로 렌더링한 뒤 정수 배율로 확대한다.
Pixel Snapping이동하는 스프라이트에 필요할 때 활성화렌더링 시 스프라이트를 픽셀 격자에 맞춘다.

Assets Pixels Per Unit은 스프라이트의 Import Settings에 있는 Pixels Per Unit과 반드시 일치시킨다. 에셋마다 서로 다른 PPU를 사용하면 같은 월드 크기의 오브젝트라도 픽셀 격자 기준이 달라져 관리가 어려워진다.

텍스처 Import Settings도 함께 점검해야 한다.

Texture Type: Sprite (2D and UI)
Filter Mode: Point (no filter)
Compression: None
Generate Mip Maps: Off
Wrap Mode: Clamp 또는 반복 용도에 맞게 설정
Pixels Per Unit: 프로젝트 공통값

Point 필터는 픽셀 사이의 색을 보간하지 않는다. 다만 Point 필터만으로는 문제를 해결할 수 없다. 카메라나 오브젝트가 반 픽셀 위치에 놓이면 가장 가까운 픽셀을 선택하는 경계가 이동하면서 깜빡임이 생길 수 있다.

해상도 대응 정책 선택하기

기준 해상도와 실제 화면의 비율이 다를 때는 모든 화면을 빈틈없이 채우면서 원본 구도를 보존할 수 없다. 무엇을 보존할지 먼저 결정해야 한다.

레터박스와 필러박스

기준 비율을 유지하고 남는 영역에 여백을 둔다. 가로가 넓으면 좌우에 필러박스가 생기고 세로가 길면 위아래에 레터박스가 생긴다. 액션 게임처럼 화면 가장자리의 정보가 중요한 경우에 안전한 선택이다.

Unity에서는 Crop Frame XCrop Frame Y를 사용해 이 정책을 적용할 수 있다. UI도 같은 안전 영역을 기준으로 배치해야 한다. 전체 화면 캔버스에 UI를 고정하면 게임 월드는 여백 안에 있는데 UI는 여백까지 침범하는 불일치가 생길 수 있다.

화면 채우기와 크롭

화면을 여백 없이 채우는 대신 기준 화면의 일부를 잘라낸다. 세로형 화면이나 배경 연출에서 효과적일 수 있지만 적 캐릭터와 상호작용 요소가 잘리는지 확인해야 한다. 중요한 게임 정보는 화면 가장자리가 아니라 안전 영역 안에 배치하는 편이 좋다.

가변 시야

기준 세로 범위는 유지하고 넓은 화면에서는 더 많은 가로 영역을 보여 주는 방식이다. 탐험 게임에서는 자연스럽지만 울트라와이드 화면에서 플레이어가 의도보다 먼 적을 먼저 발견할 수 있다. 전투 밸런스에 영향을 주는지 검토해야 한다.

16대9 기준 화면을 4대3과 울트라와이드 화면에 표시했을 때 여백, 크롭, 가변 시야가 달라지는 예시

정수 배율과 비정수 배율

320×180을 1920×1080에 표시하면 확대 배율은 정확히 6이다. 각 원본 픽셀이 6×6 화면 픽셀 블록으로 확대되므로 가장 안정적인 결과를 얻는다.

Scale=min(화면 너비기준 너비,화면 높이기준 높이)\text{Scale} = \min\left(\frac{\text{화면 너비}}{\text{기준 너비}}, \frac{\text{화면 높이}}{\text{기준 높이}}\right)

이 값이 정수가 아니면 두 가지 선택지가 있다.

  1. 가장 가까운 낮은 정수 배율을 사용하고 남는 공간을 여백으로 처리한다.
  2. 내부 렌더 텍스처를 비정수 배율로 확대한다.

첫 번째 방식은 픽셀의 모양을 보존하지만 화면 활용률이 낮을 수 있다. 두 번째 방식은 화면을 더 채우기 쉽지만 픽셀 크기가 균일하지 않거나 가장자리가 흔들릴 수 있다. 픽셀 아트의 선명함이 우선이라면 정수 배율과 여백 처리가 일반적으로 더 예측 가능하다.

Upscale Render Texture는 기준 해상도의 렌더 텍스처를 만든 뒤 확대하는 방식이다. 셰이더, 파티클, 화면 효과까지 저해상도 스타일로 통일하려면 유용하다. 반대로 고해상도 UI나 텍스트까지 함께 낮은 해상도로 렌더링하면 가독성이 떨어질 수 있으므로 UI 카메라를 분리하는 방안도 고려할 수 있다.

카메라 이동에서 생기는 흔들림 줄이기

플레이어 위치를 그대로 카메라에 복사하는 코드는 간단하지만 물리 업데이트와 렌더링 업데이트의 시점 차이 및 소수점 좌표 때문에 흔들림이 나타날 수 있다.

using UnityEngine;

public class PixelCameraFollow : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private float pixelsPerUnit = 16f;
    [SerializeField] private float followSpeed = 12f;

    private void LateUpdate()
    {
        if (target == null) return;

        Vector3 desired = new Vector3(
            target.position.x,
            target.position.y,
            transform.position.z);

        Vector3 smooth = Vector3.Lerp(
            transform.position,
            desired,
            1f - Mathf.Exp(-followSpeed * Time.deltaTime));

        float unitPerPixel = 1f / pixelsPerUnit;
        smooth.x = Mathf.Round(smooth.x / unitPerPixel) * unitPerPixel;
        smooth.y = Mathf.Round(smooth.y / unitPerPixel) * unitPerPixel;

        transform.position = smooth;
    }
}

이 예제는 카메라 위치를 월드 픽셀 단위로 반올림한다. 다만 확대 배율과 Pixel Perfect Camera 설정에 따라 실제 화면 픽셀 격자는 달라질 수 있다. Unity의 Pixel Perfect Camera를 이미 사용 중이라면 자체 Pixel Snapping과 충돌하지 않는지 확인하고 카메라 스냅과 스프라이트 스냅 중 한 쪽을 기준으로 일관되게 적용해야 한다.

물리 기반 플레이어는 Rigidbody2D의 Interpolate 설정도 확인한다. 물리 이동은 FixedUpdate에서 발생하고 카메라는 보통 LateUpdate에서 따라가므로 보간이 없으면 프레임마다 플레이어의 이동이 계단처럼 보일 수 있다.

UI와 텍스트는 별도로 다루기

월드 스프라이트에 적합한 픽셀 격자 규칙이 UI에 항상 적합한 것은 아니다. 특히 다양한 언어를 지원하는 게임에서는 비트맵 글꼴만으로 모든 문자를 처리하기 어렵다.

  • 픽셀 폰트를 쓴다면 글꼴 크기와 Canvas Scaler 배율을 정수 단위로 유지한다.
  • 일반 폰트 UI는 별도 Overlay Canvas 또는 UI 카메라에서 렌더링한다.
  • 체력 바처럼 월드에 붙는 UI는 스프라이트와 같은 PPU 및 정렬 규칙을 적용한다.
  • 화면 가장자리 버튼과 자막은 레터박스 영역을 제외한 안전 영역을 기준으로 배치한다.

테스트 체크리스트

출시 전에는 에디터 Game 뷰만 보지 말고 여러 창 크기와 실제 디스플레이에서 확인한다.

  • 16:9, 16:10, 4:3, 울트라와이드 비율에서 구도와 UI가 의도대로 유지되는가
  • 기준 해상도의 정수 배율과 비정수 배율에서 픽셀 크기가 일관적인가
  • 플레이어와 카메라가 천천히 이동할 때 타일 경계가 흔들리지 않는가
  • 스프라이트, 타일맵, 파티클, 셰이더 효과가 같은 픽셀 규칙을 따르는가
  • 전체 화면 전환과 창 크기 변경 뒤에도 카메라 설정이 유지되는가

Pixel Perfect 카메라는 하나의 체크박스로 끝나는 기능이 아니다. PPU, 기준 해상도, 종횡비 정책, 텍스처 필터링, 카메라 이동 방식을 하나의 규칙으로 맞출 때 비로소 안정적인 화면을 만들 수 있다.

#Unity#2D#Pixel Art#Camera#Resolution

계속 읽어보기

이런 글은 어떠세요?

< Back to Logs