게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌29 - JavaScript - 8년 전 등록

턴제 게임 만들기(2)

이번 시간은 지난 시간에 이어서 턴제 게임 만들기를 계속 하겠습니다. 전 시간에 간단히 메뉴만 만들었을 뿐인데 코드가 많이 기네요. 원래 프로그램이 그렇습니다. 제 홈페이지는 게임이 아닌데도 코드가 만 줄 가까이 됩니다. 이번 시간에도 코드가 많이 필요할 것 같습니다.

지금은 메뉴가 선택이 안 되는데 이제 메뉴를 선택해봅시다. 일단 저 메뉴 입력 버튼에 이벤트 리스너를 달아야겠죠? 이전 파일에서 다음과 같이 수정해줍니다.

document.getElementById('menu-button').onsubmit = function(e) {
  var input = document.getElementById('menu-input');
  var option = input.value;
  e.preventDefault();
  input.value = '';
  TurnGame.getInstance().menuInput(option); // 새로 추가
};
document.getElementById('battle-button').onsubmit = function(e) {
  var input = document.getElementById('battle-input');
  var option = input.value;
  e.preventDefault();
  input.value = '';
  TurnGame.getInstance().battleInput(option); // 새로 추가
};

입력버튼에 들어간 menuInput, battleInput 메소드를 instance에 추가합시다. 아, 그리고 몬스터 객체 리스트도 같이 만들어보죠. 간단히 세 종류만 만들겠습니다. (보스는 제 친구의 부탁으로 친구 이름입니다...)

menuInput 메소드에는 1, 2, 3번 선택지에 따라 다르게 행동하도록 코드가 들어있습니다. 1번 모험을 선택하면 몬스터를 만들고, 2번일 경우엔 hp를 전부 회복합니다. 3번일 경우에는 게임을 종료하고요. 따라서 exit과 generateMonster 메소드도 만들었습니다.

var TurnGame = (function() {
  var instance;
  var initiate = function(heroName) {
    var hero = {
      name: heroName,
      lev: 1,
      maxHp: 100,
      hp: 100,
      xp: 0,
      att: 10
    };
    var monsters = [{
      name: '슬라임',
      hp: 25 + hero.lev * 3,
      att: 10 + hero.lev,
      xp: 10 + hero.lev,
    }, {
      name: '스켈레톤',
      hp: 50 + hero.lev * 5,
      att: 15 + hero.lev * 2,
      xp: 20 + hero.lev * 2,
    }, {
      name: '찬호[보스]',
      hp: 100 + hero.lev * 10,
      att: 25 + hero.lev * 5,
      xp: 50 + hero.lev * 5,
    }];
    var monster = null;
    var turn = true;
    return {
      ... // 이전과 동일 다음 메소드 추가
      generateMonster: function() {
        monster = JSON.parse(JSON.stringify(monsters[Math.floor(Math.random() * monsters.length)]));
        document.getElementById('monster-name').innerHTML = monster.name;
        document.getElementById('monster-hp').innerHTML = 'HP: ' + monster.hp;
        document.getElementById('monster-att').innerHTML = 'ATT: ' + monster.att;
        this.setMessage(monster.name + '이(가) 공격해옵니다');
        return this.toggleMenu();
      },
      menuInput: function(input) {
        if (input === '1') {
          return this.generateMonster();
        } else if (input === '2') {
          hp = maxHp;
          return this.updateStat().setMessage('체력을 회복했습니다');
        } else if (input === '3') {
          return this.exit();
        } else {
          alert('잘못된 입력');
        }
      },
      battleInput: function(input) {}, // 구현필요
      attackMonster: function() {}, // 구현필요
      attackHero: function() {}, // 구현필요
      nextTurn: function() {}, // 구현필요
      win: function() {}, // 구현필요
      clearMonster: function() {}, // 구현필요
      gameOver: function() {}, // 구현필요
      exit: function(input) {
        document.getElementById('screen').innerHTML = '이용해주셔서 감사합니다.새로 시작하려면 새로고침하세요';
      }
    };
  };
  return {
    getInstance: function(name) {
      if (!instance) {
        instance = initiate(name);
      }
      return instance;
    }
  };
})();

이제 전투 메뉴까지는 뜹니다. 이제 전투 메뉴도 구현해보겠습니다! 구현 필요라고 되어있는 메소드들을 만들어보겠습니다. 턴제 게임이기 때문에 nextTurn 메소드는 몬스터에게 턴을 넘기는 동작을 합니다. battleInput은 menuInput처럼 각 입력별로 그에 따른 함수를 호출해줍니다.

battleInput: function (input) {
  if (input === '1') {
    return this.attackMonster();
  } else if (input === '2') {
    if (hero.hp + hero.lev * 20 < hero.maxHp) {
      hero.hp += hero.lev * 20;
    } else {
      hero.hp = hero.maxHp;
    }
    return this.showHp().setMessage('체력을 회복했습니다').nextTurn();
  } else if (input === '3') {
    return this.clearMonster().setMessage('도망쳤습니다');
  } else {
    alert('잘못된 입력');
  }
},
attackMonster: function () {
  monster.hp -= hero.att;
  document.getElementById('monster-hp').innerHTML = 'HP: ' + monster.hp;
  if (monster.hp > 0) {
    return this.setMessage(hero.att + '의 데미지를 입혔습니다.').nextTurn();
  }
  return this.win();
},
attackHero: function () {
  hero.hp -= monster.att;
  return this.showHp();
},
nextTurn: function () {
  var self = this;
  turn = !turn;
  document.getElementById('battle-button').disabled = true;
  if (!turn) {
    window.setTimeout(function () {
      self.setMessage(monster.name + '의 턴입니다');
      window.setTimeout(function () {
        document.getElementById('battle-button').disabled = false;
        if (self.attackHero()) {
          self.setMessage(monster.att + '의 데미지를 입었습니다');
          window.setTimeout(function () {
            self.setMessage(hero.name + '의 턴입니다');
          }, 1000);
        }
      }, 1000);
    }, 1000);
    return this.nextTurn();
  }
  return this;
},
win: function () {
  this.setMessage(monster.name + ' 사냥에 성공해 경험치 ' + monster.xp + '을 얻었습니다');
  hero.xp += monster.xp;
  return this.clearMonster().showXp();
},
clearMonster: function () {
  monster = null;
  document.getElementById('monster-name').innerHTML = '';
  document.getElementById('monster-hp').innerHTML = '';
  document.getElementById('monster-att').innerHTML = '';
  return this.toggleMenu();
},
gameOver: function () {
  document.getElementById('screen').innerHTML = hero.name + '은 레벨' + hero.lev + '에서 죽었습니다. 새로 시작하려면 새로고침하세요';
  return false;
},

이제 게임을 즐기면 됩니다! nextTurn 함수를 잘 봐주세요. window.setTimeout으로 메세지를 1초마다 순차적으로 표현합니다.

다음 강좌에 전체 코드를 올리겠습니다! 작동이 잘 안 되는 분들은 다음 강좌 코드를 복사하세요!

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

5개의 댓글이 있습니다.
4년 전
안녕하세요!! generateMonster 메소드에서 JSON.parse()와 JSON.stringyfy를 사용하신 이유가 궁금합니다! 두개를 빼고 사용해도 정상 작동해서요!!
4년 전
안녕하세요?

작은 텍스트기반 게임이지만 다이내믹하네요!

재밌는 예제로 진행해주셔서 감사합니다.
4년 전
안녕하세요?

첫번째 코드에서
1, 8번째줄

오타인 거 같아요.

document.getElementById('game-menu').onsubmit = function(e) {

document.getElementById('battle-menu').onsubmit = function(e) {

각각 id가 game-menu, battle-menu가 되어야 하는 거 아닌가요?

앞 페이지 코드랑 달라서요.

감사합니다.
6년 전
뭐지 ㅋㅋㅋ
6년 전
댓글이 너무 웃기다
8년 전
하하하. 현영아. 너의 진정한 친구 찬호다. 잘 지내고 있지? 나는 지금 대대장님과 함께 밤을 새면서 너의 블로그로 컴퓨터 공부를 하는 중이야. 내 이름을 보스로 해주다니 큰 영광이다! 너와 함께 학교가 끝나면 떡볶이를 먹고 벨튀를 하던 중학교 때가 그립네 *^^* 다음 휴가 때 미리 연락하겠다. 그 동안 건강히 지내라!
8년 전
하하하. 찬호야. 너의 진정한 친구 현영이다. 잘 지내고 있지? 나는 지금 집에서 혼자 밤을 새면서 나의 블로그에 포스팅을 하는 중이야. 너의 이름을 보스로 해서 실컷 때려잡고 있단다! 너와 함께 학교가 끝나면 떡볶이를 먹고 벨튀를 하던 중학교 때가 그립네 *^^* 다음 휴가 때는 부디 연락하지 말아줘. 건강히 지내라!
8년 전
아따, 이 찐따가 오래되니까 맞고 살던 때가 기억 안나냐? 쉬,,,펄 니 지금 어디여? 시방 내가 지금 너 찾으러 탈영하러 간다. 산천초목을 다 뒤져서라도 잡아족칠 것이여.. 이승에 일초라도 더 남아있고잡다며는 잘 숨어 있으라잉.
8년 전
니가 지금이라도 용서를 구하면 목숨은 부지할 수 있을 것이고 그렇지 않다며는 차라리 죽여달라고 애원하게 될 것이여... 2분 준다. 싸게싸게 답글 달아라잉
8년 전
국방 신문고에 민원 작성 중입니다! 헌병대 한 박스 경품으로 보내드립니다!
8년 전
친구야. 미안하다. 내가 용서를 빌게... 우리 10년지기 친구 아니고? 오랜 우정을 생각해서 한번만 봐주라.. ㅠㅠㅠㅠ 진짜야. 내가 다음 휴가 때 술 쏜다!!
8년 전
제발.... 진심이다... 부탁이야 나 좀 살려줘 ㅜㅠ 친구야. 사랑한다... 언제 밥 한번 묵자.
8년 전
밥은 콩밥이 제일이죠! 영창 후기 부탁합니다!
8년 전
관용이 없어불구마잉... 이미 영창 2번은 갔다온 군생활 한번 더 간다고 대단히 힘들 것도 없어야. 대신 내가 내 인맥 다 동원해서 쳐들어 갈 것이여. 영창 갔다 올 거이니께 목 깨끗이 닦고 얌전히 기다리고 있어라잉. 주제 모르고 건방지게 까분 것에 응분의 대가를 치를 것이여.
8년 전
크~ 영창 세 번 가면 군생활 한 달 더 하시네요! 하사 진급하시나요?
8년 전
친구야 신고 취소했니? 오늘 하루 종일 마음 졸이며 지낸다. 사랑한다 내 친구 조현영!
8년 전
아무리 찾아도 민원 취소는 없는데 어떡하냐 ㅠㅠ 미안하드아아아!!
8년 전
현영아 미안하다... 내가 옛날에 널 괴롭힌 벌을 받는 거 같아... 너한테 범사시미파 두목 집 벨튀하고 오라 시키고 난 멀리서 지켜보고 있고 있었던거... 쉬는시간마다 먹지도 않을 빵 사오라 시켜놓고 거스름돈 내놓으라고 윽박지르고... 다 내 잘못이다. 너에게 했던 나의 죄들 어떻게 하면 용서받을 수 있겠니? 옛날 일은 다 잊고 나에게 한번만 기회를 줘!!