Excalibur.js로 RPG 캐릭터 맵 이동 구현 — 충돌 판정·물 버그·바위 뒤 숨기, 그리고 PixelLab (AI RPG 5편)

4편까지는 캐릭터를 그리는 얘기였습니다. SDXL Turbo로 맵을 만들고, Gemini와 LoRA로 캐릭터 스프라이트를 뽑았죠. 그런데 그림만 있으면 게임이 아닙니다. 이번 편의 목표는 하나예요.

캐릭터가 맵 위에서 실제로 걸어다니게 만들기.

결론부터 말하면 성공했습니다. 길을 걷고, 물가에서 멈추고, 바위 뒤로 숨고, NPC와 대화도 합니다. 그리고 마지막에 시청자 댓글로 소개받은 도구 하나 때문에 혈압이 좀 올랐습니다. 그 얘기는 맨 뒤에서 할게요.

AI로 고전RPG 만들기 5편 — 캐릭터가 맵에서 돌아다니게 해보자

게임 엔진 선택 — Excalibur.js

개발 엔진으로는 Excalibur.js를 골랐습니다. 브라우저에서 돌아가는 TypeScript 기반 2D 게임 엔진이에요. BSD 2-Clause 라이선스라 자유롭게 쓸 수 있고, 무엇보다 이름부터 고전 RPG 느낌이 납니다. 엑스칼리버라니요.

Excalibur.js 데모 실행 화면 — 브라우저 기반 2D 게임 엔진

구현은 이번에도 AI에게 맡겼습니다. 엔진 문서를 읽고 맵을 띄우고 캐릭터를 움직이는 코드까지, 제가 직접 짠 줄은 거의 없어요. 흔히 말하는 바이브 코딩입니다. 과연 AI가 잘 만들어줄까요?

광고를 클릭해주시면 블로그운영에 큰 힘이 됩니다.

첫 실행 — 엉뚱한 캐릭터, 깨진 타일맵

첫 결과는 이랬습니다. 캐릭터 이미지를 AI에게 “알아서 찾아 쓰라”고 했더니, 주인공이 아니라 4편에서 데이터셋용으로 만들었던 소녀 캐릭터가 선택돼 있더군요. 폴더에 있는 이미지 중 하나를 그냥 집어온 거예요.

첫 실행 — 주인공 대신 소녀 캐릭터가 맵 위에 올라간 상태

더 큰 문제는 맵이었습니다. 맵 전체 이미지가 하나의 타일로 처리되면서 화면이 이렇게 깨졌어요.

타일맵 처리 실패 — 통짜 맵 이미지를 타일 하나로 잘라 반복한 결과

원인은 간단합니다. Excalibur.js는 RPG 맵을 보통 타일맵(TileMap) 방식으로 다뤄요. 작은 타일 이미지를 격자로 깔아서 맵을 만드는 거죠. 그런데 제 맵은 1편에서 SDXL Turbo로 뽑은 한 장짜리 통 이미지였습니다. AI가 타일맵 방식으로 구현하다 보니 큰 이미지를 타일 하나로 보고 반복해서 찍어버린 겁니다.

해결 — 타일맵 대신 통 이미지 방식으로

복잡한 타일 방식을 버리고 이미지를 통으로 배경에 까는 방식으로 바꿨습니다. 소녀는 다음에 바꾸기로 하고 맵부터 정리했어요.

그다음 소녀 캐릭터를 주인공으로 바꾸고 다시 실행해 보니 이번엔 다른 문제가 생겼습니다. 장애물 판별 조건이 너무 까다로워서 길을 못 지나갑니다. 분명 길인데 캐릭터가 막혀서 멈춰 서 있어요.

그래서 판별 조건을 완화했습니다. 이제 길을 잘 지나갑니다.

주인공 캐릭터로 교체 후 길 위를 이동하는 모습

여기에 하나 더 넣었습니다. 장애물에 부딪혀도 방향키를 계속 누르고 있으면 옆으로 미끄러지듯 자동으로 돌아가게 했어요. 고전 RPG에서 모서리에 걸려 멈추는 답답함, 다들 아시죠. 그걸 없앤 겁니다.

물 지형 버그

그런데 이게 뭐죠?

물을 지나갈 때 버그가 있었습니다. 이것도 AI에게 수정하라고 시켜서 해결했어요. 이쯤 되니 “버그 발견 → AI에게 설명 → 수정 확인” 사이클이 꽤 손에 익습니다.

물 지형 근처 이동 — 물 영역 판정 버그를 수정한 뒤

바위 뒤에 숨기 vs 절벽 — 깊이 처리를 둘로 나누다

이번 편에서 가장 재밌었던 문제입니다.

탑다운 RPG에서 돌출형 바위는 캐릭터가 뒤로 지나가면 가려져야 해요. 캐릭터가 바위보다 위쪽(화면상 뒤쪽)에 있으면 바위가 캐릭터를 덮어야 자연스럽죠. 이걸 깊이 정렬(depth sorting) 또는 Y 정렬이라고 합니다.

그런데 똑같은 로직을 절벽에 적용하면 이상해집니다. 절벽은 지형의 일부라서 캐릭터가 그 뒤로 “숨는” 개념이 없거든요. 같은 회색 바위 픽셀인데 처리 방식은 완전히 달라야 하는 겁니다.

그래서 두 가지 경우의 이미지를 나눠서 저장하고 로직도 분리했습니다. 돌출형 바위는 캐릭터를 가릴 수 있게, 절벽은 그러지 않게요. 이렇게 나눠주니 문제가 깔끔하게 해결됐습니다.

돌출형 바위와 절벽 처리를 분리한 뒤의 이동 화면

NPC 대화창과 맵 이동

마지막으로 NPC 대화창과 맵 간 이동까지 처리했습니다. NPC에게 다가가면 하단에 대화창이 뜨고, 1편에서 목표로 잡았던 “촌장님 댁으로 가는 길” 같은 안내도 받을 수 있어요.

NPC 대화창 — "그럼 잘가~ 촌장님 댁으로 가는 길은 오른쪽 길이야."

이제 걷고, 막히고, 숨고, 말을 겁니다. 게임다운 모양이 처음으로 나왔어요.

다음 목표는 NPC 양산 — 그리고 PixelLab

다음 목표는 NPC를 많이 만드는 것이었습니다. 마을에 주민이 촌장님 하나뿐이면 좀 쓸쓸하니까요. 4편에서 LoRA + ControlNet + 리터치로 캐릭터 하나 뽑는 데 GPU 한 시간씩 걸렸던 걸 생각하면 각오가 필요한 작업이었죠.

그러던 중 시청자 댓글로 PixelLab이라는 도구를 소개받았습니다.

PixelLab — AI로 픽셀아트 게임 에셋을 만드는 도구

PixelLab은 픽셀아트 게임 에셋 생성에 특화된 AI 도구예요. 프롬프트만 쓰면 원하는 캐릭터를 픽셀아트로, 그것도 여러 방향 이미지까지 자동으로 만들어줍니다.

간단하게 “a warrior”라고 넣어봤습니다.

PixelLab에서 "a warrior" 프롬프트로 8방향 캐릭터 생성 중

…지금까지 삽질한 거 생각하니 혈압이.

1편부터 4편까지 SDXL Turbo, Gemini, SAM, OpenPose, Canny, LoRA, 리터치 자동화까지 동원해서 겨우 얻은 결과를, 프롬프트 한 줄로 비슷하게 뽑아버리네요.

이번엔 조금 더 복잡하게 금발 캐릭터의 여러 방향 로테이션 이미지를 만들어 봤습니다.

PixelLab으로 만든 금발 캐릭터 — 걷기 애니메이션 프레임

다만 완벽하진 않았어요. 걷기 애니메이션이 다소 부자연스럽습니다. 주먹이 너무 크게 그려져서, 걸을 때마다 팔을 휘두르는 모습이 마치 정권 찌르기를 하는 것처럼 보여요. 주인공 캐릭터로 쓰기엔 아쉽습니다.

그래도 NPC 제작용으로는 충분하다는 결론을 냈습니다. NPC는 주로 서 있거나 짧게 움직이니까, 걷기 애니메이션의 어색함이 크게 티 나지 않거든요.

정리 — Excalibur.js 맵 이동 구현에서 배운 것

  • Excalibur.js는 브라우저 기반 TypeScript 2D 엔진. 고전 RPG 프로토타입에 잘 맞음. 구현은 AI에게 맡겨도 기본 동작까지는 금방 나옴.
  • 통 이미지 맵은 타일맵 방식과 안 맞는다. AI가 큰 이미지를 타일 하나로 보고 반복해버림. 배경으로 통째 까는 방식이 간단.
  • 충돌 판정은 너무 엄격하면 못 걷는다. 조건을 완화하고, 장애물에서 방향키를 계속 누르면 자동으로 돌아가게 하면 조작감이 확 좋아짐.
  • 돌출형 바위와 절벽은 같은 바위처럼 보여도 깊이 처리가 다르다. 이미지와 로직을 분리해야 함.
  • AI에게 맡길 땐 “무엇을 쓰라”를 명확히. “알아서 찾아”라고 하면 폴더에 있는 아무 이미지나 집어옴.
  • PixelLab은 NPC 양산용으로 유력한 후보. 다만 걷기 애니메이션 품질은 주인공용으로는 아직 아쉬움.

시리즈 흐름

  • 1편: SDXL Turbo로 맵, SAM v3로 지형 마스크. 캐릭터는 실패
  • 2편: Gemini(Nano Banana Pro)로 캐릭터 일관성 해결. API 비용 문제
  • 3편: 로컬 OpenPose + Canny 파이프라인, 뒷모습만 Gemini 하이브리드
  • 4편: LoRA 학습 + SAM3 데이터 증강 + 리터치 자동화. GPU 병목
  • 5편(이번): Excalibur.js로 맵 이동 구현 + 시청자 추천 PixelLab 발견

다음 편에서는 PixelLab으로 NPC를 양산해서 마을을 채워볼 생각입니다.

다음 시간에 만나요. 제발.

Share: Twitter Facebook
김민석's Picture

About 김민석

항상 공부가 부족한 개발자, 항상 시간이 부족한 딸바보, 항상 체력이 부족한 부족한남편, 그리고 고양이 집사

JungNangGu, Seoul, Korea Rep https://reddol18.pe.kr

Comments