모바일/HTML5

[HTML5 Game] Game State

박종명 2013. 9. 27. 21:00
728x90

게임은 상태의 집합이라 할 수 있다. 보통 우리가 게임을 할 때 대략 다음과 같은 수순의 단계를 만나게 된다.

 

게임 준비 -> 게임 실행 -> 게임 종료

 

물론 복잡한 게임에서는 더 다양한 단계가 있을 수 있지만, 개념을 이해하는 수준에서 위와 같이 간단히 세 가지 단계이 있고 한 번에 하나의 단계가 활성화되어 게임이 실행된다는 것을 이해하는게 중요하다.

 

이러한 게임 흐름의 각 단계를 '게임 상태'라 하며 HTML5 Game 개발에서도 이러한 상태의 전이를 통해 전반적인 게임 흐름을 관리하게 된다.

 

다음 그림은 게임 루프상에서 실행되는 게임 상태를 표현한 것이다. 게임 상태는 특정한 이벤트에 의해 서로 교체되며 한 번에 하나의 상태가 게임 루프 상에서 동작하여 게임이 진행된다.

 

 

 

게임 상태를 잘 분리해서 독립화시켜 적절히 모듈화한다면 개발과 유지보수에 좋은 영향을 미치게 된다. 따라서 게임 개발 시 각각의상태를 객체화 시켜서 관리하는 것이 권장된다.

 

 

그럼 이제 게임 상태와 상태의 변경을 통한 게임 흐름을 체험해 볼만한 간단한 샘플을 작성해 보자.

총 3가지 게임상태(ready, running, end)를 객체로 관리하고 키보드의 스페이스 바를 누르면 다음 상태로 이동하는 예인데, running 상태에서는 캔버스에 랜덤한 사격형을 그리는 이전 글(Game Loop, http://m.mkexdev.net/242)의 샘플을 그대로 사용할 것이다.

 

먼저 다음과 같이 HTML 파일을 준비한다.

<!DOCTYPE html>
<html lang="en">
 <head>  
  <script type="text/javascript" src="js/gameState.js"></script>
 </head> 
 <body>  
  <canvas id="GameCanvas" width="500" height="400" style="border: 1px solid #000;">
    HTML5 Canvas를 지원하지 않습니다. 크롬 또는 사파리와 같은 HTML5 지원 브라우저를 이용해 주세요
  </canvas>        
 </body>
</html>

 

 

다음으로 '준비/실행/종료'에 해당하는 각각의 게임상태를 객체로 관리하기 위한 3개의 생성자 함수를 정의하는데 공통 속성을 재사용하기 위해 프로토타입의 상속을 활용한다. 3가지 상태는 모두 게임 루프상에서 실행되기 위한 update(), display() 함수를 가지고 있으며 각 상태에 맞는 업데이트와 화면 갱신을 처리하도록 한다.

 

/* Define Base Game State Class */
function GameState(canvasElement){
 if(canvasElement != undefined){
     this.canvasSize = {width: canvasElement.width, height: canvasElement.height};  
     this.canvasContext = canvasElement.getContext('2d');                                    
   }
}

 

/* Define Ready State Class */
function Ready(canvasElement){
   //Call Parent Constract Function
   GameState.call(this,canvasElement);
}

 

Ready.prototype = new GameState(); //inherit

 

Ready.prototype.update = function(){ 
  this.canvasContext.fillStyle = '#000000'; 
}

 

Ready.prototype.display = function(){ 
   this.canvasContext.font = '18pt Arial';        
   this.canvasContext.textBaseline = 'top';
   this.canvasContext.fillText("Ready..." ,200, 150);
}

 

/* Define Running State Class */
function Running(canvasElement){
  //Call Parent Constract Function
  GameState.call(this,canvasElement);

  this.position = {};
}

 

Running.prototype = new GameState(); //inherit

 

Running.prototype.update = function(){ 
  this.position.x = Math.floor(Math.random() * (canvasElement.width - 20));
  this.position.y = Math.floor(Math.random() * (canvasElement.height - 20));
  
  this.canvasContext.fillStyle = 'rgb(' + Math.floor(Math.random() * 255) + ','
                    + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')'; 
}

 

Running.prototype.display = function(){ 
   this.canvasContext.fillRect(this.position.x, this.position.y, 20, 20);
}

 

/* Define End Class */
function End(canvasElement){
   //Call Parent Constract Function
   GameState.call(this,canvasElement);
}

 

End.prototype = new GameState(); //inherit

 

End.prototype.update = function(){ 
   this.canvasContext.fillStyle = '#000000'; 
}

 

End.prototype.display = function(){ 
   this.canvasContext.font = '18pt Arial';        
   this.canvasContext.textBaseline = 'top';
   this.canvasContext.fillText("End!!!" ,200, 150);
}

 

 

 

게임 상태가 준비되었으니, 각 상태가 실행되도록 다음과 같이 작성한다. 게임상태를 저장하기 위한 gameState 배열과 이 배열에서 특정 상태를 선택하기 위한 인덱스 값을 저장하는 currentGameStateIndex 변수를 선언한다. 키보드의 스페이스 바를 누르면 이 변수 값을 차례대로(0~2) 변경되도록 하여 게임 상태가 순환되도록 한다. 나머지 코드는 지금까지 학습했던 내용과 동일하므로 설명을 생략한다.

 

var fps = 10;
var canvasElement;
var gameContext;
var gameState = [];
var currentGameStateIndex = 0;

 

function init(){
   canvasElement = document.getElementById('GameCanvas');
   gameContext = canvasElement.getContext('2d'); 
 
   //Create Game State Instance & Push in gameState Array
   gameState = [new Ready(canvasElement),new Running(canvasElement),new End(canvasElement)];
 
   setInterval(gameLoop, 1000/fps);
}

 

function gameLoop(){ 
   gameState[currentGameStateIndex].update();
   gameState[currentGameStateIndex].display();
}

 

function ChangeGameState(e){
   if(e.keyCode == 32){ //32: Space Key
      gameContext.clearRect(0, 0, canvasElement.width, canvasElement.height);  
      //Change Game State Index(0 ~ 2) 
      currentGameStateIndex = (currentGameStateIndex + 1) % 3; 
   }
}

 

window.addEventListener('load', init, false);
window.addEventListener("keydown", ChangeGameState, false);

 

샘플을 브라우저로 확인해 보면 스페이스 키로 인해 다음의 3 단계가 순환되는 것을 확인할 수 있다.