서바이벌 게임의 재미는 성장에 있다.
몬스터를 잡으면 경험치가 떨어지고, 경험치를 모으면 레벨업, 레벨업하면 강해진다. 이 루프를 만드는 게 이번 커밋이었다.
몬스터가 죽으면 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")을 호출해서 게임을 일시정지하고 레벨업 선택 화면을 띄운다.
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는 화면 상단에 고정된 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}`);
}
레벨업 선택 후 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문이지만 게임의 난이도 곡선 전체가 여기서 나온다.
경험치 루프가 완성되고 나서 처음으로 "게임"처럼 느껴졌다.
죽이고 → 먹고 → 강해지고 → 더 강한 몬스터가 나오고. 이 루프가 돌아가기 시작하면 플레이어가 알아서 계속 하게 된다.