게시글

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

자동 텍스트 RPG 만들기

이번 시간에는 텍스트 RPG를 만들어보겠습니다. 그래픽 작업은 아직 못 하지만, 텍스트 RPG정도는 만들 수 있을 것 같습니다. html을 어느 정도 할 수 있다는 전제 하에 진행되지만, html을 못 한다면 그냥 복사 붙여넣기 하시면 됩니다. 중요한 건 자바스크립트 코드니까요.

<html>
<head>
<title>텍스트RPG</title>
</head>
<body>
<div id="log"></div>
<script>
// 여기에 자바스크립트 코드를 넣습니다.
</script>
</body>
</html>

이제는 콘솔에서 벗어나 html 파일에 만들 겁니다. 실행은 브라우저에서 할 거고요. 위의 코드를 복사해서 메모장에 붙여넣은 후 저장할 때 확장자가 html인 파일로 저장하세요.

다음 코드를 script에 넣으면 됩니다. 물론 아직은 화면에 아무것도 안 뜰 겁니다.

function logMessage(msg, color) {
  if (!color) { color = 'black'; }
  var div = document.createElement('div');
  div.innerHTML = msg;
  div.style.color = color;
  document.getElementById('log').appendChild(div);
}

메세지를 #log 태그에 추가하는 코드입니다. color 값을 따로 인자로 제공하면 해당하는 색의 에러 메시지가 표시됩니다.

var gameover = false;
var battle = false;
function Character(name, hp, att) {
  this.name = name;
  this.hp = hp;
  this.att = att;
}
Character.prototype.attacked = function (damage) {
  this.hp -= damage;
  logMessage(this.name + '의 체력이 ' + this.hp + '가 되었습니다');
  if (this.hp <= 0) {
    battle = false;
  }
};
Character.prototype.attack = function (target) {
  logMessage(this.name + '이 ' + target.name + '을 공격합니다');
  target.attacked(this.att);
};

일단 캐릭터들을 관장하는 캐릭터 생성자를 만들어줍니다. hp, att는 각각 체력, 공격력이고요. attack과 attacked 메소드는 각각 공격하고, 공격받는 겁니다. 공격받는 것을 보면, hp에 데미지를 받죠? 공격하는 것은 타겟에 공격력을 가합니다. 아, 그리고 게임오버와 전투중인지를 알려주는 변수 두 개를 만들었습니다.

이제 캐릭터를 상속하는 영웅과 몬스터를 만들겁니다.

function Hero(name, hp, att, lev, xp) {
  Character.apply(this, arguments);
  this.lev = lev || 1;
  this.xp = xp || 0;
}
Hero.prototype = Object.create(Character.prototype);
Hero.prototype.constructor = Hero;
Hero.prototype.attacked = function(damage) {
  this.hp -= damage;
  logMessage(this.name + '님의 체력이 ' + this.hp + '남았습니다');
  if (this.hp <= 0) {
    logMessage('죽었습니다. 레벨' + this.lev + '에서 모험이 끝납니다. F5를 눌러 다시 시작하세요', 'red');
    battle = false;
    gameover = true;
  }
};
Hero.prototype.attack = function (target) {
  logMessage(this.name + '님이 ' + target.name + '을 공격합니다');
  target.attacked(this.att);
  if (target.hp <= 0) {
    this.gainXp(target);
  }
};
Hero.prototype.gainXp = function(target) {
  logMessage('전투에서 승리하여 ' + target.xp + '의 경험치를 얻습니다', 'blue');
  this.xp += target.xp;
  if (this.xp > 100 + 10 * this.lev) {
    this.lev++;
    logMessage('레벨업! ' + this.lev + ' 레벨이 되었습니다', 'blue');
    this.hp = 100 + this.lev * 10;
    this.xp -= 10 * this.lev + 100;
  }
};

Hero가 Character을 상속하고 있습니다. 거기에 lev(레벨), xp(경험치) 속성이 추가되었네요. 그리고 원래 있던 attacked, attack 메소드를 확장했고, gainXp라는 메소드를 프로토타입에 하나 추가했습니다.

function Monster(name, hp, att, lev, xp) {
  Character.apply(this, arguments);
  this.lev = lev || 1;
  this.xp = xp || 10;
}
Monster.prototype = Object.create(Character.prototype);
Monster.prototype.constructor = Monster;

뭔가 프로그래밍하는 것 같나요? 이제 영웅과 몬스터를 만들었으니 게임 진행하는 알고리즘을 짜야겠죠? 단순하게 전투->승리->경험치획득->전투->승리->레벨업->전투->... 이런 반복이면 됩니다.

몬스터를 랜덤으로 만드는 함수입니다. 5종류 정도만 만들어보죠. 5종류 중에 골라서 몬스터 객체를 만듭니다.

function makeMonster() {
  var monsterArray = [
    ['rabbit', 25, 3, 1, 35],
    ['skeleton', 50, 6, 2, 50],
    ['soldier', 80, 4, 3, 75],
    ['king', 120, 9, 4, 110],
    ['devil', 500, 25, 6, 250]
  ];
  var monster = monsterArray[Math.floor(Math.random() * 5)];
  return new Monster(monster[0], monster[1], monster[2], monster[3], monster[4]);
}

흠.. 몬스터 중 하나가 밸런스 붕괴네요... 20% 확률로 게임오버...

var hero = new Hero(prompt('이름을 입력'), 100, 10);
logMessage(hero.name + '님이 모험을 시작합니다. 어느 정도까지 성장할 수 있을까요?');
while (!gameover) {
  var monster = makeMonster();
  logMessage(monster.name + '을 마주쳤습니다. 전투가 시작됩니다', 'green');
  battle = true;   while(battle) {
    hero.attack(monster);
    if (monster.hp > 0) {
      monster.attack(hero);
    }
  }
}

이렇게 텍스트RPG가 완성되었습니다. 밸런스는 조정하시면 됩니다. 어쩌다 보니 운빨 게임이 되어버렸네요. ㅎㅎ 새로고침하면서 몇 레벨까지 갈 수 있나 테스트해보세요. 그리고 자유롭게 게임을 패치해보세요.~

  • 다양한 스텟들 추가하기(명중률, 방어력 등등...) (힌트: 생성자에 추가하면 됩니다)
  • 자동 진행이 아닌 수동진행으로 만들기(힌트: 한 턴이 지날 때마다 멈추는 코드를 넣으면 됩니다)
  • 회복같은 메소드 추가해보기 (힌트: prototype에 추가하면 됩니다)

이제 초보를 벗어났으니 중급 강좌가 진행됩니다! 

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

댓글

21개의 댓글이 있습니다.
4년 전
안녕하세요. 블로그 아주 잘 보고있습니다! ^^
질문이 있는데.. 자바스크립트의 class기능을 사용하면..
굳이 prototype 안에 함수를 넣지 않아도 저절로 들어가는거지요?
class를 아는 상황해서 이 파트를 공부해야할까요?..
4년 전
네 class는 prototype을 자동으로 연결해줍니다. 근데 이 강좌에서는 class를 안 썼는데요?
4년 전
네 아직 초보라.. 이게 class와 같은건지 궁금했어요!ㅋㅋ 답변감사합니다
4년 전
안녕하세요. 다름이 아니라 gameOver랑 battle도 그냥 prototype으로 해도 되지 않나요??

const OptionGame = function() {};
OptionGame.prototype.gameOver = false;
OptionGame.prototype.battle = false;
이런식으로 해도 되지 않나요?
4년 전
OptionGame에 다른 메서드같은게없다면 굳이 프로토타입으로 묶을 필요는 없습니다.
4년 전
알려주셔서 감사합니다. 또 궁금한게있는데

function Hero(name, hp, att, lev, xp) {
Character.apply(this, arguments);
}

여기서 Character의 this를 상속을 받았는데
name, hp, att 이게 Character에 상속을 받았는데 왜

매개변수애 name, hp, att를 또 선언을 하는 이유가 궁금하네요.

제 생각에는
function Hero(lev, xp) {
Character.apply(this, arguments);
}

이게 맞지 않나요?
4년 전
이렇게 된다면 Hero는 name, hp, att를 어디서 받죠? 상속받는 쪽은 상속하는 부모의 속성을 가지고 있어야 합니다.
4년 전
자식에서의 name, hp, att는 따로 Character 생산자도 따로 선언을 해야하는거네요. 저는 부모에서 증복되는 name, hp,att는 선언을 하고 그걸 자식들한테 상속해서 하는줄 알았네요.
4년 전
안녕하세요. 이렇게 오래된 글에 답 달아주실지 모르겠지만 질문 남겨봅니다.
명중률 스텟을 추가하고 싶어서
function Character(name, hp, att, dex) {
this.name = name;
this.hp = hp;
this.att = att;
this.dex = dex;
}
로 변경해 주고 상속받는 녀석들에게도 dex를 추가해주었습니다.
그런데 전투에서 이길때마다 monsterArray에 있는 경험치를 받아오는 게 아니라
10의 경험치만 받아온다고 나오는 건 왜 그런건가요? 간단한 부분 같은데 지식이 짧으니 답답하네요 ㅠㅠ
4년 전
target.xp가 어디서 10으로 들어오나보네요. target.xp 값을 console.log찍어보세요.
5년 전
안녕하세요 도움 많이 받고 갑니다! 개인 공부용 블로그에 출처 표기하고 인용해 가겠습니다! 기능 추가해서 게시글 작성해보고 싶어서 댓글 남깁니다 :)
5년 전
안녕하세요 업그레이드 문제중에 질문이 있습니다. 수동진행으로 변경하는 부분에서 저는 setInterval 과 clearInterval 을 이용해서 한턴한턴 진행되게는 해봤는데 수동으로 버튼을 클릭해서 진행하는 것은 방법을 찾지 못했습니다 ㅠ 버튼을 이용한 수동으로 변경해보고 싶은데 만약에 body부분에 button을 넣고 클릭시 게임이 다음으로 진행되게 하려면 어떤식으로 구성을 해야할까요?
5년 전
button.addEventListener('click', function() {})을 붙여서 function() {} 내부에 다음으로 넘어가는 코드를 작성해주시면 됩니다.
6년 전
character 의 attack,attacked 와 hero의 attack,attacked는 전혀 다른거죠?
혹시 상속받는다는 거나 그런게 있나요?
6년 전
Hero의 attack이 Character의 attack을 덮어쓴것입니다. 덮어쓰지 않았다면 Character의 attack이 사용됩니다.
7년 전
zero님 궁금한게 있어서 질문드려요.
this.lev = lev || 1;이 안전장치라고 설명해 주셨는데 여기서 ||는 OR 연산자로 보고 lev 또는 1이기 때문에 안전 장치로써 동작한다고 이해하면 되는건가요????
7년 전
자바스크립트 49강 참고하세요~ or 연산자가 맞긴 한데 저런 류의 연산자를 특별히 default 연산자라고 부릅니다.
7년 전
빠른답변 감사드립니다! 49강을 읽어보니 이해가 되네요. ^^
7년 전
아 그렇군요 기초적인거 하나만 더 물어볼게요 lev 랑 xp 는 왜 다른것들이랑 다르게 || 를 쓴거에요?
7년 전
저건 프로그램에서 필수라는 의미로 받아들이시면 됩니다. 사실 hp랑 att도 있긴 해야겠네요. name은 사람마다 다르니 설정하면 안될것 같습니다.
7년 전
궁금한게 있는데요~
this.xp -= 10 * this.lev + 100; 이거 풀면 this.xp = this.xp - 10 * this.lev + 100;
이렇게 되는데 괄호 이렇게 안쳐줘도 되나요? -(this.xp - 10 * this.lev + 100)
그리고요~ 밑에서 세번째 몬스터 부분에서
function Monster(name, hp, att, lev, xp) {
Character.apply(this, arguments);
this.lev = lev || 1; 이쪽이랑
this.xp = xp || 10; 이쪽에서요
}
그냥 this.lev = lev 주면 안되나요? 어차피 밑에서 값입력받기때문에 괜찮을거같아서요
7년 전
아 저거 풀면 자동으로 this.xp = this.xp - (10 * this.lev + 100) 됩니다.

그리고 아랫부분은 항상 값 입력할거라면 생략해도 됩니다. 다만 확실하게 위해서 || 1 이렇게 안전장치를 두는 겁니다.
7년 전
그렇군요 ㅠㅠ 항상 빠른 답변 감사합니다. 근데 이거 하나 떼는데 원래 시간이 오래 걸리나요....상속이랑 prototype 조금 헷갈려서 전강의랑 전전강의 가끔 참조해서 그런가 2~3일은 이것만 보고 있었던거같아요..
7년 전
초보라 질문있는데요 이런 예제 공부할 때 코드가 어떤 역할을 하는지만 알고 넘어가면 되나요 아니면 안보고 짤 정도 되고 넘어가는게 좋을까요?
일단 공부를 코드 보고 이해하면서 몇번 짜는 식으로 하고 있습니다만...제대로 공부하고 있는지 모르겠네요 ㅠ
7년 전
제 블로그만 보고는 안 보고 짤 정도 되기는 힘들고요. 그냥 예제 코드들 조금씩 수정해보고 하는 정도로만 하시고 넘어가세요.
7년 전
Hero.prototype.gainXp = function(target){
logMessage('You win !, so you get ' + target.xp + 'xp', 'blue');
this.xp += target.xp;

if(this.xp >= 100){
logMessage('Level up ' + this.lev, 'blue')
this.lev++;
this.xp -= target.xp;
this.hp += 30;
}
}

Hero.prototype.gainXp에서 if문에 왜 100 + 10 * this.lev로 하신거에요??? Level이 왜 최소한 110이상이어야 해요?? 최소110이상 Level up 조건문 충족이 어려울까봐 저는 위에처럼 수정했어요...
7년 전
Level이 110 이상이 아니라 Level 1일 때, 경험치가 110 이상입니다. Level 2일 때는 경험치 120 이상이고요. 레벨이 올라감에 따라 요구 경험치가 늘어나는 것을 구현하고자 한 것입니다.

코드는 필요에 따라 수정하시면 됩니다.
7년 전
하위 클래스를 상위 클래스에 확장할때마다 (Object.create) 명시적으로 상위클래스.prototype.constructor = 상위클래스 를 추가해야 하죠??
7년 전
그렇습니다. 상위클래스가 아니라 둘 다 하위클래스겠죠?
7년 전
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/create 아.. Hero.prototype.constructor === Hero 개발자가 직접 해야되는군요. 감사합니다..
7년 전
Hero.prototype = Object.create(Character.prototype);
Hero.prototype.constructor = Hero; 하위 클래스 Character 에 상위 클래스 Hero 확장했습니다. 그래서 Hero 메소드에 attack, attacked 메소드 확장이 되었는데요. (ex/ Hero.prototype.attack, Hero.prototype.attacked) 아래 Hero.prototype.constructor = Hero 왜 하신거에요?? 암묵적으로 Hero.prototype.constructor === Hero인데 왜 하신거에요???
7년 전
Hero.prototype.constructor는 Hero가 아닙니다. 이것은 자바스크립트 상속의 버그입니다. 모질라에서는 명시적으로 저 부분을 추가하여 버그 패치를 하길 권장하고 있습니다.
8년 전
그냥 메모장에 확장자를 html로 저장하고 실행시키면 바로 실행이되는건가요?
실행이 안되길래 제가 혹시 오타냈을까봐 쭉 복사해서 저장했는데 실행이 안되네요..ㅠ
너무 초보라서 송구스럽네요..
8년 전
실행했을 때 브라우저가 실행되나요?
8년 전
네. 브라우저가 실행됩니다.
8년 전
그 후에 프롬프트가 뜨면서 사용자명을 입력하게 되어있거든요. 위에서부터 차례대로 html, js 순으로 붙여넣으시면 돼요.
5년 전
저도 위에 분이랑 같은 증상 나와서 40분정도 이리저리 뜯어본 결과 코드를 복사 붙여넣기 하고 다음 코드 붙여넣을때 그냥 Enter을 누르면 안되고 Shift+Enter로 줄 바꿈을 하고 붙여넣기를 했더니 실행이 되었습니다.

에디터를 쓰면 이런 문제가 없을거라던데 메모장에서 Enter로 줄바꿈을 해서 그런건지 아니면 다른 원인인지 궁금하네요!

저도 엄청 초보지만 도움이 되었으면 해서 남깁니다.
8년 전
결과가 나왔다 로딩걸렸다 하는건 왜그러는 걸까요??
8년 전
어떤 환경에서 로딩이 걸리는 지 설명 부탁드립니다!
8년 전
댓글입력하는 텍스트에리아부분 z-index가 아래 댓글들보다 낮아서 인지 댓글들에 가려집니다
8년 전
수정했습니다! 감사합니다!
8년 전
수동진행으로 만들려면 어떡하죠? 멈추는걸 prompt로 멈추고 입력값에따라 계속진행할지 회복을할지 하는건데 진행순서에 맞게 진행되는게 아니고 아무것도 #log에 appendChild가 되지도 않고 prompt창만뜨고 입력하면 gameover=true; 되고 게임끝나야 그제서야 메세지들이 append 됩니다
8년 전
이게 생각보다 어려습니다. 이 부분은 뒤에 턴제 게임 만들기를 참고하세요
8년 전
네 알겠습니다ㅎㅎ
8년 전
이런거 짤때는 충분히 생각하고 디테일하게 각 객체의 역할등을 기획?하고 코딩을 시작하는게 맞는건가요
8년 전
네 기획하고 짜야합니다. 무턱대고 짰다가는 나중에 아예 처음부터 다시 시작해야하는 불상사가 생길 수도 있습니다.
8년 전
다행이군요 그럼 제로조님은 어떤식으로 하나요? 머리만으론 턱없이 부족한데 펜으로 쓰고 하시는건가요?
8년 전
아뇨. 그냥 머릿속으로 큰 틀만 기획하고 그 다음부터는 부닥쳐가면서 코딩합니다. 그리고 설계가 틀렸을 경우 주기적으로 코드를 갈아엎죠.
8년 전
그렇군요 알겠습니다 감사합니다 ㅎㅎ 드디어 혼자서 해냈습니다 ㅠ 요 부분 혼자 구현해보려 첫 강의부터다시 3번정도는 했네요 ㅋㅋ 거의 외워져서 한거지만 그래도 많은 공부가 됬어요!!
8년 전
축하드립니다! 직접 만들면서 하는 게 도움이 많이 돼요~
8년 전
if (!color)은 정확이 어떤 뜻인가요 그리고 어떤경우에 이렇게 쓸수 있는지 알려주세요!
8년 전
if (!color)은 처음 함수를 호출할 때 color을 인자로 입력하지 않으면 color가 undefined가 되기 때문에 !color는 true가 됩니다. 따라서 if문 안의 내용이 실행되죠.