모바일/HTML5

[HTML5 Game] Character Animation Using Trident.js

박종명 2013. 9. 17. 20:00
728x90

앞서 두 개의 글을 통해 HTML5 상에서 애니메이션 구현을 알아봤다.

이 두 글에서는 자바스크립트의 setInterval() 함수를 통해 애니메이션 처리를 직접 구현했었다.

 

> http://m.mkexdev.net/235

> http://m.mkexdev.net/237

 

이번에는 잘 알려진 오픈소스 라이브러리를 이용해 애니메이션을 구현해 보도록 한다.

 

이 글에서 사용할 라이브러리는 Kirill Grouchnikov라는 스마트한 개발자에 의해 개발된,

Trident.js 라는 자바스크립트 애니메이션 라이브러리로 ProcessingJS 처럼 기존 JAVA 라이브러리를 자바스크립트 버전으로 포팅한 것으로 타임라인과 키프레임에 기반한 애니메이션 처리를 지원하고 Easing 함수를 통해 비선형 타임라인을 지원하는 등 애니메이션 처리와 관련된 안정되고 다양한 기능을 제공한다.

 

이 라이브러리는 github에 공개되어 있다.

> https://github.com/kirillcool/trident-js

 

공개된 라이브러리를 사용한다는 것은 개발의 생산성과 안정성을 높이고 유용하고 다양한 기능을 큰 시행착오 없이 적용할 수 있는 등 많은 장점을 가져다 준다. 따라서 대부분의 개발환경에서 이미 구현된 라이브러리 사용을 권장하고 있다.

 

다만 기본 개념이 전혀 갖춰져 있지 않은 상태에서 무작정 라이브러리에만 의존하다보면 프로그램의 원리적 동작의 이해가 부족하게 되고 복잡한 문제를 대응하는 능력이 떨어져 전반적인 프로그램 개발 역량을 성장시키지 못하게 되는 원인이 되기도 한다. 따라서 이전 두 글에서와 같이 라이브러리 의존 없이 기본적인 구현을 직접 해 봄으로써 애니메이션 처리의 기본 구현 원리를 숙지하고 난후 유용한 라이브러리에 관심을 가지는 수순을 따르는게 좋겠다.

 

그럼. 본격적으로 Trident.js를 사용해보게 될텐데, 캐릭터 애니메이션 구현을 해 보기 전에 기본적인 라이브러리 사용법을 간단히 알아보자.

 

* Trident.js 기본

Trident.js 사용해서 HTML 이미지요소의 투명도를 타임라인에 기반해 서서히 변경해 보는 간단한 예를 살펴봄으로써 이 라이브러리의 기본 사용법을 익혀보자.

 

먼저 다음과 같이 github에서 다운받은 trident.js와 이미지요소가 정의된 간단한 HTML파일을 만들자.

<html>
  <head>
    <script src="js/trident.js"></script>
    <script type="text/javascript" src="js/tridentBasic.js"></script>
  </head>
  <body>  
    <img id="myImg" src="image/iu.png" style="opacity:0.3" /> 
  </body>
</html>

 

그리고 자바스크립트에서 다음과 같은 로직을 구현한다.

function init(){
  //Create TimeLine Instance
  var rolloverTimeline = new Timeline(myImg.style);
    
  //Add Interpolator
  rolloverTimeline.addPropertiesToInterpolate([
    {
      property: "opacity", from:0.3, to:1.0, interpolator: new FloatPropertyInterpolator()     
    }
  ]);
  
  //Define Animation Duration
  rolloverTimeline.duration = 500;
    
  //Add Event Listener of Image
  myImg.addEventListener("mouseover", function(){rolloverTimeline.play()}, false);
  myImg.addEventListener("mouseout", function(){rolloverTimeline.playReverse()}, false);
}

 

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

 

Trident.js는 그래픽 편집 툴의 그것과 동일한 개념의 타임라인 객체를 제공한다. 이 객체에 설정한 각종 정보를 기반으로 애니메이션 처리가 이뤄지는데 먼저 생성자 함수로 애니메이션할 요소와 대상 속성을 설정한다.

여기서는 이미지 요소의 style속성을 애니메이션 대상 속성으로 지정했다.

var rolloverTimeline = new Timeline(myImg.style);

 

그리고 애니메이션할 속성과 시작-끝 값 그리고 시작과 끝 값 사이에 변경해야 할 값(보간값)의 형태를 지정한다. 여기서는 이미지의 투명도에 해당하는 opacity 속성을 지정하고 0.3 ~ 1.0 사이의 값을 실수 형태의 보간(FloatPropertyInterpolator)이 이뤄지도록 지정했다.

rolloverTimeline.addPropertiesToInterpolate([
   {
      property: "opacity", from:0.3, to:1.0, interpolator: new FloatPropertyInterpolator()
     }
]);

 

참고로 보간형태는 FloatPropertyInterpolator외에도 정수값 보간인 IntPropertyInterpolator과 RGB값 보간인 RGBPropertyInterpolator가 제공된다.

 

마지막으로 애니메이션이 동작할 시간을 밀리초단위로 지정하는데 여기서는 0.5초로 지정했다

rolloverTimeline.duration = 500;

 

결국 0.5초 동안 이미지 투명도가 0.3에서 1.0으로 점점 밝아 지도록 하는 것이다. 여기까지해서 Trident.js의 애니메이션 설정은 모두 끝이 났다.

 

이어지는 코드는 실제 애니메이션 처리를 하기 위한 이벤트를 등록하는 것이다.

코드에서는 이미지에 마우스를 올리면 애니메이션이 시작하도록 하고 마우스를 떼면 역방향으로 애니메이션이 진행하도록 구현했다.

myImg.addEventListener("mouseover", function(){rolloverTimeline.play()}, false);
myImg.addEventListener("mouseout", function(){rolloverTimeline.playReverse()}, false);

 

브라우저로 샘플을 실행해서 애니메이션을 확인해 보자.(눈의 즐거움을 위해 국민 여동생 등장시킴!)

 

                =>               

 

 

* Trdent.js를 활용한 캐릭터 애니메이션 구현

이제 이 라이브러리를 이용해서 캐릭터 애니메이션을 구현해 보자. 전체적인 흐름은 이전과 유사하며 이미지는 Image Sprites 기법으로 처리한다.

 

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;                                                                                   //Sprite Image X Position
}

 

Character.prototype.startAnimation = function()
{
   //Assign This Instance Context('this' keyword Changed according to the Effective Range)
   var self = this; 
 
   //Create TimeLine
   var spriteTimeline = new Timeline(self);  
   //Add Interpolate
   spriteTimeline.addPropertiesToInterpolate([
    { 
      //Interpolate Property: spritesX, Interpolate Value: 0 ~ 36, Interpolate Type: Int
      property: "spritesX", from:0, to: 36, interpolator: new IntPropertyInterpolator()
     }
   ]);    
        
   //Define Animation Duration
   spriteTimeline.duration = 2500;      
   //Add onpulse Event Listener 
   spriteTimeline.addEventListener("onpulse",

            function (timeline, durationFraction, timelinePosition) {
                    self.drawCanvas();   
             }

   );  
   //Play Animation(Infinite Looping)
   spriteTimeline.playInfiniteLoop();
}

 

Character.prototype.drawCanvas = 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;
 
}

 

캐릭터 메서드의 구조가 이전과는 조금 달라졌다.

startAnimation() 메서드에서 Trident.js의 타임라인을 생성하고 애니메이션을 시작하도록 구현했으며 캔버스에 그리는 작업인 drawCanvas() 메서드를 분리시켰다.

 

타임라인 인스턴스를 생성할 때 앞서 기본 예제와는 달리 HTML 요소가 아니라 객체 자체를 매개변수로 전달하고 있다. 이렇게 하면 객체의 특정 속성을 기준으로 애니메이션 처리를 할 수 있게 된다.

var self = this;

var spriteTimeline = new Timeline(self);

 

또한 여기서 캐릭터 인스턴스의 문맥을 나타내는 'this' 값을 self라는 지역변수에 할당하고 있는데 이렇게 하는 이유는 this 키워드가 다른 유효범위에서는 다른 문맥을 가르키기 때문에 프로그램이 정상 동작하지 않을 수 있다. 이 예제에서는 타임라인에 이벤트 리스너를 등록할 때 문제가 발생한다. 따라서 self라는 변수를 통해 캐릭터 인스턴스의 문맥이 유효할 수 있도록 하며 타임라인 위치가 변경할 때마다 동작할 이벤트 리스너를 다음과 같이 등록한다.

spriteTimeline.addEventListener("onpulse", function (timeline, durationFraction, timelinePosition) {
   self.drawCanvas();
});

 

그리고 원본 이미지의 자르기 x좌표인 spritesX 값은 더이상 직접 업데이트 해 줄 필요가 없게 되었다. Trident.js의 보간값 설정에서 다음과 같이 이 변수의 변경될 값 범위(0~36)를 지정했기 때문에 타임라인이 실행되면서 자동으로 spritesX값이 업데이트 되기 때문이다.

spriteTimeline.addPropertiesToInterpolate([
   {
      //Interpolate Property: spritesX, Interpolate Value: 0 ~ 36, Interpolate Type: Int
      property: "spritesX", from:0, to: 36, interpolator: new IntPropertyInterpolator()
   }
]);

 

마지막으로 애니메이션을 2.5초간 동작하도록 하여 이전 두 글의 fps와 값을 유사하게 맞췄다. 더불어 애니메이션이 무한 반복되도록 다음과 같이 playInfiniteLoop() 메서드를 호출한다.

spriteTimeline.playInfiniteLoop();

 

2. 프로그램 로직

나머지는 이전 코드와 모두 동일하며 onAssetLoadComplete() 함수에 약간의 변화가 있다.

이전 코드에서는 자바스크립트의 setInterval() 메서드를 이용해 직접 루프를 동작시켰지만 Trideng.js에서는 자체 타임라인을 기반으로 루프가 동작하기 때문에 더 이상 사용할 필요가 없다.

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);
 
  
   character.startAnimation();  
}

 

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

 

 

실행화면

브라우저로 실행해 보면 이전 예제와 동일한 애니메이션을 확인할 수 있다. 

 

마무리하며...

지금까지 총 3차례에 걸쳐 캐릭터 애니메이션을 구현해 보았다. 첫번째는 동작별로 분리된 이미지들을 이용해 애니메이션을 구현해 봤으며 두번째는 하나의 이미지를 기반으로 Image Sprites 기법을 활용했으며 마지막으로 이 글에서는 Image Sprites 기법과 더불어 Trident.js 라는 오픈소스 라이브러리를 활용해서 동일한 애니메이션을 구현해 보았다. 이 중에서 제일 권장되는 방법은 1 < 2 < 3 즉, 이 Trident.js를 활용하는 것이 여러모로 좋을 것이다. 라이브러리의 안정성이 곧 프로그램의 안정성으로 직결되며 반복 카운트 및 되감기 애니메이션 지원과 타임라인과 키프레임의 조합, 비선형 타임라인 지원 등 더 강력한 기능을 공짜로(?) 얻을 수 있기 때문이다. 하지만 자바스크립트의 애니메이션 처리를 위한 기본 원리를 알기 위해서는 이전 두 과정을 필수로 거쳐보기 바란다. 기본기 없이 라이브러리에만 의존한다면 언젠가 더 높은 벽을 만나게 될 것이다.