JavaScript - 텍스트 RPG 예제와 class 문법

Jenna·2022년 12월 22일
1

javascript

목록 보기
12/16
post-thumbnail

class문법을 이용하여 텍스트로 RPG게임을 구현


📌순서도 그리기



게임 만들기는 비교적 복잡한 순서도를 가진다.
차근차근 생각해보면서 화면전환에 따라 기획한다.


📌 코드보기


<body>
    <form id="start-screen">
        <input id="name-input" placeholder="주인공 이름을 입력하세요!" />
        <button id="start">시작</button>
    </form>
    <div id="screen">
        <div id="hero-stat">
            <span id="hero-name"></span>
            <span id="hero-level"></span>
            <span id="hero-hp"></span>
            <span id="hero-xp"></span>
            <span id="hero-att"></span>
        </div>
        <form id="game-menu" style="display:none">
            <div id="menu-1">1.모험</div>
            <div id="menu-2">2.휴식</div>
            <div id="menu-3">3.종료</div>
            <input id="menu-input" />
            <button id="menu-button">입력</button>
        </form>
        <form id="battle-menu" style="display:none;">
            <div id="battle-1">1.공격</div>
            <div id="battle-2">2.회복</div>
            <div id="battle-3">3.도망</div>
            <input id="battle-input" />
            <button id="battle-button">입력</button>
        </form>
        <div id="message"></div>
        <div id="monster-stat">
            <span id="monster-name"></span>
            <span id="monster-hp"></span>
            <span id="monster-att"></span>
        </div>
    </div>

    <script>
        const $startScreen = document.querySelector('#start-screen');
        const $gameMenu = document.querySelector('#game-menu');
        const $battleMenu = document.querySelector('#battle-menu');
        const $heroName = document.querySelector('#hero-name');
        const $heroLevel = document.querySelector('#hero-level');
        const $heroHp = document.querySelector('#hero-hp');
        const $heroXp = document.querySelector('#hero-xp');
        const $heroAtt = document.querySelector('#hero-att');
        const $monsterName = document.querySelector('#monster-name');
        const $monsterHp = document.querySelector('#monster-hp');
        const $monsterAtt = document.querySelector('#monster-att');
        const $message = document.querySelector('#message');

        class Game {
            constructor(name) { // 초기설정값 
                this.monster = null;
                this.hero = null;
                this.monsterList = [
                    { name: '슬라임', hp: 25, att: 10, xp: 10 },
                    { name: '스켈레톤', hp: 50, att: 15, xp: 20 },
                    { name: '마왕', hp: 150, att: 35, xp: 50 },
                ];
                this.start(name);
                this.updateHeroStat();
            }
            start(name) { //특정동작 수행 부분
                $gameMenu.addEventListener('submit', this.onGameMenuInput);
                $battleMenu.addEventListener('submit', this.onBattleMenuInput);
                this.changeScreen('game');
                this.hero = new Hero(this, name);
            }
            changeScreen(screen) {
                if (screen === 'start') {
                    $startScreen.style.display = 'block';
                    $gameMenu.style.display = 'none';
                    $battleMenu.style.display = 'none';
                } else if (screen === 'game') {
                    $startScreen.style.display = 'none';
                    $gameMenu.style.display = 'block';
                    $battleMenu.style.display = 'none';
                } else if (screen === 'battle') {
                    $startScreen.style.display = 'none';
                    $gameMenu.style.display = 'none';
                    $battleMenu.style.display = 'block';
                }
            }
            onGameMenuInput = (event) => {
                event.preventDefault();
                const input = event.target['menu-input'].value;
                if (input === '1') { //모험
                    this.changeScreen('battle');
                    const randomIndex = Math.floor(Math.random() * this.monsterList.length);
                    const randomMonster = this.monsterList[randomIndex];
                    this.monster = new Monster(
                        this,
                        randomMonster.name,
                        randomMonster.hp,
                        randomMonster.att,
                        randomMonster.xp,
                    );
                    this.updateMonsterStat();
                    this.showMessage(`몬스터와 마주쳤다. ${this.monster.name}인 것 같다!`);
                } else if (input === '2') { //휴식
                    this.hero.hp = this.hero.maxHp;
                    this.updateHeroStat();
                    this.showMessage('충분한 휴식을 취했다.');
                } else if (input === '3') { //종료
                    this.showMessage(' ');
                    this.quit();
                }
            }
            onBattleMenuInput = (event) => {
                event.preventDefault();
                const input = event.target['battle-input'].value;
                if (input === '1') { //공격
                    const { hero, monster } = this;
                    hero.attack(monster);
                    monster.attack(hero);

                    if (hero.hp <= 0) {
                        this.showMessage(`${hero.lev}에서 전사. 새 주인공을 생성하세요.`);
                        this.quit();
                    } else if (monster.hp <= 0) {
                        this.showMessage(`몬스터를 잡아 ${monster.xp} 경험치를 얻었다.`);
                        hero.getXp(monster.xp);
                        this.monster = null;
                        this.changeScreen('game');
                    } else { //전투진행중 
                        this.showMessage(`${hero.att}의 데미지를 주고, ${monster.att}의 데미지를 받았다.`);
                    }

                    this.updateHeroStat();
                    this.updateMonsterStat();
                } else if (input === '2') { //회복
                    const { hero, monster } = this;
                    hero.hp = Math.min(hero.maxHp, hero.hp + 20);
                    //hero.hp 와 hero.maxHp 중 작은것이 내 체력이다. 
                    monster.attack(hero);
                    this.showMessage('체력을 조금 회복했다!');
                    this.updateHeroStat();
                } else if (input === '3') { // 도망 
                    this.changeScreen('game');
                    this.showMessage('부리나케 도망쳤다!');
                    this.monster = null;
                    this.updateMonsterStat();
                }
            }
            updateHeroStat() {
                const { hero } = this;
                if (hero === null) {
                    $heroName.textContent = '';
                    $heroLevel.textContent = '';
                    $heroHp.textContent = '';
                    $heroXp.textContent = '';
                    $heroAtt.textContent = '';
                    return
                }
                //hero상태 바뀔때마다 화면 다시 그려주기 
                $heroName.textContent = hero.name;
                $heroLevel.textContent = `${hero.lev}Lev`;
                $heroHp.textContent = `HP : ${hero.hp}/${hero.maxHp}`;
                $heroXp.textContent = `XP : ${hero.xp}/${15 * hero.lev}`;
                $heroAtt.textContent = `ATT: ${hero.att}`;
            }
            updateMonsterStat() {
                const { monster } = this;
                if (monster === null) {
                    $monsterName.textContent = '';
                    $monsterHp.textContent = '';
                    $monsterAtt.textContent = '';
                    return;
                }
                $monsterName.textContent = monster.name;
                $monsterHp.textContent = `HP : ${monster.hp}/${monster.maxHp}`;
                $monsterAtt.textContent = `ATT: ${monster.att}`;
            }
            showMessage(text) {
                $message.textContent = text;
            }
            quit() {
                this.hero = null;
                this.monster = null;
                this.updateHeroStat();
                this.updateMonsterStat();
                $gameMenu.removeEventListener('submit', this.onGameMenuInput);
                $battleMenu.removeEventListener('submit', this.onBattleMenuInput);
                this.changeScreen('start');
                game = null;
            }
        }
        class Unit {
            constructor(game, name, hp, att, xp) {
                this.game = game;
                this.name = name;
                this.maxHp = hp;
                this.hp = hp;
                this.xp = xp;
                this.att = att;
            }
            attack(target) {
                target.hp -= this.att;
            }
        }
        class Hero extends Unit {
            constructor(game, name) {
                super(game, name, 100, 10, 0); //부모클래스의 생성자 호출 겹치는거 괄호안에 넣어주기 
                this.lev = 1;

            }
            //unit에 attack있어서 상속받아 삭제해줌 
            heal(monster) {
                this.hp += 20;
                this.hp -= monster.att;
            }
            getXp(xp) {
                this.xp += xp;
                if (this.xp >= this.lev * 15) { //경험치를 다 채우면
                    this.xp -= this.lev * 15; // xp:20, lev:1, maxXp: 15 -> xp:5, lev2, maxXp: 15
                    this.lev += 1;
                    this.maxHp += 5; //레벨업에 따른 stat상승 
                    this.att += 5; //레벨업에 따른 stat상승 
                    this.hp = this.maxHp;
                    this.game.showMessage(`레벨업! 레벨 ${this.lev}`);
                }
            }
        }
        class Monster extends Unit {
            constructor(game, name, hp, att, xp) {
                super(game, name, hp, att, xp);
            }
            //unit에 attack있어서 상속받아 삭제해줌 
        }

        let game = null;
        //게임시작 
        $startScreen.addEventListener('submit', (event) => {
            event.preventDefault();
            const name = event.target['name-input'].value;
            game = new Game(name);
        });
    </script>
</body>

📌 코드공부하기


🖍 class 문법

게임은 상호작용이 많은 프로그램이기 때문에 class문법을 사용했다.


extends로 상속해주기


클래스 문법에서는 겹치는 부분을 따로 추출한 다음 extends를 이용하여 상속받을 수 있다.


class Unit {
            constructor(game, name, hp, att, xp) {
                this.game = game;
                this.name = name;
                this.maxHp = hp;
                this.hp = hp;
                this.xp = xp;
                this.att = att;
            }
            attack(target) {
                target.hp -= this.att;
            }
        }
        class Hero extends Unit {
            constructor(game, name) {
                super(game, name, 100, 10, 0); //부모클래스의 생성자 호출 겹치는거 괄호안에 넣어주기 
                this.lev = 1;

            }
            //unit에 attack있어서 상속받아 삭제해줌 
            heal(monster) {
                this.hp += 20;
                this.hp -= monster.att;
            }
            getXp(xp) {
                this.xp += xp;
                if (this.xp >= this.lev * 15) { //경험치를 다 채우면
                    this.xp -= this.lev * 15; // xp:20, lev:1, maxXp: 15 -> xp:5, lev2, maxXp: 15
                    this.lev += 1;
                    this.maxHp += 5; //레벨업에 따른 stat상승 
                    this.att += 5; //레벨업에 따른 stat상승 
                    this.hp = this.maxHp;
                    this.game.showMessage(`레벨업! 레벨 ${this.lev}`);
                }
            }
        }
        class Monster extends Unit {
            constructor(game, name, hp, att, xp) {
                super(game, name, hp, att, xp);
            }
            //unit에 attack있어서 상속받아 삭제해줌 
        }

다음과 같이 attack이라는 함수가 hero, monster 에 겹쳐있어 unit으로 따로 분리해준 후 상속시켰다.


🖍 this와 화살표함수


function(){this} 는 객체안의 this를 가르킨다.
화살표 함수에서는 밖의 this와 내부의 this가 같다.

  • a.addEventListener는 내부의 this가 a를 가리킨다.
    this는 함수가 호출될 때 결정된다.

🖍 bind 메서드


function a () {
console.log(this);
}

  • a.bind(document)(); -> document를 출력
  • 화살표 함수는 bind를 사용할 수 없다.

profile
connecting the dots 💫

1개의 댓글

comment-user-thumbnail
2024년 9월 15일

As the tournament progressed, Max’s understanding of the game deepened. He adjusted his strategies, took calculated risks, and read his opponents’ behaviors with increasing accuracy. His diligent approach paid off, allowing him to https://fairgocasinopokies.com/ advance through the tournament rounds. By the time he reached the final table, Max’s blend of strategy and intuition saw him emerge victorious. His win was a testament to how dedication and strategic thinking can transform a novice into a seasoned player.

답글 달기