모바일/HTML5

[HTML5 Game] Shooting Down

박종명 2013. 10. 16. 22:37
728x90

앞서 전개한 글들에서 충돌처리, 총알발사, 오브젝트 이동 처리 등을 구현해 보았다.

이번 글에서는, 이러한 개별 개념을 조합하여 총알로 적(enemy) 비행체를 격추하는 샘플을 구현해 보자.

 

간략한 시나리오

- 적(enemy) 비행체는 캔버스 상단에서 좌/우로 방향을 바꿔가며 계속 이동한다.

- 키보드의 스페이스 키를 누르면 총알이 발사된다.

- 총알이 적 비행체에 명중하면 폭발한 듯한 이미지 효과를 준다.

 

격추라는 시나리오에만 충실하기 위해 아군 비행기는 등장시키지 않았으며, 총알의 출발 위치도 캔버스 하단 중앙에서만 출발하도록 한다. 또한 지속적인 테스트를 위하여 적 비행체가 격추되어도 충돌을 표현하는 이미지 효과만 줄 뿐 비행체는 계속 살아서 움직이도록 할 것이다.

 

먼저 적 비행체와 총알, 그리고 충돌 처리를 위한 객체 기반을 작성할 것인데 그전에, 주요 코드부터 간략히 살펴보자.

 

총알 발사

이전 글에서는, 과도한 연사 속도를 조절하기 위해 FPS를 기반의 조절값을 사용한 반면 여기서는 총알 발사의 전/후 시간을 속도 조절의 기준 값으로 사용했다. 즉 직전 총알 발사 시간과 현재 총알 발사 시간의 간격이 0.1초 이상 되어야만 새 총알이 배열에 추가되도록 처리한다. 그리고 총알의 발사 위치는 캔버스의 하단 중앙으로 설정한다.

if(Date.now() - this.lastShootTime >= 100){        
   this.bullets.push({x: (this.canvasSize.width / 2)
                       - (this.gameAssets.bullet.width / 2), y: this.canvasSize.height});
   this.lastShootTime = Date.now();  
  } 

 

 

오브젝트 이동

적 비행체와 총알의 이동을 위해 좌표값을 변경하는 코드이다. 게임 루프의 update() 메서드에서 매 프레임마다 호출하는 부분으로, 총발 위치는 위쪽 방향으로 이동하도록 Y 좌표값을 변경하고 적 비행체는 캔버스의 좌/우우를 계속해서 왔다갔다 하도록 방향값을 기준으로 X 좌표값을 변경시켜준다.

for(var i = 0; i < this.bullets.length; i++){    
   this.bullets[i].y -= this.speedBullet; 
}      

 

if(this.directionEnemy == 1
        && this.enemyPostion.x + this.gameAssets.enemy.width  > this.canvasClientRect.right)

{
   this.directionEnemy = -1;
}  


if(this.directionEnemy == -1
        && this.enemyPostion.x + 5 < this.canvasClientRect.left)

{   
   this.directionEnemy = 1;
}
    
this.enemyPostion.x += (this.directionEnemy) * 5; 

 

 

오브젝트 그리기 및 충돌처리

객체의 마지막 코드로, 게임 오브젝트들을 캔버스에 그린다. 게임 루프의 display() 메서드에서 매 프레임마다 호출하는 부분으로, 먼저 캔버스의 모든 내용을 지우고 적 비행체를 그린다. 다음으로 총알을 배열 수 만큼 그려주는데 총알이 캔버스 영역 밖으로 이동했다면 배열에서 제거하고 루프를 빠져나간다. 그리고 총알과 적 비행체와의 충돌처리를 통하여 격추되는 이미지를 그려줄지 판단한다. 

this.canvasContext.clearRect(0, 0, this.canvasSize.width, this.canvasSize.height); 
  
this.canvasContext.drawImage(this.gameAssets.enemy, this.enemyPostion.x, this.enemyPostion.y);
       
for(var i = 0; i < this.bullets.length; i++){
   if(this.bullets[i].y <= 0) {
      this.bullets.splice(i,1); continue;
   }
   
   this.canvasContext.drawImage(this.gameAssets.bullet, this.bullets[i].x, this.bullets[i].y);
   
   if(this.bullets[i].x +  this.gameAssets.bullet.width > this.enemyPostion.x 
       && this.bullets[i].x < this.enemyPostion.x + this.gameAssets.enemy.width    
       && this.bullets[i].y > this.enemyPostion.y 
       && this.bullets[i].y < this.enemyPostion.y + this.gameAssets.enemy.height)
    {           
      this.canvasContext.drawImage(this.gameAssets.pow, this.enemyPostion.x, this.enemyPostion.y);
    }                
}

 

 

여기까지 해서 객체기반 작성이 완료되었다. 나머지 코드는 각종 초기화 및 이미지 리소스 다운로드, 위의 객체를 기반으로 게임 루프를 구현하는 것이다. 다음 코드는 객체를 포함한 전체 소스이다.

 function ShootingDownObj(gameAssets,canvasElement){
    this.canvasSize = {width: canvasElement.width, height: canvasElement.height};
    this.canvasContext = canvasElement.getContext('2d');
    this.canvasClientRect = canvasElement.getBoundingClientRect();     
    this.enemyPostion = {x: 10, y:10}; 
    this.gameAssets = gameAssets;
    this.bullets = [];
    this.speedBullet = 15;   
    this.directionEnemy = 1;
    this.lastShootTime = Date.now();
 
    this.addBullet = function(){   
        if(Date.now() - this.lastShootTime >= 100){        
         this.bullets.push({x: (this.canvasSize.width / 2)
             - (this.gameAssets.bullet.width / 2), y: this.canvasSize.height});
         this.lastShootTime = Date.now();  
        }  
    }
 
    this.movePosition = function(){
        for(var i = 0; i < this.bullets.length; i++){    
           this.bullets[i].y -= this.speedBullet;
        }      
        if(this.directionEnemy == 1
          && this.enemyPostion.x + this.gameAssets.enemy.width  > this.canvasClientRect.right){
            this.directionEnemy = -1;
        }  
        if(this.directionEnemy == -1
         && this.enemyPostion.x + 5 < this.canvasClientRect.left){   
            this.directionEnemy = 1;
        }
    
        this.enemyPostion.x += (this.directionEnemy) * 5;   
    }
   
    this.shotBullets = function(){  
       this.canvasContext.clearRect(0, 0, this.canvasSize.width, this.canvasSize.height); 
       this.canvasContext.drawImage(this.gameAssets.enemy,
                                                                        this.enemyPostion.x, this.enemyPostion.y);
       
      for(var i = 0; i < this.bullets.length; i++){
          if(this.bullets[i].y <= 0) {
             this.bullets.splice(i,1);
             continue;
          }
   
          if(this.bullets[i].x +  this.gameAssets.bullet.width > this.enemyPostion.x 
           && this.bullets[i].x < this.enemyPostion.x + this.gameAssets.enemy.width    
           && this.bullets[i].y > this.enemyPostion.y 
           && this.bullets[i].y < this.enemyPostion.y + this.gameAssets.enemy.height){           
            this.canvasContext.drawImage(this.gameAssets.pow,
                                                         this.enemyPostion.x, this.enemyPostion.y);
          }           
            this.canvasContext.drawImage(this.gameAssets.bullet, this.bullets[i].x, this.bullets[i].y);
       }    
    } 
}

var fps = 30;
var canvasElement;
var gameContext; 
var shootingDownObj;   
var gameAssets;  
var currentAssetImageLoadCount = 0;    
var isKeyDown = [];

 

function init(){
  canvasElement = document.getElementById('GameCanvas');
  gameContext = canvasElement.getContext('2d');
   
  var bulletImage = new Image();  
  bulletImage.src = 'image/bullet.png';       
  bulletImage.onload = onAssetImageLoadComplete; 
 
  var enemyImage = new Image();  
  enemyImage.src = 'image/enemy.png';       
  enemyImage.onload = onAssetImageLoadComplete; 
 
  var powImage = new Image();  
  powImage.src = 'image/pow.png';       
  powImage.onload = onAssetImageLoadComplete;  
 
  gameAssets = {bullet: bulletImage, enemy: enemyImage, pow: powImage};     
}

 

function onAssetImageLoadComplete(){ 
  if(++currentAssetImageLoadCount >= 3){  
    shootingDownObj = new ShootingDownObj(gameAssets,canvasElement);      
    setInterval(gameLoop, 1000 / fps);
  } 
}

 

function gameLoop(){
  update();
  display();
}

 

function update(){   
  if(isKeyDown[32]){      
    shootingDownObj.addBullet();
  } 
  shootingDownObj.movePosition();
}

 

function display(){
  shootingDownObj.shotBullets();

}

 

function onKeyDown(e){  
  isKeyDown[e.keyCode] = true;   
}

 

function onKeyUp(e){
  isKeyDown[e.keyCode] = false;
}

 

window.addEventListener("load", init, false);
window.addEventListener("keydown",onKeyDown,false);
window.addEventListener("keyup",onKeyUp,false);

 

 

 

브라우저를 통해 확인해보면, 다음 그림과 같이 총알이 명중하면 폭발한듯한 이미지 효과를 확인할 수 있다.