スマレジエンジニアyushiのブログ

スマレジエンジニアのブログ

【テトリス #5】ブロックを動かす

テトリスを作っています。

テトリス カテゴリーの記事一覧 - スマレジエンジニアyushiのブログ

技術スタックは下記の通りです。

  • TypeScript
  • React
  • SCSS
  • Create React App

今回はブロックを動かします。

キーハンドラ

まずはキーハンドラを更新します。

左右移動の他、上キーでは右回りに回転するようにします。

    const keyEventHandler = (e: KeyboardEvent) => {
      switch (e.key) {
        case 'ArrowLeft':
        case 'h':
          move('left');
          break;
        case 'ArrowRight':
        case 'l':
          move('right');
          break;
        case 'ArrowUp':
        case 'k':
          move('turn');
          break;
        case ' ':
        case 'ArrowDown':
          nextStep();
          break;
      }
    };
    window.addEventListener('keydown', keyEventHandler);

回転の計算ロジックの追加

回転の計算ロジックを追加します。
前回作成したturnOnceを利用します。

export const turn = (
  x: number,
  y: number,
  centerX: number,
  centerY: number,
  turnVal: Turn
): [number, number] => {
  let res: [number, number] = [x, y];
  for (let i = 0; i < turnVal; i++) {
    res = turnOnce(res[0], res[1], centerX, centerY);
  }
  return res;
};

データ構造の変更

ブロックのデータ構造に、回転状態を追加します。

export type Block = {
  x: number;
  y: number;
+  turn: Turn;
  type: BlockType;
};

Turn型は0〜3で、右回り90度ずつの回転状態を示します。

また、ブロックに対して、その要素のタイルを返すロジックを用意します。

export const getTiles = (block: Block) => {
  const blockShape = BLOCK_SHAPES[block.type];

  return blockShape.tiles.map((tile) =>
    turn(
      tile[0],
      tile[1],
      blockShape.center[0],
      blockShape.center[1],
      block.turn
    )
  );
};

移動ロジックの追加

block に対して、 moveの型通りの移動を行います。
移動の結果、ボードの幅(boardWidth)を超えた場合は移動不可のためnullを返却する。

export const moveBlock = (
  block: Block,
  move: MoveType,
  boardWidth: number
): Block | null => {
  const movedBlock = (() => {
    switch (move) {
      case 'left':
        return {
          ...block,
          x: block.x - 1,
        };
      case 'right':
        return {
          ...block,
          x: block.x + 1,
        };
      case 'turn':
        return {
          ...block,
          turn: ((block.turn + 1) % 4) as Turn,
        };
    }
  })();

  const tilesX = getTiles(movedBlock).map(([x]) => x + movedBlock.x);

  // 移動先が不正ならnullを返却する.
  return Math.min(...tilesX) < 0 || Math.max(...tilesX) >= boardWidth
    ? null
    : movedBlock;
};

状態更新

キー入力のの度に現在落下中のブロックであるfallingBlockを更新します。

 const move = (m: MoveType) => {
    if (fallingBlock === null) {
      return;
    }

    setFallingBlock(moveBlock(fallingBlock, m, boardWidth) || fallingBlock);
  };

Pull Request

https://github.com/nek0meshi/tetris/pull/12

あとがき

ここからゴリゴリのロジックが増えていきます。