본문으로 건너뛰기
JSL107's Tech Note
돌아가기

캐릭터가 뒤로 걷는 이유를 이미지 선택 경로에서 찾다

9분 분량이 글 고치기
목차 · 7

SpriteKit 캐릭터는 목적지까지 이동했지만 통통 뛰거나 바닥을 미끄러졌고, 위로 걸을 때는 얼굴과 뒤통수가 번갈아 보였어요. 이동 중에도 앉거나 상호작용하는 자세를 유지하는 캐릭터가 있었고요. 이 글에서는 PR #567, #568, #569, #571, #573의 변경 이력을 따라 화면에 표시할 이미지를 고르는 경로를 추적해요. 프레임 속도나 이동 거리처럼 제공된 근거로 확인할 수 없는 값은 다루지 않아요.

원화 존재 여부
→ 이동 방향에 맞는 포즈 선택
→ 정지·걸음 프레임 교차
→ 상호작용 포즈와의 우선순위
→ 화면에 표시할 최종 이미지

어느 단계에서 어긋나든 사용자가 보는 모습은 비슷했어요. “뒤로 걷는다”, “미끄러진다”, “걷는 모습이 이상하다”는 보고만으로는 원인을 특정하기 어려웠죠.

걷는 그림 위에 움직임을 더했더니 도약이 됐다

초기에는 도트 캐릭터가 걷는 것처럼 보이도록 몸 전체를 위아래로 움직이는 bob 효과를 썼어요. 별도의 걸음 원화가 없던 때라 상하 흔들림과 좌우 기울임, 착지할 때의 눌림이 걷는 모습을 드러내는 주요 신호였어요. 걸음 원화가 추가되자 다리가 교차하는 프레임과 몸 전체의 상하 운동이 같은 움직임을 중복해서 표현했고, 걷는 모습이 작은 도약처럼 보였어요.

PR #567 이전부터 세 차례 조정했어요.

단계적용한 조치관찰된 결과
#542 이전bob 0.11칸, 기울임과 눌림 적용옆으로 통통 뛰는 것처럼 보임
#560기울임과 눌림 제거, bob 0.035칸으로 축소튀는 느낌이 남음
#565걸음 원화가 있는 방향에만 bob 적용사용자 보고에서 “약간 통통 뛰는 느낌”이 남음
#567OfficeScene.walk의 bob 액션과 bobHeight 제거보행 원화만으로 움직임을 표현하도록 단순화

진폭을 0.11칸에서 0.035칸으로 낮춰 약 68% 줄였는데도 증상은 사라지지 않았어요. 문제는 bob의 크기나 적용 조건이 아니라 상하 운동 자체였어요. 별도의 보행 원화가 없거나 과장된 움직임을 표현하려는 경우라면 bob은 여전히 쓸 만하지만, 이번에는 다리 교차 원화와 겹쳐 걷기를 도약처럼 보이게 했어요.

원화를 추가하는 것만으로 방향 문제가 해결되지는 않았다

bob을 제거한 뒤에는 방향별 원화를 점검했어요. 위로 이동하는 캐릭터가 앞모습을 보이면 좌표상으로는 제대로 이동해도 뒷걸음질하는 것처럼 보이거든요. PR #568에서는 스무 명 전원에게 뒷모습 걸음 원화를 제공했어요.

이 과정에서 에셋 파일명과 실제 그림의 의미가 어긋난 것도 발견했어요. 일곱 명의 뒷모습 원화로 요청한 파일은 -walk-up이었지만, 받은 파일에는 앞모습 위치에 쓰는 -walk라는 이름이 붙어 있었어요. 그림 자체는 뒷모습이었고요. 그대로 반영했다면 위쪽 보행을 고치는 대신 아래쪽 보행이 깨졌을 거예요.

필요한 배치
agent-N-walk.png     → 정면 걸음
agent-N-walk-up.png  → 후면 걸음
잘못된 배치
agent-N-walk.png     → 후면 걸음

에셋이 있는지만 검사해서는 이 문제를 찾기 어려워요. 파일명, 포즈 계약, 실제 픽셀이 나타내는 방향이 모두 일치해야 해요. 정면 원화가 없을 때 후면 원화로 대신하는 규칙도 결함을 숨길 수 있어요. 이미지가 비어 보이지는 않아도 캐릭터가 뒤로 걷는 오류로 이어지니까요.

같은 PR에서는 착석 원화를 쓰는 범위도 바로잡았어요. sit은 허리 아래가 없는 그림이라 책상 상판이 하반신을 가려 주는 자리에서만 자연스러워요. 소파나 회의 테이블 앞에 그대로 쓰자 상반신만 바닥에 떠 있는 듯 보였어요. sit과 가구 앞 착석을 뜻하는 sitting을 같은 이름으로 합쳐 장면마다 다른 차폐 조건을 구분하지 못한 게 원인이었어요. 포즈 이름에는 방향뿐 아니라 어떤 가구가 신체 일부를 가리는지도 담겨 있었던 거죠.

절반의 프레임만 방향을 지키고 있었다

전원에게 뒷모습 원화를 붙였는데도 “여전히 뒤로 걷는다”는 보고가 들어왔어요. CharacterNode.currentPose()는 walkStep의 홀짝에 따라 걸음 그림과 정지 그림을 번갈아 반환했어요. 아래로 걸을 때는 두 그림 모두 앞모습이라 문제가 없었지만, 위로 걸을 때는 달랐어요.

아래로 이동
idle(앞모습) ↔ walk(앞모습)
→ 방향이 유지된다.
위로 이동
idle(앞모습) ↔ walk-up(뒷모습)
→ 걸음마다 얼굴과 뒤통수가 교차한다.

방향에 맞는 walk-up은 전체 보행 프레임의 절반에서만 쓰였어요. 나머지 절반에는 방향 개념이 없는 cozyIdlePose, 곧 앞모습 정지 그림이 들어갔어요. PR #569에서는 위로 걷는 동안 walk-up 한 장을 계속 쓰도록 바꿨어요. 정지 뒷모습 원화가 없는 상태에서 프레임을 교차하면 얼굴이 섞이므로, 애니메이션보다 방향이 일관되게 보이는 쪽을 택했어요.

변경 전: idle ↔ walk-up
방향 불일치 / 다리 교차
임시 해법: walk-up 고정
방향 일치 / 다리 정지

다리가 멈춰 보이는 품질 저하를 감수한 절충이었어요. 이전 테스트는 포즈 계약이 올바른 방향의 그림을 반환하는지 확인했지만, 그 그림이 애니메이션의 어느 프레임에서 얼마나 자주 쓰이는지는 검증하지 않았어요. 개별 반환값 테스트를 통과해도 시간에 따라 이어지는 프레임 열은 잘못될 수 있었어요.

정지 뒷모습을 추가해 프레임 교차를 되살렸다

PR #571에서는 스무 명의 정지 뒷모습 원화 agent-{N}-walk-up-idle.png를 추가했어요. 이제 위쪽 보행도 방향을 유지하면서 두 프레임을 교차할 수 있었어요.

#569
walk-up 고정
→ 방향 유지
→ 다리 움직임 없음
#571
walk-up-idle ↔ walk-up
→ 두 프레임 모두 후면
→ 정지 다리와 내디딘 다리가 교차

교차 규칙을 안전하게 되살리는 데 필요한 후면 정지 원화를 먼저 갖춘 뒤 동작을 확장했어요. 포즈 계약과 에셋 검사, 회귀 테스트에도 새 포즈를 포함했고요. 필요한 원화 없이 애니메이션 규칙부터 일반화하면 방향이 잘못된 그림이 섞이고, 원화만 추가하고 선택 경로를 바꾸지 않으면 새 에셋은 쓰이지 않아요.

후면을 완성했더니 정면의 빈자리가 보였다

위쪽 보행을 정리한 뒤에는 아래쪽과 좌우 보행의 불균형이 남았어요. 후면 걸음 원화는 스무 명 모두 갖고 있었지만 정면 걸음 원화는 일곱 명에게만 있었어요. 나머지 열세 명은 아래나 좌우로 이동할 때 걸음 프레임 대신 정지 그림을 썼고, 몸의 기울기만 남아 바닥을 미끄러지는 것처럼 보였어요.

방향과 대상PR #573 이전 상태
위로 이동하는 20명후면 방향 유지, 다리 교차
아래·좌우로 이동하는 7명정면 방향 유지, 다리 교차
아래·좌우로 이동하는 13명정지 그림으로 이동해 미끄러져 보임

PR #573에서는 0, 4~15번 캐릭터의 정면 걸음 원화 열세 장을 추가했어요. 이로써 스무 명 모두 정면과 후면의 정지·걸음 프레임을 갖췄어요. CozyAssetCheck의 frontWalkIndices도 일부 명단만이 아니라 전원을 검사하도록 확장했어요.

원화를 모두 갖춘 뒤에도 방향 간 대체 금지 규칙은 유지했어요. 나중에 한쪽 에셋이 누락됐을 때 반대 방향 그림을 조용히 선택하면 뒷걸음질 문제가 다시 나타나기 때문이에요. hasAsset을 합성한 가상 조건에서도 정면 원화가 없을 때 후면 원화로 넘어가지 않는지 회귀 테스트로 확인했어요. 테스트에는 현재 에셋이 있는지뿐 아니라 일부 입력이 빠졌을 때 무엇을 선택하는지도 포함했어요.

마지막 관문은 activeInteractionPose였다

원화와 프레임 교차를 모두 바로잡았는데도 일부 캐릭터는 상호작용 자세로 움직였어요. currentPose()가 올바른 걸음 이미지를 계산해도 화면에 표시할 최종 이미지를 정할 때 activeInteractionPose의 우선순위가 더 높으면 보행 포즈를 쓰지 않아요.

이동 방향 판정
→ 정면·후면 포즈 선택
→ walkStep에 따른 정지·걸음 교차
→ activeInteractionPose 우선 적용
→ 최종 이미지 표시

이동을 시작하기 전에 활성 상호작용 자세를 끊도록 정비했어요. 이동 중에도 상호작용 포즈가 남아 있으면 캐릭터의 위치 상태는 “걷는 중”인데 이미지 상태는 “앉기 또는 상호작용 중”이 되니까요. 제공된 근거에는 이 변경의 별도 PR 번호나 정확한 조건문, 테스트 코드가 없어 상태 우선순위의 구체적인 구현 순서와 재현 수치까지는 확인할 수 없어요.

다음 보행 결함에서 확인할 것

비슷한 문제가 다시 나타나면 최종 이미지가 선택되는 경로를 거꾸로 추적할 수 있어요.

실패·중복·재시작 조건을 구체적으로 검증한 결과는 이번 근거에 포함돼 있지 않아요. 후속 작업에서는 이동 명령이 연속으로 들어오거나 중간에 취소되는 경우까지 상태 전이를 확장해 확인해야 해요.


이 글 고치기
이 글 공유하기:

이전 글
Slack 에이전트에 웹 콘솔을 붙일 때 AG-UI가 맡는 일