Unityで作るローマ字入力とかな入力に両対応した多機能なタイピングゲームの制作講座 1回目
こんにちは!ジェイです。
タイピングゲームを作ろうと思った時に、「Unity タイピングゲーム 作り方」で検索するとたくさん情報が出てきます。
それを参考に作ってみるとある程度のタイピングゲームは作れるし、ローマ字入力のみのタイピングゲームは見かけることもあります。
しかし、ローマ字とかな入力両方に対応してなおかつ、ローマ字の柔軟な入力対応している情報はたくさんあるのに今のところ、まったくありませんでした。
そこでUnity初心者でも、シンプルかつどんな状況にも対応できるような多機能なタイピングゲームの作り方をこれから説明します。
対象者
・Unityの基本操作ができてC#の基本的な文法(クラス、変数、関数)などは理解できている
タイピングソフトを作るのに必要な処理
・問題文章の読み込み打つ文字に変換してデータを格納する
・データをテキストを表示
・どのキーを入力したか取得して正誤を判定する
・正解、ミスした時にそれぞれの処理を行う
・タイピングが終了した時に結果を表示する
大きく分けると以上の処理になります。
データの読み込みの処理では、ローマ字入力でもかな入力でも、同じ処理で済みますが、それ以降の入力判定や終了などは、ローマ字入力とかな入力で異なる処理を実行しなければなりません。
それに加えてローマ字入力は、打ち方が複数あるので、そのすべてに対応するのはなかなか大変です。
そこで、私が作ったタイピングゲーム用のスクリプトを用いて、必要最低限の入力判定から表示までの流れを行ってみます。
準備
最初からプロジェクトを作る場合は以下通りに作業ください。その際にはこちらからサンプルプロジェクトをダウンロードしてScriptフォルダからCTypeEngineとCTypeRomaEngine.csとCTypeKanaEngine.csの3つのスクリプトをコピーしてください。
CGameManager.csに関しては、自分で作って空のオブジェクトにアタッチして、サンプルを見ながら色々試してみましょう!
しかし、その前にTextMeshProを使えるようにしておきたいので、以下の手順を済ませておきましょう。
1.UnityHubを開いて3Dを選び好きな名前でプロジェクトを作成する
2.ヒエラルキーで右クリック→UI→テキスト-TextMeshProでTextMeshProを2つ配置する
最初に以下のウィンドウが出るのでImport TMP Essentialsをクリックする
3.TextMeshProで日本語を使えるようにするために以下の手順を実行します
フォントをダウロードしてstatic/NotoSansJP-Medium.ttfをUnityでFontフォルダを作ってその中にドラッグ&ドロップします。
上記の注意点としては、japanese_full.txtをダウンロードして貼り付けてしまうとテキストエディタの設定されている文字コードに変換されてエラーが出てしまうことがあります。
DownloadZIPでダウンロードせずにRawをクリックしてコピペしても動くので、できない場合はこちらの方法を試してみてください。
ここまでで日本語が表示できるようになったので、こちらからサンプルプロジェクトをダウンロードしてプロジェクトを開くと以下の様になります。
実行してみるとすぐにローマ字入力でタイピングができるようになっています。
このプロジェクトは最低限ではありますが、タイピングゲームに必要な処理がすべて実装してます。
具体的に書き出すと
・ひらがなからローマ字を生成する
・ひらがなから入力判定するのに必要な文字を作る
・キー入力から正誤判定をして正解なら文字を進めて間違いならその文字を赤色にして打ち終わったら終了を表示する
・テキストメッシュに代入してUnity上に表示する
ローマ字入力については以上の内容です。
かな入力の場合に関しては、ローマ字を生成するかわりに濁点と半濁点を分解する処理に差し替えるだけで済みます。例 が→か゛
一方で、ローマ字入力は様々な入力方法があるのでこれに対応するにはかなりの分量の辞書を用意して入力した文字と比較する必要があります。
更に、表示文字と違った場合には、打ったローマ字に変更する機能など、サンプルプロジェクトではこれらのすべての機能を実装してます。
処理の流れとしてはローマ字入力でもかな入力でも同じですが、これを簡単に分岐できる方法として、C#のクラスの継承とオーバーライドを使います。
覚えようと思うとたくさんの機能はありますが、タイピングゲームを作る際には以下の内容さえ理解できてれば十分です。
今回のサンプルプロジェクトでは、CTypeEngineクラスを基底クラスとして、CRomaTypeEngineとCKanaTypeEngine派生して、メソッドをオーバーライドしてやることによって、最初のインスタンス化の部分のみの変更ですべての処理を差し替えられるようにしてます。
例
CTypeEngine TypeEngine = new CRomaTypeEngine(); // ローマ字入力
CTypeEngine TypeEngine = new CKanaTypeEngine(); // かな入力
このたった一行の宣言だけで、ローマ字入力とかな入力の違いを吸収してくれるめちゃめちゃありがたい機能です!
これを使わないとやたらif分で分岐したり、同じ仕様の関数がたくさんできてしまったりと大変です。
後にきちんと説明しますが、基底クラス型の変数は派生クラス型でインスタンス化できるのです。
プロジェクトの構成の説明
シーンに追加されてるのは
・Canvas下のViewTextMesh(TextMeshPro)とInputTextMesh(TextMeshPro)
・空のゲームオブジェクトを追加してコンポーネントとしてCGameManagerをアタッチ
以上の2点だけです。
もし自分で最初から作る場合は、このCGameManagerを使わずに自分でスクリプトを作ってどこのオブジェクトでもいいのでアタッチしても大丈夫です。
次にCTypeEngineとCRomaTypeEngineとCTypeKanaEngineの3つのスクリプトを自分で作ったプロジェクトにコピーします。
全体の処理の流れ
まず、Start関数でローマ字入力ならnew CRomaTypeEngine();でかな入力ならnew CKanaTypeEngine();でインスタンス化します。
MakeInputTextやMakeSearchStrはオーバーライドされた関数なので、この差し替えだけで関数の中身もすべてインスタンス化したクラスに定義した関数にさし変わります。
>|C#|
void Start()
{
TypeEngine = new RomaTypeEngine(); // ここをnew CKanaTypeEngine()にするとかな入力になる
string input_text = TypeEngine.MakeInputText(InputText[0]); // かな表示文章の1行目を入れる
Debug.Log(input_text);
foreach(var str in TypeEngine.MakeSearchStr(InputText[0], 0))
{
Debug.Log(str);
}
ViewTextMesh.text = ViewText[0];
InputTextMesh.text = input_text;
}
||<
ひらがなからタイピングする文章を生成する
MakeInputText関数は、ひらがなをワードを引数として入力するとローマ字入力用の文字かかな入力用の文字を生成してくれます。
例えば、
string input_text = TypeEngine.MakeInputText("であいとわかれのきせつ");
のように引数に「であいとわかれのきせつ」と入力すると「deaitowakarenokisetu」か「て゛あいとわかれのきせつ」とローマ字、かな、それぞれに対応したワードになります。
ひらがなからタイピングするワード候補を生成する
MakeSearchStrはこれからタイピングする文章の候補を生成します。
例えば、第一引数にひらがなのワード、第二引数に指定するのは、今打っている文字数です。
List<string> serach_texts = TypeEngine.MakeSearchStr("であいとわかれのきせつ", 0);
ローマ字入力の場合にserach_texts 入っている値は、「de」
かな入力の場合にserach_texts入ってる値は「W」と「w」でこれは「て」に当たるキーです。
List<string> serach_texts = TypeEngine.MakeSearchStr("であいとわかれのきせつ",10);
ローマ字入力の場合にserach_texts 入っている値は、「tu」と「tsu
」
かな入力の場合にserach_texts入ってる値は「P」「p」でこれは「せ」に当たるキーです。かな入力の場合は、濁点と半濁点を分解した文字数になるので、10文字目は「せ」になるのに注意が必要です。
最期に生成したワードをTextMeshProに代入して表示させます。
ViewTextMesh.text = ViewText[0];
InputTextMesh.text = input_text;
ここまでで、すべての初期化処理が終了して、最初の文章が表示されてる状態になります!
ここまでで重要なのは、MakeInputTextでひらがなのワードから文章を生成して、MakeSearchStrはひらがなのワードから、これから入力する文章を生成するということしです。
中身についても今後、解説する予定ですが、使い方さえわかっていれば、タイピングゲームは十分に作れます。
入力処理
取得できないキーについて
Unityでキーボードの入力を取得するには、Input.GetKeyDownなどを使用するのが一般的ですが、日本語キーボードを使うと判定できないキーが存在します。
Unityのキー取得で以下のキーの取得方法がわかる人いませんか?
— ジェイ@ゲーム制作 タイピング (@JY20160816) 2024年3月31日
InputSystemも探したけどまったく見当たらなかったです><
これだとかな入力のタイピングソフト作れないですね💦#Unity pic.twitter.com/1fGuiqSIRq
解決方法はTwitterで教えてもらえたので本当に助かりました!もしかしたら、Unityでかな入力のタイピングゲームが少ないのはこれが原因なのでは!?
2021.3.9f1ではOnGUIで,
— Aseteyan (@Aseteyan) 2024年3月31日
EventType.KeyDownかつe.shiftがfalseのときに
if ((e.keyCode == KeyCode.Backslash && e.functionKey) || e.keyCode.ToString() == "226") {
// ろ
} else if (e.keyCode == KeyCode.Backslash) {
// ー
}
とすることで判定できてます!
ということで、この点を考慮するとOnGUI()で入力したキーを取得して、それを元にMakeSearchStrで生成したワードと比較して、正解なら正解の処理を間違いなら間違った時の処理を記述していきます。
シフトキー判定
まずは、Update関数でシフトキーの入力の有無を取得しておきます。
>|C#|
bool LeftShift = false, RightShift = false;
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
LeftShift = true;
}
if (Input.GetKeyUp(KeyCode.LeftShift))
{
LeftShift = false;
}
if (Input.GetKeyDown(KeyCode.RightShift))
{
RightShift = true;
}
if (Input.GetKeyUp(KeyCode.RightShift))
{
RightShift = false;
}
}
||<
シフト以外のすべてのキーはOnGUIで取得して、TypeEngine.Keypressに渡すとタイピングする文章が全文章打ち終わったかどうか判定して、打ち終わったらtrue、打ち終わってなければ、falseが返ってきます。
Event.current.type == EventType.KeyDownと書けば、キーが押された時のイベントを取得できますが、キーを話した時やキーコードが無効な時は弾くようにしています。
>|C#|
private void OnGUI()
{
Event e = Event.current;
if (e.type == EventType.KeyDown && e.type != EventType.KeyUp && e.keyCode != KeyCode.None
&& e.keyCode != KeyCode.LeftShift && e.keyCode != KeyCode.RightShift)
{
if (TypeEngine.KeyPress(InputText, e, LeftShift || RightShift))
{
ViewTextMesh.text = "終了";
InputTextMesh.text = "";
}
else
{
// 漢字文字を表示させる
ViewTextMesh.text = ViewText[TypeEngine.TextPos];
// すでに打ち終わった文字
string str1 = "<color=#808080>" + TypeEngine.NowInputText.Substring(0, TypeEngine.InputPos) + "</color>";
// これから打つ1文字
string str2 = string.Empty;
if (TypeEngine.TypeMissFlag)
{
str2 = "<color=#FF0000>" + TypeEngine.NowInputText.Substring(TypeEngine.InputPos, 1) + "</color>";
}
else
{
str2 = "<color=#0000FF>" + TypeEngine.NowInputText.Substring(TypeEngine.InputPos, 1) + "</color>";
}
// これから打つ1文字より後ろの最期の文字まで
string str3 = "<color=#FFFFFF>" + TypeEngine.NowInputText.Substring(TypeEngine.InputPos + 1) + "</color>";
// 入力文字に反映させる
InputTextMesh.text = str1 + str2 + str3;
}
}
}
||<
このKeyPressで渡しているInputTextは、先ほど説明したMakeInputTextやMakeSearchStrで生成する前の文章を渡しています。
理由は初期化以降のMakeInputTextやMakeSearchStrはKeyPress関数の内部で呼ばれているからです。
あまり内部と外部の両方で呼べるようにするのは、設計としてはよくないのですが、この2つの関数の使い方を理解するのが、タイピングゲームを作る上で非常に重要なことなので、説明のために外部からも呼べるようにpublicにしています。
TextMeshProへの表示
あらかじめUnityのエディタ側からアタッチして用意しておいたViewTextMeshとInputTextMeshにTypeEngine.KeyPressで処理した結果を参照して文字の色を変えたりして表示させます。
漢字文字を表示させるのは簡単で以下の一行で済みます。(TextPosは何行目の文章を売っているかの行数が入っている)
// 漢字文字を表示させる
ViewTextMesh.text = ViewText[TypeEngine.TextPos];
次にタイピングした文字を表示されるのですが、この時に打ち終わった色とこれから打つ色とそれより後ろの文字の色の3種類の色分けをします。
更にこれから打つ色を不正解の時には赤色に変更してミスしたのをわかりやすくします。
文字の色を一文字ずつ変えるには<color>タグを使ってRGBを16進数で指定して色を変えたい文字を挟むとその色に変更できます。
実行してみるとタイピングして正解の時は文字が進んで間違いの時は赤文字で表示されるのを確認できます。
また、先ほど説明した通り、ローマ字入力とかな入力の変更はStart関数内の
TypeEngine = new CRomaTypeEngine();
の一行を
TypeEngine = new CKanaTypeEngine();
としてやるだけで簡単に変更できます。
ここまででローマ字入力だけでなくかな入力にも対応しているタイピングゲームができました。ちなみにこの時点でローマ字入力の柔軟なうちわけにも対応しています。例えば「し」だったら「si」でも「shi」でもどっちでも打ち分けられます。
「っ」を「xtu」や「ltu」などにも対応しているので色々と打ち分けを試してみましょう!
まとめ
今回、学んだことをまとめると
1.ひらがなからタイピング文章を生成する(MakeInputStr)
2.ひらがなから入力判定するのに必要な文字を作る(MakeSearchStr)
3.キー入力から正誤判定をして正解なら文字を進めて間違いならその文字を赤色にして打ち終わったら終了を表示する
4.テキストメッシュに代入してUnity上に表示する
5.ローマ字入力とかな入力の切り替え
大きくわけて以上の5つになります。
この処理はUnityでタイピングゲームを作ろうと思ったら必要になるので、覚えておきましょう。
今回はTypeEngineの使い方のみ説明したので次回以降はTypeEngineのスクリプト内についても説明する予定です。
最終的にはUIで様々なパラメーターを変更してプレイヤーが好みの状態でタイピングゲームをプレイできるところまで作ります
それでは、次回のタイピングゲーム作り方講座で会いましょう!