react.js

리액트로 테트리스 만들어보자[4]

윤만석 2022. 10. 31. 13:47

 

https://orgin00.tistory.com/50

 

리액트로 테트리스 만들어보자[3]

https://orgin00.tistory.com/49 리액트로 테트리스 만들어보자[2] https://orgin00.tistory.com/48 리액트로 테트리스 만들어보자[1] App.js 전체 코드입니다. 난잡하지만 끊어서 보겠습니다. 첫번째 글에서는 커..

orgin00.tistory.com

블록이 자동으로 떨어지게 만들어 보겠습니다.

  useEffect(() => {
    const repeatMotion = (timeStamp) => {
      if (timeStamp - cur > speed[data.stage]/*시간 1000ms=1s*/) {//현재 시간과 cur의 차이가 1초가되면
        if (!validateMove(CurrentBlock, map, 0, 1)) { //만약 현재 블록이 더이상 움직일수 없다면
          stack(CurrentBlock, map); //쌓습니다.
          ChangeBlocks();//그리고 블록을 바꿉니다.
        }

        setCur(timeStamp); //1초가 지났으므로 cur에 현재 시간을 저장합니다. 또다시 1초 진행시 반복됩니다.

        reDraw(); //redraw.
      }
     /* setFilledlines(isLineFilled(map));
      if (FilledLines.length > 0) {
        if (timeForRemoved === 0) {
          setTimeForRemoved(timeStamp);
        }

        if (timeStamp - timeForRemoved > 400) {
          setData({
            ...data,
            score: data.score + CalScore(FilledLines.length),
            stage: CalStage(data.score + CalScore(FilledLines.length)),
          });
          lineRemove(FilledLines, map);
          setFilledlines([]);
          setTimeForRemoved(0);
          reDraw();
        }
      }

      if (
        JSON.stringify(CurrentBlock.type) === JSON.stringify(NextBlock.type)
      ) {
        setNextBlock(createRandomBlock());
        reDraw();
      }
      if (map[1].some((c) => c > 0)) {
        setOnGame(false);
        alert("game Over");
      }*/
      repeatRef.current = requestAnimationFrame(repeatMotion);
    };
    //if (onGame) repeatRef.current = requestAnimationFrame(repeatMotion);

    return () => cancelAnimationFrame(repeatRef.current); //언마운트시 해제해야 recursion에러가 발생하지 않습니다.
  });

주석처리한 부분은 이후에 작성할 로직이므로 넘기겠습니다.

기본적으로 callback함수 requestAnimationFrame를 이용해서 함수를 무한반복 할것입니다.

repeatMotion함수를 인자로 받습니다.

 

validateMove.js

import { copy } from "./common";
import { move, rotate } from "./keyHandler";
/*copy.js
export const copy = (obj) => JSON.parse(JSON.stringify(obj));
  //깊은 복사 는 대상 변수값이 바뀔 때 원본 변숫값은 유지된다. 아래 코드는 깊은 복사 의 예이다. 타입 스크립트에서는 number 와 boolean 타입은 깊은 복사 형태로, 객체 와 배열 은 얕은 복사 방식으로 동작한다
//wanna-b.tistory.com/18 참조
*/
export const validate = (block, matrix) => {
  let isValid = true;
  // some 메서드는 return true일 때 break, return false일 때는 continue 효과를 갖습니다.
  block.type.some((row, dy) => {
    row.some((value, dx) => {
      if (value !== 0) {
        if (
          block.x + dx < 0 ||
          block.x + dx > 9 ||
          block.y + dy < 0 ||
          block.y + dy > 20 ||
          matrix[block.y + dy][block.x + dx] > 0
        ) {
          isValid = false;
          return true;
        }
      }
    });
    if (!isValid) {
      return true;
    }
  });

  return isValid;
};

export const validateMove = (block, matrix, x, y) => {
  const newBlock = copy(block);
  move(newBlock, x, y);
  if (validate(newBlock, matrix)) {
    move(block, x, y);
    return true;
  }

  return false;
};

export const validateRotate = (block, matrix) => {
  const newBlock = copy(block);
  rotate(newBlock);

  if (validate(newBlock, matrix)) {
    rotate(block);
    return true;
  }
  return false;
};

블록은 validateMove로 움직이게됩니다. 깊은복사를 이용해서 newBlock을 복사해서 움직여보고, 만약 이 움직임이 available하면 실제 블록을 move하고 true를 반환합니다.

move와 rotate함수는 아래와 같습니다.

//move는 단순히 x,y좌표를 더해주면 됩니다.
export const move = (block, x, y) => {
  block.x += x;
  block.y += y;
};
//rotate경우 조금 복잡합니다. 
export const rotate = (block) => {

//x,y좌표를 서로 바꾸어줍니다.
  block.type.forEach((row, y) => { 
    for (let x = 0; x < y; x++) {
      const temp = block.type[x][y];
      block.type[x][y] = block.type[y][x];
      block.type[y][x] = temp;
    }
  });
//행렬을 뒤집어주면 시계방향으로 회전한 효과가 나옵니다.
  block.type.forEach((row) => {
    row.reverse();
  });
};
//선형대수학

 

따라서 1초마다 블록이 아래로 떨어지고, 바닥에 닿거나 다른 블록에 닿게되면 블록이 멈추고 새로운 블록으로 바뀌게 됩니다.

 

이번에는 키입력에 따라 블록이 움직이고 회전하게 만들어보겠습니다.

 

App.js에 EventHandler를 추가합니다.

 useEffect(() => {
    const keyHandler = (event) => {
      const inputKey = event.keyCode;
      const KEY = {
        LEFT: 37,
        RIGHT: 39,
        UP: 38,
        DOWN: 40,
        SPACE: 32,
      };
      switch (inputKey) {
        case KEY.UP:
          validateRotate(CurrentBlock, map);
          break;
        case KEY.DOWN:
          validateMove(CurrentBlock, map, 0, 1);
          break;
        case KEY.LEFT:
          validateMove(CurrentBlock, map, -1, 0);
          break;

        case KEY.RIGHT:
          validateMove(CurrentBlock, map, 1, 0);
          break;

        case KEY.SPACE:
          while (validateMove(CurrentBlock, map, 0, 1));
          break;
        default:
          return;
      }
    };
    
    if (onGame) window.addEventListener("keydown", keyHandler); //게임시작시 이벤트핸들러를 추가합니다.
    
    return () => window.removeEventListener("keydown", keyHandler); //언마운트시 해제합니다.
  });

다음 글에서는 위에 주석처리한 부분을 처리하겠습니다.

 

https://github.com/Mansook/TetrisBot

 

GitHub - Mansook/TetrisBot: tetris

tetris. Contribute to Mansook/TetrisBot development by creating an account on GitHub.

github.com