[タイピングゲーム？]ReactとTypeScriptでタイピングゲームを作ろう

## はじめに
初めましての人も、そうでない人も、こんにちは！
皆さんは張り込み捜査をしたことはありますか？
張り込み捜査と言えばやはりあんぱんと牛乳ですよね！

しかし、あんぱんを食べ過ぎて頭がおかしくなり、紙に「あんぱん」と殴り書きしたくなるのはあるあるではないでしょうか？ 
さらに、殴り書きしたくなっても紙と鉛筆がなくて苦しい思いをした方もいると思います!

そんな方々の悩みを解決するために、PC1台あれば遊べる「あんぱん」だけを題材にしたタイピングゲームを作成したので、最後まで読んでいただけると幸いです！ 
そして今回作成するゲーム名は便宜上"あんぱんゲーム"で統一させていただきます！

## 事前準備
### 使用PC M1 Macbook バージョン14以上

あんぱんゲームを作るために、まずはReactをインストールします！ ターミナルを開き、そこに
```
npx create-react-app anpan --template typescript
```
というコマンドを入力してください！ 

今回はTypeScriptというコードを使用するために、最後に--template typescriptを入れています。フォルダ名は「anpan」にさせていただきました!
もしかしたら、うまくインストールできない方がいるかもしれませんが、慌てずにChatGPTに質問するか、ターミナルやPCを再起動させてからもう一度このコマンドを入力すると、うまくいくかもしれません!

コマンドを入力すると、「anpan」というフォルダの中にたくさんのフォルダやファイルがあるかもしれません!
多くのフォルダの中に、srcというフォルダがあると思います!このフォルダに手を加えて作成していきます!

## 作成してみよう！
まずはスタート画面を作成していきます！ 
srcの中にあるApp.tsxを開いてみてください!
何かたくさんコードが書いてあると思いますが、以下のコードをコピペしてください!
```App.tsx
import React, { useState } from 'react';
import './App.css';
import Game from './game'; // Gameコンポーネントのインポート

function App() {
  const [gameStarted, setGameStarted] = useState(false); // ゲームが開始されたかどうかの状態

  // ゲームを開始する関数
  const startGame = () => {
    setGameStarted(true); // ゲームが開始されたことを記録
  };

  // ゲームが開始されている場合、Gameコンポーネントを表示する
  if (gameStarted) {
    return <Game />;
  }

  // ゲームが開始されていない場合、スタートボタンを表示する
  return (
    <div className="App">
      <div className="title">あんぱんゲーム</div>
      <button className='Start_Button' onClick={startGame}>Start</button>
    </div>
  );
}

export default App;
```
これでスタート画面が作られました！
これではデザイン面がそっけないので少しいじってみたいと思います！
同じsrcフォルダ内にあるApp.cssを開いてください！そのcssファイルを以下のコードに変えてみてください！
```App.css
.App {
  text-align: center;
  background-color: black;
  position: relative;
  height: 100vh;
}

.title{
  color: red;
  position: relative;
  top: 100px;
  font-size: 100px;
}

.Start_Button{
  position: relative;
  top: 250px;
  background-color: red;
  color: black;
  font-size: 70px;
  border-radius: 10px;
  width: 300px;
  padding: 10px;
  border: none;
}
```
今回はそこまでデザインに力を入れていないですが、いかがでしょうか？

次に、ゲームをプレイするためのコードを作成します!
srcフォルダに新しくgame.tsxとgame.cssを作成してください!そして、以下のコードをコピペしてください!
```game.tsx
import React, { useState, useEffect } from 'react';
import './game.css';
import Score from './score';

const Game: React.FC = () => {
  const [inputValue, setInputValue] = useState('');
  const [score, setScore] = useState(0);
  const [timeLeft, setTimeLeft] = useState(60);
  const [gameOver, setGameOver] = useState(false);

  useEffect(() => {
    if (timeLeft === 0) {
      setGameOver(true);
    } else {
      const timer = setInterval(() => {
        setTimeLeft(time => time - 1);
      }, 1000);
      return () => clearInterval(timer);
    }
  }, [timeLeft]);

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { value } = event.target;
    setInputValue(value);
    if (value === 'あんぱん') {
      setScore(score => score + 1);
      setInputValue('');
    }
  };

  const handleRestart = () => {
    setScore(0);
    setTimeLeft(60);
    setGameOver(false);
  };

  if (gameOver) {
    return <Score score={score} onRestart={handleRestart} onBackToTitle={function (): void {
      throw new Error('Function not implemented.');
    } } />;
  }
  

  return (
    <div className="game-container">
      <h1 className='title'>あんぱんゲーム</h1>
      <p className='timer'>制限時間: {timeLeft}秒</p>
      <p className='score'>スコア: {score}</p>
      <input
        className='input'
        type="text"
        value={inputValue}
        onChange={handleChange}
        autoFocus
      />
    </div>
  );
};

export default Game;
```
```game.css
.game-container{
    position: absolute;
    width: 100%;
    min-height: 100vh;
    background-color: black;
}

.title{
    color: red;
    font-size: 3rem;
    margin-top: 10%;
}

.timer{
    color: red;
    font-size: 2rem;
    margin-top: 10%;
}

.score{
    color: red;
    font-size: 2rem;
    margin-top: 5%;
}

.input{
    outline: none;
    border: none;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: red;
    color: black;
    font-size: 50px;
    border-radius: 10px;
    width: 200px;
    padding: 10px;
    border: none;
}
```
これでスタート画面でSTARTボタンを押したら、60秒間必死に「あんぱん」とたくさん打ち込んでみてください！気が晴れると思います（？）

やはりゲームというからには、スコア発表も欲しいですね！ということで、最後にsrcフォルダの中にscore.tsxとscore.cssを作成してください！
そこに
```score.tsx
import React from 'react';
import './score.css';

interface ScoreProps {
  score: number;
  onRestart: () => void;
  onBackToTitle: () => void; // タイトルへ戻るためのコールバック関数を追加
}

const Score: React.FC<ScoreProps> = ({ score, onRestart, onBackToTitle }) => {
  const generateAnpan = (count: number) => {
    let anpan = '';
    for (let i = 0; i < count; i++) {
      anpan += 'あんぱん ';
    }
    return anpan.trim(); // 末尾の余分な空白を削除
  };

  return (
    <div className="score-container">
      <h1 className="title">スコア: {score}</h1>
      <h2 className="anpan-background">{generateAnpan(score)}</h2>
      <div className="button-container">
        <button className="restart-button" onClick={onRestart}>再開</button>
      </div>
    </div>
  );
};

export default Score;
```
```score.css
body {
    margin: 0;
    padding: 0;
    background-color: black; /* 背景色をブラックに設定 */
}

.score-container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: auto; /* スクロールを可能に */
    color: red;
}

.title {
    font-size: 3rem;
    margin-top: 10%;
    color: red;
    top: 0;
}

.anpan-background {
    font-size: 2rem;
    -ms-writing-mode: tb-rl;
	writing-mode: vertical-rl;
}

.restart-button {
    position: absolute;
    top: 24%;
    left: 35%;
    transform: translate(-50%, -50%);
    background-color: red;
    color: black;
    font-size: 50px;
    border-radius: 10px;
    width: 200px;
    padding: 10px;
    border: none;
}
```
これらをコピペしてみてください！
すると、スコアとたくさんのあんぱんが見られるはずです！

## 実際にプレイしてみた感想
私はタイピング速度があまり早い方ではないので、本気で遊んで30スコアしか出ませんでした（泣）
そこで、このゲームの抜け穴として「あんぱん」を事前にコピーして、ゲーム開始時にCommand+Vを長押しでやってみたところ、なんと余裕の700越えでした！！
初見でスコア画面を見て、思わず笑いが出てしまいましたw
皆さんもあんぱんを食べ過ぎておかしくなりそうになった時や暇な時にでも、ぜひ遊んでみてください！

## まとめ
あんぱんに絞ったタイピングゲームを作成しました！
もうすでにお気づきの方が多いかもしれませんが、某アニメの演出をオマージュ（パクリではないと思いたい）したものになっています！
最後までご覧いただきありがとうございました！またお会いしましょー！


## githubURL

https://github.com/FrohleinYoshie/AnpanGame