개인 개발 프로젝트/공룡 점프 게임

[공룡 점프 게임] 5. 공룡과 병균 컴포넌트 작성

종범2 2020. 2. 2. 16:38

배경 컴포넌트에 이어 공룡과 병균 컴포넌트를 작성하였다. 공룡 컴포넌트에서는 공룡 이미지를 렌더링하고 스페이스바를 누르는 이벤트가 발생하면 공룡 이미지를 위아래로 이동시켜 점프 효과를 낸다.

 

Character.js

import React, { useState, useEffect} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import CharacterImg from '../image/character.png';
const useStyles = makeStyles(theme => ({
  root: {

  },
  character: {
    position: 'absolute',
    width:'80px',
    height:'120px',
    left: '150px',
  },
}))
const Character = () => {
  // init
  const updateTime = 20;
  const initTop = 375;
  const jumpHeight = 200;
  const [top, setTop] = useState(initTop);
  const speed = 10;
  const timeOutList = [];
  var isJump = false;
  // css
  const classes = useStyles();
  // 컴포넌트가 mount 되는 경우 key event 등록
  // unmount 되는 경우 모든 timeout을 삭제하고 key event를 삭제
  useEffect(()=>{
    document.addEventListener('keydown', handleKeyDown)
    return () => {
      for (let i=0; i< timeOutList.length; i++){
        clearTimeout(timeOutList[i]);
      }
      document.removeEventListener('keydown', handleKeyDown);
    }
  },[])
  // 스페이스바를 누르는 경우 점프
  const handleKeyDown = (e) => {
    if (e.keyCode === 32) {
      if (!isJump){
        isJump = !isJump;
        jump();
      }
    }
  }
  // 점프
  const jump = () => {
    for (let i = 0; i < 2*jumpHeight/speed+1; i++) {
      let timeOut = setTimeout(() => {
        if (i < jumpHeight/speed) {
          setTop(initTop - speed*i);
        } else {
          setTop(initTop - speed*(2*jumpHeight/speed-i));
        }
        if (i=== 2*jumpHeight/speed)
        isJump = false;
      }, updateTime * i)
      timeOutList.push(timeOut);
    }

  }
  // 렌더링
  return (
    <div>
      <img id="character" src = {CharacterImg} className={classes.character} style={{top:top}} />
    </div>
  )
}
export default Character;

우선 스페이스바를 누르는 이벤트와 공룡을 점프시키는 함수를 바인딩하기 위해 useEffect 안에 이벤트를 정의한다. useEffece에 []라는 빈 배열을 전달하였으므로 컴포넌트가 처음 마운트 될 때만 이벤트를 정의하게 된다. 그리고 return에 이벤트를 삭제하는 함수와 setTimeout을 삭제하는 함수를 실행하여 컴포넌트가 언마운트 되면 모든 이벤트를 삭제하도록 하였다.

 

스페이스바를 누르면 handleKeyDown이라는 함수 내부의 jump라는 함수를 실행하는데 공룡이 점프 중인 동안에는 jump 함수를 실행하지 않도록 설정하였다. jump 함수 내부에서는 setTimeOut 함수를 실행하여 특정 시간 동안 공룡의 위치가 위로 갔다가 다시 아래로 내려오도록 top라는 state값을 설정했다. 공룡의 위치는 css의 top값에 top라는 state값을 대입하여 설정한다.

 

Enemy.js

import React, { useState, useEffect } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import EnemyImg from '../image/enemy.png';
const useStyles = makeStyles(theme => ({
  root: {

  },
  character: {
    position: 'absolute',
    width: '80px',
    height: '80px',
    top: '410px'
  },
}))
const Enemy = (props) => {
  // init
  const updateTime = 20;
  const initLeft = 1000 - 20;
  const moveWidth = 1000 - 50;
  const speed = 10;
  const timeOutList = [];
  // State
  const [left, setLeft] = useState(initLeft);
  const [isMoving, setIsMoving] = useState(false);
  // props.isMove가 변하면 props.isMove를 isMove에 저장
  useEffect(()=>{
    //setIsMove(props.isMove);
    if (props.isMove) {
      setIsMoving(true);
      setLeft(initLeft);
      move();
    }
  },[props.isMove])
  // 컴포넌트가 Unmount되면 timeout을 모두 삭제
  useEffect(()=>{
    return () => {
      for (let i=0; i< timeOutList.length; i++){
        clearTimeout(timeOutList[i]);
      }
    }
  },[])
  // css
  const classes = useStyles();
  // 움직임을 시작하며 끝까지가면 움직임 끝
  const move = () => {
    for (let i = 0; i < moveWidth / speed; i++) {
      let timeOut = setTimeout(() => {
        setLeft(initLeft - speed * i);
        if (i === moveWidth / speed - 1)
          setIsMoving(false);
      }, updateTime * i);
      timeOutList.push(timeOut);
    }
  }
  // 움직이는 경우만 렌더링
  return (
    <div>
      {
        isMoving ?
          <img id="enemy" src={EnemyImg} className={classes.character} style={{ left:left }}></img>
          : null
      }
    </div>
  )
}
export default Enemy;

병균 컴포넌트에서는 기본적으로 아무것도 렌더링 하지 않는다. 그러다 props로 전달받은 isMove값이 true가 되면 병균 이미지를 렌더링하고 왼쪽으로 이동시킨다. 그리고 병균 이미지가 왼쪽 끝에 도달하면 다시 이미지를 렌더링 하지 않는다.

 

이를 위해 useEffect에 [props.isMove]를 전달하여 이 값이 true로 변화하면 병균 이미지를 렌더링하고 오른쪽에서 왼쪽으로 이동시키는 move 함수를 호출하도록 하였다. move 함수 내부에서는 setTimeOut 함수를 실행하여 특정 시간 동안 병균의 위치가 오른쪽에서 왼쪽으로 움직이도록 left라는 state값을 설정했다. 병균의 위치는 css의 left값에 left라는 state값을 대입하여 설정한다. 두 번째 useEffect에는 []라는 빈 배열을 전달하고 return에 setTimeout을 삭제하는 함수를 실행하여 컴포넌트가 언마운트 되면 모든 이벤트를 삭제하도록 하였다. 여기까지 컴포넌트를 마지막으로 작성하고 다음으로 웹 게임을 배포한다.

 

[전체 글]

[공룡 점프 게임] 7. 마무리

[공룡 점프 게임] 6. React 웹 게임 Heroku에 배포

[공룡 점프 게임] 5. 공룡과 병균 컴포넌트 작성

[공룡 점프 게임] 4. 배경 컴포넌트 작성

[공룡 점프 게임] 3. 메인화면 작성

[공룡 점프 게임] 2. React 앱 생성 및 설정

[공룡 점프 게임] 1. 프로젝트 소개