ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [테트리스] 기본 HTML 레이아웃, Canvas 구현
    개발/개발일지 2023. 11. 13. 21:30

    이전글

    2023.11.13 - [개발/개발일지] - [테트리스] 프로젝트 구상

     

    [테트리스] 프로젝트 구상

    1. 프로젝트 구상 웹으로 간단하게 할 수 있는 테트리스이다. 백엔드까지 확장해 점수를 저장하거나 멀티플레이 기능을 지원한다. 주요 기능 HTML5 Canvas 기반 테트리스 유저 기능 플레이 결과 저

    yeahx4.tistory.com

     

     

    개요

    테트리스는 10x20의 맵을 채워가는 게임이다. 복잡한 그래픽이 들어가진 않기 때문에 단순히 table이나 다른 방법으로도 구현이 가능하지만, 연습 겸 HTML5의 Canvas를 이용해서 진행해 보기로 한다.

     

    Canvas

    HTML의 Canvas

    HTML에서 canvas태그를 이용하면 바로 사용이 가능하다. 참고로 canvas를 지원하는 브라우저의 버전은 아래와 같다. 요새는 대부분 지원한다. (IE 빼고..)

    코드는 아래와 같다.

    <canvas id="canvas" width="320px" height="640px"></canvas>

    다만, HTML상에서 width와 height를 지정해주고 있는데, 지정한 크기가 CSS상의 크기와 다를 경우(또는 미지정할 경우) 내용이 확대되어 보이거나 왜곡될 수 있다. 10x20의 크기로 각 칸당 32px의 크기를 가진다.

     

    Javascript에서의 Canvas

    JS를 이용하면 Canvas에 원하는 그림, 선, 이미지 등을 그려 넣을 수 있다.

    canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')

    정확히는 HTMLCanvasElement의 getContext를 통해 얻은 context객체로 가능하다. 테트리스는 2d게임이므로 2d context를 가져온다.

     

     

    화면 갱신

    테트리스도 게임이기 때문에 화면을 주기적으로 갱신해줄 필요가 있다. 테트리스는 다른 다이나믹한 게임처럼 많은 횟수의 새로고침이 필요하진 않지만 requestAnimationFrame을 이용하면 화면의 주사율에 맞춰 특정 함수를 실행시킬 수 있다. 매 반복마다 canvas의 내용물을 전부 제거하고 새롭게 그려넣으면 화면이 부드럽게 갱신되는 것 같은 효과를 줄 수 있다. 테트리스는 화면에서 갱신되는 부분이 많은 게임도 아니고 화면 전체를 지우고 다시 그리는 과정은 굉장히 비효율적이지만 화면 구성이 복잡하지 않고 구현이 간단하기 때문에 일단은 별도의 최적화 없이 진행한다.

    function clearCanvas(ctx) {
        ctx.clearRect(0, 0, w, h)
    }
    function draw() {
        const ctx = canvas.getContext('2d')
        ctx.imageSmoothingEnabled = false
    
    	// 화면 렌더링
    
        requestAnimationFrame(draw)
    }

     

    요소 그리기

    테트리스는 굉장히 간단한 요소들로 canvas가 구성된다. 선으로 Grid를 그리고 Rect로 각 칸을 표현할 수 있다.

    function drawGrid(ctx) {
        for (let x = 0; x < w; x++) {
            for (let y = 0; y < h; y++) {
                ctx.beginPath()
                ctx.moveTo(x * size, y * size)
                ctx.lineTo(x * size + size, y * size);
                ctx.lineWidth = 1
                ctx.strokeStyle = '#aeaeae'
                ctx.stroke()
                ctx.moveTo(x * size, y * size)
                ctx.lineTo(x * size, y * size + size);
                ctx.stroke()
            }
        }
    }

    선을 그릴 때 특정 단계를 않으면 위의 clearCanvas로 지워지지 않는다. 많은 요소가 지워지지 않고 계속 덧그려질 경우 성능이 점점 저하되어 버벅거리는 페이지를 볼 수 있다. 선을 그리는 단계는 아래와 같다.

    1. ctx.beginPath()
    2. moveTo(x, y) 로 (x, y)로 이동한다. 참고로 왼쪽 위가 (0, 0)이다.
    3. ctx.lineTo(x, y) 로 moveTo한 위치부터 lineTo 위치까지 선을 긋는다. 아직 실제로 그어지진 않는다.
    4. stroke를 통해 실제로 선을 긋는다.

    중간에 lineWidth와 strokeStyle 등을 지정해 색이나 두께 등을 지정할 수 있다.

     

    사각형을 그리는 방법은 비교적 쉽다.

    function renderMap(ctx) {
        forEach((x, y) => {
            ctx.fillStyle = map[x][y].color
            ctx.fillRect(x * size + 1, y * size + 1, size - 1, size - 1)
        })
    }

     

     

    HTML 레이아웃

    기본적인 CSS를 적용해주면 아래와 같은 모양이 된다.

     

YEAHx4