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

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

【テトリス #2】背景とブロックの描画

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

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

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

  • TypeScript
  • React
  • SCSS
  • Create React App

背景の作成

まずは背景を作ります。

背景は、10 x 25個のタイルの積み重ねで作ります。

function Board() {
  const createLine = (i: number) =>
    [...Array(10).keys()].map((j) => <Tile key={j + 10 * i} />);
  const lines = [...Array(25).keys()].map((i) => (
    <div className="TileLine" key={i}>
      {createLine(i)}
    </div>
  ));

  return <div className="Board lines-container">{lines}</div>;
}
.Tile {
  display: inline-block;
  margin: 1px;
  border: solid 1px #555;
  width: $tile-size;
  height: $tile-size;
}

.TileLine {
  line-height: 0;
}

完成品は以下です。

Pull Request: https://github.com/nek0meshi/tetris/pull/7

ブロックの表示

次にブロックを表示します。ブロックとは順々に落ちてくる正方形のもの、縦長のもの、S字のものなどの塊で、それを構成する4要素をタイルと呼ぶことにします。

最終的には動かす必要がありますが、まずは表示だけ作ります。

features/blocs/index.ts

features/blocs/index.tsを作成します。こちらのファイルでは、表示やReactの機能に依存しない型・定数などを定義します。BlockTypeは、1つ1つがブロックの形を示しています。BLOCK_SHAPESは、ブロックごとのタイルの配置の座標を定義しておきます。

export type BlockType = 'i' | 'o' | 's' | 'z' | 'j' | 'l' | 't';

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

export const BLOCK_SHAPES = {
  i: [
    [0, 0],
    [1, 0],
    [2, 0],
    [3, 0],
  ],
  o: [
    [0, 0],
    [0, 1],
    [1, 0],
    [1, 1],
  ],
  ...
};

/**
 * x, yで指定されるタイルにブロックが存在する場合は、それを返却する.
 */
export const findBlock = (
  blocks: Block[],
  x: number,
  y: number
): Block | undefined => {
  for (const block of blocks) {
    for (const shape of BLOCK_SHAPES[block.type]) {
      if (x === block.x + shape[0] && y === block.y + shape[1]) {
        return block;
      }
    }
  }
};

hooks/useBlocks.ts

hooks/useBlocks.tsは、ブロックの状態管理などを司るReact hookです。

一旦、ダミーデータを返すようにしておきます。

import { useState } from 'react';
import { Block } from '../features/blocks';

const DUMMY_BLOCKS: Block[] = [
  { x: 1, y: 1, type: 'i' },
  { x: 1, y: 3, type: 'o' },
  { x: 4, y: 0, type: 's' },
  { x: 3, y: 2, type: 'z' },
  { x: 7, y: 0, type: 'j' },
  { x: 5, y: 3, type: 'l' },
  { x: 7, y: 2, type: 't' },
];

const useBlocks = () => {
  const [blocks] = useState(DUMMY_BLOCKS);

  return {
    blocks,
  };
};

Board.tsの更新

前項で定義したダミーデータを表示します。

下記のcreateTileでは、ボード上のタイルごとに、もしそこにブロックが存在すればその色に塗りつぶすようにします。

function Board() {
  const { blocks } = useBlocks();
  const createTile = (x: number, y: number) => {
    const block = findBlock(blocks, x, y);
    const classNames = [block ? COLOR_NAME[block.type] : ''];

    return <Tile key={y + BOARD_WIDTH * x} classNames={classNames} />;
  };
  const createLine = (i: number) =>
    [...Array(10).keys()].map((j) => <Tile key={j + 10 * i} />);
  const lines = [...Array(25).keys()].map((i) => (
    [...Array(BOARD_WIDTH).keys()].map((j) => createTile(j, i));

  ...

Pull Request: https://github.com/nek0meshi/tetris/pull/6

あとがき

ReactもTypeScriptも、慣れないながら楽しいです。

表示と動作を分けて実装を進められたのは、うまくやれたなと思いました。

次回はブロックを動かしていきます。