2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Next.jsでタイピングゲームを作ってみた

Posted at

はじめに

初投稿です。
最近TypeScriptを勉強したので、next.jsを使ってタイピングゲームを作りました。

作ったもの

デモ
https://typing-game-neco75.netlify.app

github
https://github.com/neco75/TypingGame-next.js

実装

今回は最低限の機能を実装することを目標にして、以下の要素を実装しました。

  • スコア機能
  • 制限時間
  • タイプした文字列の正誤判定

よくある1タイプごとに判定して、間違えている場合入力できない機能を実装したかったのですがうまくいきませんでした…

プロジェクトのセットアップ

最初に、新しいNext.jsプロジェクトを作成

npx create-next-app typing-game
cd typing-game

必要なライブラリをインストール

npm install react react-dom next
npm install typescript @types/react @types/node
npm install random-words

コード内容

プロジェクトの使わないファイルを消去、変更します。今回はcomponentsディレクトリを作成し、その下にTypingGame.tsxを用意しました。以下がindex.tsxとtypingGame.tsxのコードです

index.tsx
import React from 'react';
import TypingGame from '../components/TypingGame';

const Home: React.FC = () => {
  return (
    <div>
      <TypingGame />
    </div>
  );
};

export default Home;

TypingGame.tsx
import randomWords from 'random-words';
import React, { useState, useEffect } from 'react';

const TypingGame: React.FC = () => {
  const [word, setWord] = useState<string>('');
  const [input, setInput] = useState<string>('');
  const [score, setScore] = useState<number>(0);
  const [missCount, setMissCount] = useState<number>(0);
  const [time, setTime] = useState<number>(30);
  const [gameOver, setGameOver] = useState<boolean>(false);

  useEffect(() => {
    generateNewWord();
  }, []);

  useEffect(() => {
    let timer: NodeJS.Timeout;

    if (time > 0 && !gameOver && missCount < 5) {
      timer = setTimeout(() => {
        setTime(time - 1);
      }, 1000);
    } else if ((time === 0 || missCount >= 5) && !gameOver) {

      setGameOver(true);
      alert("Game Over");
    }

    return () => clearTimeout(timer);
  }, [time, gameOver, missCount]);

  const generateNewWord = () => {
    const newWord = randomWords();
    setWord(newWord);
    setInput('');
  };

  const reset = () => {
    setInput('');
    setScore(0);
    setMissCount(0);
    setTime(30);
    setGameOver(false);
    document.getElementById("text")!.style.backgroundColor = "white";
    generateNewWord();
  };

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const userInput = e.target.value;
    setInput(userInput);

    if (!gameOver && missCount < 5) {
      if (userInput === word) {
        document.getElementById("text")!.style.backgroundColor = "white";
        setScore(score + 10);
        setTime(time + 2);
        generateNewWord();
      } else if (userInput.length >= word.length) {
        document.getElementById("text")!.style.backgroundColor = "pink";
        setInput('');
        setScore(Math.max(score - 1, 0));
        setMissCount(missCount + 1);

        if (missCount + 1 >= 5) {
          setGameOver(true);
          alert("Game Over");
        }

        setTime(Math.max(time - 3, 0));
      }
    }
  };

  return (
    <div>
      <h1>Typing Game</h1>
      <p>Time: {time}</p>
      <p>Score: {score}</p>
      <p>MissCount: {missCount}</p>
      <h2>Word: {word}</h2>
      <input id="text" type="text" value={input} onChange={handleInputChange} />
      <input id="reset" type="button" value="Reset" onClick={reset} />
    </div>
  );
};

export default TypingGame;

ゲームのロジック

ゲームの基本的なロジックは次のようになります。

  • タイピングされた文字列が正しい場合、スコアを増加させ、新しい単語を表示。
  • タイピングされた文字列が不正確な場合、スコアを減少させ、ミスカウントを増加
  • タイマーがゼロになるか、ミスカウントが5以上になるとゲームオーバーとなる

起動

サーバーを起動します。

npm run dev

デフォルトの http://localhost:3000 で実行されるはずです。

最後に

React自体殆ど触ったことなくてなかなか形にするのが大変でした。まだまだ改善点があるので、もう少し勉強したいです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?