whatisthis?
project. (js) 자동 텍스트 RPG 게임 - (수정) 본문
지난시간 제작해보았던 자동 텍스트 RPG 게임의 기능을 추가하고,
더 개선된 버전을 제작해보았다.
이전 코드는 아래 [더보기] 를 참고 🔻
index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>텍스트 RPG</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="log"></div>
<script src="app.js"></script>
</body>
</html>
style.css
* {
margin: 0;
box-sizing : border-box;
}
html {
font-family:Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
font-size: 16px;
line-height: 2;
color: #1f2d3d;
}
body {
text-align: center;
width: 100%;
height: 100vh;
background-color: #f8f9fb;
}
body::after {
content: "text-RPG";
display: block;
margin-top: 50px;
color: #463bc1;
font-size: 12px;
font-weight: 600;
}
app.js
// 메세지를 #log에 추가
function logMessage(msg, color) {
if(!color) {color = 'black';}
const div = document.createElement('div');
div.innerHTML = msg;
div.style.color = color;
document.getElementById('log').appendChild(div);
}
// 캐릭터 생성자
let gameover = false;
let battle = false;
function Character(name, hp, att) {
this.name = name;
this.hp = hp;
this.att = att;
}
// 메서드 ㅡ attacked / attack
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);
};
// 영웅 생성자 ㅡ Character 상속
function Hero(name, hp, att, lev, xp) {
Character.apply(this, arguments);
this.lev = lev || 1; //(lev = 1)
this.xp = xp || 0; //(xp = xp)
}
Hero.prototype = Object.create(Character.prototype);
Hero.prototype.constructor = Hero;
// 메서드 ㅡ attacked / attack / gainXp(경험치)
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;
}
};
// 몬스터 생성자 ㅡ Character 상속
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;
// 몬스터 랜덤으로 만드는 함수
function makeMonster() {
const monsterArr = [
['rabbit', 25, 3, 1, 35],
['skeleton', 50, 6, 2, 50],
['soldier', 80, 4, 3, 75],
['king', 120, 9, 4, 110],
['devil', 500, 12, 6, 250]
];
const monster = monsterArr[Math.floor(Math.random() * 5)];
return new Monster(monster[0], monster[1], monster[2], monster[3], monster[4]);
}
///// 게임 진행 ㅡ ( 전투 > 승리 > 경험치업 > 전투 > 승리 > 레벨업 > ... )
const hero = new Hero(prompt('이름을 입력하세요.'), 100, 10);
logMessage(hero.name + ' 님의 모험이 시작됩니다.');
while(!gameover) {
const monster = makeMonster();
logMessage(monster.name + '을 마주쳤습니다. 전투가 시작됩니다.', 'green');
battle = true;
while(battle) {
hero.attack(monster);
if(monster.hp > 0) {
monster.attack(hero);
}
}
}
index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>텍스트 RPG</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>auto-text RPG</h1>
<div id="user-container">
<div id="user-name">
<h2>Your Name</h2>
<form action="#" id="name-form">
<input required type="text" maxlength="4" placeholder="name" />
<button type="submit">OK</button>
</form>
</div>
<div id="select-character">
<h2>Choose Character!</h2>
<input type="button" id="select-wizard">
<input type="button" id="select-tanker">
</div>
</div>
<div id="log">
</div>
<script src="app.js"></script>
</body>
</html>
- 우선, h1과 h2를 추가하였고
- div 컨테이너를 세분화해서 나눴다. (스타일 적용을 위해)
style.css
* {
margin: 0;
box-sizing : border-box;
}
html {
font-family:Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
font-size: 16px;
line-height: 2;
color: #1f2d3d;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
background-color: #d2d8e4;
}
#log::after {
content: "@thisisyjin";
display: block;
margin-top: 50px;
color: #463bc1;
font-size: 12px;
font-weight: 600;
}
#user-container {
display: flex;
align-items: center;
}
#user-name {
margin: 30px;
text-align: center;
}
#name-form input, #name-form button {
border: 1px solid #d2d8e4;
height: 30px;
}
#name-form {
padding-top: 15px;
padding-bottom: 15px;
}
#select-wizard {
width: 60px;
height: 60px;
border: 0;
background: url( "./source/wizard.png" );
cursor:pointer;
background-size: cover;
margin-left: 10px;
margin-right: 30px;
}
#select-tanker {
width: 60px;
height: 60px;
border: 0;
background: url( "./source/tanker.png" );
cursor:pointer;
background-size: cover;
}
#log {
text-align: center;
height: 1vh;
}
중구난방이지만 ..
나중에 다듬어서 깔끔한 UI로 변경할 예정.
CSS 는 너무 어렵다!
app.js
// 메세지를 #log에 추가
function logMessage(msg, color) {
if(!color) {color = '#2f2f2f';}
const div = document.createElement('div');
div.innerHTML = msg;
div.style.color = color;
document.getElementById('log').appendChild(div);
}
// 캐릭터 생성자
let gameover = false;
let battle = false;
function Character(name, hp, att) {
this.name = name;
this.hp = hp;
this.att = att;
}
// 메서드 ㅡ attacked / attack
Character.prototype.attacked = function(damage) {
this.hp -= damage;
logMessage(this.name + ' 의 체력이 ' + this.hp + ' 가 되었습니다.');
if(this.hp <= 0) {
battle = false;
} else {
logMessage(this.name + ' 님의 체력이 ' + this.hp + ' 남았습니다.');
}
};
Character.prototype.attack = function(target) {
logMessage(this.name + '이 ' + target.name + ' 을 공격합니다.');
target.attacked(this.att);
};
// 영웅 생성자 ㅡ Character 상속
function Hero(name, hp, att, lev, xp) {
Character.apply(this, arguments);
this.lev = lev || 1; //(lev = 1)
this.xp = xp || 0; //(xp = xp)
}
Hero.prototype = Object.create(Character.prototype);
Hero.prototype.constructor = Hero;
// 메서드 ㅡ attacked / attack / gainXp(경험치)
Hero.prototype.attacked = function(damage) {
this.hp -= damage;
if(this.hp <= 0){
logMessage('죽었습니다. 레벨 ' + this.lev + '에서 모험이 끝납니다. F5를 눌러 리스폰하세요.', 'red');
battle = false;
gameover = true;
} else {
logMessage(this.name + ' 님의 체력이 ' + this.hp + ' 남았습니다.');
}
};
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;
}
};
// 몬스터 생성자 ㅡ Character 상속
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;
// 몬스터 랜덤으로 만드는 함수
function makeMonster() {
const monsterArr = [
['rabbit', 25, 3, 1, 35],
['skeleton', 50, 6, 2, 50],
['soldier', 80, 4, 3, 75],
['king', 120, 9, 4, 110],
['devil', 500, 12, 6, 250]
];
const monster = monsterArr[Math.floor(Math.random() * 5)];
return new Monster(monster[0], monster[1], monster[2], monster[3], monster[4]);
}
///// 게임 진행 ㅡ ( 전투 > 승리 > 경험치업 > 전투 > 승리 > 레벨업 > ... )
// 이름 ㅡ form과 input(text)
const nameForm = document.getElementById('name-form');
const nameInput = document.querySelector('#name-form input');
let savedName = '';
let charcterType = '';
// 캐릭터선택 ㅡ input(button)
const wizardButton = document.getElementById('select-wizard');
const tankerButton = document.getElementById('select-tanker');
function typeWizard() {
const hero = new Hero(savedName, 100, 15);
logMessage(hero.name + ' (Wizard)님의 모험이 시작됩니다.');
while(!gameover) {
const monster = makeMonster();
logMessage('❕ ' + monster.name + '을 마주쳤습니다. 전투가 시작됩니다.', 'green');
battle = true;
while(battle) {
hero.attack(monster);
if(monster.hp > 0) {
monster.attack(hero);
}
}
}
}
function typeTanker() {
const hero = new Hero(savedName, 150, 10);
logMessage(hero.name + ' (Tanker)님의 모험이 시작됩니다.');
while(!gameover) {
const monster = makeMonster();
logMessage('❕ ' + monster.name + '을 마주쳤습니다. 전투가 시작됩니다.', 'green');
battle = true;
while(battle) {
hero.attack(monster);
if(monster.hp > 0) {
monster.attack(hero);
}
}
}
}
// 이름을 저장함 ㅡ 캐릭터 선택
nameForm.addEventListener('submit', nameSave);
function nameSave() {
savedName = nameInput.value;
wizardButton.addEventListener('click', typeWizard);
tankerButton.addEventListener('click', typeTanker);
}
- 우선 생성자 부분과 logMessage부분은 동일하다.
- 변경된 것은, addEventListener부분 위주.
- 나는 addEventListener을 중첩해서 사용해보았다.
이게 효율적인지는 모르겠다.
나중에는 form에 제출된 값을 기준으로 다르게 작동하도록 변경할것임.
1. 폼이 제출 (form submit)
- 이름을 입력하고, 제출을 했을 때
(input태그에 required 속성을 적어놨으므로 반드시 뭐라도 적어서 submit 해야함)
> 2. nameSave 함수 실행
- 함수밖에서 변수로 미리 선언해둔 savedName 변수에
input태그(element)의 value를 대입한다.
그리고나서 addEventListener을 기다린다.
>> 3. wizard 클릭시 ㅡ typeWizard 함수 실행
>>>> 4. hero = new hero(savedName, 100, 15)로 캐릭터 생성 후 플레이함.
>> 3. tanker 클릭시 ㅡ typeTanker 함수 실행
>>>> 4. hero = new hero(savedName, 150, 10)로 캐릭터 생성 후 플레이함.
변경사항
1 / 우선, prompt로 입력받던 이름(name)을 다른 방법으로 입력받고
2 / 캐릭터 종류를 세분화하여 hp와 att(공격력)을 달리 하여 유저가 선택할 수 있도록 했다.
3 / 추가적으로, CSS를 개선하여 보기 좋게 웹사이트를 제작했다.
(물론, 이건 js 연습이니까 베타버전 정도라 생각)
<캐릭터 종류>
wizard | hp: 100 / att : 15 |
tanker | hp: 150 / att : 10 |
- 이름을 입력받는 것은 input (text)를 이용해서 입력하고, form이 submit 되면
이름이 저장되는 것으로 하였다.
- 캐릭터 선택은 처음엔 radio로 해볼까 고민했는데, 뭔가 CSS 적용이 힘들 것 같아서버튼에 이미지를 background로 입히는 것으로 결정했다.
>> 이 과정에서<button> <img/> </button>을 해야할지<input type="button" />을 우선 만들고나서 CSS를 입힐지 고민했다.
그러나, img 태그가 정보를 담고있는 느낌보다는로고나 아이콘 느낌이므로 CSS 처리를 택했다.
#select-wizard {
width: 60px;
height: 60px;
border: 0;
background: url( "./source/wizard.png" );
cursor:pointer;
background-size: cover;
}
#select-tanker {
width: 60px;
height: 60px;
border: 0;
background: url( "./source/tanker.png" );
cursor:pointer;
background-size: cover;
}
플레이 화면
게임 로그 🔻
- 깃헙에 새로 업로드 해두었다.
- 나중에 차차 수정 + 개선해가면서 완성할 예정!
사담 👻
- 이것은 생성자함수로 캐릭터를 생성하지만 / 나중에 클래스 배우고나서 클래스 버전으로 수정할거임.
(아마 vscode에서 생성자함수를 권장하지않는 걸 보니 이제 클래스를 더 쓰나보다.)
- 나중엔 제대로 몬스터들과의 밸런스를 맞춰서
진짜 rpg게임처럼 만들어보는 것이 최종 목표이다.
- 레벨별로 등장하는 몬스터 확률도 다르게 조정 (밸런스를 위해)
- 나중엔 사용자가 스킬을 골라서 직접 턴제싸움으로 고퀄리티(?) 웹 RPG를 만들어보고싶다.
캐릭터 종류도 더 업그레이드하고, 포켓몬처럼 타입을 추가해서 서로 스킬별로 상성도 있으면 재밌을 듯.
(우주로 가는 프로젝트 규모^^. 게임회사 하나 차릴듯)
'PRACTICE > SELF' 카테고리의 다른 글
javaScript. 턴제 게임 - 텍스트 기반 RPG (中) (0) | 2022.01.31 |
---|---|
javaScript. 턴제 게임 - 텍스트 기반 RPG (上) (0) | 2022.01.30 |
project. (js) 숫자야구게임 - 웹 ver (수정) (0) | 2022.01.28 |
javaScript. 숫자야구게임 - 웹 ver. (1) | 2022.01.27 |
javaScript. 자동 텍스트 RPG 게임 (0) | 2022.01.27 |