본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2026. 2. 2. · 2 Views
카메라와 뷰포트 완벽 가이드
Flame 엔진의 카메라 시스템을 마스터하는 방법을 알아봅니다. 플레이어 추적, 줌 기능, 화면 경계 제한까지 게임 개발에 필수적인 카메라 제어 기법을 실무 예제와 함께 설명합니다.
목차
1. Camera 컴포넌트 이해
김개발 씨는 첫 번째 Flame 게임을 만들고 있었습니다. 캐릭터도 만들고, 배경도 그렸는데 뭔가 이상합니다.
캐릭터가 화면 밖으로 나가버리면 그냥 사라져 버리는 것이었습니다. "어떻게 하면 캐릭터를 계속 화면에 보이게 할 수 있을까?" 이때 선배 박시니어 씨가 지나가며 한마디 합니다.
"카메라를 써야지!"
CameraComponent는 게임 세계를 바라보는 눈입니다. 마치 영화 촬영 감독이 카메라를 들고 배우를 따라다니며 촬영하는 것처럼, Flame의 카메라도 게임 월드 안에서 원하는 부분만 화면에 보여주는 역할을 합니다.
Flame 1.0 이후로는 FlameGame에 기본으로 내장되어 있어서 별도 설정 없이도 바로 사용할 수 있습니다.
다음 코드를 살펴봅시다.
import 'package:flame/game.dart';
import 'package:flame/components.dart';
class MyGame extends FlameGame {
@override
Future<void> onLoad() async {
// FlameGame은 camera와 world를 기본 제공합니다
// world: 게임 오브젝트들이 존재하는 공간
// camera: world를 비추는 카메라
final player = Player();
world.add(player); // world에 플레이어 추가
// 카메라의 뷰파인더 위치 확인
print('카메라 위치: ${camera.viewfinder.position}');
}
}
김개발 씨는 입사 3개월 차 주니어 개발자입니다. 요즘 한창 Flame 엔진으로 2D 게임을 만드는 재미에 빠져 있습니다.
오늘은 넓은 맵을 가진 RPG 게임을 만들어보려고 합니다. 그런데 문제가 생겼습니다.
맵 크기를 1000x1000 픽셀로 만들었더니, 캐릭터가 조금만 움직여도 화면 밖으로 사라져 버립니다. 휴대폰 화면은 고작 400x800 픽셀 정도인데, 어떻게 이 넓은 세상을 보여줄 수 있을까요?
선배 개발자 박시니어 씨가 다가와 화면을 들여다봅니다. "아, 카메라 설정을 안 했구나.
Flame에서는 CameraComponent를 사용해서 월드를 비춰야 해." 그렇다면 CameraComponent란 정확히 무엇일까요? 쉽게 비유하자면, 카메라는 마치 드론에 달린 촬영 장비와 같습니다.
드론이 하늘에서 지상의 특정 부분만 촬영하듯이, 게임 카메라도 광활한 게임 월드 중에서 화면에 보여줄 부분만 선택해서 비춥니다. 드론의 위치를 바꾸면 촬영되는 풍경이 달라지듯, 카메라의 위치를 바꾸면 보이는 게임 세계도 달라집니다.
Flame 1.0 버전 이전에는 카메라 시스템이 꽤 복잡했습니다. 개발자가 직접 좌표를 계산하고, 화면에 그릴 위치를 일일이 조정해야 했습니다.
코드가 지저분해지기 쉬웠고, 버그도 자주 발생했습니다. 하지만 Flame 1.0 이후로는 완전히 새로운 카메라 시스템이 도입되었습니다.
FlameGame 클래스를 상속받으면 camera와 world라는 두 가지 핵심 요소가 자동으로 제공됩니다. world는 게임 오브젝트들이 살아 숨쉬는 공간이고, camera는 그 공간을 비추는 렌즈입니다.
위의 코드를 살펴보겠습니다. FlameGame을 상속받은 MyGame 클래스에서는 별도의 카메라 생성 코드가 없습니다.
그냥 바로 **world.add()**로 플레이어를 추가하면 됩니다. camera 속성도 이미 존재하기 때문에 camera.viewfinder.position으로 현재 카메라 위치를 확인할 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 모바일 러닝 게임을 개발한다고 가정해봅시다.
캐릭터가 달리면서 앞으로 계속 이동하는데, 카메라 없이는 캐릭터가 금세 화면 밖으로 사라져 버립니다. 카메라를 사용하면 캐릭터가 항상 화면 중앙에 위치하도록 만들 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 컴포넌트를 add() 대신 **world.add()**로 추가하지 않는 것입니다.
일반 add()를 사용하면 해당 컴포넌트는 카메라의 영향을 받지 않습니다. 이건 UI 요소에는 유용하지만, 게임 월드의 오브젝트라면 반드시 world에 추가해야 합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.
"아, 그래서 add() 대신 world.add()를 써야 했군요!" CameraComponent를 제대로 이해하면 광활한 게임 월드도 자유자재로 화면에 담을 수 있습니다. 이제 다음 단계로 카메라를 직접 움직여보겠습니다.
실전 팁
💡 - FlameGame을 상속받으면 camera와 world가 자동으로 생성됩니다
- 게임 오브젝트는 add()가 아닌 world.add()로 추가해야 카메라의 영향을 받습니다
- UI 요소처럼 카메라 영향을 받지 않아야 하는 컴포넌트는 일반 add()를 사용합니다
2. 카메라 움직이기
김개발 씨가 카메라의 기본 개념을 이해하고 나니, 이제 궁금한 게 생겼습니다. "그러면 이 카메라를 어떻게 움직이죠?" 게임에서는 사용자 입력에 따라 카메라가 이동하거나, 특정 이벤트가 발생하면 카메라가 다른 곳을 비춰야 할 때가 많습니다.
박시니어 씨가 웃으며 대답합니다. "viewfinder의 position을 바꾸면 돼."
카메라를 움직인다는 것은 곧 viewfinder의 위치를 바꾸는 것입니다. viewfinder는 카메라가 바라보는 지점을 의미합니다.
마치 망원경으로 특정 지점을 바라보듯이, viewfinder.position 값을 변경하면 카메라가 비추는 영역이 달라집니다. 이를 통해 스크롤, 패닝, 컷신 연출 등 다양한 카메라 효과를 구현할 수 있습니다.
다음 코드를 살펴봅시다.
class MyGame extends FlameGame with KeyboardEvents {
@override
Future<void> onLoad() async {
world.add(GameMap()); // 넓은 게임 맵
// 카메라 초기 위치 설정
camera.viewfinder.position = Vector2(200, 200);
}
// 키보드로 카메라 이동
void moveCamera(Vector2 delta) {
camera.viewfinder.position += delta;
}
// 특정 위치로 카메라 즉시 이동
void jumpToPosition(Vector2 target) {
camera.viewfinder.position = target;
}
}
김개발 씨는 전략 시뮬레이션 게임을 구상하고 있었습니다. 넓은 전장을 내려다보면서, 손가락으로 드래그하면 다른 지역을 볼 수 있어야 합니다.
마치 지도 앱에서 지도를 끌어당기듯이 말입니다. 그런데 막상 구현하려니 막막합니다.
"카메라가 있다는 건 알겠는데, 이걸 어떻게 움직이지?" 박시니어 씨가 화이트보드에 그림을 그리며 설명합니다. "카메라 시스템을 이해하려면 viewfinder라는 개념을 알아야 해.
viewfinder는 말 그대로 카메라의 뷰파인더, 즉 카메라가 바라보는 지점이야." 쉽게 비유하자면, viewfinder는 마치 CCTV의 방향과 같습니다. CCTV가 왼쪽을 향하면 왼쪽 복도가 보이고, 오른쪽으로 돌리면 오른쪽 복도가 보입니다.
viewfinder.position을 바꾸는 것은 CCTV의 촬영 방향을 바꾸는 것과 같습니다. Flame의 카메라 시스템에서 가장 중요한 속성은 camera.viewfinder.position입니다.
이 Vector2 값을 변경하면 카메라가 비추는 영역이 즉시 바뀝니다. Vector2(0, 0)이면 월드의 원점을, Vector2(500, 300)이면 (500, 300) 지점을 화면 중앙에 비춥니다.
위의 코드를 살펴보겠습니다. onLoad에서 **camera.viewfinder.position = Vector2(200, 200)**으로 초기 위치를 설정합니다.
이렇게 하면 게임이 시작될 때 (200, 200) 지점이 화면 중앙에 나타납니다. moveCamera 메서드는 현재 위치에 delta 값을 더합니다.
예를 들어 **delta = Vector2(10, 0)**을 전달하면 카메라가 오른쪽으로 10픽셀 이동합니다. 이 방식은 드래그 제스처나 키보드 입력에 반응할 때 유용합니다.
jumpToPosition 메서드는 카메라를 특정 위치로 즉시 이동시킵니다. 컷신에서 특정 지점을 보여주거나, 미니맵을 클릭해서 해당 위치로 이동할 때 사용합니다.
실제 현업에서 전략 게임을 개발한다면, 미니맵 클릭 시 jumpToPosition으로 즉시 이동하고, 화면 가장자리에 마우스를 두면 moveCamera로 천천히 스크롤되도록 구현합니다. 많은 RTS 게임들이 이런 패턴을 사용합니다.
주의할 점이 있습니다. 카메라 위치를 변경할 때 음수 좌표나 맵 바깥 좌표로 이동하면, 빈 공간이 화면에 보일 수 있습니다.
이 문제는 뒤에서 다룰 화면 경계 제한으로 해결할 수 있습니다. 김개발 씨는 코드를 수정하고 테스트해봅니다.
키보드 화살표를 누르자 화면이 부드럽게 이동합니다. "오, 진짜 움직인다!" 카메라 이동을 마스터하면 맵 탐색, 컷신 연출, 스크롤 뷰 등 다양한 기능을 구현할 수 있습니다.
하지만 매번 수동으로 카메라를 움직이는 건 번거롭습니다. 다음에는 플레이어를 자동으로 따라가는 방법을 알아보겠습니다.
실전 팁
💡 - viewfinder.position은 카메라가 바라보는 월드 좌표입니다
- += 연산으로 상대적 이동, = 연산으로 절대적 이동이 가능합니다
- 빠른 이동이 필요하면 즉시 할당, 부드러운 이동이 필요하면 update에서 점진적으로 변경합니다
3. 플레이어 따라가기
김개발 씨의 게임에서 캐릭터가 맵을 돌아다닐 수 있게 되었습니다. 그런데 여전히 불편한 점이 있습니다.
캐릭터가 움직일 때마다 카메라 위치를 수동으로 바꿔줘야 합니다. "캐릭터가 움직이면 카메라도 자동으로 따라가게 할 수는 없을까?" 박시니어 씨가 답합니다.
"follow 메서드를 쓰면 돼. 한 줄이면 끝이야."
camera.follow() 메서드는 특정 컴포넌트를 자동으로 추적하도록 설정합니다. 마치 촬영 감독이 배우에게 "저 사람 따라가!"라고 지시하는 것과 같습니다.
follow를 설정하면 해당 컴포넌트가 어디로 이동하든 카메라가 자동으로 따라가며, 그 대상이 항상 화면 중앙에 위치하게 됩니다.
다음 코드를 살펴봅시다.
class MyGame extends FlameGame {
late Player player;
@override
Future<void> onLoad() async {
player = Player();
world.add(player);
// 플레이어를 따라가도록 설정
camera.follow(player);
}
}
class Player extends SpriteComponent with HasGameRef {
@override
Future<void> onLoad() async {
sprite = await gameRef.loadSprite('player.png');
size = Vector2(64, 64);
position = Vector2(100, 100);
}
void move(Vector2 direction) {
position += direction * 5; // 카메라가 자동으로 따라옴
}
}
김개발 씨는 플랫포머 게임을 만들고 있었습니다. 마리오처럼 캐릭터가 점프하고 달리면, 카메라가 자연스럽게 따라가야 합니다.
하지만 update 메서드에서 매 프레임마다 카메라 위치를 계산하는 건 번거롭고 실수하기도 쉽습니다. "카메라야, 그냥 저 캐릭터 따라가!" 이렇게 말할 수 있다면 얼마나 좋을까요?
박시니어 씨가 웃으며 말합니다. "그게 바로 follow 메서드야.
딱 한 줄이면 돼." follow 메서드는 마치 영화 촬영에서 스테디캠 오퍼레이터가 배우를 따라 움직이는 것과 같습니다. 오퍼레이터에게 "저 배우 따라가"라고 지시하면, 배우가 어디로 가든 카메라가 따라갑니다.
개발자가 매번 "지금 배우가 여기 있으니까 카메라를 저기로 옮겨"라고 지시할 필요가 없습니다. 위의 코드를 살펴보겠습니다.
onLoad에서 player를 생성하고 world에 추가한 뒤, **camera.follow(player)**를 호출합니다. 이 한 줄만으로 플레이어 추적 기능이 완성됩니다.
Player 클래스의 move 메서드를 보면, position을 변경하는 것 외에는 아무것도 하지 않습니다. 카메라 관련 코드가 전혀 없습니다.
그럼에도 플레이어가 움직이면 카메라가 자동으로 따라갑니다. follow 설정이 매 프레임마다 카메라 위치를 업데이트해주기 때문입니다.
실제 현업에서는 다양한 장르의 게임에서 follow를 활용합니다. 플랫포머, 액션 RPG, 슈팅 게임 등 플레이어 캐릭터가 있는 대부분의 게임에서 이 기능을 사용합니다.
특히 모바일 게임에서는 화면이 작기 때문에 플레이어 추적이 필수입니다. follow를 해제하고 싶다면 어떻게 할까요?
camera.stop() 메서드를 호출하면 됩니다. 예를 들어 컷신이 시작될 때 follow를 해제하고, 컷신이 끝나면 다시 follow를 설정하는 식으로 활용할 수 있습니다.
주의할 점도 있습니다. follow 대상은 반드시 world에 추가된 컴포넌트여야 합니다.
일반 add()로 추가한 UI 요소를 follow하면 예상치 못한 동작이 발생할 수 있습니다. 김개발 씨가 코드를 실행해봅니다.
캐릭터가 움직이자 카메라가 부드럽게 따라갑니다. "와, 진짜 한 줄로 끝나네요!" follow 메서드만 있으면 플레이어 추적 기능은 간단히 구현됩니다.
하지만 때로는 더 드라마틱한 연출이 필요할 때가 있습니다. 다음에는 줌 인/아웃 기능을 알아보겠습니다.
실전 팁
💡 - camera.follow(target)으로 추적을 시작하고, camera.stop()으로 해제합니다
- follow 대상은 반드시 world에 추가된 컴포넌트여야 합니다
- 컷신이나 특별한 연출이 필요할 때는 follow를 잠시 해제하고 수동 제어합니다
4. 줌 인/아웃 구현
김개발 씨의 게임이 점점 모양새를 갖춰가고 있습니다. 그런데 보스가 등장하는 장면에서 뭔가 임팩트가 부족합니다.
"보스가 나타날 때 화면이 확 가까워지면 더 긴장감 있을 것 같은데..." 박시니어 씨가 고개를 끄덕입니다. "줌 기능을 넣어봐.
viewfinder의 zoom 속성을 조절하면 돼."
viewfinder.zoom 속성은 카메라의 확대/축소 배율을 조절합니다. 기본값 1.0은 원본 크기이고, 2.0으로 설정하면 2배 확대, 0.5로 설정하면 2배 축소됩니다.
마치 카메라 렌즈를 줌 인/줌 아웃하는 것처럼, 게임 월드를 가깝게 또는 멀리서 바라볼 수 있습니다. 보스전 연출, 미니맵 효과, 핀치 줌 등에 활용됩니다.
다음 코드를 살펴봅시다.
class MyGame extends FlameGame {
@override
Future<void> onLoad() async {
world.add(GameWorld());
// 기본 줌 레벨 설정
camera.viewfinder.zoom = 1.0;
}
// 줌 인 (가까이 보기)
void zoomIn() {
camera.viewfinder.zoom += 0.1;
}
// 줌 아웃 (멀리 보기)
void zoomOut() {
camera.viewfinder.zoom -= 0.1;
if (camera.viewfinder.zoom < 0.5) {
camera.viewfinder.zoom = 0.5; // 최소 줌 제한
}
}
// 보스 등장 연출
void bossZoomEffect() {
camera.viewfinder.zoom = 2.0; // 2배 확대
}
}
김개발 씨는 액션 게임의 보스전을 연출하고 있었습니다. 보스가 처음 등장하는 순간, 카메라가 확 가까워지면서 보스의 위압감을 보여주고 싶습니다.
콘솔 게임에서 흔히 볼 수 있는 연출이죠. "줌 기능이라...
복잡하지 않을까요?" 박시니어 씨가 고개를 젓습니다. "아니, 의외로 간단해.
viewfinder.zoom 값만 바꾸면 돼." 줌 기능은 마치 카메라 렌즈의 초점 거리를 조절하는 것과 같습니다. 망원 렌즈로 바꾸면 멀리 있는 피사체가 가깝게 보이고, 광각 렌즈로 바꾸면 더 넓은 범위가 한 화면에 담깁니다.
viewfinder.zoom도 이와 같은 원리입니다. zoom = 1.0은 기본 배율입니다.
게임 오브젝트가 원래 크기 그대로 보입니다. zoom = 2.0으로 설정하면 2배 확대되어, 64x64 픽셀 스프라이트가 화면에서 128x128 픽셀 크기로 보입니다.
반대로 zoom = 0.5면 절반 크기로 축소되어 더 넓은 영역이 화면에 들어옵니다. 위의 코드를 살펴보겠습니다.
zoomIn 메서드는 현재 zoom 값에 0.1을 더합니다. 호출할 때마다 조금씩 확대됩니다.
zoomOut 메서드는 0.1을 빼면서, 최소값 0.5 이하로 내려가지 않도록 제한합니다. zoom이 너무 작아지면 게임이 제대로 보이지 않기 때문입니다.
bossZoomEffect 메서드는 zoom을 2.0으로 즉시 설정합니다. 보스가 등장하는 순간 이 메서드를 호출하면, 화면이 확 가까워지면서 보스의 디테일이 부각됩니다.
실제 현업에서는 다양한 방식으로 줌을 활용합니다. 모바일 게임에서는 핀치 제스처로 줌 인/아웃 기능을 제공하기도 합니다.
전략 게임에서는 줌 아웃해서 전체 전장을 보다가, 특정 유닛을 선택하면 줌 인하는 방식을 사용합니다. 부드러운 줌 효과를 원한다면 어떻게 할까요?
update 메서드에서 목표 zoom 값을 향해 점진적으로 변경하면 됩니다. 예를 들어 zoom += (targetZoom - zoom) * 0.1 같은 보간 공식을 사용할 수 있습니다.
주의할 점이 있습니다. zoom 값이 0 이하가 되면 에러가 발생합니다.
항상 양수 값을 유지하도록 제한을 걸어야 합니다. 또한 zoom이 너무 크면 픽셀이 뭉개져 보기 안 좋아질 수 있으니, 최대값도 적절히 제한하는 것이 좋습니다.
김개발 씨가 보스 등장 장면을 테스트합니다. 보스가 나타나는 순간 화면이 확대되면서 긴장감이 살아납니다.
"오, 진짜 게임 같아졌어요!" 줌 기능을 마스터하면 다양한 연출이 가능해집니다. 하지만 한 가지 문제가 남았습니다.
카메라가 맵 바깥으로 벗어나면 빈 공간이 보입니다. 다음에는 이 문제를 해결하는 화면 경계 제한을 알아보겠습니다.
실전 팁
💡 - zoom = 1.0이 기본이고, 값이 클수록 확대됩니다
- 최소/최대 zoom 값을 제한해서 극단적인 확대/축소를 방지하세요
- 부드러운 줌 효과는 update에서 보간을 사용해 구현합니다
5. 화면 경계 제한
김개발 씨의 게임이 거의 완성되어 갑니다. 그런데 테스트 중에 이상한 현상을 발견했습니다.
캐릭터가 맵 끝으로 가면 카메라가 맵 바깥까지 비추면서 검은 빈 공간이 화면에 나타납니다. "이거 어떻게 막죠?" 박시니어 씨가 답합니다.
"setBounds로 카메라가 이동할 수 있는 범위를 제한하면 돼."
camera.setBounds() 메서드는 카메라가 이동할 수 있는 영역을 제한합니다. 마치 공원의 울타리처럼, 카메라가 지정된 경계 밖으로 나가지 못하도록 막아줍니다.
이를 통해 맵 바깥의 빈 공간이 화면에 노출되는 것을 방지하고, 플레이어에게 완성도 높은 게임 경험을 제공할 수 있습니다.
다음 코드를 살펴봅시다.
class MyGame extends FlameGame {
final double mapWidth = 2000;
final double mapHeight = 1500;
@override
Future<void> onLoad() async {
world.add(GameMap());
player = Player();
world.add(player);
camera.follow(player);
// 카메라 이동 범위를 맵 크기로 제한
camera.setBounds(
Rectangle.fromLTWH(0, 0, mapWidth, mapHeight),
);
}
}
// Rectangle 클래스 활용 예시
class Rectangle {
static Rect fromLTWH(double l, double t, double w, double h) {
return Rect.fromLTWH(l, t, w, h);
}
}
김개발 씨는 광활한 오픈 월드 RPG를 만들고 있었습니다. 맵 크기는 2000x1500 픽셀인데, 화면 크기는 400x800 픽셀입니다.
캐릭터가 맵 구석으로 가면 카메라도 따라가는데, 문제는 맵 바깥이 검은색으로 보인다는 것입니다. "마치 세상의 끝에 도달한 것 같아요.
이건 좀 이상한데..." 박시니어 씨가 화면을 보며 말합니다. "아, 카메라 경계를 설정 안 했구나.
setBounds를 써야 해." setBounds는 마치 동물원의 울타리와 같습니다. 동물들이 울타리 밖으로 나가지 못하듯이, 카메라도 설정된 경계 밖으로 이동할 수 없습니다.
캐릭터가 맵 끝에 있어도 카메라는 경계 안에서만 움직이므로, 항상 맵 내부만 화면에 표시됩니다. 위의 코드를 살펴보겠습니다.
camera.setBounds() 메서드에 Rectangle.fromLTWH로 만든 사각형을 전달합니다. fromLTWH는 Left, Top, Width, Height의 약자로, (0, 0) 위치에서 시작해서 가로 2000, 세로 1500 크기의 사각형을 정의합니다.
이렇게 설정하면 카메라는 이 사각형 영역 안에서만 움직입니다. 캐릭터가 (0, 0) 근처에 있어도 카메라는 화면 크기의 절반 이상 왼쪽으로 이동하지 않습니다.
결과적으로 맵 바깥의 빈 공간이 절대 화면에 나타나지 않습니다. 실제 현업에서는 맵 크기를 동적으로 가져와서 setBounds를 설정합니다.
예를 들어 타일맵을 사용한다면, 타일 개수와 타일 크기를 곱해서 전체 맵 크기를 계산한 뒤 경계로 설정합니다. setBounds를 해제하고 싶다면 **camera.setBounds(null)**을 호출하면 됩니다.
특정 구간에서 맵 바깥을 보여주는 연출이 필요하다면 이 방법을 사용할 수 있습니다. 주의할 점이 있습니다.
화면 크기보다 맵이 작으면 setBounds가 예상대로 동작하지 않을 수 있습니다. 맵은 항상 화면보다 크거나 같아야 합니다.
또한 줌 레벨에 따라 실제로 보이는 영역이 달라지므로, 줌을 사용한다면 그에 맞춰 경계를 조정해야 합니다. 김개발 씨가 setBounds를 설정하고 테스트합니다.
이제 캐릭터가 맵 끝에 가도 검은 공간이 보이지 않습니다. "완벽해요!
진짜 게임 같아졌어요!" 화면 경계 제한을 마스터하면 완성도 높은 게임을 만들 수 있습니다. 마지막으로 뷰포트 설정에 대해 알아보겠습니다.
실전 팁
💡 - 맵 크기에 맞춰 setBounds를 설정하면 빈 공간 노출을 방지합니다
- setBounds(null)로 경계 제한을 해제할 수 있습니다
- 맵 크기는 화면 크기보다 크거나 같아야 정상 동작합니다
6. 뷰포트 설정
김개발 씨가 거의 완성된 게임을 여러 기기에서 테스트해봤습니다. 그런데 이상한 문제가 생겼습니다.
아이폰에서는 잘 보이던 게임이 아이패드에서는 너무 넓게 보이고, 안드로이드 태블릿에서는 비율이 이상합니다. "기기마다 화면 크기가 다른데, 어떻게 일관된 게임 화면을 보여주죠?" 박시니어 씨가 답합니다.
"뷰포트를 제대로 설정해야 해."
Viewport는 카메라가 촬영한 영상을 실제 화면에 어떻게 표시할지 결정합니다. 마치 TV 화면 비율을 4:3이나 16:9로 맞추는 것처럼, 뷰포트를 통해 게임 화면의 크기와 비율을 제어할 수 있습니다.
FixedResolutionViewport를 사용하면 모든 기기에서 동일한 게임 해상도를 유지할 수 있습니다.
다음 코드를 살펴봅시다.
import 'package:flame/game.dart';
import 'package:flame/camera.dart';
class MyGame extends FlameGame {
MyGame() : super(
camera: CameraComponent.withFixedResolution(
width: 800,
height: 600,
),
);
@override
Future<void> onLoad() async {
world.add(GameWorld());
// 뷰포트 타입 확인
print('뷰포트: ${camera.viewport}');
}
}
// 또는 onLoad에서 설정
class MyGame2 extends FlameGame {
@override
Future<void> onLoad() async {
// 고정 해상도 뷰포트 설정
camera.viewport = FixedResolutionViewport(
resolution: Vector2(800, 600),
);
}
}
김개발 씨는 드디어 게임을 완성하고 출시 준비를 하고 있었습니다. 그런데 QA팀에서 피드백이 왔습니다.
"아이폰에서는 괜찮은데, 아이패드에서 게임 오브젝트가 너무 작아 보여요." 화면 크기가 다른 수많은 기기에서 어떻게 일관된 게임 경험을 제공할 수 있을까요? 박시니어 씨가 설명합니다.
"뷰포트라는 개념을 이해해야 해. 뷰포트는 카메라가 찍은 영상을 실제 화면에 어떻게 뿌릴지 결정하는 거야." 뷰포트는 마치 프로젝터의 스크린 설정과 같습니다.
같은 영화라도 프로젝터 설정에 따라 작은 화면에 선명하게 나올 수도 있고, 큰 화면에 흐릿하게 나올 수도 있습니다. 뷰포트는 게임 세계를 다양한 크기의 화면에 어떻게 맞출지 결정합니다.
Flame에서 가장 많이 쓰이는 것은 FixedResolutionViewport입니다. 이름에서 알 수 있듯이 해상도를 고정합니다.
예를 들어 800x600으로 설정하면, 아이폰이든 아이패드든 관계없이 게임 세계는 항상 800x600 크기로 렌더링됩니다. 실제 화면이 더 크면 자동으로 스케일업되고, 비율이 다르면 레터박스(검은 여백)가 추가됩니다.
위의 코드를 살펴보겠습니다. 첫 번째 방법은 FlameGame 생성자에서 **CameraComponent.withFixedResolution()**을 사용하는 것입니다.
width와 height를 지정하면 해당 해상도가 고정됩니다. 두 번째 방법은 onLoad에서 camera.viewport에 직접 FixedResolutionViewport를 할당하는 것입니다.
두 방법 모두 동일한 결과를 얻습니다. 실제 현업에서 모바일 게임을 개발할 때, 기준이 되는 해상도를 먼저 정합니다.
보통 16:9 비율의 해상도를 선택합니다. 그리고 FixedResolutionViewport로 이 해상도를 고정하면, 모든 기기에서 동일한 게임 경험을 제공할 수 있습니다.
뷰포트 종류는 여러 가지가 있습니다. MaxViewport는 화면 전체를 사용하되 해상도를 고정하지 않습니다.
FixedAspectRatioViewport는 비율만 고정하고 해상도는 화면에 맞춥니다. 게임 특성에 맞는 뷰포트를 선택하면 됩니다.
주의할 점이 있습니다. FixedResolutionViewport를 사용하면 실제 화면 해상도와 게임 해상도가 다를 수 있습니다.
터치 좌표를 게임 좌표로 변환할 때 이를 고려해야 합니다. Flame이 자동으로 처리해주지만, 직접 좌표 계산을 할 때는 주의가 필요합니다.
김개발 씨가 FixedResolutionViewport를 설정하고 다시 테스트합니다. 이제 아이폰이든 아이패드든 게임이 동일하게 보입니다.
"드디어 완성이다!" 지금까지 Flame 엔진의 카메라와 뷰포트 시스템을 모두 살펴보았습니다. Camera 컴포넌트 이해, 카메라 움직이기, 플레이어 따라가기, 줌 인/아웃, 화면 경계 제한, 뷰포트 설정까지.
이 여섯 가지를 마스터하면 어떤 게임이든 원하는 대로 화면을 연출할 수 있습니다.
실전 팁
💡 - FixedResolutionViewport는 모든 기기에서 동일한 해상도를 보장합니다
- 게임 특성에 따라 MaxViewport, FixedAspectRatioViewport 등을 선택할 수 있습니다
- 뷰포트 설정 후 터치 좌표 변환이 필요한 경우 주의가 필요합니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
AAA급 게임 프로젝트 완벽 가이드
Flutter와 Flame 엔진을 활용하여 AAA급 퀄리티의 모바일 게임을 개발하는 전체 과정을 다룹니다. 기획부터 앱 스토어 출시까지, 실무에서 필요한 모든 단계를 이북처럼 술술 읽히는 스타일로 설명합니다.
빌드와 배포 자동화 완벽 가이드
Flutter 앱 개발에서 GitHub Actions를 활용한 CI/CD 파이프라인 구축부터 앱 스토어 자동 배포까지, 초급 개발자도 쉽게 따라할 수 있는 빌드 자동화의 모든 것을 다룹니다.
게임 분석과 메트릭스 완벽 가이드
Flutter와 Flame으로 개발한 게임의 성공을 측정하고 개선하는 방법을 배웁니다. Firebase Analytics 연동부터 A/B 테스팅, 리텐션 분석까지 데이터 기반 게임 운영의 모든 것을 다룹니다.
게임 보안과 치팅 방지 완벽 가이드
Flutter와 Flame 게임 엔진에서 클라이언트 보안부터 서버 검증까지, 치터들로부터 게임을 보호하는 핵심 기법을 다룹니다. 초급 개발자도 쉽게 따라할 수 있는 실전 보안 코드와 함께 설명합니다.
애니메이션 시스템 커스터마이징 완벽 가이드
Flutter와 Flame 게임 엔진에서 고급 애니메이션 시스템을 구현하는 방법을 다룹니다. 스켈레탈 애니메이션부터 절차적 애니메이션까지, 게임 개발에 필요한 핵심 애니메이션 기법을 실무 예제와 함께 배워봅니다.