하노이 탑 쌓기 게임 만들기(하)

자바스크립트 하노이 탑 쌓기/하노이 타워 게임 스크린샷

<이 화면은 스크린샷 그림입니다>

하노이 탑 쌓기 게임 만들기(상)에서는 게임에 사용될 상수, 변수들의 선언과 화면을 그리는 데 필요한 함수들을 만들어 보았습니다. 이번에는 Text Game Maker JS를 사용하여 키입력을 받는 방법과 데이터를 조작하는 함수들을 만들어 게임을 완성해 봅시다.

소스 코드

하노이 탑 쌓기 게임 만들기(하)의 소스 코드를 여기를 클릭하여 확인해 주세요. 전체 코드를 복사한 후 TM 스타터 프로그램의 main.js 파일에 붙여넣기합니다. 그 다음 index.html을 더블 클릭하면 아래와 같이 웹 브라우저가 나타나게 됩니다.

자바스크립트 하노이 탑 쌓기/하노이 타워 게임 브라우저 스크린샷

main.js의 코드를 살펴봅시다. drawGameOver 함수까지(151번째 줄)는 변경사항이 없고 reset 함수부터 추가된 코드들을 살펴봅시다.

function reset(){
  towerData = resetTowerData();
  moveCount = 0;
  cursorPosition = 0;
  cursorDiskValue = 0;
  isGameOver = false;

  TMS.cursor.hide();
  TMS.clearScreen();
  TMI.keyboard.clearKeyPressed(); // <-
  drawFrame();
  drawMove();
  drawCursor();
  drawTower();
}

reset 함수에 TMI.keyboard.clearKeyPressed(); 코드가 추가되었습니다. 이 함수는 TMI에 기억된 키 입력을 초기화하는 함수입니다. TMI에 대한 자세한 설명은 TM.InputManager, TM.InputManager_Keyboard 문서에서 확인할 수 있습니다.

function moveCursor(dir){
  var isCursorMoved = false;
  if((dir== 1 && cursorPosition<NUM_OF_POLES-1)
  || (dir==-1 && cursorPosition>0)){
    cursorPosition += dir;
    isCursorMoved = true;
  }
  return isCursorMoved;
}

커서를 이동하는 함수입니다. 움직일 위치(-1 왼쪽으로 한칸, +1 오른쪽으로 한칸)을 받아 조건에 맞으면 커서를 이동시키고 실제로 성공여부를 return합니다.

커서는 장대 위에만 존재할 수 있기 때문에 cursorPosition는 0보다 작거나 NUM_OF_POLES-1보다 클 수 없습니다. (첫번째 장대의 위치 값은 0)

function pickUpDisk(){
  var isDiskPickedUp = false;
  if(cursorPosition >= 0
  && cursorPosition <= NUM_OF_POLES
  && cursorDiskValue === 0){
    cursorDiskValue = towerData[cursorPosition].pop();
    isDiskPickedUp = true;
  }
  return isDiskPickedUp;
}

커서 위치의 장대에서 가장 위쪽 원판을 집는 함수입니다. towerData에서 값을 찾아 cursorDiskValue에 값을 넣고 성공여부를 return합니다.

실제로 해당 코드가 실행되기 위해서는 아래의 조건을 모두 만족해야 합니다.

  • cursorPosition >= 0 && cursorPosition <= NUM_OF_POLES: 현재 커서의 위치가 유효한 장대의 위치에 있어야 합니다.
  • cursorDiskValue === 0: 현재 커서가 원판 값을 가지고 있지 않아야 합니다.

조건이 모두 만족되면 현재 장대 위치의 배열(towerData[cursorPosition])에서 pop함수를 사용하여 마지막 값(장대의 가장 위쪽 원판의 값)을 빼서 cursorDiskValue에 넣습니다.

function dropDisk(){
  var isDiskDropped = false;
  if(cursorPosition >= 0
  && cursorPosition <= NUM_OF_POLES
  && cursorDiskValue > 0){
    if(towerData[cursorPosition].length == 0
    || towerData[cursorPosition][towerData[cursorPosition].length-1] > cursorDiskValue){
      towerData[cursorPosition].push(cursorDiskValue);
      cursorDiskValue = 0;
      isDiskDropped = true;
    }
  }
  return isDiskDropped;
}

반대로 커서 위치의 원판을 장대에 내려 놓는 함수입니다. 조건에 맞으면 curserDiskValue의 값을 towerData에 넣고 성공여부를 return합니다.

실제로 해당 코드가 실행되기 위해서는 아래의 조건을 모두 만족해야 합니다.

  • cursorPosition >= 0 && cursorPosition <= NUM_OF_POLES: 현재 커서의 위치가 유효한 장대의 위치에 있어야 합니다.
  • cursorDiskValue > 0: 현재 커서가 원판 값을 가지고 있아야 합니다.
  • towerData[cursorPosition].length == 0: 현재 장대 위치의 배열이 원판을 하나도 가지고 있지 않거나,
    towerData[cursorPosition][towerData[cursorPosition].length-1] > cursorDiskValue: 그 배열의 마지막 값(장대의 가장 위쪽 원판의 값)이 현재 커서가 가진 원판 값보다 커야 합니다.

조건이 모두 만족되면 현재 장대 위치의 배열(towerData[cursorPosition])에서 push함수를 사용하여 cursorDiskValue의 값을 배열 가장 뒤(장대의 가장 위쪽)에 넣어 주고, cursorDiskValue는 0으로 바꾸어 줍니다.

function checkGameOver(){
  var isGameOver = (towerData[NUM_OF_POLES-1][NUM_OF_DISKS-1]==1)?true:false;
  return isGameOver;
}

게임이 종료되었는지, 즉 모든 원판이 가장 오른쪽 장대로 이동하였는지를 판별하는 함수입니다.

가장 오른쪽 장대의 가장 위쪽 원판 위치의 값이 1이면(3칸짜리 원판이 가장 오른쪽 위쪽으로 이동한 경우) 게임이 종료된 것입니다.

var mainInterval = window.setInterval(function(){
  if(TMI.keyboard.checkKeyPressed(KEYSET.ESC)){
    reset();
  }

  if(!isGameOver){
    var isCursorUpdated = false;
    var isTowerUpdated = false;
    var isMoveUpdated = false;
    if(TMI.keyboard.checkKeyPressed(KEYSET.RIGHT)
    && moveCursor(1)){
      isCursorUpdated = true;
    }
    if(TMI.keyboard.checkKeyPressed(KEYSET.LEFT)
    && moveCursor(-1)){
      isCursorUpdated = true;
    }
    if(TMI.keyboard.checkKeyPressed(KEYSET.ENTER)){
      if(cursorDiskValue && dropDisk()){
        moveCount++;
        isCursorUpdated = true;
        isTowerUpdated = true;
        isMoveUpdated = true;
        isGameOver = checkGameOver();
      }
      else if(!cursorDiskValue && pickUpDisk()){
        isCursorUpdated = true;
        isTowerUpdated = true;
      }
    }

    if(isCursorUpdated) drawCursor();
    if(isTowerUpdated) drawTower();
    if(isMoveUpdated) drawMove();
    if(isGameOver) drawGameOver();
  }

  TMD.print('debug-data',{
    moveCount: moveCount,
    cursorPosition: cursorPosition,
    cursorDiskValue: cursorDiskValue,
    isGameOver: isGameOver,
  });
}, 40);

키 입력을 받고 키입력에 따라 게임을 진행하는 부분입니다. 부분적으로 상세하게 살펴봅시다.

var mainInterval = window.setInterval(function(){
 //...
}, 40);

window.setInterval 함수(자바스크립트의 기본함수입니다)로 40 millisecond 마다 //...의 코드를 반복하게 됩니다.

다음은 반복되는 코드들를 살펴봅시다.

  if(TMI.keyboard.checkKeyPressed(KEYSET.ESC)){
    reset();
  }

TMI.keyboard.checkKeyPressed 함수는 이전 확인 이후로 해당 키가 눌렸는지를 확인하는 함수입니다. 게임 진행중에 언제라도 ESC 키를 누르면 게임이 초기화 됩니다.

다음은 if(!isGameOver) 안의 부분 즉, 게임종료가 아닌 때에 실행되는 코드를 살펴봅시다. 반대로 생각하면 게임이 종료인 경우에는 실행되지 않는 코드들입니다.

    var isCursorUpdated = false;
    var isTowerUpdated = false;
    var isMoveUpdated = false;

각 요소들의 데이터가 수정되었는지 아닌지를 가지는 변수입니다. 기본값은 false이며 값이 수정되면 true로 바꾸어 줍니다. 각각의 값들이 true이면 뒤에 나오는 코드를 통해서 화면에 해당 요소를 업데이트하게 됩니다.

    if(TMI.keyboard.checkKeyPressed(KEYSET.RIGHT)
    && moveCursor(1)){
      isCursorUpdated = true;
    }
    if(TMI.keyboard.checkKeyPressed(KEYSET.LEFT)
    && moveCursor(-1)){
      isCursorUpdated = true;
    }

오른쪽 화살표 키가 눌린 경우, moveCursor(1)을 실행하고 만약 커서가 실제로 움직였다면 truereturn되므로 isCursorUpdatedtrue를 대입합니다. 왼쪽 화살표 키가 눌린 경우도 유사하게 작동합니다.

    if(TMI.keyboard.checkKeyPressed(KEYSET.ENTER)){
      if(cursorDiskValue && dropDisk()){
        moveCount++;
        isCursorUpdated = true;
        isTowerUpdated = true;
        isMoveUpdated = true;
        isGameOver = checkGameOver();
      }
      else if(!cursorDiskValue && pickUpDisk()){
        isCursorUpdated = true;
        isTowerUpdated = true;
      }
    }

엔터키가 눌린 경우 현재 cursorDiskValue의 값이 있는지(커서가 원판을 가지고 있는지), 있다면 다음으로(&&) dropDisk()를 실행하고 drop이 성공인 경우 moveCount를 1증가 시키고 각종 값들이 업데이트되었음을 true로 대입한 후 checkGameOver()로 게임이 종료되었는지를 확인합니다. cursorDiskValue의 값이 없다면 다음으로(&&) pickupDisk()를 실행하고 커서와 타워가 업데이트 되었음을 true로 대입합니다.

    if(isCursorUpdated) drawCursor();
    if(isTowerUpdated) drawTower();
    if(isMoveUpdated) drawMove();
    if(isGameOver) drawGameOver();

화면에 업데이트해야 할 부분이 있다면 함수를 호출하여 화면에 텍스트를 출력합니다.

  TMD.print('debug-data',{
    moveCount: moveCount,
    cursorPosition: cursorPosition,
    cursorDiskValue: cursorDiskValue,
    isGameOver: isGameOver,
  });

마지막으로 원래 마지막에 있던 debug-data를 interval 속으로 넣었습니다. 이제 실시간 변수 정보를 바로 확인할 수 있습니다.

예제 코드의 실행

이 화면은 실제 실행중인 프로그램으로 입니다. 키 입력을 하기 위해 우선 게임 화면을 마우스로 한번 클릭한 후 화살표키와 엔터키, ESC키를 눌러서 게임이 제대로 작동하는지를 살펴봅시다.


이상으로 하노이 타워 만들기 강의가 끝났습니다. 수고하셨습니다!

댓글

댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP