[HTML5 Game] Character Animation
HTML5의 Canvas 요소를 활용하여 그럴싸한 2D 게임을 개발할 수 있다.
UDACITY에서도 HTML5 기반의 게임 개발 강의를 무료로 제공하고 있다
=> https://www.udacity.com/course/cs255
여기서 제공하는 캐릭터 애니메이션 구현 기법을 살펴보자. 코드는 내 입맛에 맞게 수정을 가했지만 전체적인 맥락은 동일하다.
대략적은 구현 흐름을 보면, 캐릭터의 움직임을 위해서 각각의 컷을 개별 이미지로 준비하고 이 이미지들을 Canvas에 차례대로 반복해서 그려주는 것이다. 이미지들은 매우 빠른 속도로 Canvas에 그려지는데 이때 적용되는 개념이 초당 프레임(FPS, frame per second)이다.
FPS는 게임 개발 영역에서는 자연스러운 개념으로 인간이 느끼지 못하는 속도로 프레임을 교체하여 자연스러운 움직임이 가능하도록 하는 것으로 영화 필름의 재생을 생각해 보면 이해하기 쉽다.
아래 그림은 캐릭터 움직임을 19개 이미지로 분할한 모습을 보여준다.
* HTML
애니메이션 처리는 모두 자바스크립트에서 구현한다. HTML 파일은 단지 Canvas 요소와 자바스크립트을 참조하는 코드만 존재한다.
<html lang="en">
<head>
<script type="text/javascript" src="js/animationCharacter.js"></script>
</head>
<body>
<canvas id="GameCanvas" width="300" height="300" style="border: 1px solid #000;">
HTML5 Canvas를 지원하지 않습니다. 크롬 또는 사파리와 같은 HTML5 지원 브라우저를 이용해 주세요
</canvas>
</body>
</html>
* JavaScript
자바스크립트로 이미지를 다운로드하여 애니메이션을 구현한다. 애니메이션 처리를 위한 자바스크립트의 핵심 함수는 setInterval()이다. 이 함수는 주어진 간격으로 함수를 반복(loop) 실행시켜주는 함수로 setTimeout() 함수와 함께 HTML5 게임 개발에 필수적으로 사용되는 함수이다. 먼저 캐릭터 객체를 위한 코드부터 살펴보자
1. 캐릭터, 생성자 함수와 메서드
this.assets = assets; //Asset Image Array
this.canvasSize = {width: canvasElement.width, height: canvasElement.height}; //Canvas Size
this.canvasContext = canvasElement.getContext('2d'); //Canvas Context
this.currentFrame= 0; //Current Frame
}
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.assets[this.currentFrame], 100, 100);
//Update Current Frame
this.currentFrame = ++this.currentFrame % this.assets.length;
}
생성자 함수에서는 게임 캐릭터의 개별 이미지를 담을 assets 배열과, 이 이미지를 그려줄 Canvas 요소를 매개변수로 받아서 초기화 작업을 수행한다. CurrentFrame는 개별 이미지를 담고 있는 assets 배열의 인덱스로 사용될 변수로 총 19개의 이미지를 순서대로 교체하기 위해 0~18까지의 값을 가지게 된다.
그리고 실제 애니메이션 동작을 처리하는 startAnimation 메서드에서는 먼저 Canvas를 초기화시킨다. Canvas를 초기화하지 않으면 이전에 그린 그림과 새로 그리는 그림이 서로 겹쳐지게 되어 잔상이 남게 되기 때문에 초기화 하는 작업을 제일 먼저 수행한다.
다음으로 19개의 캐릭터 이미지를 담고 있는 assets 배열에서 CurrentFrame에 해당하는 이미지를 선택해서 Canvas에 그려준다. 마지막으로 19개의 이미지를 순회하기 위해 CurrentFrame 값을 업데이트 하는데 0~18까지 순회하도록 나머지 연산을 수행한다. 결국 이 startAnimation 메서드가 끊임없이 반복 수행됨으로써 캐릭터의 움직임을 구현할 수 있게 되는 것이다.
2. 프로그램 로직
이제 이미지를 다운로드해서 setInterval()함수와 캐릭터 객체를 활용하는 코드를 살펴보자.
먼저 전역변수를 정의한다.
var character; //Character Instance
var canvasElement; //Canvas Element
var assetfiles; //Asset Image File Array
var assets = []; //Asset Image Ojbect Array
var currentAssetLoadCount = 0; //Asset Image File Load Count
fps는 frame per count 약자로 초당 프레임 수를 나타내는데 30이라는 값은 1초에 30번 함수를 호출하겠다는 의미이다. 즉 1초에 30번 프레임이 이동 시키겠다는 의미이다.
다음으로 초기화함수를 정의한다.
canvasElement = document.getElementById("GameCanvas");
//Define Asset Image File Array
assetfiles =
[ 'image/robowalk/robowalk00.png', 'image/robowalk/robowalk01.png',
'image/robowalk/robowalk02.png', 'image/robowalk/robowalk03.png', 'image/robowalk/robowalk04.png', 'image/robowalk/robowalk05.png',
'image/robowalk/robowalk06.png', 'image/robowalk/robowalk07.png', 'image/robowalk/robowalk08.png', 'image/robowalk/robowalk09.png', 'image/robowalk/robowalk10.png', 'image/robowalk/robowalk11.png',
'image/robowalk/robowalk12.png', 'image/robowalk/robowalk13.png', 'image/robowalk/robowalk14.png', 'image/robowalk/robowalk15.png', 'image/robowalk/robowalk16.png','image/robowalk/robowalk17.png',
'image/robowalk/robowalk18.png'
];
for (var i = 0; i < assetfiles.length; i++) {
//Create Asset Image Ojbect
var asset = new Image();
asset.src = assetfiles[i];
//Insert Asset Image in Asset Image Array
assets.push(asset);
//Assign Imgae Load Event
asset.onload = onAssetLoadComplete;
}
}
페이지가 로딩되면 제일 처음 실행될 초기화 함수에서는 HTML DOM에서 Canvas 요소를 선택하고 총 19개의 캐릭터 이미지를 파일을 불러와서 이미지 객체를 생성하여 assets 배열에 담는다. 실제 웹 환경에서는 이미지 다운로드 시간에 지연이 있을 수 있으므로 이미지 로드가 모두 완료된 후 다음 로직을 수행해야 한다. 이미지 로딩이 완료되면 발생하는 onload 이벤트에 onAssetLoadComplete 함수를 할당한다.
onAssetLoadComplete 함수 코드는 다음과 같다.
//Check Load Complete of All Images
if(++currentAssetLoadCount >= assetfiles.length){
//Create Character Instance
character = new Character(assets,canvasElement);
//Run Game Loop
setInterval(animationLoop, 1000 / fps);
}
}
function animationLoop(){
character.startAnimation();
}
이 함수에서는 모든 캐릭터 이미지가 로딩 완료되었는지 체크하고 완료되었다면 캐릭터 객체를 생성하고 setInterval() 함수로 반복 루프를 시작시킨다. setInterval() 함수는 반속 실행을 위한 시간 간격을 밀리세컨트 단위로 설정하게 되는데 1000/fps 즉 1000/30은 0.33초 마다 함수를 수행하라는 의미가 되므로 결과적으로 1초에 30번 Character 객체의 startAnimation() 함수가 수행시키게 된다.
그리고 마지막으로 페이지가 로딩되면 초기화 함수가 수행될 수 있도록 이벤트를 연결시킨다.
실행화면
코드를 모두 작성하고 HTML5를 지원하는 브라우저(크롬 or 사파리)로 확인하면 로봇 캐릭터가 움직이는 애니메이션을 확인할 수 있다. 다음 그림은 화면을 캡쳐한 것이다.
마무리 하며...
이번 글에서는 UDACITY 강의를 기반으로 HTML5 기반의 애니메이션 처리를 살펴봤다. 여기서는 총 19개에 달하는 각각의 캐릭터 이미지를 사용했지만 실제 게임 개발에서는 이렇게 많은 이미지를 사용한다는 것은 매우 비효율적인 것이 된다. 그래서 대부분 Image Sprite 라는 기법을 이용해 모든 에셋이 포함된 하나의 이미지를 기반으로 필요한 에셋만 잘라내서 활용하는 방식이 사용된다. 또한 이러한 애니메이션 처리를 위한 오픈소스 라이브러리도 많이 존재한다. 다음 글에서는 Image Sprite 기법과 애니메이션 라이브러리를 사용하여 애니메이션을 구현하는 기법을 알아보자.
참고> 이 글에서 사용된 게임 에셋 이미지(UDACITY 이미지) 다운로드 경로
=> https://www.udacity.com/media/js/standalone/libs/gamedev_assets/robowalk/robowalk01.png