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

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

【タイピングゲーム 第1回】タイピングゲームを作る

タイピングゲームを作っていこうと思います。

学生時代に少しタイピングゲームにハマっていた時期があったのですが、
当時遊んでいたゲームたちはFlash Playerとともに死んでしまいました...。

夜の森タイピング neutralx0.net

皿打 neutralx0.net

僕は絵が描けないので同じようなものは作れそうにないですが、
勉強も兼ねて簡単なタイピングゲームを作ってみようかと思います。

技術要素

今回もVue3を使っていきます。

これまでより上手くComposition APIやTypeScriptを使えたらいいなと思っています。

実装

本体のVueファイルはこのような内容です。

<template>
  <div>
    <div v-if="gameStatus === GAME_STATUS_INITIAL">
      <h1>Enterキーを押して, ゲームを開始ください。</h1>
    </div>
    <div v-else-if="gameStatus === GAME_STATUS_FINISHED">
      <h1>ゲームが終了しました。</h1>
      <p>Enterキーを押してください。</p>
    </div>
    <div v-else>
      <h1>{{ currentText }}</h1>

      <p>{{ currentInput }}</p>
    </div>
  </div>
</template>

<script lang="ts">
import { ref, computed, onMounted, defineComponent } from 'vue'

const GAME_STATUS_INITIAL = 0
const GAME_STATUS_RUNNING = 1
const GAME_STATUS_FINISHED = 2

export default defineComponent({
  setup: () => {
    const gameStatus = ref(GAME_STATUS_INITIAL)
    const texts = ref([])
    const currentIndex = ref(0)
    const currentInput = ref('')
    const currentText = computed(() => texts.value[currentIndex.value] || null)
    const currentRemainingText = computed(() => (currentText.value || '').slice(currentInput.value.length))

    const start = () => {
      gameStatus.value = GAME_STATUS_RUNNING
      texts.value = [
        // サンプルデータ.
        'poppusinanaide',
        'soratobusakana',
        'karasuhamassiro',
      ]
    }
    const reset = () => {
      gameStatus.value = GAME_STATUS_INITIAL
    }

    const onKeyInput = (e: KeyboardEvent) => {
      switch (gameStatus.value) {
        case GAME_STATUS_INITIAL:
          if (e.key === "Enter") {
            start()
          }
          break
        case GAME_STATUS_RUNNING:
          if (e.key === currentRemainingText.value.slice(0, 1)) {
            currentInput.value += e.key
          }
          if (currentRemainingText.value.length === 0) {
            // 現在のテキストの入力完了
            currentInput.value = ''
            if (currentIndex.value + 1 === texts.value.length) {
              // ゲーム終了
              gameStatus.value = GAME_STATUS_FINISHED
            }

            currentIndex.value++
          }
          break
        case GAME_STATUS_FINISHED:
          if (e.key === "Enter") {
            reset()
          }
          break
      }
    }

    onMounted(() => {
      window.addEventListener('keydown', onKeyInput)
    })

    return {
      // constants
      GAME_STATUS_INITIAL,
      GAME_STATUS_RUNNING,
      GAME_STATUS_FINISHED,

      // data
      gameStatus,
      texts,
      currentIndex,
      currentInput,
      currentText,

      // computed
      currentRemainingText,

      // methods
      onKeyInput,
    }
  }
})
</script>

今回は、用意した3つのセンテンスを入力したらそれで終了、と言う簡単なルールにしています。

初期状態・ゲーム中・終了の3状態の画面を用意し、切り替えています。

テキストフィールドを使わずに、キーイベントを取得しています。
入力判定や表示の装飾を柔軟に行うためです。

画面

スタート画面

f:id:yushi0:20210822234841p:plain

ゲーム画面

f:id:yushi0:20210822234856p:plain

Pull Request

https://github.com/nek0meshi/typing/pull/1

後記

徐々に機能拡張していこうと思います。