← 전체 글로

AI 사용기

AI 활용기: 카메라 파라미터를 조율하는 대신 '항상 화면 안'을 수식으로 못박았다

턴제 전투 게임에서 캐릭터가 카메라 밖으로 사라지는 버그를 파라미터 미세조정으로 두 번 고쳤다가 세 번째로 재발했다. 세 번째는 값을 고치지 않고 '두 유닛이 항상 화면 안에 들어오는 최소 거리'를 계산하는 함수로 문제 자체를 없앴다. 게임 카메라 분야에서 같은 발상을 공식화한 사례들과 비교했다.

이 글의 목적

미세조정한 파라미터가 자꾸 재발하는 버그를 만나는 사람에게, '값을 더 잘 맞추기'와 '수학적으로 보장되는 구조로 바꾸기'가 다른 해결이라는 걸 실제 사례로 보여준다.

핵심 내용

카메라가 두 캐릭터를 놓치는 버그를 dolly/tilt 비율 같은 파라미터로 두 번 고쳤지만 다른 연출(dodge, hit)에서 계속 재발했다. 세 번째 시도에서 AI와 함께 두 유닛의 위치·크기로 필요한 최소 화면폭을 역산해 카메라 거리의 하한선을 구하는 함수(min_dist)로 바꾸자, 클로즈업 강도를 얼마로 두든 그 하한선 밑으로는 절대 못 내려가게 되어 같은 종류의 버그가 구조적으로 사라졌다. 같은 원리를 Unity Cinemachine의 Group Framing과 카메라 시야각(CPF) 교집합을 이용한 occlusion-free 다중 타깃 논문이 각각 제품·학술 쪽에서 이미 공식화해뒀다.

읽고 나서

같은 파라미터를 여러 번 손봐도 다른 상황에서 같은 버그가 재발한다면, 값을 더 정교하게 맞추기 전에 '이 값이 절대 넘으면 안 되는 경계'를 수식으로 세울 수 있는지부터 확인한다.

목차

먼저 답하면

혼자 만드는 턴제 전투 게임의 Godot 이식 작업에서, 카메라가 회피·피격 연출 중에 캐릭터를 화면 밖으로 완전히 놓치는 버그가 있었다. 처음 두 번은 “이 연출에서 카메라가 너무 세게 돈다”는 식으로 원인을 짚고 팬(pan) 비중이나 줌 강도 같은 파라미터 값을 손봤다. 고쳐진 것처럼 보였지만 다른 연출에서 같은 증상이 또 나왔다. 세 번째에는 값을 만지지 않고, “두 유닛이 항상 화면 안에 들어오려면 카메라가 최소 얼마나 떨어져 있어야 하는가”를 유닛 위치·크기·카메라 시야각으로 직접 계산하는 함수를 만들어 그 값 밑으로는 클로즈업이 아무리 강해도 못 내려가게 만들었다. 그 뒤로 같은 종류의 버그가 다시 나오지 않았다.

파라미터로 두 번 고치고, 세 번째에 재발했다

전투 연출은 회피(dodge)와 피격(hit) 이벤트마다 카메라가 캐릭터 쪽으로 돌리인(dolly-in)했다. 첫 버그 리포트는 “리베가 dodge·hit 중 화면 밖으로 완전히 사라진다”였다. 원인은 옛 HTML 프로토타입에 있던 화면 경계 클램프가 3D로 옮기면서 빠진 것이었고, _clamp_stage()(모든 이동에 스테이지 경계 적용)와 _cam_mid()(반응형 이벤트일 때 카메라가 두 유닛의 중간점을 보게 하고 돌리인 폭을 줄임)를 추가해 해결했다. 검증도 통과했다.

문제는 이 수정이 “이 이벤트에서는 팬 비중을 줄인다”는 식의 이벤트별 파라미터 조정이었다는 점이다. 다른 이벤트, 다른 카메라 강도 조합에서는 똑같은 증상이 다시 나올 여지가 남아 있었다. 실제로 재발했다.

값을 고치는 대신 “절대 못 넘는 하한선”을 만들었다

세 번째 시도에서는 접근을 바꿨다. _cam_on_cam_mid처럼 상황별로 다른 파라미터를 쓰던 함수들을 하나로 합치고, 카메라 거리를 직접 정하지 않았다. 대신:

  1. 리베와 적의 현재 위치·크기로 화면에 두 유닛을 모두 담으려면 필요한 최소 화면 폭·높이를 계산한다.
  2. 카메라의 시야각(FOV)과 화면비로, 그 폭·높이를 담으려면 카메라가 최소 얼마나 떨어져 있어야 하는지(min_dist)를 역산한다.
  3. 실제 카메라 거리는 min_dist * lerp(1.38, 1.04, tight)로 정한다. tight(클로즈업 강도)를 얼마로 두든 이 식의 결과는 min_dist 밑으로 내려가지 않는다.

클로즈업이 이전보다 아주 살짝 덜 타이트해지는 트레이드오프는 생겼지만, 두 유닛이 화면 안에 들어오는 것 자체는 연출 강도와 무관하게 항상 보장된다. 파라미터를 “잘 맞추는” 문제가 아니라 애초에 그 범위를 벗어날 수 없게 만든 것이다.

같은 발상을 게임업계가 이미 제품·논문으로 공식화해뒀다

이번 사례를 정리하며 비슷한 접근이 이미 표준화돼 있는지 찾아봤다.

  • Unity Cinemachine의 Group Framing: 여러 오브젝트를 CinemachineTargetGroup으로 묶으면, 그룹 전체의 바운딩 정보를 계산해 카메라가 줌·돌리로 항상 그룹이 프레임 안에 들어오도록 조정한다. 오브젝트별로 가중치(Weight)를 줄 수는 있지만, “이 값이면 화면 밖으로 나갈 수도 있다”는 식의 파라미터 미세조정이 아니라 프레이밍 자체가 구조적으로 보장되는 방식이라는 점이 이번에 만든 min_dist 함수와 같은 방향이다.
  • 다중 타깃 occlusion-free 카메라 제어 연구: 카메라가 두 타깃을 동시에 담으려면 각 타깃의 “카메라가 놓일 수 있는 영역(CPF)“의 교집합(Intersection Area) 안에 있어야 한다는 조건과, 두 타깃 사이의 각도가 카메라 시야각을 넘지 않아야 한다는 조건(AOV constraint)을 명시적으로 정식화한다. 값을 조정해 우연히 맞추는 게 아니라, 애초에 “이 조건을 만족하는 위치가 아니면 카메라를 두지 않는다”는 제약으로 문제를 정의한다.

두 사례 모두 “카메라가 놓치지 않게 값을 잘 맞춘다”가 아니라 “놓칠 수 없는 위치·거리의 집합을 먼저 구하고, 그 안에서만 움직인다”는 순서를 따른다. 이번에 Godot에서 만든 min_dist 함수도 결국 같은 순서였다 — 다만 규모가 훨씬 작고 카메라가 “항상 두 유닛의 중간을 향한다”는 단순한 가정 위에서 즉석으로 유도한 버전이었다.

다음에 적용할 것

같은 버그가 이벤트를 바꿔가며 재발한다면, 그건 대개 “고친 그 상황”에서만 맞는 값을 찾았다는 신호다. 다음에는 파라미터를 더 정교하게 맞추기 전에 “이 변수가 넘으면 안 되는 경계를 수식으로 세울 수 있는가”부터 확인하기로 했다. 이번처럼 기하학적으로 유도할 수 있는 경우가 아니어도, 최소한 “이 조합에서 왜 이 버그가 재발했는가”를 값이 아니라 조건의 형태로 다시 써보는 것만으로도 같은 종류의 재발을 줄일 수 있었다.

확인한 자료