ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JavaScript 입문 : textRPG 만들기 - 사냥감 변경, 클래스간 상호 작용(클래스 상속)
    컴퓨터 알아가기/JavaScript 2022. 9. 11. 19:30
    728x90
    반응형

    이 글은 제로초 TV의 자바스크립트 강좌를 기본으로 하고 있습니다. 

     

    지난시간 프로그래밍은 잘 작동이 되었지만 헌터가 계속적으로 사냥감을 획득하지 못하고 전사하는 경우가 발생이 되었습니다. 이는 설정해 놓은 동물들의 파워가 최초부터 워낙 강했기 때문인데요. 이 부분을 약간 수정하고 상호간 공격이 가능하도록 수정할 필요가 있습니다. 

     

    1. animalList 수정

     

    좀 더 사냥감 획득이 용이하게 내용을 변경해 주었습니다. 

     

     

    이렇게 하고나니 브라우저상 어느 정도 싸움이 됩니다. 

     

     

    현재까지의 자바스크립트 코딩 내역입니다. 

     

    // 객체지향프로그램 (OOP) 활용
    // 클래스로 재구성 3탄 - 모험: animal생성, 전투메뉴 생성은 이전완료, 전투메뉴중 공격
    // animal의 공격력 조절, 게임이 안되니 수정
    
    const $startScreen = document.querySelector('#start-screen');
    const $screen = document.querySelector('#screen');
    const $generalMenu = document.querySelector('#general-menu');
    const $battleMenu = document.querySelector('#battle-menu');
    const $message = document.querySelector('#message');
    
    // 주인공 Status 연결
    const $hunterStatus = document.querySelector('#hunter-status');
    const $hunterName = document.querySelector('#hunter-name');
    const $hunterLevel = document.querySelector('#hunter-level');
    const $hunterPower = document.querySelector('#hunter-power');
    const $hunterUpgrade = document.querySelector('#hunter-upgrade');
    const $hunterAttack = document.querySelector('#hunter-attack');
    
    // 적군 현황 연결
    const $animalStatus = document.querySelector('#animal-status');
    const $animalName = document.querySelector('#animal-name');
    const $animalLevel = document.querySelector('#animal-level');
    const $animalPower = document.querySelector('#animal-power');
    const $animalUpgrade = document.querySelector('#animal-upgrade');
    const $animalAttack = document.querySelector('#animal-attack');
    
    // hunter와 animal을 class로 재구성 (OOP), 상호작용 및 상호접근 용이
    class General { // 생성자는 대문자로, 
      constructor(name) { // 이름 매개변수로 생성
        this.animal = null;    
        this.hunter = null; // new General(name)으로부터 받아서 
        this.animalList = [
          {name: '멧돼지', power: 10, att: 10, upgrade: 10},
          {name: '사자', power: 20, att: 15, upgrade: 20},
          {name: '호랑이', power: 30, att: 20, upgrade: 30},
          {name: '코끼리', power: 50, att: 40, upgrade: 50},
          {name: '공룡', power: 100, att: 70, upgrade: 100},
        ];
        this.start(name); // General객체내 start시킴
        
      }
    
      start(name) { // 특정 동작을 같이 묶는게 좋다
        // addEventListener가 있으니까 여기서 this는 향후 화살표 함수로 받음
        $generalMenu.addEventListener('submit', this.onGeneralMenuInput);    
        $battleMenu.addEventListener('submit', this.onBattleMenuInput);
        this.changeScreen('general');     
        this.hunter = new Hunter(this, name);
        this.updateHunter();
      }
    
      changeScreen(screen) { // screen 변하는 기능 한곳에
        if (screen === 'start') { // 처음 시작이라는 표시, 어떤 단어도 OK
          $startScreen.style.display = 'block';
          $generalMenu.style.display = 'none';
          $battleMenu.style.display = 'none';
        } else if (screen === 'general') {
          $startScreen.style.display = 'none';
          $generalMenu.style.display = 'block';
          $battleMenu.style.display = 'none';
        } else if (screen === 'battle') {
          $startScreen.style.display = 'none';
          $generalMenu.style.display = 'none';
          $battleMenu.style.display = 'block';
        }
      }
    
      // addEventListener가 붙으면 this는 화살표 함수로 풀어야 한다 (암기)
      // 화살표함수의 this는 밖에 있는 this를 그대로 가져옴
      onGeneralMenuInput = (event) => { // start()에서 generalMenu 활성화
        event.preventDefault();
        const general = event.target['general-input'].value; // id가져와서 활성화, value는 text
        if (general === '1') { // 모험
          this.changeScreen('battle'); // 전투메뉴로 만들고 
          console.log(this); // 여기서 this는 General
          // 랜덤하게 animal 생성
          const randomIndex = Math.floor(Math.random() * this.animalList.length);
          const randomAnimal = this.animalList[randomIndex];
          // new Animal( ) 생성
          this.animal = new Animal(
            this,
            randomAnimal.name,
            randomAnimal.power,
            randomAnimal.att,
            randomAnimal.upgrade,
            );
          this.updateAnimal();
          this.showMessage(`사냥할 동물이 나타났다! ${this.animal.name}인것 같다!`);
        } else if (general === '2') { // 휴식
          
        } else if (general === '3') { // 종료
          this.quit();      
        }
      };
    
      onBattleMenuInput = (event) => { // start()에서 battleMenu 활성화
        event.preventDefault();
        const battle = event.target['battle-input'].value; // id가져와서 활성화
        if (battle === '1') { // 공격, 서로 공격
          const { hunter, animal } = this;
          hunter.attack(animal);
          animal.attack(hunter);
          // 기본적인 공격 메시지 
          // this.showMessage(`헌터가 ${hunter.att}의 데미지를 주고 ${animal.att}의 데미지를 받았다.`);
    
          // 공격해서 나올수 있는 경우의 수를 고민
          // 1. hunter power가 0이하면 전사 및 새로운 hunter생성
          // 2. animal power가 0이하면 전사 및 animal의 upgrade power를 얻음 (get)
          // 3. 1,2가 아닌 경우 기본적인 공격메시지 송출
          if (hunter.power <= 0) { // 1.
            this.showMessage(`${hunter.level}레벨에서 전사. 새 주인공을 생성하세요`);
            this.quit(); // 게임종료에 대한 함수
          } else if (animal.power <= 0) { // 2.
            this.showMessage(`사냥에 성공하여 사냥감이 가지고 있는 ${animal.upgrade}의 힘을 얻었다.`);
            hunter.getUpgrade(animal.upgrade); // 사냥감 힘 얻는 함수, hunetr내 설정
            this.animal = null; // 없어지고 
            this.changeScreen('general'); // 일반모드로 
          } else { // 3.
            this.showMessage(`헌터가 ${hunter.att}의 데미지를 주고 ${animal.att}의 데미지를 받았다.`);
          }
    
          // 각 상태가 업데이트되야 함
          this.updateHunter();
          this.updateAnimal();      
          
        } else if (battle === '2') { // 회복
    
        } else if (battle === '3') { // 도망 : 일반메뉴로 전환
          this.changeScreen('general');
        }
      };
    
      // hunter상태 update, 없으면 빈칸, 레벨과 파워변하는 상황 (초기선언)
      updateHunter() {
        const { hunter } = this; // 업데이트 할 변수선언, 구조분해할당 모습
        if (hunter === null) { // hunter가 없으면 
          $hunterName.textContent = '';
          $hunterLevel.textContent = '';
          $hunterPower.textContent = '';
          $hunterUpgrade.textContent = '';
          $hunterAttack.textContent = '';
          return; // return 하고
        } // hunter가 있으면 
        $hunterName.textContent = hunter.name;
        $hunterLevel.textContent = `레벨: ${hunter.level}`;
        $hunterPower.textContent = `파워: ${hunter.power} / ${hunter.maxPower}`;
        $hunterUpgrade.textContent = `업그레이드: ${hunter.upgrade} / ${15 * hunter.level}`;
        $hunterAttack.textContent = `공격력: ${hunter.att} `;
      }
    
      updateAnimal() {
        const { animal } = this; // 업데이트 할 변수선언, 구조분해할당 모습
        if (animal === null) { // animal이 없으면 
          $animalName.textContent = '';      
          $animalPower.textContent = '';      
          $animalAttack.textContent = '';
          return; // return 하고
        } // animal이 있으면 
        $animalName.textContent = animal.name;    
        $animalPower.textContent = `파워: ${animal.power} / ${animal.maxPower}`;    
        $animalAttack.textContent = `공격력: ${animal.att} `;
      }
    
      showMessage(text) { // 메시지 보이게 
        $message.textContent = text;
      }
      
      // 게임종료 quit()
      quit() {
        this.hunter = null;
        this.animal = null;
        this.updateHunter();
        this.updateAnimal();
        $generalMenu.removeEventListener('submit', this.onGeneralMenuInput);
        $battleMenu.removeEventListener('submit', this.onBattleMenuInput);
        general = null;
      }
    
    }
    
    class Hunter { // hunter는 General에 속하고 name도 속하는 
      constructor(general, name) {
        this.general = general;
        this.name = name;
        this.level = 1;
        this.maxPower = 100;
        this.power = 100;
        this.upgrade = 0;
        this.att = 10;
        
      }
      attack(target) {
        target.power -= this.att;
        
      }
      heal(animal) {
        this.power += 20;
        this.power -= animal.att;
      }
      // 사냥감 파워 획득
      getUpgrade(upgrade) {
        this.upgrade += upgrade; // upgrade 될때마다 아래 if문을 한번씩 검토, 에러방지
        if (this.upgrade >= this.level * 15) { // 경험치를 다 채우면
          this.upgrade -= this.level * 15 // 레벨이 오르기 위한 필요power는 15이기에 레벨바뀌고 다시 0으로 세팅
          this.level += 1;
          this.maxPower += 20; // upgrade되면 최대체력은 10늘어나고 
          this.att += 20; // upgrade되면 공격력도 10늘어나고 
          this.power = this.maxPower; // upgrade되면 최대체력은 현재체력으로 되고 
          this.general.showMessage(`레벨업 완료! 현재레벨 ${this.level}`);
        }    
      }
    }
    
    class Animal {
      constructor(general, name, power, att, upgrade) {
        this.general = general;
        this.name = name;
        this.maxPower = power;
        this.power = power;
        this.att = att;
        this.upgrade = upgrade;
        
      }
      attack(target) {
        target.power -= this.att;
      }
    }
    
    // 게임 시작 프로그램
    let general = null;
    // start-screen 활성화 
    $startScreen.addEventListener('submit', (event) => {
      event.preventDefault();
      const name = event.target['name-input'].value; // hunter 이름 입력
      general = new General(name); // 이름 입력받아 새로운 게임 만들기, 생성자 실행조건 new 필요
    });

     

     

    2. 클래스 상속 extends

     

    Hunter와 Animal 클래스를 보면 공통된 항목을 발견할 수 있습니다. 이미 배웠듯이 super(  )라는 부모클래스의 매개변수를 받는 메소드를 통하여 클래스 상속을 시킬 수 있습니다.

     

    여기서 공통된 메소드인 attack(target)또한 부모클래스에 넣어 사용할 수 있습니다. class Unit이라는 부모클래스를 만들고 다음과 같이 작성하면 클래스 상속 처리가 됩니다. 

     

     

    이렇게 필요한 부모클래스로부터 내용을 상속받아 클래스간 상호작용을 하는 내용을 공부해 보았습니다. 이제는 연습만이 살 길인것 같습니다.  얼마나 많은 연습과 노력을 하느냐에 따라 이러한 코딩이 나의 것이 되느냐 아니면 단지 흉내만 내느냐의 문제로 나타날 것 같습니다. 

     

    클래스 상속이 적용된 최종 자바스크립트 코드입니다. 

     

    // 나머지 코드 작성 :휴식, 회복
    // 휴식 : 체력 회복, 전투중에 체력을 20회복, 회복후 동물에게 한번 공격 당함, 최대체력 넘을 수 없음
    // 클래스 상속 
    
    const $startScreen = document.querySelector('#start-screen');
    const $screen = document.querySelector('#screen');
    const $generalMenu = document.querySelector('#general-menu');
    const $battleMenu = document.querySelector('#battle-menu');
    const $message = document.querySelector('#message');
    
    // 주인공 Status 연결
    const $hunterStatus = document.querySelector('#hunter-status');
    const $hunterName = document.querySelector('#hunter-name');
    const $hunterLevel = document.querySelector('#hunter-level');
    const $hunterPower = document.querySelector('#hunter-power');
    const $hunterUpgrade = document.querySelector('#hunter-upgrade');
    const $hunterAttack = document.querySelector('#hunter-attack');
    
    // 적군 현황 연결
    const $animalStatus = document.querySelector('#animal-status');
    const $animalName = document.querySelector('#animal-name');
    const $animalLevel = document.querySelector('#animal-level');
    const $animalPower = document.querySelector('#animal-power');
    const $animalUpgrade = document.querySelector('#animal-upgrade');
    const $animalAttack = document.querySelector('#animal-attack');
    
    // hunter와 animal을 class로 재구성 (OOP), 상호작용 및 상호접근 용이
    class General { // 생성자는 대문자로, 
      constructor(name) { // 이름 매개변수로 생성
        this.animal = null;    
        this.hunter = null; // new General(name)으로부터 받아서 
        this.animalList = [
          {name: '멧돼지', power: 10, att: 10, upgrade: 10},
          {name: '사자', power: 20, att: 15, upgrade: 20},
          {name: '호랑이', power: 30, att: 20, upgrade: 30},
          {name: '코끼리', power: 50, att: 40, upgrade: 50},
          {name: '공룡', power: 100, att: 70, upgrade: 100},
        ];
        this.start(name); // General객체내 start시킴
        
      }
    
      start(name) { // 특정 동작을 같이 묶는게 좋다
        // addEventListener가 있으니까 여기서 this는 향후 화살표 함수로 받음
        $generalMenu.addEventListener('submit', this.onGeneralMenuInput);    
        $battleMenu.addEventListener('submit', this.onBattleMenuInput);
        this.changeScreen('general');     
        this.hunter = new Hunter(this, name);
        this.updateHunter();
      }
    
      changeScreen(screen) { // screen 변하는 기능 한곳에
        if (screen === 'start') { // 처음 시작이라는 표시, 어떤 단어도 OK
          $startScreen.style.display = 'block';
          $generalMenu.style.display = 'none';
          $battleMenu.style.display = 'none';
        } else if (screen === 'general') {
          $startScreen.style.display = 'none';
          $generalMenu.style.display = 'block';
          $battleMenu.style.display = 'none';
        } else if (screen === 'battle') {
          $startScreen.style.display = 'none';
          $generalMenu.style.display = 'none';
          $battleMenu.style.display = 'block';
        }
      }
    
      // addEventListener가 붙으면 this는 화살표 함수로 풀어야 한다 (암기)
      // 화살표함수의 this는 밖에 있는 this를 그대로 가져옴
      onGeneralMenuInput = (event) => { // start()에서 generalMenu 활성화
        event.preventDefault();
        const general = event.target['general-input'].value; // id가져와서 활성화, value는 text
        if (general === '1') { // 모험
          this.changeScreen('battle'); // 전투메뉴로 만들고 
          console.log(this); // 여기서 this는 General
          // 랜덤하게 animal 생성
          const randomIndex = Math.floor(Math.random() * this.animalList.length);
          const randomAnimal = this.animalList[randomIndex];
          // new Animal( ) 생성
          this.animal = new Animal(
            this,
            randomAnimal.name,
            randomAnimal.power,
            randomAnimal.att,
            randomAnimal.upgrade,
            );
          this.updateAnimal();
          this.showMessage(`사냥할 동물이 나타났다! ${this.animal.name}인것 같다!`);
        } else if (general === '2') { // 휴식
          
        } else if (general === '3') { // 종료
          this.quit();      
        }
      };
    
      onBattleMenuInput = (event) => { // start()에서 battleMenu 활성화
        event.preventDefault();
        const battle = event.target['battle-input'].value; // id가져와서 활성화
        if (battle === '1') { // 공격, 서로 공격
          const { hunter, animal } = this;
          hunter.attack(animal);
          animal.attack(hunter);
          // 기본적인 공격 메시지 
          // this.showMessage(`헌터가 ${hunter.att}의 데미지를 주고 ${animal.att}의 데미지를 받았다.`);
    
          // 공격해서 나올수 있는 경우의 수를 고민
          // 1. hunter power가 0이하면 전사 및 새로운 hunter생성
          // 2. animal power가 0이하면 animal의 upgrade power를 얻음 (get)
          // 3. 1,2가 아닌 경우 기본적인 공격메시지 송출
          if (hunter.power <= 0) { // 1.
            this.showMessage(`${hunter.level}레벨에서 전사. 새 주인공을 생성하세요`);
            this.quit(); // 게임종료에 대한 함수
          } else if (animal.power <= 0) { // 2.
            this.showMessage(`사냥에 성공하여 사냥감이 가지고 있는 ${animal.upgrade}의 힘을 얻었다.`);
            hunter.getUpgrade(animal.upgrade); // 사냥감 힘 얻는 함수, hunetr내 설정
            this.animal = null; // 없어지고 
            this.changeScreen('general'); // 일반모드로 
          } else { // 3.
            this.showMessage(`헌터가 ${hunter.att}의 데미지를 주고 ${animal.att}의 데미지를 받았다.`);
          }
    
          // 각 상태가 업데이트되야 함
          this.updateHunter();
          this.updateAnimal();      
          
        } else if (battle === '2') { // 회복
    
        } else if (battle === '3') { // 도망 : 일반메뉴로 전환
          this.changeScreen('general');
        }
      };
    
      // hunter상태 update, 없으면 빈칸, 레벨과 파워변하는 상황 (초기선언)
      updateHunter() {
        const { hunter } = this; // 업데이트 할 변수선언, 구조분해할당 모습
        if (hunter === null) { // hunter가 없으면 
          $hunterName.textContent = '';
          $hunterLevel.textContent = '';
          $hunterPower.textContent = '';
          $hunterUpgrade.textContent = '';
          $hunterAttack.textContent = '';
          return; // return 하고
        } // hunter가 있으면 
        $hunterName.textContent = hunter.name;
        $hunterLevel.textContent = `레벨: ${hunter.level}`;
        $hunterPower.textContent = `파워: ${hunter.power} / ${hunter.maxPower}`;
        $hunterUpgrade.textContent = `업그레이드: ${hunter.upgrade} / ${15 * hunter.level}`;
        $hunterAttack.textContent = `공격력: ${hunter.att} `;
      }
    
      updateAnimal() {
        const { animal } = this; // 업데이트 할 변수선언, 구조분해할당 모습
        if (animal === null) { // animal이 없으면 
          $animalName.textContent = '';      
          $animalPower.textContent = '';      
          $animalAttack.textContent = '';
          return; // return 하고
        } // animal이 있으면 
        $animalName.textContent = animal.name;    
        $animalPower.textContent = `파워: ${animal.power} / ${animal.maxPower}`;    
        $animalAttack.textContent = `공격력: ${animal.att} `;
      }
    
      showMessage(text) { // 메시지 보이게 
        $message.textContent = text;
      }
      
      // 게임종료 quit()
      quit() {
        this.hunter = null;
        this.animal = null;
        this.updateHunter();
        this.updateAnimal();
        $generalMenu.removeEventListener('submit', this.onGeneralMenuInput);
        $battleMenu.removeEventListener('submit', this.onBattleMenuInput);
        general = null;
      }
    
    }
    
    // 비숫한 캐릭터의 부모 class를 만들고 나머지는 상속시킴
    // 부모클래스는 기본적 변수처리
    class Unit {
      constructor(general, name, power, att, upgrade) {
        this.general = general;
        this.name = name;    
        this.maxPower = power;
        this.power = power;
        this.att = att;
        this.upgrade = upgrade;
      }
      attack(target) {
        target.power -= this.att;
      }
    }  
    
    // Hunter는 공통점빼고 정의
    class Hunter extends Unit { 
      constructor(general, name) {
        super(general, name, 100, 10, 0);
        this.level = 1;       
      }
      
      // 사냥감 파워 획득
      getUpgrade(upgrade) {
        this.upgrade += upgrade; // upgrade 될때마다 아래 if문을 한번씩 검토, 에러방지
        if (this.upgrade >= this.level * 15) { // 경험치를 다 채우면
          this.upgrade -= this.level * 15 // 레벨이 오르기 위한 필요power는 15이기에 레벨바뀌고 다시 0으로 세팅
          this.level += 1;
          this.maxPower += 20; // upgrade되면 최대체력은 10늘어나고 
          this.att += 20; // upgrade되면 공격력도 10늘어나고 
          this.power = this.maxPower; // upgrade되면 최대체력은 현재체력으로 되고
          this.general.showMessage(`
          사냥에 성공하여 사냥감이 가지고 있는 ${upgrade}의 힘을 얻었다.       
          레벨업 완료! 현재레벨 ${this.level}
          `);
        }    
      }
    }
    
    // animal도 공통점 빼고 정의
    class Animal extends Unit {
      constructor(general, name, power, att, upgrade) {
        super(general, name, power, att, upgrade);    
      }  
    }
    
    // 게임 시작 프로그램
    let general = null;
    // start-screen 활성화 
    $startScreen.addEventListener('submit', (event) => {
      event.preventDefault();
      const name = event.target['name-input'].value; // hunter 이름 입력
      general = new General(name); // 이름 입력받아 새로운 게임 만들기, 생성자 실행조건 new 필요
    });
    반응형

    댓글

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
Designed by Tistory.