#はじめに
前回【Unity】柔軟な入力に対応したタイピングゲームの作り方(導入編)【C#】という記事の続きです。
現在のタイピングゲームの要となる「柔軟な入力(「つ」→「tsu」、「ふ」→「fu」)」に対応させます。
WindowsとMacで異なる入力方法
タイピングの入力方法については、以下のURLが参考になります。
Windows:https://www.cc.saga-u.ac.jp/system/CenterSystem/ime_romaji.htm
Mac:https://support.apple.com/ja-jp/guide/japanese-input-method/jpim10277/6.2.1/mac/10.15
よくよく確認すると、WindowsとMacでは入力方法が異なることがわかります。(例:Windowsでは「か」を「ca」と入力できるが、Macではできない)
なので、WindowsとMacを区別する必要があります。
ちなみにこの機能は省略しても構いません。
public class TypingManager : MonoBehaviour
{
private bool _isWindows; //追加する
private bool _isMac; //追加する
void Start()
{
// 以下を追加する
if(SystemInfo.operatingSystem.Contains("Windows"))
{
_isWindows = true;
}
if(SystemInfo.operatingSystem.Contains("Mac"))
{
_isMac = true;
}
// ここまで
}
}
柔軟な入力に対応させるアルゴリズム「前後比較法」
現在の日本語入力タイピングゲームにおいて、柔軟な入力に対応させるアルゴリズムはいくつか存在します。
今回は「前後比較法(私が勝手に名付けた方法です)」を使います。
「前後比較法」は、今入力すべき文字とその文字の前後を比較することによって「どのひらがなを入力するのか」を判断して、入力してもOKな文字を抽出するアルゴリズムです。
例えば「うちわ(utiwa)」を考えましょう。
まず最初の入力文字が「u」なので、入力するひらがなは「う」であることが判明します。「う」の別入力が「wu」であるので「w」を入力しても構わないことがわかります。
当然ですが、別入力を行いますとローマ字を改良する必要があります。今回の例で「w」を入力するとローマ字表記は「wutiwa」に変化します。
次の入力文字も「u」ですが、前の文字が「w」なので「う」の入力途中であることが判明します。よって今回は「w」を入力することができません。
そして「u」の入力後、入力文字が「t」になります。この入力文字の前が母音で、後の文字が「i」なので「ち」を入力することが判明します。同様に「ち」の別入力は「chi」なので「c」を入力してもOKとなります。
もちろん「c」を入力した後は「wuchiwa」に変形する必要があります。
こんな感じでほかの文字に対応させることができます。
InputKey()
を大幅に改造しよう!
上記のアルゴリズムを用いて、InputKey()
を大幅に改造します。
int InputKey(char inputChar)
{
char prevChar3 = _romanIndex >= 3 ? _roman[_romanIndex - 3] : '\0';
char prevChar2 = _romanIndex >= 2 ? _roman[_romanIndex - 2] : '\0';
char prevChar = _romanIndex >= 1 ? _roman[_romanIndex - 1] : '\0';
char currentChar = _roman[_romanIndex];
char nextChar = _roman[_romanIndex + 1];
char nextChar2 = nextChar == '@' ? '@' : _roman[_romanIndex + 2];
if (inputChar == '\0')
{
return 0;
}
if (inputChar == currentChar)
{
return 1;
}
//「い」の柔軟な入力(Windowsのみ)
if (_isWindows && inputChar == 'y' && currentChar == 'i' &&
(prevChar == '\0' || prevChar == 'a' || prevChar == 'i' || prevChar == 'u' || prevChar == 'e' ||
prevChar == 'o'))
{
_roman.Insert(_romanIndex, 'y');
return 1;
}
if (_isWindows && inputChar == 'y' && currentChar == 'i' && prevChar == 'n' && prevChar2 == 'n' &&
prevChar3 != 'n')
{
_roman.Insert(_romanIndex, 'y');
return 1;
}
if (_isWindows && inputChar == 'y' && currentChar == 'i' && prevChar == 'n' && prevChar2 == 'x')
{
_roman.Insert(_romanIndex, 'y');
return 1;
}
//「う」の柔軟な入力(「whu」はWindowsのみ)
if (inputChar == 'w' && currentChar == 'u' && (prevChar == '\0' || prevChar == 'a' || prevChar == 'i' ||
prevChar == 'u' || prevChar == 'e' || prevChar == 'o'))
{
_roman.Insert(_romanIndex, 'w');
return 1;
}
if (inputChar == 'w' && currentChar == 'u' && prevChar == 'n' && prevChar2 == 'n' && prevChar3 != 'n')
{
_roman.Insert(_romanIndex, 'w');
return 1;
}
if (inputChar == 'w' && currentChar == 'u' && prevChar == 'n' && prevChar2 == 'x')
{
_roman.Insert(_romanIndex, 'w');
return 1;
}
if (_isWindows && inputChar == 'h' && prevChar2 != 't' && prevChar2 != 'd' && prevChar == 'w' &&
currentChar == 'u')
{
_roman.Insert(_romanIndex, 'h');
return 1;
}
//「か」「く」「こ」の柔軟な入力(Windowsのみ)
if (_isWindows && inputChar == 'c' && prevChar != 'k' &&
currentChar == 'k' && (nextChar == 'a' || nextChar == 'u' || nextChar == 'o'))
{
_roman[_romanIndex] = 'c';
return 1;
}
//「く」の柔軟な入力(Windowsのみ)
if (_isWindows && inputChar == 'q' && prevChar != 'k' && currentChar == 'k' && nextChar == 'u')
{
_roman[_romanIndex] = 'q';
return 1;
}
//「し」の柔軟な入力
if (inputChar == 'h' && prevChar == 's' && currentChar == 'i')
{
_roman.Insert(_romanIndex, 'h');
return 1;
}
//「じ」の柔軟な入力
if (inputChar == 'j' && currentChar == 'z' && nextChar == 'i')
{
_roman[_romanIndex] = 'j';
return 1;
}
//「しゃ」「しゅ」「しぇ」「しょ」の柔軟な入力
if (inputChar == 'h' && prevChar == 's' && currentChar == 'y')
{
_roman[_romanIndex] = 'h';
return 1;
}
//「じゃ」「じゅ」「じぇ」「じょ」の柔軟な入力
if (inputChar == 'z' && prevChar != 'j' && currentChar == 'j' &&
(nextChar == 'a' || nextChar == 'u' || nextChar == 'e' || nextChar == 'o'))
{
_roman[_romanIndex] = 'z';
_roman.Insert(_romanIndex + 1, 'y');
return 1;
}
if (inputChar == 'y' && prevChar == 'j' &&
(currentChar == 'a' || currentChar == 'u' || currentChar == 'e' || currentChar == 'o'))
{
_roman.Insert(_romanIndex, 'y');
return 1;
}
//「し」「せ」の柔軟な入力(Windowsのみ)
if (_isWindows && inputChar == 'c' && prevChar != 's' && currentChar == 's' &&
(nextChar == 'i' || nextChar == 'e'))
{
_roman[_romanIndex] = 'c';
return 1;
}
//「ち」の柔軟な入力
if (inputChar == 'c' && prevChar != 't' && currentChar == 't' && nextChar == 'i')
{
_roman[_romanIndex] = 'c';
_roman.Insert(_romanIndex + 1, 'h');
return 1;
}
//「ちゃ」「ちゅ」「ちぇ」「ちょ」の柔軟な入力
if (inputChar == 'c' && prevChar != 't' && currentChar == 't' && nextChar == 'y')
{
_roman[_romanIndex] = 'c';
return 1;
}
//「cya」=>「cha」
if (inputChar == 'h' && prevChar == 'c' && currentChar == 'y')
{
_roman[_romanIndex] = 'h';
return 1;
}
//「つ」の柔軟な入力
if (inputChar == 's' && prevChar == 't' && currentChar == 'u')
{
_roman.Insert(_romanIndex, 's');
return 1;
}
//「つぁ」「つぃ」「つぇ」「つぉ」の柔軟な入力
if (inputChar == 'u' && prevChar == 't' && currentChar == 's' &&
(nextChar == 'a' || nextChar == 'i' || nextChar == 'e' || nextChar == 'o'))
{
_roman[_romanIndex] = 'u';
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
if (inputChar == 'u' && prevChar2 == 't' && prevChar == 's' &&
(currentChar == 'a' || currentChar == 'i' || currentChar == 'e' || currentChar == 'o'))
{
_roman.Insert(_romanIndex, 'u');
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
//「てぃ」の柔軟な入力
if (inputChar == 'e' && prevChar == 't' && currentChar == 'h' && nextChar == 'i')
{
_roman[_romanIndex] = 'e';
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
//「でぃ」の柔軟な入力
if (inputChar == 'e' && prevChar == 'd' && currentChar == 'h' && nextChar == 'i')
{
_roman[_romanIndex] = 'e';
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
//「でゅ」の柔軟な入力
if (inputChar == 'e' && prevChar == 'd' && currentChar == 'h' && nextChar == 'u')
{
_roman[_romanIndex] = 'e';
_roman.Insert(_romanIndex + 1, 'x');
_roman.Insert(_romanIndex + 2, 'y');
return 1;
}
//「とぅ」の柔軟な入力
if (inputChar == 'o' && prevChar == 't' && currentChar == 'w' && nextChar == 'u')
{
_roman[_romanIndex] = 'o';
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
//「どぅ」の柔軟な入力
if (inputChar == 'o' && prevChar == 'd' && currentChar == 'w' && nextChar == 'u')
{
_roman[_romanIndex] = 'o';
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
//「ふ」の柔軟な入力
if (inputChar == 'f' && currentChar == 'h' && nextChar == 'u')
{
_roman[_romanIndex] = 'f';
return 1;
}
//「ふぁ」「ふぃ」「ふぇ」「ふぉ」の柔軟な入力(一部Macのみ)
if (inputChar == 'w' && prevChar == 'f' &&
(currentChar == 'a' || currentChar == 'i' || currentChar == 'e' || currentChar == 'o'))
{
_roman.Insert(_romanIndex, 'w');
return 1;
}
if (inputChar == 'y' && prevChar == 'f' && (currentChar == 'i' || currentChar == 'e'))
{
_roman.Insert(_romanIndex, 'y');
return 1;
}
if (inputChar == 'h' && prevChar != 'f' && currentChar == 'f' &&
(nextChar == 'a' || nextChar == 'i' || nextChar == 'e' || nextChar == 'o'))
{
if (_isMac)
{
_roman[_romanIndex] = 'h';
_roman.Insert(_romanIndex + 1, 'w');
}
else
{
_roman[_romanIndex] = 'h';
_roman.Insert(_romanIndex + 1, 'u');
_roman.Insert(_romanIndex + 2, 'x');
}
return 1;
}
if (inputChar == 'u' && prevChar == 'f' &&
(currentChar == 'a' || currentChar == 'i' || currentChar == 'e' || currentChar == 'o'))
{
_roman.Insert(_romanIndex, 'u');
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
if (_isMac && inputChar == 'u' && prevChar == 'h' && currentChar == 'w' &&
(nextChar == 'a' || nextChar == 'i' || nextChar == 'e' || nextChar == 'o'))
{
_roman[_romanIndex] = 'u';
_roman.Insert(_romanIndex + 1, 'x');
return 1;
}
//「ん」の柔軟な入力(「n'」には未対応)
if (inputChar == 'n' && prevChar2 != 'n' && prevChar == 'n' && currentChar != 'a' && currentChar != 'i' &&
currentChar != 'u' && currentChar != 'e' && currentChar != 'o' && currentChar != 'y')
{
_roman.Insert(_romanIndex, 'n');
return 1;
}
if (inputChar == 'x' && prevChar != 'n' && currentChar == 'n' && nextChar != 'a' && nextChar != 'i' &&
nextChar != 'u' && nextChar != 'e' && nextChar != 'o' && nextChar != 'y')
{
if (nextChar == 'n')
{
_roman[_romanIndex] = 'x';
}
else
{
_roman.Insert(_romanIndex, 'x');
}
return 1;
}
//「うぃ」「うぇ」「うぉ」を分解する
if (inputChar == 'u' && currentChar == 'w' && nextChar == 'h' (nextChar2 == 'a' || nextChar2 == 'i' || nextChar2 == 'e' || nextChar2 == 'o' ))
{
_roman[_romanIndex] = 'u';
_roman[_romanIndex] = 'x';
}
//「きゃ」「にゃ」などを分解する
if (inputChar == 'i' && currentChar == 'y' &&
(prevChar == 'k' || prevChar == 's' || prevChar == 't' || prevChar == 'n' || prevChar == 'h' ||
prevChar == 'm' || prevChar == 'r' || prevChar == 'g' || prevChar == 'z' || prevChar == 'd' ||
prevChar == 'b' || prevChar == 'p') &&
(nextChar == 'a' || nextChar == 'u' || nextChar == 'e' || nextChar == 'o'))
{
if (nextChar == 'e')
{
_roman[_romanIndex] = 'i';
_roman.Insert(_romanIndex + 1, 'x');
}
else
{
_roman.Insert(_romanIndex, 'i');
_roman.Insert(_romanIndex + 1, 'x');
}
return 1;
}
//「しゃ」「ちゃ」などを分解する
if (inputChar == 'i' &&
(currentChar == 'a' || currentChar == 'u' || currentChar == 'e' || currentChar == 'o') &&
(prevChar2 == 's' || prevChar2 == 'c') && prevChar == 'h')
{
if (nextChar == 'e')
{
_roman.Insert(_romanIndex, 'i');
_roman.Insert(_romanIndex + 1, 'x');
}
else
{
_roman.Insert(_romanIndex, 'i');
_roman.Insert(_romanIndex + 1, 'x');
_roman.Insert(_romanIndex + 2, 'y');
}
return 1;
}
//「しゃ」を「c」で分解する(Windows限定)
if (_isWindows && inputChar == 'c' && currentChar == 's' && prevChar != 's' && nextChar == 'y' &&
(nextChar2 == 'a' || nextChar2 == 'u' || nextChar2 == 'e' || nextChar2 == 'o'))
{
if (nextChar2 == 'e')
{
_roman[_romanIndex] = 'c';
_roman[_romanIndex + 1] = 'i';
_roman.Insert(_romanIndex + 1, 'x');
}
else
{
_roman[_romanIndex] = 'c';
_roman.Insert(_romanIndex + 1, 'i');
_roman.Insert(_romanIndex + 2, 'x');
}
return 1;
}
//「っ」の柔軟な入力
if ((inputChar == 'x' || inputChar == 'l') &&
(currentChar == 'k' && nextChar == 'k' || currentChar == 's' && nextChar == 's' ||
currentChar == 't' && nextChar == 't' || currentChar == 'g' && nextChar == 'g' ||
currentChar == 'z' && nextChar == 'z' || currentChar == 'j' && nextChar == 'j' ||
currentChar == 'd' && nextChar == 'd' || currentChar == 'b' && nextChar == 'b' ||
currentChar == 'p' && nextChar == 'p'))
{
_roman[_romanIndex] = inputChar;
_roman.Insert(_romanIndex + 1, 't');
_roman.Insert(_romanIndex + 2, 'u');
return 1;
}
//「っか」「っく」「っこ」の柔軟な入力(Windows限定)
if (_isWindows && inputChar == 'c' && currentChar == 'k' && nextChar == 'k' &&
(nextChar2 == 'a' || nextChar2 == 'u' || nextChar2 == 'o'))
{
_roman[_romanIndex] = 'c';
_roman[_romanIndex + 1] = 'c';
return 1;
}
//「っく」の柔軟な入力(Windows限定)
if (_isWindows && inputChar == 'q' && currentChar == 'k' && nextChar == 'k' && nextChar2 == 'u')
{
_roman[_romanIndex] = 'q';
_roman[_romanIndex + 1] = 'q';
return 1;
}
//「っし」「っせ」の柔軟な入力(Windows限定)
if (_isWindows && inputChar == 'c' && currentChar == 's' && nextChar == 's' &&
(nextChar2 == 'i' || nextChar2 == 'e'))
{
_roman[_romanIndex] = 'c';
_roman[_romanIndex + 1] = 'c';
return 1;
}
//「っちゃ」「っちゅ」「っちぇ」「っちょ」の柔軟な入力
if (inputChar == 'c' && currentChar == 't' && nextChar == 't' && nextChar2 == 'y')
{
_roman[_romanIndex] = 'c';
_roman[_romanIndex + 1] = 'c';
return 1;
}
//「っち」の柔軟な入力
if (inputChar == 'c' && currentChar == 't' && nextChar == 't' && nextChar2 == 'i')
{
_roman[_romanIndex] = 'c';
_roman[_romanIndex + 1] = 'c';
_roman.Insert(_romanIndex + 2, 'h');
return 1;
}
//「l」と「x」の完全互換性
if (inputChar == 'x' && currentChar == 'l')
{
_roman[_romanIndex] = 'x';
return 1;
}
if (inputChar == 'l' && currentChar == 'x')
{
_roman[_romanIndex] = 'l';
return 1;
}
return 2;
}
こうすることによって、「し」を「shi」、んを「xn」などと入力することができます。
問題文を設定する際の注意点
このアルゴリズムを実装した場合、TypingManager
のquestions
に格納するQuestion
クラスのメンバであるroman
には、最短で入力できて(例:「じゃ」は「zya」ではなく「ja」)、なるべく単純(「ふ」を「fu」ではなく「hu」)になるように設定します。
- 「し」「ち」→「si」「ti」
- 「じゃ」「じょ」→「ja」「jo」
- 「ふぁ」「ふぉ」→「fa」「fo」
- 「てぃ」「とぅ」→「thi」「twu」
- 「うぃ」「うぉ」→「whi」「who」
- 末尾の「ん」→「nn」
- 「んあ」「んな」→「nna」「nnna」