2013년 7월 11일 목요일

[HTML5 Game Development] Canvas

게임을 만들려면 2D 렌더링 엔진이 필요하다. HTML5에서는 HTML 엘리먼트 Canvas를 이용해 렌더링을 처리 할 수 있다.



그렇다면 Canvas란 무엇일까?

Canvas는 HTML5에 추가된 엘리먼트로 API를 통해 그래프, 이미지, 텍스트 등을 캔버스에 그릴 수 있다. Canvas는 HTML 엘리먼트로 두 개의 속성을 갖는다. width, height.
width, height 속성을 이용해 캔버스의 크기를 지정할 수 있다.

캔버스 생성하기
다음처럼 코드를 작성하고 브라우저에서 열면 빈 화면만 나오겠지만 캔버스가 생성된다. 여기서 살펴볼 점은 canvas에서 getContext 메서드를 호출하는 부분이다. 이름을 보니 Context를 반환하는 메서드인듯하다.

<html>
<body>
<canvas id="my_canvas"></canvas>
</body>
<script>
var canvas  = null,
    context = null;

function setup() {
    canvas = document.getElementById("my_canvas");
    context = canvas.getContext("2d");
    canvas.width = 300;
    canvas.height = 500;
}

setup();
</script>
</html>

Context란 무엇일까?
getContext를 호출하면 drawing context가 반환되는데 우리는 drawing context의 API를 호출해 Canvas에 우리가 원하는 그림을 그릴 수 있다.
getContext를 호출할 때 "2d"를 인자로 넣어 CanvasRenderingContext2D를 얻어온다. 그리고 CanvasRenderingContext2D의 메서드를 호출하면 된다.
Name & ArgumentsReturnDescription
getContext(in DOMStringcontextId)RenderingContextReturns a drawing context on the canvas, or null if the context ID is not supported. A drawing context lets you draw on the canvas. Calling getContext with "2d" returns aCanvasRenderingContext2D object, whereas calling it with "experimental-webgl" (or "webgl") returns a WebGLRenderingContext object. Thicontext is only available on browsers that implement WebGL.
출처:https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement

getContext를 호출할 때 "2d"를 인자로 넣으면 CanvasRenderingContext2D가 반환된다. 그리고 필요한 CanvasRenderingContext2D API를 호출한다.

Canvas에 이미지를 올려보자

이미지를 canvas에 올리려면 3가지가 필요하다

  • Image() 객체를 생성한다.
  • onload 함수를 구현한다.
  • Image.src에 이미지 URL을 세팅한다.

<html>
<body>
<canvas id="my_canvas"></canvas>
</body>
<script>
var canvas  = null,
    context = null,
    img  = null;

function setup() {
    canvas = document.getElementById("my_canvas");
    context = canvas.getContext("2d");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    img = new Image();
    img.onload = onImageLoad;
    img.src = "./1373428533_Armed_robot.png";
}

function onImageLoad() {
    console.log("load");
}
setup();
</script>
</html>


이 소스를 실행하면 콘솔창에 "load"라고 뜰 뿐 이미지가 그려지진 않는다. 일단 이전 소스와 바뀐 점은 canvas의 width와 height를 고정값이 아닌 브라우저 사이즈로 변경했다. 그리고 Image객체의 인스턴스를 생성했으며 img의 onload 함수를 구현했고 src에 이미지 경로를 세팅했다. 이미지를 canvas에 올리는 데 필요한 3가지를 다 했지만 Canvas에 그림은 여전히 그려지지 않는다.
조금 전 Context를 설명하면서 Canvas에 그림을 그리려면 Context의 메서드를 이용한다고 했었다. 이 소스에서 빠진 부분이 바로 그 부분이다.
이제 onImageLoad 메서드를 다음처럼 수정해보자.

function onImageLoad() {
    context.drawImage(img, 00);
}

수정하면 다음 캡쳐화면처럼 이미지가 정상적으로 그려진다.


drawImage 메서드를 호출할 때 첫 번째 인자를 img 객체이고 두번째, 세번째는 x, y좌표이다. 즉 캔버스 어느 위치에 이미지를 그릴 것인지 지정하는 것이다.
drawImage에 대한 상세한 설명은 API를 참고하도록 한다.

캐릭터를 움직이게 만들어보자

캐릭터를 움직이는 것처럼 만드는 방법은 의외로 간단하다. 일정 시간 간격으로 캔버스 위에 이미지를 그리면 된다. 이는 setInterval 함수를 이용하면 가능하다. setInterval 함수는 우리가 지정한 시간마다 우리가 지정한 함수를 호출해한다.
제일 먼저 이미지 경로를 선언해놓는다.

var assets = ["img/robowalk00.png",
              "img/robowalk01.png",
              "img/robowalk02.png",
              "img/robowalk03.png",
              "img/robowalk04.png",
              "img/robowalk05.png",
              "img/robowalk06.png",
              "img/robowalk07.png",
              "img/robowalk08.png",
              "img/robowalk09.png",
              "img/robowalk10.png",
              "img/robowalk11.png",
              "img/robowalk12.png",
              "img/robowalk13.png",
              "img/robowalk14.png",
              "img/robowalk15.png",
              "img/robowalk16.png",
              "img/robowalk17.png",
              "img/robowalk18.png"];

이제 이미지 경로를 이미지 객체로 만든다.

var frames = [];

for (var i = 0; i < assets.length; i++) {
    var img = new Image();
    img.src = assets[i];
        
    frames.push(img);
}

캔버스에 이미지를 그리는 함수를 정의한다.

var idx = 0;

function animate () {
    context.clearRect(00, canvas.width, canvas.height);
    
    context.drawImage(frames[idx++]00);
    idx %= frames.length;
}

변수 idx에는 현재 캔버스에 그린 이미지 번호가 저장되며 배열의 끝까지 도달하면 변수의 값은 다시 0으로 돌아간다.
animate함수 맨 첫 줄에 clearRect를 호출하는 이유는 이전에 그려진 이미지를 지워야하기 때문이다. 지우지 않으면 이미지 위에 이미지가 또 그려져 움직이는 효과를 내지 못한다.
이제 animate 함수를 setInterval로 호출하는 일만 남았다.

전체 소스는 다음과 같다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
    <canvas id="my_canvas"></canvas>
</body>
<script>
var assets = ["img/robowalk00.png",
              "img/robowalk01.png",
              "img/robowalk02.png",
              "img/robowalk03.png",
              "img/robowalk04.png",
              "img/robowalk05.png",
              "img/robowalk06.png",
              "img/robowalk07.png",
              "img/robowalk08.png",
              "img/robowalk09.png",
              "img/robowalk10.png",
              "img/robowalk11.png",
              "img/robowalk12.png",
              "img/robowalk13.png",
              "img/robowalk14.png",
              "img/robowalk15.png",
              "img/robowalk16.png",
              "img/robowalk17.png",
              "img/robowalk18.png"];
             
var frames = [],
    idx = 0,
    canvas;

function setup() {
    canvas = document.getElementById("my_canvas");
    context = canvas.getContext("2d");
    
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    for (var i = 0; i < assets.length; i++) {
        var img = new Image();
        img.src = assets[i];
        
        frames.push(img);
    }
    
    setInterval(animate, 1000/30);
}

function animate () {
    context.clearRect(00, canvas.width, canvas.height);
    
    context.drawImage(frames[idx++]00);
    idx %= frames.length;
}

setup();
</script>
</html>

* 이 자료는 udacity의 HTML5 Game Development 강의를 듣고 공부한 내용을 정리한 것이다.

댓글 없음: