[2024.08.21 TIL] 로그라이크 텍스트 게임

박지영·2024년 8월 21일
0

Today I Learned

목록 보기
28/88

로그라이크 텍스트 게임 만들어보기

기획 초안

게임 특징

  • 랜덤 요소

  • 선형 구조

  • 턴제

  • 세이브 불가능

스테이지 레벨 디자인

필수 기능

  • 선형적 진행 스테이지 방식 1 ~ 10 스테이지까지

  • 스테이지 마다 플레이어 및 적 점진적 성장 (랜덤 수치로)

  • 스테이지 클리어 시 확률로 랜덤 스탯 보상

추가 기능 (하고 싶은 것)

  • 아이템 기능

  • 직업 기능

  • 크리티컬 확률 기능

  • +@

전투 디자인

필수 기능

  • 공격 - 100% 확률로 공격 최소 공격력 ~ 최대 공격력 사이 랜덤한 대미지

  • 방어 - 일정 확률로 받는 대미지를 무효로 하고 공격력의 60% 대미지로 반격

  • 도망 - 낮은 확률로 퇴각 -> 스테이지 클리어

  • 연속 공격 - 일정 확률로 공격을 두 번 연속 시행. 실패 시 턴 스킵

추가 기능 (하고 싶은 것)

  • 추가 스킬

  • 소모품 아이템

클래스 구조

플레이어 클래스 구조

  • 생성자 구조
    • 체력 - 기본 50 + 랜덤으로 0 ~ 50 추가 체력
    • 최소 공격력 - 기본 1 + 0 ~ 5 추가 공격력
    • 최대 공격력 배율 - 기본 1 + 0 ~ 2 추가 배율
    • 방어 확률 - 기본 55
    • 도망 확률 - 기본 5
    • 연속 공격 확률 - 기본 33
    • 방어 수치 - 기본 1
export class Player {
  constructor() {
    // 체력
    this.hp = 50 + Math.round(Math.random() * 50);
    // 최소 공격력
    this.damege = 1 + Math.round(Math.random() * 5);
    // 최대 공격력 배율
    this.maxDamegeMag = 1 + Math.round(Math.random() * 2);
    // 방어 확률
    this.defenseChance = 55;
    // 도망 확률
    this.runChance = 5;
    // 연속 공격 확률
    this.doubleAttackChance = 33;
    // 방어 수치
    this.defense = 1;
  }
  • 메소드 구조
  // 공격
  attack(monster) {
    // 최소 공격력 + 난수 * 공격력 편차(최소공 * 최대공 배율 - 최소공)
    const result =
      this.damege + Math.round(Math.random() * (this.damege * this.maxDamegeMag - this.damege));

    monster.hp -= result - monster.defense;

    return result;
  }

  // 방어
  counter(monster) {
    const roll = Math.random() * 100;
    const result = [];

    // 확률 체크
    if (roll < this.defenseChance) {
      let counter =
        this.damege + Math.round(Math.random() * (this.damege * this.maxDamegeMag - this.damege));

      // 60%의 대미지만
      counter = Math.round(counter * 0.6);
      monster.hp -= counter;

      result.push(true);
      result.push(counter);
    }

    return result;
  }

  // 연속 공격
  doubleAttack(monster) {
    const roll = Math.random() * 100;
    const result = [];

    // 확률 체크
    if (roll < this.doubleAttackChance) {
      result.push(true);
      // 공격 2번 실행
      result.push(this.attack(monster));
      result.push(this.attack(monster));
    }
    return result;
  }

  // 도망
  run() {
    // 25% 확률로 도망
    const run = Math.floor(Math.random() * 100);

    return run < 100 ? true : false;
  }
}

몬스터 클래스 구조

  • 생성자 구조
export class Monster {
  constructor(stage) {
    // 체력
    this.hp = 15 * stage + Math.round(Math.random() * 5 * stage);
    // 최소 공격력
    this.damege = stage + Math.round(Math.random() * 3 * stage);
    // 최대 공격력 배율
    this.maxDamegeMag = 1 + Math.round(Math.random() * 2);
    // 방어 수치
    this.defense = stage;
  }
  • 메소드 구조
  // 공격
  attack(player) {
    const result =
      this.damege + Math.round(Math.random() * (this.damege * this.maxDamegeMag - this.damege));

    player.hp -= result - player.defense;

    return result;
  }
}

게임 구조

스테이지 구조

스테이지 클리어 및 게임 종료 조건

export async function startGame() {
  console.clear();
  const player = new Player();
  let stage = 1;

  while (stage <= 10) {
    const monster = new Monster(stage);
    await battle(stage, player, monster, result, increase);
	
    // 스테이지 클리어 및 게임 종료 조건
    // 게임 종료 조건
    // 플레이어 hp가 0 이하면 게임 종료
    if (clear === false) {
      break;
    }
    // 스테이지 클리어 조건
    // 몬스터 hp가 0 이하가 되면 스테이지 클리어
    if (clear === 0) {
      stage++;
    }
  }
}

스테이지 클리어 보상 구조

  • 기본 보상
if (clear === 0) {
   stage++;
   // 기본 보상
   // 최소 공격력 +1
   // 방어력 + 1
   player.damege += 1     
   player.defense += 1;
 }
  • 랜덤성 보상
// 몬스터 hp가 0 이하가 되면 스테이지 클리어
    if (clear === 0) {
      stage++;
      // 기본 보상
      player.damege += 1;
      player.defense += 1;

      // 클리어 보상
      // 6가지 중 한가지 랜덤으로 선정
      let rn = Math.floor(Math.random() * (Object.keys(player).length - 1));

      // player 인스턴스에 키 배열의 인덱스 키 이름 구하기
      const stat = Object.keys(player)[rn];

      // 클리어 보상 랜덤 로직 시행
      switch (rn) {
        // 체력
        case 0:
          // 20 ~ 50
          increase = '체력';
          result = 20 + Math.round(Math.random() * 31);
          player[stat] += result;
          break;
        // 최소 공격력
        case 1:
          // 5 ~ 20
          increase = '최소 공격력';
          result = 5 + Math.round(Math.random() * 16);
          player[stat] += result;
          break;
        // 최대 공격력 배율
        case 2:
          // 0.1 ~ 1
          increase = '최대 공격력 배율';
          result = Math.ceil(Math.random() * 100) / 100;
          player[stat] += result;
          break;
        // 방어 확률
        case 3:
          // 3 ~ 10
          increase = '방어 확률';
          result = 3 + Math.round(Math.random() * 7);
          player[stat] += result;
          break;
        // 도망 확률
        case 4:
          // 1 ~ 3
          increase = '도망 확률';
          result = 1 + Math.round(Math.random() * 2);
          player[stat] += result;
          break;
        // 연속 공격 확률
        case 5:
          // 3 ~ 7
          increase = '연속 공격 확률';
          result = 3 + Math.round(Math.random() * 4);
          player[stat] += result;
          break;
        // 방어 수치
        case 6:
          // 1 ~ 3
          increase = '방어 수치';
          result = 1 + Math.round(Math.random() * 2);
          player[stat] += result;
          break;
      }
    }
profile
신입 개발자

0개의 댓글