5
5

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.

【Unity】柔軟な入力に対応したタイピングゲームの作り方(改良編)【C#】

Last updated at Posted at 2021-04-15

#はじめに
前回【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を区別する必要があります。
ちなみにこの機能は省略しても構いません。

TypingManager.cs
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() を大幅に改造します。

TypingManager.cs
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」などと入力することができます。

問題文を設定する際の注意点

このアルゴリズムを実装した場合、TypingManagerquestionsに格納するQuestionクラスのメンバであるromanには、最短で入力できて(例:「じゃ」は「zya」ではなく「ja」)、なるべく単純(「ふ」を「fu」ではなく「hu」)になるように設定します。

  • 「し」「ち」→「si」「ti」
  • 「じゃ」「じょ」→「ja」「jo」
  • 「ふぁ」「ふぉ」→「fa」「fo」
  • 「てぃ」「とぅ」→「thi」「twu」
  • 「うぃ」「うぉ」→「whi」「who」
  • 末尾の「ん」→「nn」
  • 「んあ」「んな」→「nna」「nnna」
5
5
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
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?