개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← Lion Survival

경험치와 레벨업 — 아이템과 UI

2026년 5월 20일
phaser3uilevelupgameplay

서바이벌 게임의 재미는 성장에 있다.

몬스터를 잡으면 경험치가 떨어지고, 경험치를 모으면 레벨업, 레벨업하면 강해진다. 이 루프를 만드는 게 이번 커밋이었다.

ExpUp — 경험치 아이템

몬스터가 죽으면 die 안에서 확률적으로 ExpUp을 드롭한다.

// Mob.js
if (Math.random() < this.m_dropRate) {
  new ExpUp(this.scene, this.x, this.y);
}

ExpUp은 필드에 남아있다가 플레이어가 밟으면 획득된다.

// PlayingScene.js
this.m_expUps = this.physics.add.group();
this.physics.add.overlap(
  this.m_player,
  this.m_expUps,
  this.pickExpUp,
  null,
  this
);

pickExpUp(player, expUp) {
  expUp.disableBody(true, true);
  expUp.destroy();
  this.m_expUpSound.play();
  this.m_expBar.increase(expUp.m_exp);

  if (this.m_expBar.m_currentExp >= this.m_expBar.m_maxExp) {
    pause(this, "levelup");
  }
}

disableBody(true, true)는 물리 비활성화와 동시에 화면에서 숨긴다. destroy는 메모리까지 해제한다. 순서를 지켜야 한다.

경험치가 최대치에 도달하면 pause(this, "levelup")을 호출해서 게임을 일시정지하고 레벨업 선택 화면을 띄운다.

ExpBar — 경험치 바 UI

Phaser.GameObjects.Graphics를 상속해서 직접 그린다.

draw() {
  this.clear();

  // 검은 테두리
  this.fillStyle(0x000000)
      .fillRect(this.m_x, this.m_y, Config.width, this.HEIGHT);

  // 흰 배경
  this.fillStyle(0xffffff)
      .fillRect(
        this.m_x + this.BORDER,
        this.m_y + this.BORDER,
        Config.width - 2 * this.BORDER,
        this.HEIGHT - 2 * this.BORDER
      );

  // 파란 경험치 채움
  let d = Math.floor(
    ((Config.width - 2 * this.BORDER) / this.m_maxExp) * this.m_currentExp
  );
  this.fillStyle(0x3665d5)
      .fillRect(this.m_x + this.BORDER, this.m_y + this.BORDER, d, this.HEIGHT - 2 * this.BORDER);
}

비율 계산이 핵심이다. (현재 경험치 / 최대 경험치) * 바 너비로 채워진 픽셀 수를 구한다.

setScrollFactor(0)은 카메라가 움직여도 UI가 화면에 고정되게 한다. 게임 UI는 전부 이 설정이 필요하다.

TopBar — HP와 레벨 표시

TopBar는 화면 상단에 고정된 UI로 HP와 현재 레벨을 관리한다.

decreaseHp(damage) {
  this.m_hp = Math.max(0, this.m_hp - damage);
  // HP 바 다시 그리기
  this.draw();
}

gainLevel() {
  this.m_level += 1;
  // 레벨 텍스트 업데이트
  this.m_levelText.setText(`Lv. ${this.m_level}`);
}

afterLevelUp — 레벨에 따른 변화

레벨업 선택 후 PlayingScene.afterLevelUp이 호출된다.

afterLevelUp() {
  this.m_topBar.gainLevel();

  switch (this.m_topBar.m_level) {
    case 2:
      removeOldestMobEvent(this);               // 기존 몬스터 스폰 이벤트 제거
      addMobEvent(this, 1000, "mob2", ..., 20, 0.8); // 더 강한 몬스터로 교체
      setAttackScale(this, "claw", 4);           // 발톱 크기 증가
      break;
    case 3:
      addAttackEvent(this, "catnip", 10, 2);    // 새 무기 추가
      break;
    case 5:
      addAttackEvent(this, "beam", 10, 1, 1000); // 빔 추가
      break;
    case 7:
      addMob(this, "lion", "lion_anim", 200, 0); // 보스 등장
      setBackground(this, "background2");
      break;
  }
}

레벨이 오를수록 몬스터는 강해지고, 무기는 추가되거나 강화된다. 7레벨에서 보스가 등장하면서 배경도 바뀐다. 단순한 switch문이지만 게임의 난이도 곡선 전체가 여기서 나온다.

경험치 루프가 완성되고 나서 처음으로 "게임"처럼 느껴졌다.

죽이고 → 먹고 → 강해지고 → 더 강한 몬스터가 나오고. 이 루프가 돌아가기 시작하면 플레이어가 알아서 계속 하게 된다.

← 이전 글전투 시스템 — 자동 공격과 이펙트
다음 글 →매니저 패턴 — PlayingScene을 얇게 유지하기