Serverless로 GatherTown 스타일의 "Small Village" 구현 - Part 3: Tilemap으로 배경 만들기

“Serverless로 GatherTown 스타일 Small Village 구현 Part 3: Tilemap 활용해 배경 제작하는 방법과 구현 과정 상세 가이드!”
gathermetaversephaserTilemapTilesetServerlesssmall villagetiled
avatar
2025.04.12
·
9 min read

5047
  1. Serverless로 GatherTown 스타일의 "Small Village" 구현 - Part 1

  2. Serverless로 GatherTown 스타일의 "Small Village" 구현 - Part 2

  3. Serverless로 GatherTown 스타일의 "Small Village" 구현 - Part 3

이번 글에서는 Tiled와 Phaser를 사용하여 타일맵 기반 배경을 구현해 보도록 하겠습니다.

아래 링크를 클릭해 "Small Village"를 바로 방문해 보실 수 있습니다.
👉 smallvillage.netlify.app

Tilemap과 Tileset 개념

Tilemap이란?

Tilemap은 2D 게임의 배경, 지형, 구조물을 구성하기 위해 사용하는 데이터 구조로, 작은 정사각형 또는 직사각형 이미지를 격자(Grid) 형태로 배치하여 맵을 만듭니다. Tilemap은 JSON 또는 TMX 형식으로 저장되며, 각 타일이 어떤 이미지인지, 위치는 어디인지를 나타내는 정보를 포함합니다.

다음은 Super Mario Bros의 전체 맵 예시입니다. 이 맵은 Tileset의 이미지 타일을 조합하여 구성되었습니다. Tilemap은 이런 맵을 구성하는 타일 ID와 위치 정보를 포함하고 있습니다.

5048

Tileset이란?

Tileset은 Tilemap에서 사용하는 타일 이미지들의 집합입니다. 여러 개의 타일을 하나의 스프라이트 시트 형태로 묶은 이미지 파일로 제공됩니다. 게임 엔진은 Tileset에서 특정 타일을 참조하여 Tilemap에 배치합니다.

다음은 Super Mario Bros의 Tileset 예시입니다. 이 Tileset에는 풀밭, 길, 블록 등 다양한 요소가 포함되어 있으며, 타일 하나하나가 Tilemap의 일부로 사용됩니다.

5049

Small Village의 Tileset은

Small Village 프로젝트의 맵은 Serene Village - revamped - RPG Tileset을 사용해 제작되었습니다. 이 Tileset은 RPG 스타일의 마을을 만들기에 적합한 타일을 포함하고 있으며, 게임의 분위기를 한층 더 풍부하게 만들어줍니다.

다음은 Serene Village Tileset의 일부입니다. 여기에는 길, 나무, 건물, 꽃 등의 타일이 포함되어 있어 다양한 맵을 제작할 수 있습니다.

5050

Tiled로 맵 생성하기

Tiled란 무엇인가?

Tiled는 2D 타일 기반 게임 맵을 제작할 수 있는 오픈소스 도구입니다. 간단한 레이아웃부터 복잡한 맵까지 직관적으로 제작이 가능합니다.

새 맵 생성

  • Tiled를 열고 File > New > New Map를 클릭합니다.

  • 타일 크기를 설정합니다. tileset의 크기에 맞게 넣어줍니다.

  • 맵 크기도 정해줍니다.

  • Tile layer format은 Base64 uncompressed로 선택합니다.

5051

Tileset 추가

  • File > New > New Tileset을 선택하여 사용할 tileset 이미지를 추가합니다.

  • tileset 크기는 맵의 타일 크기와 동일해야 합니다.

  • Embed in map을 체크해줍니다.

5052

Tilemap 로딩

Tilemap을 Phaser로 로딩하려면, Tileset 이미지와 Tiled로 생성한 맵 파일을 각각 로드하고 연결해야 합니다. 이 과정에서 Phaser의 tilemapTiledJSON()addTilesetImage() 메서드를 활용합니다.

addTilesetImage()의 첫 번째 매개변수는 Tiled에서 Tileset을 생성할 때 입력한 이름입니다. (예: "Serene_Village_32x32") 두 번째 매개변수는 Phaser에서 로드한 Tileset 이미지의 키입니다. (예: "serene-village")

preload() {
  // map
  this.load.image(
    "serene-village",
    "/assets/tilesets/Serene_Village_32x32.png"
  );
  this.load.tilemapTiledJSON("default", "/assets/tilemaps/default.json");
}

async create() {
  // map
  const map = this.make.tilemap({ key: "default", tileWidth: 32, tileHeight: 32 });
  const tileset = map.addTilesetImage("Serene_Village_32x32", "serene-village");

  const groundLayer = map.createLayer("ground", tileset, 0, 0);
  const decoration0Layer = map.createLayer("decoration_0", tileset, 0, 0);
  const decoration1Layer = map.createLayer("decoration_1", tileset, 0, 0);
  const decoration2Layer = map.createLayer("decoration_2", tileset, 0, 0);
  const above0Layer = map.createLayer("above_0", tileset, 0, 0);
  const above1Layer = map.createLayer("above_1", tileset, 0, 0);
  
  above0Layer.setDepth(10);
  above1Layer.setDepth(11);

}

Tiled로 작업할 때, 레이어를 여러 개로 분리하는 것은 일반적인 디자인 패턴입니다. 이렇게 하면 맵 요소를 "깊이"에 따라 배치할 수 있어 시각적 효과와 상호작용을 더 자연스럽게 구현할 수 있습니다.

예를 들어 집 앞쪽에 우체통, 집 뒤쪽에 나무들을 겹쳐서 배치할 수 있습니다. 또한 캐릭터가 집/나무 뒤쪽에 있을 때는 집/나무가 캐릭터 위쪽에 표시되도록 할 수 있습니다.

기본적으로 레이어의 순서는 생성된 순서에 따라 결정됩니다. 그러나 above0Layer와 above1Layer와 같이 캐릭터 위에 표시되어야 하는 레이어의 경우, setDepth() 메서드를 사용하여 깊이 값을 명시적으로 설정해 캐릭터보다 상위에 렌더링되도록 합니다.

5053

충돌처리

맵을 만들었다면, 이제 캐릭터가 물 위를 걷거나 집을 통과하지 못하도록 충돌 처리를 설정해야 합니다. 이를 위해 Tiled에서 특정 타일에 충돌 속성을 추가한 후, Phaser에서 이를 반영합니다.

Tiled에서 Tilesets 화면 하단의 Edit Tileset 버튼을 클릭해서 타일셋 편집기를 엽니다.

5054

캐릭터와 충돌처리가 되어야 하는 타일들을 선택하고 편집기 왼쪽 하단의 + 버튼을 누르고 "collides"라는 이름의 bool 속성을 추가하고 체크합니다.

5055

Tiled에서 설정한 "collides" 속성을 기반으로, Phaser에서 해당 타일과 캐릭터 간의 충돌 처리를 설정합니다.

decoration0Layer.setCollisionByProperty({ collides: true });
decoration1Layer.setCollisionByProperty({ collides: true });
decoration2Layer.setCollisionByProperty({ collides: true });

this.physics.add.collider(this.sprite, decoration0Layer);
this.physics.add.collider(this.sprite, decoration1Layer);
this.physics.add.collider(this.sprite, decoration2Layer);

개발 중 충돌 처리가 제대로 설정되었는지 확인하려면, 충돌 타일을 시각적으로 표시할 수 있습니다.

const debugGraphics = this.add.graphics().setAlpha(0.75);

decoration0Layer.renderDebug(debugGraphics, {
  tileColor: null,
  collidingTileColor: new Phaser.Display.Color(243, 134, 48, 255), // 충돌 타일 색상
  faceColor: new Phaser.Display.Color(40, 39, 37, 255), // 타일의 모서리 색상
});

캐릭터 이동 처리 시에 setVelocityX(), setVelocityY()을 사용해야 충돌 감지가 됩니다.

마치며

이번 글에서는 Tilemap을 활용해 Small Village의 기본 모습을 구현해봤습니다. Tiled로 타일맵을 디자인하고, Phaser에서 이를 로드해 맵을 구성하는 과정을 함께 살펴봤습니다. 이렇게 맵을 만들어 보니, 정말로 Gather Town과 비슷한 분위기가 느껴지네요!

맵을 제작하면서 Tilemap과 Tileset의 관계를 이해하고, 레이어를 분리하여 시각적 깊이감과 충돌 처리를 구현해 봤습니다. 특히, 집 뒤에 캐릭터가 숨거나 나무를 피해가는 모습은 단순한 기능이지만, 게임의 완성도를 크게 높여주는 중요한 요소인 것 같습니다.

프로젝트 전체 코드는 GitHub 저장소에서 확인하실 수 있습니다. https://github.com/hissinger/small-village