Reactのコードを打ち込むタイピングゲームを作った話&個人開発の流れ
はじめに
自己紹介
こんにちは、株式会社ソーシャルPLUSのフロントエンドエンジニア @zomysan です。
ソフトウェアエンジニアとしての経験は10年と少しで、趣味は個人開発と食べることです。直近では Discord の読み上げ Bot「shovel」のWebインターフェイス(マイページ)機能・有償プランなどを作りました。
この記事について
個人開発でタイピングゲームを作ったので、それについて前半・後半に分けて話します。
前半では作ったタイピングゲームの技術選定の理由や 何を大事にして作ったのか? ということを紹介します。
後半は今回の開発を実例として、私が開発をするときの進め方について紹介します。誰にでも合うものではないかもしれませんが、少しでも参考になれば嬉しいです。
ゲームの紹介
tstt
で遊びながら学ぼう!
TypeScript Touch Typing 略して tstt というゲームです。ぜひ一度遊んでみてください。
Spaceキーを押すと始まります。コードスニペットが出てくるので、ひたすら打ち込みます。例とぴったり同じである必要はなく、TypeScriptとしてだいたい正しければOKです。10問正解すると終了です。
開発のきっかけ
Reactの初学者である知人が「ローマ字や記号の羅列だけでなく、もっと実践的なタイピング練習をしたい」と言っていたのが開発のきっかけです。
タイピングゲームはすでに世の中に多くありますが、TypeScript + React のコードで練習できるゲームは見当らなかったため、作ってみることにしました。(もしそういうゲームをご存知の方がいらっしゃいましたら是非教えてください!)
使ったツールと技術の選択理由
フロントエンドのみで完結するゲームなので構成はシンプルです。
言語・ライブラリ: React, TypeScript
TypeScript + React は「いつもの」で選びました。学習のためにVue.jsを選びたい気持ちもあったのですが、今回は少しでも早く動くところまで持っていきたかったのでReactにしました。
ビルドツール: Vite
ビルドツールにはViteを選びました。Next.jsをよく使うのですが、ルーティングもサーバーとの通信もない今回のシンプルなアプリにはNext.jsのほとんどの機能が不要です。
Viteは使ったことがなかったんですが、評判も良く、少し前に個人開発で導入した Vitest の使い心地がとてもよかったため試しに使ってみることにしました。小さいアプリというのはありますが、起動もホットリロードも早く満足しています。
ホスティングサービス: Vercel
他のプロジェクトですでにVercelの有償プランを利用しているので、今回も採用しました。GitHubからリポジトリを連携するだけでデプロイされるのが本当に楽ですね。
コンセプト・こだわり
ここからは、タイピングゲームを作る上でこだわったことについて紹介します。
急かさない、焦らせない
タイピングゲームと聞いてどのような体験をイメージするでしょうか?残り時間のカウントダウンが表示されていて、カウントダウンが0になるまでにできるだけたくさんの文字数を打ち込んで、スコアが高くなっているか視界の隅で気にする…… といった感じではないでしょうか。
私もそのイメージに引っ張られ、画面上部に残り時間とスコアを出そうと考えていました。しかし、そのUIだと残り時間とスコアが見えていることで、ホームポジションで正確に打つことよりも、少しでも早く打ってスコアを上げることに意識が向いてしまうことに気づきました。
なので一旦完全に考え直し、仕様変更することにしました。残り時間のカウントダウンをやめ、固定の問題数を解き終わるまでじっくり取り組めるようにしました。スコアと経過時間なども表示せず、今打ち込むべきコードだけを表示するようにしてみました。
結果として、落ちついて打鍵に向き合える雰囲気になりました。
また上の画像にあるとおり、ゲーム進行中は「TypeScript Touch Typing」というメインタイトルの文字色を薄くしています。これも今打ち込むコードに集中できるようにする工夫のひとつです。
競わせるより個人の成長を重視
タイピングゲームといえばランキングですが、今回は実装しないことにしていました。負荷やバックエンドが必要になることなどももちろん理由の1つですが、ランキングがあると自分のスコアだけに集中できなくなってしまうことが最も大きな理由です。
もちろんランキングがあると奮起するタイプの人もいるとは思いますし、盛り上がるのも事実です。しかし、今回はあくまで じっくり練習して、自分の力を伸ばすことに集中できるゲームにしたい という思いがあったので、ランキングは実装しないことにしました。
そういう意味では結果のシェア機能もなくすべきなのですが、そこはちょっと迷って付けることにしました。ポスト文面にスコアがバーンと出てしまうとどうしても比べてしまうので、シェアされる内容はそのうち修正するかもしれません。
実際の開発時と近い触り心地
TypeScript + React で開発していくなら、ほとんどの人が Visual Studio Code(以下 VSCode)を使うのではないでしょうか。なので、VSCodeを使い慣れている人が違和感を覚えないよう、またこれからVSCodeを使っていく人が違和感なくVSCodeに入れるよう、VSCodeと似た書き心地になるよう工夫しました。
具体的には括弧が補完されます。{
を打つと、自動的に }
も入力されます。タイピングゲームで補完機能がついているというのはちょっとヘンな感じですが、あくまで開発のための基礎練だからこれで良しとしています。
また、文字列として完全に同じでなくても、TypeScriptとして同じコードとして解釈できるなら正解とするようにしています。スペースや文字列を表すクォーテーションが多少違っても問題ありません。
たとえば、以下の2つは同じと解釈されます。
type RecordOfStrings = Record<string, string>;
type RecordOfStrings=Record<string,string>;
このようにした理由は、これらを正確に反映してタイピングできなくても、適切に設定した VSCode であればこのような瑣末なスタイルの違いは prettier が修正してくれるからです。
文末のセミコロンについてはどうするべきか悩みましたが、現時点では必須にしています。が、自分でコーディングするときにいちいち打っているかというと正直まったく打っていないので、必須にしなくてもいいのかも? 🤔
個人開発の進めかた - プロジェクトが形になるまで
ここからは少し視点を変えて、「作りたい!」となってから実際にデプロイするまでにどのような流れで作っていったかを紹介します。
以下のようなステップで進めていきます。今回のような新規開発でも、すでにあるプロダクトへの機能追加でも、だいたい同じような流れでやっています。
- 作りたくなる
- 要件を決める
- もう少し細かく考える
- 実装する
- リリースする
「要件を決める」以降のそれぞれのステップについて、実際の例と一緒に順番に紹介します。
要件を決める
まず、頭の中にある「作りたい」の気持ちを整理して俯瞰で見られるようにします。だいたい1〜2時間程度かけることが多いです。
今回は最初に手書きでこのように要件を整理しました。汚い字ですみませんが、実際のメモに一部加工のうえ、そのまま載せています。
要件を整理することで、細かい仕様で迷ったときにこれらの要件に立ち返って自信をもって判断できます。
個人的に気に入っているのは「やらないこと」を書いておくことです。「やらないこと」を決めるのは、諦めや消極的な気持ちからではなく、明確にその機能やアプリケーションのあり方を定めることだと思います。
今回はランキング機能をやらないと決めることで、他人と競い合わせるのではなく、一人一人が自分の成長と向き合えるゲームであることを大事にしたいんだ、ということをはっきりさせることができました。
もうすこし細かく考える
最初に頭の中にあることを要件という形で整理できたら、もう少し具体的なところを考えていきます。ここも1時間程度かなと思います。ここで思いついたことを要件に追加したり、考えてみたらイマイチだった要件を削除したりもします。
場合によってはいきなりここから始めることもありますが、やはり最初に「なぜやるのか」「なんのためにやるのか」があったほうがこのステップにスムーズに入れるように思います。
作るものによってどう具体化していくかは変わりますが、今回はゲームであり、ゲームの状態(開始前、ゲーム中、結果表示)によって大きく画面構成が変わるだろうなというのが頭にありました。なので、状態遷移図とラフなワイヤーフレーム的なものを組み合わせた感じで整理しました。
この時点でわりとシンプルな構成にしたい、という気持ちがあったことがわかります。また、「コンセプト・こだわり」の項でも触れたとおり、得点表示や残り時間は書いてみたものの不要と判断し、あとから消すことにしました。このような変更は実際にコードを書いてしまうとそれなりの手戻りになるので、ラフに事前に見える形にしておくことのメリットがわかります。
ここまではiPad + GoodNotes というアプリで進めることが多いです。また、外出先やリビングなどちょっとリラックスできる場所で作業すると捗ります。不思議とPC前だとあまりやる気がでません。
実装
だいたいやることが決まったので実際に手を動かしていきます。ここからはPC前に腰を据えての作業となります。なにを作るかはある程度決まっているので、どう作るかを考えるのに集中できます。
最初のリリースまでは3日ほどだったと思います。どれくらい作業時間をとったかは正確には覚えていませんが、だいたい10時間かからない程度かなと思います。コミット数はだいたい20コミット程度でした。
リリース
開発中も都度Vercelにデプロイしながら進めてはいるので、「作ったよ〜」と発表するのが実質のリリースとなります。
私が所属するソーシャルPLUSには、日報に「個人開発でこういうの作ったよ」と書いたら試したりフィードバックしてくれたりする人たちがいます(大変ありがたいことです)。なので、日報で小さくリリースするのが私個人としては恒例になっています。前職はそういう雰囲気ではなかったので、かわりに友人や家族に見てもらったりしていました。
そこである程度感触を掴んだら、また少し実装や仕様を修正します。そうしてある程度自信が持てたら、不特定多数の方に向けてリリースをしていきます。
もちろん最初からバーンとリリースできるならそうしてもよいと思いますが、尻込みしてしまうタイプはこのようにちょっとずつ見せる範囲を広げていくのがおすすめです。
おわりに
今後 tstt でやりたいこと
とりあえず遊べる状態になっているのでリリースしましたが、やりたいことは山積みなので継続的にやっていこうと思います。
最低限コードを整えて公開
今の実装は外に出すにはちょっとラフすぎるので、もう少し整えたらリポジトリを Public にしたいと思います。例文はどんどん増やしたいので、オープンソースにしておくことで誰かが追加してくれたらいいなと目論んでいます。
スコア保存機能
自分の成長度合いがわかるゲームにしたいので、日付とスコアを保存するようにしたいです。少しずつスコアが上がっている様子がグラフなどで見られたらモチベーションアップにもなりますよね。
ログイン機能はつけるつもりがないので、保存先はLocalStorageなどにしようかなと考えています。
問題文・エディタの改善
いまは1行にまとめてコードを表示していますが、見づらいのもあるので複数行で表示する対応もしたいです。そうなると入力欄も複数行に対応する必要が出てくるので、独自実装のエディタをやめたいなというのも出てきます。
また、エディタと問題文が見づらいのでSyntax Highlightを導入したいです。前述の複数行対応もあわせて、monaco-editor に置き換えたいです。
コードスニペットに解説をつける
ちょっとこれはどうするか迷うんですが、どうせ毎日打ち込むならそのコードがどういう意味を持つのかをついでに学習できたら良さそうと思っています。
タイピングの速度を上げるという目的とはややズレますが、たとえば1行で「特定のプロパティだけを型から除きたいときに使います」とか添えられていたら、実際に開発するときに思い出して使えることにつながりそうに思います。
感想など
開発者人生ではじめてゲームと呼べるものを作りました。慣れているReactで作ったものの、ゲームとなるとやはり管理画面などとは考えることが違っていて非常に楽しかったです。
今回作ったのはウェブアプリケーションの延長的なゲームでしたが、もう少しゲームらしいゲームにも挑戦してみたいという気持ちも芽生えました 🪴
ここまで読んでいただきありがとうございました!
Discussion
Typescript+Reactとかとは少し違いますが、Monkeytypeに、Javascriptのモードがあった気がします。(実践的かはさておき)
おお、こちらもキーワードの練習ができて良さそうですね!追記しておきます。教えていただきありがとうございます!!