React+Redux+Canvas로 간단한 게임 구조 구성하기

Explanation

얼마 전에 Canvas를 이용해서 간단한 게임을 만들어 봤는데요, 그 내용을 모두 적기엔 글이 너무 길어질 것 같아서 간단하게 제가 생각해본 구조?? 에 대해 정리해보려 합니다.

대략 Webpack, Babel, React, Redux, Canvas를 사용하고요, 크게 React로 컴포넌트를 나누어 구성하고 Redux로 현재 게임의 상태를 저장하고 Canvas로 화면을 그립니다. Babel로 ES6+ 문법을 사용하고 Webpack은 이 모든 구성을 쉽게 모아서 사용할 수 있고 hot-loader로 일일이 새로고침을 할 필요가 없어서 편해요. 그리고 polyfill을 설치하면 Async/Await도 사용할 수 있습니다.

예제 소스를 설치하면 대략적으로 시작할 수 있는 밑바탕 정도로 사용할 수 있을 거 같아요.
예제 : https://github.com/falsy/blog-post-example/tree/master/react-redux-canvas

간략하게 코드에 대해 이야기해보면,

// src/views/dashboard.js
...

  render() {
    return (
      <div>
        <canvas id={'sample'} width={'500'} height={'500'}></canvas>
        <p onClick={this.startAnimation.bind(this)}>애니메이션</p>
      </div>
    );
  }
}

const mstp = (state) => {
  return {
    canvas: state.canvas
  };
};

export default connect(mstp)(Dashboard)

우선 id값으로 sample을 가지고 있는 canvas를 추가했고요,

// src/views/dashboard.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { setCanvas } from '../actions/canvas';
import { printMarble, animationMarble } from '../actions/marble';

class Dashboard extends Component {

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    const { dispatch } = this.props;
    const canvas = document.getElementById('sample');
    dispatch(setCanvas(canvas));
    printMarble(canvas);
  }

  ...

canvas가 랜더링되고 나면, 나중에 컴포넌트가 많아져도 Redux로 해당 canvas에 그림을 제어하기 위해서 Redux에 canvas를 기억해 두었습니다. 그리고 초기 그림?을 'printMarble' 라는 함수를 통해 canvas에 그립니다.

// src/actions/marble.js
export function printMarble(canvas) {
  const ctx = canvas.getContext('2d');
  const x = 100;
  const y = 240;
  const r = 10;
  ctx.beginPath();
  ctx.arc(x, y, r, 0, Math.PI * 2, true);
  ctx.fillStyle = 'blue';
  ctx.fill();
}

...

파라미터로 canvas 엘리먼트를 받아오고 반지름 10px의 파란색 원을 x축 100px, y축 240px에 그립니다.

// src/views/dashboard
...

  startAnimation() {
    animationMarble(this.props.canvas.element);
  }

  render() {
    return (
      <div>
        <canvas id={'sample'} width={'500'} height={'500'}></canvas>
        <p onClick={this.startAnimation.bind(this)}>애니메이션</p>
      </div>
    );
  }
}

그리고 '애니메이션'이라는 텍스트를 클릭하면 animationMarble 라는 함수를, 그리고 redux에 기억해 놓은 canvas를 파라미터로 실행합니다.

// src/actions/marble.js
...

export function animationMarble(canvas) {
  const ctx = canvas.getContext('2d');
  const x = 100;
  const y = 240;
  const r = 10;
  let moveSize = 0;

  const animate = setInterval(() => {
    ctx.clearRect(x - r + moveSize, y - r, 20, 20);
    moveSize += 5;
    const moveX = x +  moveSize;
    ctx.beginPath();
    ctx.arc(moveX, y, r, 0, Math.PI * 2, true);
    ctx.fillStyle = 'blue';
    ctx.fill();
    if(moveX >= 400) {
      clearInterval(animate);
      ctx.clearRect(x - r + moveSize, y - r, 20, 20);
    }
  }, 10);
}

animationMarble 함수는 실행되면 setInterval 메서드로 함수를 반복 실행시켜 애니메이션을 구현합니다. .clearRect()로 이전 위치에 파란 원을 지우고 X축으로 += 5; 씩 추가된 X값으로 파란 원을 새로 그립니다. 그리고 X축으로 400px이상으로 이동하게되면 반복을 멈추고 파란 원을 지우는 것으로 애니메이션이 끝납니다.

그리고 끝으로 제가 간단하게 만들어본 게임은 아래의 링크를 통해서 확인해 볼 수 있습니다.
http://game.tenqube.kr/
제가 Canvas를 제대로 사용해 본 적이 없어서 캔버스를 사용했는데요, 위와 같은 게임은 그냥 canvas를 사용하지 않고 html, javascript로 만드는 게 더 좋을 거 같아요ㅎ

끝