모바일/HTML5

[HTML5 Game] Character Animation Using Sprites

박종명 2013. 9. 17. 07:44
728x90

이전 글에서 캐릭터의 움직임을 위한 애니메이션 기법을 알아봤다.

이전 글: http://m.mkexdev.net/235

 

이 글에서는 캐릭터 애니메이션을 처리하기 위해 각각의 동작을 분리된 개별 이미지로 처리했었다.

하지만 이런 방식은 많은 이미지 리소스를 다운로드 해야 하기 때문에 비효율적이다.

 

이번 글에서는 Image Sprites 기법을 이용해 애니메이션을 구현하는 예를 살펴 보겠다.

Image Sprites 모든 캐릭터의 모든 동작을 하나의 이미지로 만들어 필요한 부분만 잘라내서 사용하는 방식으로 리소스의 다운로드 비용을 줄이고 효율성을 높이는 기법으로 실제 게임 개발에 많이 사용되고 있기도 하다.

 

아래 그림은 http://opengameart.org 의 샘플 이미지로 8 * 36 즉, 총 8가지 패턴의 서로 다른 동작 36개를 하나의 이미지로 모아놓은 것이다. 이번 예에서는 이 이미지를 Sprites 해서 애니메이션을 구현해 볼 것이다.

 

 

 

* 간단한 개념

HTML5 Canvas에 이미지를 그릴 때, 이미지 원본의 특정 좌표에서 원하는 너비,높이 만큼 이미지를 그릴 수 있다. 예를 들어 아래 그림과 같이 4개의 서로 다른 모습이 하나의 이미지에 포함되어 있을 때 개별 이미지의 너비/높이 값으로 특정 이미지 좌표와 크기를 산출할 수 있으며 이렇게 산출된 값으로 원하는 영역의 이미지를 Canvas에 그릴 수 있게 된다. 즉 아래 그림과 같이 4개의 동작일 경우, (0,0) ~ (384,0)까지 128*128 크기로 차례대로 그려주면 되는 것이다. 

* HTML

HTML은 이전 글과 동일한 구조다.

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

 

* JavaScript

자바스크립트 역시 전체적인 흐름은 이전 글의 내용과 동일하다.

다만 이전 글에서는 개별 이미지를 담는 배열과 이 배열의 인덱스를 탐색하기 위한 프레임 값의 업데이트가 있었던 것에 반해, 여기서는 하나의 이미지에서 원하는 부분을 잘라내기 위한 이미지 X좌표 값의 업데이트가 구현된다.

 

1. 캐릭터, 생성자 함수와 메서드

/* Define Character Class */
function Character(assetObj,canvasElement){
   this.assetObj = assetObj;                                                                           //Custom Asset Object
   this.canvasSize = {width: canvasElement.width, height: canvasElement.height}; //Canvas Size
   this.canvasContext = canvasElement.getContext('2d');                                  //Canvas Context
   this.spritesX = 0;                                                                                      //Image X Position
}

Character.prototype.startAnimation = function(){ 
  //Clear Canvas  
  this.canvasContext.clearRect(0, 0, this.canvasSize.width, this.canvasSize.height);

 
  //Draw Image on Canvas
  this.canvasContext.drawImage(this.assetObj.assetImage,
       this.spritesX * this.assetObj.spritesWidth, this.assetObj.spritesY,
       this.assetObj.spritesWidth, this.assetObj.spritesHeight,
       100, 100,
       this.assetObj.spritesWidth, this.assetObj.spritesHeight);
   
 //Update Sprite Image X Position       
 this.spritesX  = ++this.spritesX  % this.assetObj.spritesCount;  
}

 

이번 코드의 생성자 함수에서는 이미지 배열이 아닌 하나의 이미지 정보를 담고 있는 커스텀 객체를 매개변수로 전달받는다. 그리고 원본 이미지에서 잘라 낼 X 좌표 값을 위한 spritesX 변수를 선언하였다. 이 변수는 총 36개의 동작의 X좌표 계산을 위한 변수이며 '0 에서 35'의 값을 가지게 된다. 이 값에 개별 동작의 너비 값인 128px를 곱해 주면 0 ~ 4480 즉, 실제 잘라 낼 이미지의 X좌표를 계산할 수 있게 된다.

 

HTML5의 Canvas는 원본 이미지에서 원하는 부분을 잘라내어 그릴 수 있도록 아래와 같은 매개변수를 가진 drawImage()함수를 제공한다.

 

drawImage 매개변수>

 - image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh

 

drawImage 매개변수 설명>

 - 이미지 객체, 자르기 X , 자르기 Y , 자를 너비, 자를 높이, 그릴 위치 X 값, 그릴 위치 Y 값, 그릴 너비, 그릴 높이

(실제 제대로 된 해석은 원본 X값, 대상 X값.. 이런 식이지만 원본 이미지를 자른다는 개념을 명확히 하기 위해 임의대로 설명함)

 

 

2. 프로그램 로직

하나의 이미지만을 사용하기 때문에 이전 코드에 비해 전역변수가 줄어 들었다. 그리고 초당 프레임 수인 fps도 15로 작게 설정되었다. 샘플을 실행해 보면 알겠지만 초당 30 프레임으로 동작시키면 캐릭터가 너무 빠르게 움직이므로 적절한 값으로 변경해 주었다.

var fps = 15;             //frame per second
var character;             //Character Instance
var canvasElement;     //Canvas Element
var asset;                  //Asset Image Ojbect

 

그리고 초기화 함수를 보면 단 한개의 이미지만을 로딩하는 것을 확인할 수 있으며 이미지 객체와 개별 동작의 수, 개별 이미지의 너비/높이 및 Y 좌표 값을 담은 커스텀 객체를 생성해서 캐릭터 객체의 생성자 함수로 전달해 주고 있다. 이 값들을 이용해 원본 이미지에서 어떤 부분의 이미지들을 애니메이션 할 것인가가 결정된다.

function init(){ 
 canvasElement = document.getElementById("GameCanvas"); 
  
  //Create Asset Image Ojbect
  asset = new Image(); 
  asset.src = 'image/zombie.png';       
  //Assign Imgae Load Event
  asset.onload = onAssetLoadComplete
}

 

function onAssetLoadComplete(){ 
  //Create Custom Asset Object 
var assetObj = 
   {assetImage:asset, spritesCount:36, spritesWidth:128, spritesHeight:128,
     spritesY: 128 * 0}; 


  //Create Character Instance    
  character = new Character(assetObj,canvasElement);
  //Run Game Loop     
  setInterval(animationLoop, 1000 / fps); 
}

 

function animationLoop(){
  character.startAnimation();
}

 

window.addEventListener("load", init, false);

 

 

실행화면

코드를 실행하면 좀비가 걸어가다 머리가 터지며 죽는 모습의 애니메이션을 확인할 수 있다. 

 

마무리 하며...

지금까지 캐릭터 이미지를 구현하기 위한 두 가지 방법을 알아 보았다. 사실 두 가지 방법은 이미지를 몇 개 사용하는지만 다를 뿐 동일한 프로그램 로직을 가지고 있다. 실제 게임 개발에서는 Image Sprites 기법을 많이 사용하므로 이 기법으로 여러가지 테스트 프로그램을 작성해 보는 것이 도움이 될 것이다.

 

두개의 글을 통해 애니메이션 구현 기법을 충분히 숙지하였으니, 다음 글에서는 공개된 라이브러리를 사용해서 애니메이션을 구현해 보도록 하겠다. 라이브러리를 활용하면 좀 더 안정적으로 그리고 생산성 높게 개발할 수 있으므로 자신의 개발 무기로 장착해 두는 것이 좋을 것이다.