본문 바로가기

블로그 / 개발일지

"보이는 곳, 보이는 때에만 맞게": 억울하지 않은 피하기 게임 만들기

피하기 게임에서 가장 나쁜 경험은 피했는데 맞는 것이다. AvoidNow에서 티라노 돌진 예고, 꼬리 판정, 충격파, 온라인 입력 지연을 하나씩 점검해 고친 기록.

Salmonholic ·· 6분 읽기

밤 분위기 아레나 위쪽을 티라노 돌진 띠가 가로지르고, 여러 발자국 장판과 충격파 링이 겹쳐 있는 AvoidNow 후반 화면

AvoidNow의 규칙 문서 첫머리에는 이런 문장이 있다. 모든 위험은 경고 표시(텔레그래프) → 지연 → 피해 판정 흐름을 따른다. 레이드 게임의 장판처럼, 바닥에 빨간 자리가 먼저 그려지고 그게 차오르는 동안 피하면 된다. 규칙은 단순한데, 막상 만들어 보니 이 약속을 지키는 게 어려웠다. 그림과 판정이 조금만 어긋나도 플레이어는 “피했는데 맞았다”고 느낀다. 피하기 게임에서 이건 치명적이다. 다시 할 이유가 사라진다.

9월 27일 새벽부터 아침까지 이 어긋남을 찾아 고쳤다. 마지막 커밋 76a3a92의 제목을 그대로 옮기면 “Make every hazard hit only where and when it is visible”, 모든 위험이 보이는 곳에서, 보이는 때에만 맞게 한다는 것이다. 여기까지 온 과정을 순서대로 적는다.

원칙: 발밑 원, 그리고 그림보다 작은 판정

먼저 이미 정해 둔 것부터. 이 게임은 3/4 탑다운이라 캐릭터가 서 있는 모습으로 그려진다. 그래서 몸 전체가 아니라 발밑 그림자 자리의 원 하나로 맞았는지를 본다. 원의 반지름은 16픽셀로, 그림자보다 일부러 작게 잡았다. 결정 문서에는 “스쳤는데 맞았다”를 줄이려는 선택이라고 적혀 있다. 판정이 그림보다 크면 억울하고, 조금 작으면 “아슬아슬하게 살았다”는 기분이 든다. 그 차이를 아예 보상으로 만든 게 아슬아슬 점수다. 발자국 가장자리 34픽셀 안으로 스치고 살면 50점과 콤보를 준다.

발자국 모양도 한 번 고쳤다. 초기에는 발바닥 타원과 발가락 원 세 개를 따로 그려서, 여러 개가 겹치면 거품처럼 보였다. 품질 점검(커밋 5c01419)에서 외곽선 하나짜리 발자국 한 덩어리로 다시 그리고, 가운데 발가락이 앞으로 나온 자연스러운 세 발가락 배치로 바꿨다. 이때 Godot 쪽 판정과 서버 쪽 판정을 같은 모양으로 함께 바꿨다. 그림만 바꾸고 판정을 두면, 그게 바로 억울함의 씨앗이다.

경고 중에 보이지 않던 티라노

첫 번째로 잡은 건 티라노 돌진이다(커밋 c375910). 티라노는 아레나를 가로지르는 띠 모양 장판을 남기며 달려간다. 문제는 경고 시간 동안 티라노가 화면에 없었다는 것이다. 판정 순간에 아레나 바깥 250픽셀에서 초속 1350픽셀로 튀어 들어왔다. 게다가 오래 버틸수록 경고가 짧아지는 규칙 때문에, 후반에는 돌진 경고가 0.74초까지 줄었다. 바닥의 빨간 띠를 못 보고 있으면, 갑자기 티라노에 치인 셈이 된다.

고친 내용은 세 가지다.

  • 경고 동안 티라노가 아레나 가장자리에 서서 발을 구르며 기다린다. 머리 위에 빨간 느낌표가 깜빡인다. 느낌표와 몸은 판정 상자와 같은 자리나 그보다 앞에 그리고, 절대 뒤에 두지 않는다.
  • 미리 잡혀 있던 위험의 경고음을 경고가 시작되는 순간에 튼다. 소리가 먼저 알려 준다.
  • 돌진 경고는 난이도와 상관없이 최소 1초를 보장한다. 이 값은 Godot 쪽과 서버 쪽 규칙 파일에 같이 넣었다.

충격파가 발자국 안에서 시작하던 문제

15분 뒤 커밋 3869c7b는 “나를 노리는 발”의 충격파다. 이 발은 내 위치를 따라오다 고정되고, 내려찍은 자리에서 둥근 충격파가 퍼진다. 그런데 충격파 링이 발자국 반지름의 0.7배, 즉 발자국 안쪽에서 시작해 밖으로 커졌다. 발이 찍힌 직후 이미 판정이 끝난 발자국 안으로 걸어 들어가면, 안쪽에서 퍼지던 링에 맞았다.

거대 공룡 발이 찍힌 자리에 노란 충격파 링이 퍼지고 주변에 발자국 장판이 깔린 장면 수정 전 녹화의 충격파. 발이 찍힌 뒤 링이 퍼지는 동안, 발자국 안쪽은 안전해 보이지만 실제로는 아니었다

플레이어 입장에서는 발이 이미 내려왔고 그 자리는 끝난 자리다. 그래서 규칙을 바꿨다. 발자국은 찍히는 0.12초 동안만 피해를 주고, 링은 발자국 가장자리에서 시작하며 발자국 안쪽은 절대 치지 않는다. 덕분에 “찍힌 발자국 안으로 뛰어들어 충격파를 피하는” 새로운 요령이 생겼다. 억울함 하나를 없앴더니 공략이 하나 늘었다.

그림보다 길던 꼬리, 그림보다 앞서던 판정

아침에 한 판정 전수 점검(76a3a92)에서는 위험 종류마다 “그려지는 모양”과 “판정 모양”을 나란히 비교했다. 두 가지가 걸렸다.

하나는 꼬리 휩쓸기다. 판정이 16도짜리 고정 부채꼴이었다. 그런데 그림 속 꼬리는 뿌리가 굵고 끝으로 갈수록 가늘다. 부채꼴은 가는 끝 부분에서 그림보다 훨씬 넓었다. 꼬리 끝 옆에 서 있으면 꼬리에 닿지 않았는데도 맞았다.

가시 꼬리가 왼쪽 위에서 휘둘러지고, 꼬리가 쓸고 갈 붉은 부채꼴 경고가 아래쪽 원시인 발밑까지 깔린 장면 수정 전 녹화의 꼬리 휩쓸기. 붉은 부채꼴은 꼬리가 쓸고 갈 경고 영역이다. 당시 판정은 지금 꼬리 각도를 중심으로 한 16도 부채꼴이라, 가는 꼬리 끝 근처에서 그림보다 넓었다

이제 꼬리 판정은 그림을 따라간다. 뿌리 쪽 반굵기 55픽셀에서 끝 쪽 18픽셀로 가늘어지는 띠다. 이 두 숫자는 꼬리 그림의 불투명한 부분 폭을 재서 정했다.

다른 하나는 세로로 달리는 티라노다. 판정 상자는 머리에서 뒤로 몸길이만큼인데, 서 있는 그림은 위로 길다. 아래로 달릴 땐 그림이 상자를 따라가지 못하고 뒤처졌고, 위로 달릴 땐 경고 중에 화면 밖에 있었다. 아래로 달릴 땐 그림을 머리 쪽에, 위로 달릴 땐 꼬리 쪽에 두어 그림이 늘 상자를 덮게 하고, 그림자도 발밑으로 옮겼다. 익룡 급강하와 나를 노리는 발은 추적 위치를 “고정될 때까지”가 아니라 “떨어질 때까지” 서버가 보내게 했다. 그래야 그려진 자리와 고정된 자리가 같다.

온라인: 내 입력이 서버에 닿는 43ms

마지막은 눈에 보이지 않는 어긋남이다. AvoidNow는 싱글도 서버가 판정한다. 화면에서는 내 캐릭터를 먼저 움직여 보여 주고(예측), 서버 결과로 살짝 보정한다. 그런데 내 입력은 서버에 도착해서 적용되기까지 시간이 걸린다. 한국 서버에서 잰 입력 지연이 약 43ms였다. 화면의 장판은 서버 시각 기준으로 그리는데, 서버에서 내 몸은 43ms 전 입력으로 서 있다. 화면에서 아슬아슬하게 빠져나오거나 대시로 뚫어도, 서버에서는 아직 장판 안이다.

그래서 클라이언트가 장판을 “서버 시각 − 입력 지연”에 맞춰 그리게 했다. 장판이 입력 지연만큼 늦게 보이는 대신, 화면에서 피한 것은 서버에서도 피한 것이 된다. 지연 값은 스냅샷마다 조금씩 따라가도록 부드럽게 갱신하고, 최대 0.15초로 제한했다. 피격 표시(“-1”) 같은 서버 연출에도 서버 시각을 붙여서, 발이 내려찍히는 그림보다 “-1”이 먼저 뜨는 일이 없게 했다.

문제 전 후
티라노 경고 화면 밖, 후반 0.74초 가장자리에서 대기, 최소 1초
충격파 발자국 안 0.7r에서 시작 발자국 가장자리에서 시작
꼬리 판정 16도 고정 부채꼴 55→18px로 가늘어지는 띠
온라인 장판 시각 서버 시각 서버 시각 − 입력 지연(최대 0.15초)

돌아보면 전부 “먼저 만들고 나중에 본” 것들이다. 규칙 한 줄을 지키려면 패턴 하나를 추가할 때마다 그림과 판정을 겹쳐 보는 수밖에 없다. 이 판정이 실제로 공정한지는 AvoidNow에서 한 판 해 보면 알 수 있고, 판정을 알고 나서 쓸 수 있는 요령은 AvoidNow 공략에 모았다.

#AvoidNow#게임 디자인#판정#넷코드#피하기 게임

함께 읽으면 좋은 글

전체 글 →