見出し画像

柔軟に入力できる日本語タイピングゲームを本格的に作ってみた。

 この記事は、Unityゲーム開発者ギルド Advent Calendar 2023、12日目の記事です。


はじめに

 ローマ字入力タイピングゲームの多くは「し」を「shi」「ふ」を「fu」と入力できるなど、柔軟に入力することができます。率直に言いますと、このプログラムを組むのは結構むずかしかったです。この記事はアドカレ記事で技術解説はほとんどありませんが、ローマ字入力タイピングゲームを作るのに苦戦している方の手助けになれば幸いかと思います。

作ったもの

unityroomにて体験できます。
https://unityroom.com/games/adventartyping

使用技術

  • Unity 2022.3.14f

  • Universal Render Pipeline(URP)

  • TextMeshPro

  • UniRx

  • VContainer

設計思想

 今回は「GameManager.cs」にまとめてただ動かすだけではなく、MV(R)P(Model-View-(Reactive)Presenter)パターンを用いて開発します。また独自なりに入力処理を行うProcessという概念をプラスした「MV(R)P+P(Model-View-(Reactive)Presenter+Process)」という設計思想を採用しました。

私なりの、各々のスクリプトの役割ルールは以下の通りです。

  • ViewだけがMonoBehaviourを継承できる

  • ViewとModelは別のスクリプトに依存しない

  • PresenterはModelのReactivePropertyをSubscribeし、Viewに値を渡す

  • Presenterだけが、ReactivePropertyをSubscribleできる

  • Processだけが、ModelとViewのメソッドを叩ける

  • Processだけが、ViewのObservableをSubscribeできる

ソースコード

アーキテクチャを構築するのに必要となったEnum、Interface、Structを始めに紹介します。

GameStateName.cs

namespace Enums
{
    public enum GameStateName
    {
        None,
        Typing
    }
}

OperatingSystemName.cs

namespace Enums
{
    public enum OperatingSystemName
    {
        None,
        Windows,
        Mac
    }
}

TypeResult.cs

namespace Enums
{
    public enum TypeResult
    {
        Correct,
        Incorrect,
        Finished
    }
}

ITypingTextDifference.cs

後述するTypingTextView.csないしTypingTextViewPresenter.csのための空のInterfaceです。

namespace Interfaces
{
    public interface ITypingTextDifference
    {
        
    }
}

TypingText.cs

using System;

namespace Structs
{
    [Serializable]
    public struct TypingText
    {
        public string title; 
        public string hiragana;
    }
}

TypingTextDifferenceCharacters.cs

using System.Collections.Generic;
using Interfaces;

namespace Structs
{
    public struct TypingTextDifferenceCharacters: ITypingTextDifference
    {
        public IReadOnlyList<char> characters;
    }
}

TypingTextDifferenceCharactersIndex.cs

using Interfaces;

namespace Structs
{
    public struct TypingTextDifferenceCharactersIndex: ITypingTextDifference
    {
        public int charactersIndex;
    }
}

TypingTextDifferenceTitle.cs

using Interfaces;

namespace Structs
{
    public struct TypingTextDifferenceTitle: ITypingTextDifference
    {
        public string title;
    }
}

必要なEnum、Interface、Structの記述は以上です。


 Modelから紹介します。Modelは主にゲームロジックを処理するPure C#なスクリプトです。また、Modelに記載される変数は全てReactivePropertyとしました。

ConvertHiraganaToRomanModel.cs

「ひらがな文字列」から「入力用のローマ字」に変換するModelスクリプトです。1メソッドで1200行にもなりました。

using System.Collections.Generic;

namespace Models
{
    public class ConvertHiraganaToRomanModel
    {
        public IEnumerable<char> ConvertHiraganaToRoman(IReadOnlyList<char> hiragana)
        {
            var roman = new List<char>();

            for (var i = 0; i < hiragana.Count; i++) 
            {
                var nextHiragana = i < hiragana.Count - 1 ? hiragana[i + 1] : '@';
                switch (hiragana[i])
                {
                    case 'あ':
                    {
                        roman.Add('a');
                        break;
                    }
                    case 'い':
                    {
                        roman.Add('i');
                        break;
                    }
                    case 'う':
                    {
                        switch (nextHiragana)
                        {
                            case 'ぁ':
                            {
                                roman.Add('w');
                                roman.Add('h');
                                roman.Add('a');
                                break;
                            }
                            case 'ぃ':
                            {
                                roman.Add('w');
                                roman.Add('h');
                                roman.Add('i');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('w');
                                roman.Add('h');
                                roman.Add('e');
                                break;
                            }
                            case 'ぉ':
                            {
                                roman.Add('w');
                                roman.Add('h');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('u');
                                break;
                            }
                        }
                        break;
                    }
                    case 'え':
                    {
                        roman.Add('e');
                        break;
                    }
                    case 'お':
                    {
                        roman.Add('o');
                        break;
                    }
                    case 'か':
                    {
                        roman.Add('k');
                        roman.Add('a');
                        break;
                    }
                    case 'き':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('k');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('k');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('k');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('k');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('k');
                                roman.Add('i');
                                break;
                            }
                        }

                        break;
                    }
                    case 'く':
                    {
                        roman.Add('k');
                        roman.Add('u');
                        break;
                    }
                    case 'け':
                    {
                        roman.Add('k');
                        roman.Add('e');
                        break;
                    }
                    case 'こ':
                    {
                        roman.Add('k');
                        roman.Add('o');
                        break;
                    }
                    case 'さ':
                    {
                        roman.Add('s');
                        roman.Add('a');
                        break;
                    }
                    case 'し':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('s');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('s');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('s');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('s');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('s');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'す':
                    {
                        roman.Add('s');
                        roman.Add('u');
                        break;
                    }
                    case 'せ':
                    {
                        roman.Add('s');
                        roman.Add('e');
                        break;
                    }
                    case 'そ':
                    {
                        roman.Add('s');
                        roman.Add('o');
                        break;
                    }
                    case 'た':
                    {
                        roman.Add('t');
                        roman.Add('a');
                        break;
                    }
                    case 'ち':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('t');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('t');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('t');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('t');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('t');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'つ':
                    {
                        switch (nextHiragana)
                        {
                            case 'ぁ':
                            {
                                roman.Add('t');
                                roman.Add('s');
                                roman.Add('a');
                                break;
                            }
                            case 'ぃ':
                            {
                                roman.Add('t');
                                roman.Add('s');
                                roman.Add('i');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('t');
                                roman.Add('s');
                                roman.Add('e');
                                break;
                            }
                            case 'ぉ':
                            {
                                roman.Add('t');
                                roman.Add('s');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('t');
                                roman.Add('u');
                                break;
                            }
                        }

                        break;
                    }
                    case 'て':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('t');
                                roman.Add('h');
                                roman.Add('a');
                                break;
                            }
                            case 'ぃ':
                            {
                                roman.Add('t');
                                roman.Add('h');
                                roman.Add('i');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('t');
                                roman.Add('h');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('t');
                                roman.Add('h');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('t');
                                roman.Add('h');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('t');
                                roman.Add('e');
                                break;
                            }
                        }

                        break;
                    }
                    case 'と':
                    {
                        switch (nextHiragana)
                        {
                            case 'ぁ':
                            {
                                roman.Add('t');
                                roman.Add('w');
                                roman.Add('a');
                                break;
                            }
                            case 'ぃ':
                            {
                                roman.Add('t');
                                roman.Add('w');
                                roman.Add('i');
                                break;
                            }
                            case 'ぅ':
                            {
                                roman.Add('t');
                                roman.Add('w');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('t');
                                roman.Add('w');
                                roman.Add('e');
                                break;
                            }
                            case 'ぉ':
                            {
                                roman.Add('t');
                                roman.Add('w');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('t');
                                roman.Add('o');
                                break;
                            }
                        }

                        break;
                    }
                    case 'な':
                    {
                        roman.Add('n');
                        roman.Add('a');
                        break;
                    }
                    case 'に':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('n');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('n');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('n');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('n');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('n');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ぬ':
                    {
                        roman.Add('n');
                        roman.Add('u');
                        break;
                    }
                    case 'ね':
                    {
                        roman.Add('n');
                        roman.Add('e');
                        break;
                    }
                    case 'の':
                    {
                        roman.Add('n');
                        roman.Add('o');
                        break;
                    }
                    case 'は':
                    {
                        roman.Add('h');
                        roman.Add('a');
                        break;
                    }
                    case 'ひ':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('h');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('h');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('h');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('h');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('n');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ふ':
                    {
                        switch (nextHiragana)
                        {
                            case 'ぁ':
                            {
                                roman.Add('f');
                                roman.Add('a');
                                break;
                            }
                            case 'ぃ':
                            {
                                roman.Add('f');
                                roman.Add('i');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('f');
                                roman.Add('e');
                                break;
                            }
                            case 'ぉ':
                            {
                                roman.Add('f');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('h');
                                roman.Add('u');
                                break;
                            }
                        }
                        break;
                    }
                    case 'へ':
                    {
                        roman.Add('h');
                        roman.Add('e');
                        break;
                    }
                    case 'ほ':
                    {
                        roman.Add('h');
                        roman.Add('o');
                        break;
                    }
                    case 'ま':
                    {
                        roman.Add('m');
                        roman.Add('a');
                        break;
                    }
                    case 'み':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('m');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('m');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('m');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('m');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('m');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'む':
                    {
                        roman.Add('m');
                        roman.Add('u');
                        break;
                    }
                    case 'め':
                    {
                        roman.Add('m');
                        roman.Add('e');
                        break;
                    }
                    case 'も':
                    {
                        roman.Add('m');
                        roman.Add('o');
                        break;
                    }
                    case 'や':
                    {
                        roman.Add('y');
                        roman.Add('a');
                        break;
                    }
                    case 'ゆ':
                    {
                        roman.Add('y');
                        roman.Add('u');
                        break;
                    }
                    case 'よ':
                    {
                        roman.Add('y');
                        roman.Add('o');
                        break;
                    }
                    case 'ら':
                    {
                        roman.Add('r');
                        roman.Add('a');
                        break;
                    }
                    case 'り':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('r');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('r');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('r');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('r');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('r');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'る':
                    {
                        roman.Add('r');
                        roman.Add('u');
                        break;
                    }
                    case 'れ':
                    {
                        roman.Add('r');
                        roman.Add('e');
                        break;
                    }
                    case 'ろ':
                    {
                        roman.Add('r');
                        roman.Add('o');
                        break;
                    }
                    case 'わ':
                    {
                        roman.Add('w');
                        roman.Add('a');
                        break;
                    }
                    case 'を':
                    {
                        roman.Add('w');
                        roman.Add('o');
                        break;
                    }
                    case 'ん':
                    {
                        var hashSet = new HashSet<char>
                        {
                            'あ', 'い', 'う', 'え', 'お', 'な', 'に', 'ぬ', 'ね', 'の', 'や', 'ゆ', 'よ', 'ん', '@'
                        };

                        if (hashSet.Contains(nextHiragana))
                        {
                            roman.Add('n');
                            roman.Add('n');
                        }
                        else
                        {
                            roman.Add('n');
                        }
                        break;
                    }
                    case 'が':
                    {
                        roman.Add('g');
                        roman.Add('a');
                        break;
                    }
                    case 'ぎ':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('g');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('g');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('g');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('g');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('g');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ぐ':
                    {
                        roman.Add('g');
                        roman.Add('u');
                        break;
                    }
                    case 'げ':
                    {
                        roman.Add('g');
                        roman.Add('e');
                        break;
                    }
                    case 'ご':
                    {
                        roman.Add('g');
                        roman.Add('o');
                        break;
                    }
                    case 'ざ':
                    {
                        roman.Add('z');
                        roman.Add('a');
                        break;
                    }
                    case 'じ':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('z');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('z');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('z');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('z');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('z');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ず':
                    {
                        roman.Add('z');
                        roman.Add('u');
                        break;
                    }
                    case 'ぜ':
                    {
                        roman.Add('z');
                        roman.Add('e');
                        break;
                    }
                    case 'ぞ':
                    {
                        roman.Add('z');
                        roman.Add('o');
                        break;
                    }
                    case 'だ':
                    {
                        roman.Add('d');
                        roman.Add('a');
                        break;
                    }
                    case 'ぢ':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('d');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('d');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('d');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('d');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('d');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'づ':
                    {
                        roman.Add('d');
                        roman.Add('u');
                        break;
                    }
                    case 'で':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('d');
                                roman.Add('h');
                                roman.Add('a');
                                break;
                            }
                            case 'ぃ':
                            {
                                roman.Add('d');
                                roman.Add('h');
                                roman.Add('i');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('d');
                                roman.Add('h');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('d');
                                roman.Add('h');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('d');
                                roman.Add('h');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('d');
                                roman.Add('e');
                                break;
                            }
                        }

                        break;
                    }
                    case 'ど':
                    {
                        switch (nextHiragana)
                        {
                            case 'ぁ':
                            {
                                roman.Add('d');
                                roman.Add('w');
                                roman.Add('a');
                                break;
                            }
                            case 'ぃ':
                            {
                                roman.Add('d');
                                roman.Add('w');
                                roman.Add('i');
                                break;
                            }
                            case 'ぅ':
                            {
                                roman.Add('d');
                                roman.Add('w');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('d');
                                roman.Add('w');
                                roman.Add('e');
                                break;
                            }
                            case 'ぉ':
                            {
                                roman.Add('d');
                                roman.Add('w');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('d');
                                roman.Add('o');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ば':
                    {
                        roman.Add('b');
                        roman.Add('a');
                        break;
                    }
                    case 'び':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('b');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('b');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('b');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('b');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('b');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ぶ':
                    {
                        roman.Add('b');
                        roman.Add('u');
                        break;
                    }
                    case 'べ':
                    {
                        roman.Add('b');
                        roman.Add('e');
                        break;
                    }
                    case 'ぼ':
                    {
                        roman.Add('b');
                        roman.Add('o');
                        break;
                    }
                    case 'ぱ':
                    {
                        roman.Add('p');
                        roman.Add('a');
                        break;
                    }
                    case 'ぴ':
                    {
                        switch (nextHiragana)
                        {
                            case 'ゃ':
                            {
                                roman.Add('p');
                                roman.Add('y');
                                roman.Add('a');
                                break;
                            }
                            case 'ゅ':
                            {
                                roman.Add('p');
                                roman.Add('y');
                                roman.Add('u');
                                break;
                            }
                            case 'ぇ':
                            {
                                roman.Add('p');
                                roman.Add('y');
                                roman.Add('e');
                                break;
                            }
                            case 'ょ':
                            {
                                roman.Add('p');
                                roman.Add('y');
                                roman.Add('o');
                                break;
                            }
                            default:
                            {
                                roman.Add('p');
                                roman.Add('i');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ぷ':
                    {
                        roman.Add('p');
                        roman.Add('u');
                        break;
                    }
                    case 'ぺ':
                    {
                        roman.Add('p');
                        roman.Add('e');
                        break;
                    }
                    case 'ぽ':
                    {
                        roman.Add('p');
                        roman.Add('o');
                        break;
                    }
                    case 'っ':
                    {
                        switch (nextHiragana)
                        {
                            case 'か' or 'き' or 'く' or 'け' or 'こ':
                            {
                                roman.Add('k');
                                break;
                            }
                            case 'さ' or 'し' or 'す' or 'せ' or 'そ':
                            {
                                roman.Add('s');
                                break;
                            }
                            case 'た' or 'ち' or 'つ' or 'て' or 'と':
                            {
                                roman.Add('t');
                                break;
                            }
                            case 'は' or 'ひ' or 'ふ' or 'へ' or 'ほ':
                            {
                                roman.Add('h');
                                break;
                            }
                            case 'ま' or 'み' or 'む' or 'め' or 'も':
                            {
                                roman.Add('m');
                                break;
                            }
                            case 'や' or 'ゆ' or 'よ':
                            {
                                roman.Add('y');
                                break;
                            }
                            case 'が' or 'ぎ' or 'ぐ' or 'げ' or 'ご':
                            {
                                roman.Add('g');
                                break;
                            }
                            case 'ざ' or 'じ' or 'ず' or 'ぜ' or 'ぞ':
                            {
                                roman.Add('z');
                                break;
                            }
                            case 'だ' or 'ぢ' or 'づ' or 'で' or 'ど':
                            {
                                roman.Add('d');
                                break;
                            }
                            case 'ば' or 'び' or 'ぶ' or 'べ' or 'ぼ':
                            {
                                roman.Add('b');
                                break;
                            }
                            case 'ぱ' or 'ぴ' or 'ぷ' or 'ぺ' or 'ぽ':
                            {
                                roman.Add('p');
                                break;
                            }
                            default:
                            {
                                roman.Add('x');
                                roman.Add('t');
                                roman.Add('u');
                                break;
                            }
                        }
                        break;
                    }
                    case 'ー':
                    {
                        roman.Add('-');
                        break;
                    }
                    case '、':
                    {
                        roman.Add(',');
                        break;
                    }
                    case '。':
                    {
                        roman.Add('.');
                        break;
                    }
                }
            }

            return roman;
        }
    }
}

CurrentTypingTextModel.cs

現在入力中の問題文を処理するModelスクリプトです。これも長い・・・。

using System;
using System.Collections.Generic;
using Enums;
using UniRx;

namespace Models
{
    public class CurrentTypingTextModel
    {
        private readonly ReactiveProperty<string> _reactivePropertyTitle = new("");
        public IObservable<string> OnChangedTitle => _reactivePropertyTitle;
        private readonly ReactiveProperty<IReadOnlyList<char>> _reactivePropertyCharacters = new(new List<char>());
        public IObservable<IReadOnlyList<char>> OnChangedCharacters => _reactivePropertyCharacters;
        private readonly ReactiveProperty<int> _reactivePropertyCharactersIndex = new(0);
        public IObservable<int> OnChangedCharactersIndex => _reactivePropertyCharactersIndex;
        private readonly ReactiveProperty<OperatingSystemName>
            _reactivePropertyOperatingSystemName = new(OperatingSystemName.None);

        public void SetOperatingSystemName(OperatingSystemName operatingSystemName)
        {
            _reactivePropertyOperatingSystemName.Value = operatingSystemName;
        }

        public void SetTitle(string title)
        {
            _reactivePropertyTitle.Value = title;
        }

        public void SetCharacters(IEnumerable<char> characters)
        {
            _reactivePropertyCharacters.Value = new List<char>(characters);
        }

        public void ResetCharactersIndex()
        {
            _reactivePropertyCharactersIndex.Value = 0;
        }
        
        public TypeResult TypeCharacter(char inputCharacter)
        {
            var currentCharactersIndex = _reactivePropertyCharactersIndex.Value;
            var currentCharacters = new List<char>(_reactivePropertyCharacters.Value);
            
            var prevChar3 = currentCharactersIndex >= 3 ? currentCharacters[currentCharactersIndex - 3] : '\0';
            var prevChar2 = currentCharactersIndex >= 2 ? currentCharacters[currentCharactersIndex - 2] : '\0';
            var prevChar = currentCharactersIndex >= 1 ? currentCharacters[currentCharactersIndex - 1] : '\0';
            var currentChar = currentCharacters[currentCharactersIndex];
            var nextChar = currentCharactersIndex < currentCharacters.Count - 1
                ? currentCharacters[currentCharactersIndex + 1]
                : '\0';
            var nextChar2 = currentCharactersIndex < currentCharacters.Count - 2
                ? currentCharacters[currentCharactersIndex + 2]
                : '\0';

            var newCharacters = new List<char>(currentCharacters);

            var isCorrect = false;

            if (inputCharacter == newCharacters[currentCharactersIndex])
            {
                isCorrect = true;
            }

            //「い」の入力判定(Windowsのみ)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter is 'y' &&
                     currentChar is 'i' &&
                     prevChar is '\0' or 'a' or 'i' or 'u' or 'e' or 'o')
            {
                newCharacters.Insert(currentCharactersIndex, 'y');
                isCorrect = true;
            }

            //「う」の入力判定(「whu」はWindowsのみ)
            else if (inputCharacter is 'w' &&
                     currentChar is 'u' &&
                     prevChar is '\0' or 'a' or 'i' or 'u' or 'e' or 'o')
            {
                newCharacters.Insert(currentCharactersIndex, 'w');
                isCorrect = true;
            }

            else if (inputCharacter == 'w' &&
                     currentChar == 'u' &&
                     prevChar == 'n' &&
                     prevChar2 == 'n' &&
                     prevChar3 != 'n')
            {
                newCharacters.Insert(currentCharactersIndex, 'w');
                isCorrect = true;
            }

            else if (inputCharacter == 'w' &&
                     currentChar == 'u' &&
                     prevChar == 'n' &&
                     prevChar2 == 'x')
            {
                newCharacters.Insert(currentCharactersIndex, 'w');
                isCorrect = true;
            }

            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'h' &&
                     currentChar == 'u' &&
                     prevChar2 != 't' && prevChar2 != 'd' &&
                     prevChar == 'w')
            {
                newCharacters.Insert(currentCharactersIndex, 'h');
                isCorrect = true;
            }

            //「か」「く」「こ」の柔軟な入力(Windowsのみ)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'c' &&
                     prevChar != 'k' &&
                     currentChar == 'k' && nextChar is 'a' or 'u' or 'o')
            {
                newCharacters[currentCharactersIndex] = 'c';
                isCorrect = true;
            }

            //「く」の柔軟な入力(Windowsのみ)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'q' &&
                     prevChar != 'k' &&
                     currentChar == 'k' && nextChar == 'u')
            {
                newCharacters[currentCharactersIndex] = 'q';
                isCorrect = true;
            }

            //「し」の柔軟な入力
            else if (inputCharacter == 'h' && prevChar == 's' && currentChar == 'i')
            {
                newCharacters.Insert(currentCharactersIndex, 'h');
                isCorrect = true;
            }

            //「じ」の柔軟な入力
            else if (inputCharacter == 'j' && currentChar == 'z' && nextChar == 'i')
            {
                newCharacters[currentCharactersIndex] = 'j';
                isCorrect = true;
            }

            //「しゃ」「しゅ」「しぇ」「しょ」の柔軟な入力
            else if (inputCharacter == 'h' && prevChar == 's' && currentChar == 'y')
            {
                newCharacters[currentCharactersIndex] = 'h';
                isCorrect = true;
            }

            //「じゃ」「じゅ」「じぇ」「じょ」の柔軟な入力
            else if (inputCharacter == 'j' && prevChar != 'z' && currentChar == 'z' &&
                     nextChar == 'y' && nextChar2 is 'a' or 'u' or 'e' or 'o')
            {
                newCharacters[currentCharactersIndex] = 'j';
                newCharacters.RemoveAt(currentCharactersIndex + 1);
                isCorrect = true;
            }

            else if (inputCharacter == 'y' && prevChar == 'j' &&
                     currentChar is 'a' or 'u' or 'e' or 'o')
            {
                newCharacters.Insert(currentCharactersIndex, 'y');
                isCorrect = true;
            }

            //「し」「せ」の柔軟な入力(Windowsのみ)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'c' && prevChar != 's' &&
                     currentChar == 's' &&
                     nextChar is 'i' or 'e')
            {
                newCharacters[currentCharactersIndex] = 'c';
                isCorrect = true;
            }

            //「ち」の柔軟な入力
            else if (inputCharacter == 'c' && prevChar != 't' && currentChar == 't' && nextChar == 'i')
            {
                newCharacters[currentCharactersIndex] = 'c';
                newCharacters.Insert(currentCharactersIndex + 1, 'h');
                isCorrect = true;
            }

            //「ちゃ」「ちゅ」「ちぇ」「ちょ」の柔軟な入力
            else if (inputCharacter == 'c' && prevChar != 't' && currentChar == 't' && nextChar == 'y')
            {
                newCharacters[currentCharactersIndex] = 'c';
                isCorrect = true;
            }

            //「cya」=>「cha」
            else if (inputCharacter == 'h' && prevChar == 'c' && currentChar == 'y')
            {
                newCharacters[currentCharactersIndex] = 'h';
                isCorrect = true;
            }

            //「つ」の柔軟な入力
            else if (inputCharacter == 's' && prevChar == 't' && currentChar == 'u')
            {
                newCharacters.Insert(currentCharactersIndex, 's');
                isCorrect = true;
            }

            //「つぁ」「つぃ」「つぇ」「つぉ」の柔軟な入力
            else if (inputCharacter == 'u' && prevChar == 't' && currentChar == 's' &&
                     nextChar is 'a' or 'i' or 'e' or 'o')
            {
                newCharacters[currentCharactersIndex] = 'u';
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            else if (inputCharacter == 'u' && prevChar2 == 't' && prevChar == 's' &&
                     currentChar is 'a' or 'i' or 'e' or 'o')
            {
                newCharacters.Insert(currentCharactersIndex, 'u');
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            //「てぃ」の柔軟な入力
            else if (inputCharacter == 'e' && prevChar == 't' && currentChar == 'h' && nextChar == 'i')
            {
                newCharacters[currentCharactersIndex] = 'e';
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            //「でぃ」の柔軟な入力
            else if (inputCharacter == 'e' && prevChar == 'd' && currentChar == 'h' && nextChar == 'i')
            {
                newCharacters[currentCharactersIndex] = 'e';
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            //「でゅ」の柔軟な入力
            else if (inputCharacter == 'e' && prevChar == 'd' && currentChar == 'h' && nextChar == 'u')
            {
                newCharacters[currentCharactersIndex] = 'e';
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                newCharacters.Insert(currentCharactersIndex + 2, 'y');
                isCorrect = true;
            }

            //「とぅ」の柔軟な入力
            else if (inputCharacter == 'o' && prevChar == 't' && currentChar == 'w' && nextChar == 'u')
            {
                newCharacters[currentCharactersIndex] = 'o';
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            //「どぅ」の柔軟な入力
            else if (inputCharacter == 'o' && prevChar == 'd' && currentChar == 'w' && nextChar == 'u')
            {
                newCharacters[currentCharactersIndex] = 'o';
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            //「ふ」の柔軟な入力
            else if (inputCharacter == 'f' && currentChar == 'h' && nextChar == 'u')
            {
                newCharacters[currentCharactersIndex] = 'f';
                isCorrect = true;
            }

            //「ふぁ」「ふぃ」「ふぇ」「ふぉ」の柔軟な入力(一部Macのみ)
            else if (inputCharacter == 'w' && prevChar == 'f' &&
                     currentChar is 'a' or 'i' or 'e' or 'o')
            {
                newCharacters.Insert(currentCharactersIndex, 'w');
                isCorrect = true;
            }

            else if (inputCharacter == 'y' && prevChar == 'f' && currentChar is 'i' or 'e')
            {
                newCharacters.Insert(currentCharactersIndex, 'y');
                isCorrect = true;
            }

            else if (inputCharacter == 'h' && prevChar != 'f' && currentChar == 'f' &&
                     nextChar is 'a' or 'i' or 'e' or 'o')
            {
                if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Mac)
                {
                    newCharacters[currentCharactersIndex] = 'h';
                    newCharacters.Insert(currentCharactersIndex + 1, 'w');
                }
                else
                {
                    newCharacters[currentCharactersIndex] = 'h';
                    newCharacters.Insert(currentCharactersIndex + 1, 'u');
                    newCharacters.Insert(currentCharactersIndex + 2, 'x');
                }

                isCorrect = true;
            }

            else if (inputCharacter == 'u' && prevChar == 'f' &&
                     currentChar is 'a' or 'i' or 'e' or 'o')
            {
                newCharacters.Insert(currentCharactersIndex, 'u');
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Mac && inputCharacter == 'u' &&
                     prevChar == 'h' &&
                     currentChar == 'w' &&
                     nextChar is 'a' or 'i' or 'e' or 'o')
            {
                newCharacters[currentCharactersIndex] = 'u';
                newCharacters.Insert(currentCharactersIndex + 1, 'x');
                isCorrect = true;
            }

            //「ん」の柔軟な入力(「n'」には未対応)
            else if (inputCharacter == 'n' && prevChar2 != 'n' && prevChar == 'n' && currentChar != 'a' &&
                     currentChar != 'i' &&
                     currentChar != 'u' && currentChar != 'e' && currentChar != 'o' && currentChar != 'y')
            {
                newCharacters.Insert(currentCharactersIndex, 'n');
                isCorrect = true;
            }

            else if (inputCharacter == 'x' && prevChar != 'n' && currentChar == 'n' && nextChar != 'a' &&
                     nextChar != 'i' &&
                     nextChar != 'u' && nextChar != 'e' && nextChar != 'o' && nextChar != 'y')
            {
                if (nextChar == 'n')
                {
                    newCharacters[currentCharactersIndex] = 'x';
                }
                else
                {
                    newCharacters.Insert(currentCharactersIndex, 'x');
                }

                isCorrect = true;
            }

            //「うぃ」「うぇ」「うぉ」を分解する
            else if (inputCharacter == 'u' && currentChar == 'w' && nextChar == 'h' &&
                     nextChar2 is 'a' or 'i' or 'e' or 'o')
            {
                newCharacters[currentCharactersIndex] = 'u';
                newCharacters[currentCharactersIndex + 1] = 'x';
                isCorrect = true;
            }

            //「きゃ」「にゃ」などを分解する
            else if (inputCharacter == 'i' && currentChar == 'y' &&
                     prevChar is 'k' or 's' or 't' or 'n' or 'h' or 'm' or 'r' or 'g' or 'z' or 'd' or 'b' or 'p' &&
                     nextChar is 'a' or 'u' or 'e' or 'o')
            {
                if (nextChar == 'e')
                {
                    newCharacters[currentCharactersIndex] = 'i';
                    newCharacters.Insert(currentCharactersIndex + 1, 'x');
                }
                else
                {
                    newCharacters.Insert(currentCharactersIndex, 'i');
                    newCharacters.Insert(currentCharactersIndex + 1, 'x');
                }

                isCorrect = true;
            }

            //「しゃ」「ちゃ」などを分解する
            else if (inputCharacter == 'i' &&
                     currentChar is 'a' or 'u' or 'e' or 'o' &&
                     prevChar2 is 's' or 'c' && prevChar == 'h')
            {
                if (nextChar == 'e')
                {
                    newCharacters.Insert(currentCharactersIndex, 'i');
                    newCharacters.Insert(currentCharactersIndex + 1, 'x');
                }
                else
                {
                    newCharacters.Insert(currentCharactersIndex, 'i');
                    newCharacters.Insert(currentCharactersIndex + 1, 'x');
                    newCharacters.Insert(currentCharactersIndex + 2, 'y');
                }

                isCorrect = true;
            }

            //「しゃ」を「c」で分解する(Windows限定)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'c' &&
                     currentChar == 's' &&
                     prevChar != 's' && nextChar == 'y' &&
                     nextChar2 is 'a' or 'u' or 'e' or 'o')
            {
                if (nextChar2 == 'e')
                {
                    newCharacters[currentCharactersIndex] = 'c';
                    newCharacters[currentCharactersIndex + 1] = 'i';
                    newCharacters.Insert(currentCharactersIndex + 1, 'x');
                }
                else
                {
                    newCharacters[currentCharactersIndex] = 'c';
                    newCharacters.Insert(currentCharactersIndex + 1, 'i');
                    newCharacters.Insert(currentCharactersIndex + 2, 'x');
                }

                isCorrect = true;
            }

            //「っ」の柔軟な入力
            else if (inputCharacter is 'x' or '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'))
            {
                newCharacters[currentCharactersIndex] = inputCharacter;
                newCharacters.Insert(currentCharactersIndex + 1, 't');
                newCharacters.Insert(currentCharactersIndex + 2, 'u');
                isCorrect = true;
            }

            //「っか」「っく」「っこ」の柔軟な入力(Windows限定)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'c' && currentChar == 'k' &&
                     nextChar == 'k' &&
                     nextChar2 is 'a' or 'u' or 'o')
            {
                newCharacters[currentCharactersIndex] = 'c';
                newCharacters[currentCharactersIndex + 1] = 'c';
                isCorrect = true;
            }

            //「っく」の柔軟な入力(Windows限定)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'q' && currentChar == 'k' &&
                     nextChar == 'k' && nextChar2 == 'u')
            {
                newCharacters[currentCharactersIndex] = 'q';
                newCharacters[currentCharactersIndex + 1] = 'q';
                isCorrect = true;
            }

            //「っし」「っせ」の柔軟な入力(Windows限定)
            else if (_reactivePropertyOperatingSystemName.Value == OperatingSystemName.Windows &&
                     inputCharacter == 'c' && currentChar == 's' &&
                     nextChar == 's' &&
                     nextChar2 is 'i' or 'e')
            {
                newCharacters[currentCharactersIndex] = 'c';
                newCharacters[currentCharactersIndex + 1] = 'c';
                isCorrect = true;
            }

            //「っちゃ」「っちゅ」「っちぇ」「っちょ」の柔軟な入力
            else if (inputCharacter == 'c' && currentChar == 't' && nextChar == 't' && nextChar2 == 'y')
            {
                newCharacters[currentCharactersIndex] = 'c';
                newCharacters[currentCharactersIndex + 1] = 'c';
                isCorrect = true;
            }

            //「っち」の柔軟な入力
            else if (inputCharacter == 'c' && currentChar == 't' && nextChar == 't' && nextChar2 == 'i')
            {
                newCharacters[currentCharactersIndex] = 'c';
                newCharacters[currentCharactersIndex + 1] = 'c';
                newCharacters.Insert(currentCharactersIndex + 2, 'h');
                isCorrect = true;
            }

            //「l」と「x」の完全互換性
            else if (inputCharacter == 'x' && currentChar == 'l')
            {
                newCharacters[currentCharactersIndex] = 'x';
                isCorrect = true;
            }

            else if (inputCharacter == 'l' && currentChar == 'x')
            {
                newCharacters[currentCharactersIndex] = 'l';
                isCorrect = true;
            }


            if (isCorrect)
            {
                _reactivePropertyCharacters.Value = newCharacters;
                _reactivePropertyCharactersIndex.Value++;

                if (_reactivePropertyCharactersIndex.Value >= _reactivePropertyCharacters.Value.Count)
                {
                    return TypeResult.Finished;
                }

                return TypeResult.Correct;
            }

            return TypeResult.Incorrect;
        }
    }
}

GameStateModel.cs

ステートを管理することによるModelです。単純に名前が記載されるだけです。

using Enums;
using UniRx;

namespace Models
{
    public class GameStateModel
    {
        private readonly ReactiveProperty<GameStateName> _reactivePropertyGameStateName = new(GameStateName.None);
        public GameStateName GameStateName => _reactivePropertyGameStateName.Value;

        public void ChangeGameStateName(GameStateName gameStateName)
        {
            _reactivePropertyGameStateName.Value = gameStateName;
        }
    }
}

NextTypingTextModel.cs

次の問題文をあらかじめ用意するModelスクリプトです。

using System;
using Structs;
using UniRx;

namespace Models
{
    public class NextTypingTextModel
    {
        private readonly ReactiveProperty<string> _reactivePropertyTitle = new("");
        public IObservable<string> OnChangedTitle => _reactivePropertyTitle;

        private readonly ReactiveProperty<string> _reactivePropertyCharacters = new("");

        public TypingText TypingText => new()
        {
            title = _reactivePropertyTitle.Value,
            hiragana = _reactivePropertyCharacters.Value,
        };

        public void SetTypingText(TypingText typingText)
        {
            _reactivePropertyTitle.Value = typingText.title;
            _reactivePropertyCharacters.Value = typingText.hiragana;
        }
    }
}

TypingSpeedModel.cs

 タイピングの速度(key / min)を計測するModelスクリプトです。

using System;
using UniRx;
using UnityEngine;

namespace Models
{
    public class TypingSpeedModel
    {
        private readonly ReactiveProperty<int> _reactivePropertyTypingSpeed = new(0);
        public IObservable<int> OnChangedTypingSpeed => _reactivePropertyTypingSpeed;

        private readonly ReactiveProperty<float> _reactivePropertyTime = new(0.0f);
        private readonly ReactiveProperty<int> _reactivePropertyTypeCount = new(0);

        public void AddTime(float deltaTime)
        {
            _reactivePropertyTime.Value += deltaTime;
        }

        public void IncreaseTypeCount()
        {
            _reactivePropertyTypeCount.Value++;
        }

        public void CalculateTypingSpeed()
        {
            if (_reactivePropertyTime.Value > 0.0f)
            {
                _reactivePropertyTypingSpeed.Value =
                    Mathf.RoundToInt(_reactivePropertyTypeCount.Value * 60 / _reactivePropertyTime.Value);
            }
            else
            {
                _reactivePropertyTypingSpeed.Value = 0;
            }
        }

        public void StartMeasure()
        {
            _reactivePropertyTypeCount.Value = 0;
            _reactivePropertyTime.Value = 0.0f;
        }
    }
}

TypingTextStoreModel.cs

タイピングの問題文を格納するModelスクリプトです。

using Structs;
using UniRx;

namespace Models
{
    public class TypingTextStoreModel
    {
        private readonly ReactiveCollection<TypingText> _reactiveCollectionTypingTexts = new()
        {
            new TypingText
            {
                title = "犬も歩けば棒に当たる",
                hiragana = "いぬもあるけばぼうにあたる"
            },
            new TypingText
            {
                title = "論より証拠",
                hiragana = "ろんよりしょうこ"
            },
            new TypingText
            {
                title = "花より団子",
                hiragana = "はなよりだんご"
            },
            new TypingText
            {
                title = "需要と供給",
                hiragana = "じゅようときょうきゅう"
            },
            new TypingText
            {
                title = "七福神",
                hiragana = "しちふくじん"
            },
            new TypingText
            {
                title = "モッツァレラチーズ",
                hiragana = "もっつぁれらちーず"
            },
        };

        public TypingText RandomTypingText =>
            _reactiveCollectionTypingTexts[UnityEngine.Random.Range(0, _reactiveCollectionTypingTexts.Count)];
    }
}

Modelスクリプトの紹介は以上となります。


 次にViewを紹介をします。Viewは主に表示の処理を行うMonoBehaviour継承のスクリプトです。個人的にはコンポーネントの操作に関係なく、MonoBehaviourを継承しGameObjectにアタッチするスクリプト=Viewとしております。

GameStarter.cs

 ゲームを開始してから、一定時間後にイベントを発行するViewスクリプトです。

using System;
using System.Collections;
using UniRx;
using UnityEngine;

namespace Views
{
    public class GameStarter : MonoBehaviour
    {
        [SerializeField] private float duration;

        private readonly Subject<Unit> _subjectOnGameStart = new();
        public IObservable<Unit> OnGameStart => _subjectOnGameStart;

        private IEnumerator Start()
        {
            yield return new WaitForSeconds(duration);
            _subjectOnGameStart.OnNext(Unit.Default);
        }
    }
}

KeyDetector.cs

 MonoBehaviour標準のイベント関数OnGUIを用いて、入力キーを検出しイベント発行するスクリプトです。

using System;
using UniRx;
using UnityEngine;

namespace Views
{
    public class KeyDetector : MonoBehaviour
    {
        private readonly Subject<char> _subjectOnCharacterDetected = new();
        public IObservable<char> OnCharacterDetected => _subjectOnCharacterDetected;

        private void OnGUI()
        {
            if (Event.current.type == EventType.KeyDown)
            {
                switch (Event.current.keyCode)
                {
                    case KeyCode.A:
                    {
                        _subjectOnCharacterDetected.OnNext('a');
                        break;
                    }
                    case KeyCode.B:
                    {
                        _subjectOnCharacterDetected.OnNext('b');
                        break;
                    }
                    case KeyCode.C:
                    {
                        _subjectOnCharacterDetected.OnNext('c');
                        break;
                    }
                    case KeyCode.D:
                    {
                        _subjectOnCharacterDetected.OnNext('d');
                        break;
                    }
                    case KeyCode.E:
                    {
                        _subjectOnCharacterDetected.OnNext('e');
                        break;
                    }
                    case KeyCode.F:
                    {
                        _subjectOnCharacterDetected.OnNext('f');
                        break;
                    }
                    case KeyCode.G:
                    {
                        _subjectOnCharacterDetected.OnNext('g');
                        break;
                    }
                    case KeyCode.H:
                    {
                        _subjectOnCharacterDetected.OnNext('h');
                        break;
                    }
                    case KeyCode.I:
                    {
                        _subjectOnCharacterDetected.OnNext('i');
                        break;
                    }
                    case KeyCode.J:
                    {
                        _subjectOnCharacterDetected.OnNext('j');
                        break;
                    }
                    case KeyCode.K:
                    {
                        _subjectOnCharacterDetected.OnNext('k');
                        break;
                    }
                    case KeyCode.L:
                    {
                        _subjectOnCharacterDetected.OnNext('l');
                        break;
                    }
                    case KeyCode.M:
                    {
                        _subjectOnCharacterDetected.OnNext('m');
                        break;
                    }
                    case KeyCode.N:
                    {
                        _subjectOnCharacterDetected.OnNext('n');
                        break;
                    }
                    case KeyCode.O:
                    {
                        _subjectOnCharacterDetected.OnNext('o');
                        break;
                    }
                    case KeyCode.P:
                    {
                        _subjectOnCharacterDetected.OnNext('p');
                        break;
                    }
                    case KeyCode.Q:
                    {
                        _subjectOnCharacterDetected.OnNext('q');
                        break;
                    }
                    case KeyCode.R:
                    {
                        _subjectOnCharacterDetected.OnNext('r');
                        break;
                    }
                    case KeyCode.S:
                    {
                        _subjectOnCharacterDetected.OnNext('s');
                        break;
                    }
                    case KeyCode.T:
                    {
                        _subjectOnCharacterDetected.OnNext('t');
                        break;
                    }
                    case KeyCode.U:
                    {
                        _subjectOnCharacterDetected.OnNext('u');
                        break;
                    }
                    case KeyCode.V:
                    {
                        _subjectOnCharacterDetected.OnNext('v');
                        break;
                    }
                    case KeyCode.W:
                    {
                        _subjectOnCharacterDetected.OnNext('w');
                        break;
                    }
                    case KeyCode.X:
                    {
                        _subjectOnCharacterDetected.OnNext('x');
                        break;
                    }
                    case KeyCode.Y:
                    {
                        _subjectOnCharacterDetected.OnNext('y');
                        break;
                    }
                    case KeyCode.Z:
                    {
                        _subjectOnCharacterDetected.OnNext('z');
                        break;
                    }
                    case KeyCode.Alpha0:
                    {
                        _subjectOnCharacterDetected.OnNext('0');
                        break;
                    }
                    case KeyCode.Alpha1:
                    {
                        _subjectOnCharacterDetected.OnNext('1');
                        break;
                    }
                    case KeyCode.Alpha2:
                    {
                        _subjectOnCharacterDetected.OnNext('2');
                        break;
                    }
                    case KeyCode.Alpha3:
                    {
                        _subjectOnCharacterDetected.OnNext('3');
                        break;
                    }
                    case KeyCode.Alpha4:
                    {
                        _subjectOnCharacterDetected.OnNext('4');
                        break;
                    }
                    case KeyCode.Alpha5:
                    {
                        _subjectOnCharacterDetected.OnNext('5');
                        break;
                    }
                    case KeyCode.Alpha6:
                    {
                        _subjectOnCharacterDetected.OnNext('6');
                        break;
                    }
                    case KeyCode.Alpha7:
                    {
                        _subjectOnCharacterDetected.OnNext('7');
                        break;
                    }
                    case KeyCode.Alpha8:
                    {
                        _subjectOnCharacterDetected.OnNext('8');
                        break;
                    }
                    case KeyCode.Alpha9:
                    {
                        _subjectOnCharacterDetected.OnNext('9');
                        break;
                    }
                    case KeyCode.Minus:
                    {
                        _subjectOnCharacterDetected.OnNext('-');
                        break;
                    }
                    case KeyCode.Caret:
                    {
                        _subjectOnCharacterDetected.OnNext('^');
                        break;
                    }
                    case KeyCode.Backslash:
                    {
                        _subjectOnCharacterDetected.OnNext('\\');
                        break;
                    }
                    case KeyCode.At:
                    {
                        _subjectOnCharacterDetected.OnNext('@');
                        break;
                    }
                    case KeyCode.LeftBracket:
                    {
                        _subjectOnCharacterDetected.OnNext('[');
                        break;
                    }
                    case KeyCode.Semicolon:
                    {
                        _subjectOnCharacterDetected.OnNext(';');
                        break;
                    }
                    case KeyCode.Colon:
                    {
                        _subjectOnCharacterDetected.OnNext(':');
                        break;
                    }
                    case KeyCode.RightBracket:
                    {
                        _subjectOnCharacterDetected.OnNext(']');
                        break;
                    }
                    case KeyCode.Comma:
                    {
                        _subjectOnCharacterDetected.OnNext(',');
                        break;
                    }
                    case KeyCode.Period:
                    {
                        _subjectOnCharacterDetected.OnNext('.');
                        break;
                    }
                    case KeyCode.Slash:
                    {
                        _subjectOnCharacterDetected.OnNext('/');
                        break;
                    }
                    case KeyCode.Underscore:
                    {
                        _subjectOnCharacterDetected.OnNext('_');
                        break;
                    }
                    case KeyCode.Backspace:
                    {
                        _subjectOnCharacterDetected.OnNext('\b');
                        break;
                    }
                    case KeyCode.Return:
                    {
                        _subjectOnCharacterDetected.OnNext('\r');
                        break;
                    }
                    case KeyCode.Space:
                    {
                        _subjectOnCharacterDetected.OnNext(' ');
                        break;
                    }
                }
            }
        }
    }
}

TypingTextView.cs

 タイピング中の問題文ないし入力文字列を表示制御するViewスクリプトです。

using System.Collections.Generic;
using Interfaces;
using Structs;
using TMPro;
using UniRx;
using UniRx.Triggers;
using UnityEngine;

namespace Views
{
    public class TypingTextView : MonoBehaviour
    {
        [SerializeField] private TextMeshProUGUI textMeshProTitle;
        [SerializeField] private TextMeshProUGUI textMeshProCharacters;

        [SerializeField] private TMP_FontAsset fontUntyped;
        [SerializeField] private TMP_FontAsset fontTyped;
        [SerializeField] private TMP_FontAsset fontCurrent;

        private string _title;
        private List<char> _characters = new();
        private int _charactersIndex;
        
        private readonly Queue<ITypingTextDifference> _queueTypingTextDifferences = new();
        private void Awake()
        {
            this.UpdateAsObservable()
                .Subscribe(_ =>
                {
                    var isTitleUpdated = false;
                    var isCharactersUpdated = false;
                    while (_queueTypingTextDifferences.Count > 0)
                    {
                        var typingTextDifference = _queueTypingTextDifferences.Dequeue();

                        switch (typingTextDifference)
                        {
                            case TypingTextDifferenceTitle typingTextDifferenceTitle:
                            {
                                _title = typingTextDifferenceTitle.title;
                                isTitleUpdated = true;
                                break;
                            }
                            case TypingTextDifferenceCharacters typingTextDifferenceCharacters:
                            {
                                _characters = new List<char>(typingTextDifferenceCharacters.characters);
                                isCharactersUpdated = true;
                                break;
                            }
                            case TypingTextDifferenceCharactersIndex typingTextDifferenceCharactersIndex:
                            {
                                _charactersIndex = typingTextDifferenceCharactersIndex.charactersIndex;
                                isCharactersUpdated = true;
                                break;
                            }
                        }
                    }

                    if (isTitleUpdated)
                    {
                        textMeshProTitle.text = _title;
                    }

                    if (isCharactersUpdated)
                    {
                        var textTypingCharacters = "<font=\"" + fontTyped.name + "\">";

                        for (var i = 0; i < _characters.Count; i++)
                        {
                            if (i == _charactersIndex)
                            {
                                textTypingCharacters += "</font><font=\"" + fontCurrent.name + "\">" + _characters[i] +
                                                        "</font><font=\"" + fontUntyped.name + "\">";
                            }
                            else
                            {
                                textTypingCharacters += _characters[i];
                            }
                        }

                        textTypingCharacters += "</font>";

                        textMeshProCharacters.text = textTypingCharacters;
                    }
                }).AddTo(gameObject);
        }

        public void EnqueueTypingTextDifference(ITypingTextDifference typingTextDifference)
        {
            _queueTypingTextDifferences.Enqueue(typingTextDifference);
        }
    }
}

 3種類のTextMeshProのFontAssetを格納する変数があり、TextMeshProのリッチテキスト機能であるFontタグを用いて文字の色や光り具合を変えています。

 TextMesh Proをインポートした際に「Assets」ディレクトリに自動生成される「TextMesh Pro」ディレクトリの中にある「Resources/Fonts & Materials」ディレクトリに3種類のTextMesh ProのFont Assetを作成(もとい複製)し、そのFont Assetに付属しているMaterialの標準Shaderプロパティを変更しています。

NextTypingTextView.cs

 NEXTの問題文を表示制御するViewスクリプトです。

using System.Collections.Generic;
using TMPro;
using UniRx;
using UniRx.Triggers;
using UnityEngine;

namespace Views
{
    public class NextTypingTextView : MonoBehaviour
    {
        [SerializeField] private TextMeshProUGUI textMeshProTitle;

        private readonly Queue<string> _queueTitle = new();

        private string _title;

        private void Awake()
        {
            this.UpdateAsObservable()
                .Subscribe(_ =>
                {
                    var isTitleUpdated = false;
                    while (_queueTitle.Count > 0)
                    {
                        var title = _queueTitle.Dequeue();

                        _title = title;
                        isTitleUpdated = true;
                    }

                    if (isTitleUpdated)
                    {
                        textMeshProTitle.text = _title;
                    }
                }).AddTo(gameObject);
        }

        public void EnqueueTitle(string title)
        {
            _queueTitle.Enqueue(title);
        }
    }
}

TypingSpeedView.cs

 タイピング速度を表示制御するViewスクリプトです。

using System.Collections.Generic;
using TMPro;
using UniRx;
using UniRx.Triggers;
using UnityEngine;

namespace Views
{
    public class TypingSpeedView : MonoBehaviour
    {
        [SerializeField] private TextMeshProUGUI textMeshPro;

        private readonly Queue<int> _queueTypingSpeed = new();

        private int _typingSpeed;
        
        private void Awake()
        {
            this.UpdateAsObservable()
                .Subscribe(_ =>
                {
                    var isTypingSpeedUpdated = false;

                    while (_queueTypingSpeed.Count > 0)
                    {
                        var typingSpeed = _queueTypingSpeed.Dequeue();
                        _typingSpeed = typingSpeed;
                        isTypingSpeedUpdated = true;
                    }
                    if (isTypingSpeedUpdated)
                    {
                        textMeshPro.text = "タイピング速度:" + _typingSpeed + " key/min";
                    }
                }).AddTo(gameObject);
        }

        public void EnqueueTypingSpeed(int typingSpeed)
        {
            _queueTypingSpeed.Enqueue(typingSpeed);
        }
    }
}

Viewの紹介はこれにて以上です。


 (Reactive)Presenterの領域に入ります。個人的な(Reactive)Presenterのルールとして「Modelに記載されたReactivePropertyをSubscribeして、Viewに値を渡す」としております。「PresenterだけがReactivePropertyをSubscribeできる」ものとしております。

TypingTextViewPresenter.cs

 TypingTextModel.csに記述されたReactivePropertyをSubscribeし、TypingTextView.csに値を渡す(Reactive)Presenterスクリプトです。

using System;
using System.Collections.Generic;
using Models;
using Structs;
using UniRx;
using VContainer;
using VContainer.Unity;
using Views;

namespace Presenters
{
    public class TypingTextViewPresenter : IInitializable, IDisposable
    {
        private readonly TypingTextModel _typingTextModel;
        private readonly TypingTextView _typingTextView;

        private readonly CompositeDisposable _compositeDisposable = new();

        [Inject]
        public TypingTextViewPresenter(TypingTextModel typingTextModel, TypingTextView typingTextView)
        {
            _typingTextModel = typingTextModel;
            _typingTextView = typingTextView;
        }

        public void Initialize()
        {
            _typingTextModel.OnChangedTitle.Subscribe(title =>
            {
                _typingTextView.EnqueueTypingTextDifference(new TypingTextDifferenceTitle
                {
                    title = title
                });
            }).AddTo(_compositeDisposable);

            _typingTextModel.OnChangedCharacters.Subscribe(characters =>
            {
                _typingTextView.EnqueueTypingTextDifference(new TypingTextDifferenceCharacters
                {
                    characters = new List<char>(characters)
                });
            }).AddTo(_compositeDisposable);

            _typingTextModel.OnChangedCharactersIndex.Subscribe(charactersIndex =>
            {
                _typingTextView.EnqueueTypingTextDifference(new TypingTextDifferenceCharactersIndex
                {
                    charactersIndex = charactersIndex
                });
            }).AddTo(_compositeDisposable);
        }

        public void Dispose()
        {
            _compositeDisposable.Dispose();
        }
    }
}

NextTypingTextViewPresenter.cs

 NextTypingTextModel.csに記述されたReactivePropertyをSubsrcibeし、NextTypingTextView.csに値を渡す(Reactive)Presenterスクリプトです。

using System;
using Models;
using UniRx;
using VContainer;
using VContainer.Unity;
using Views;

namespace Presenters
{
    public class NextTypingTextViewPresenter : IInitializable, IDisposable
    {
        private readonly NextTypingTextModel _nextTypingTextModel;
        private readonly NextTypingTextView _nextTypingTextView;

        private readonly CompositeDisposable _compositeDisposable = new();
        
        [Inject]
        public NextTypingTextViewPresenter(NextTypingTextModel nextTypingTextModel,
            NextTypingTextView nextTypingTextView)
        {
            _nextTypingTextModel = nextTypingTextModel;
            _nextTypingTextView = nextTypingTextView;
        }

        public void Initialize()
        {
            _nextTypingTextModel.OnChangedTitle.Subscribe(title =>
            {
                _nextTypingTextView.EnqueueTitle(title);
            }).AddTo(_compositeDisposable);
        }

        public void Dispose()
        {
            _compositeDisposable.Dispose();
        }
    }
}

TypingSpeedViewPresenter.cs

  TypingSpeedModel.csに記述されたReactivePropertyをSubscribeし、TypingSpeedView.csに値を渡す(Reactive)Presenterスクリプトです。

using System;
using Models;
using UniRx;
using VContainer;
using VContainer.Unity;
using Views;

namespace Presenters
{
    public class TypingSpeedViewPresenter : IInitializable, IDisposable
    {
        private readonly TypingSpeedModel _typingSpeedModel;
        private readonly TypingSpeedView _typingSpeedView;

        private readonly CompositeDisposable _compositeDisposable = new();

        [Inject]
        public TypingSpeedViewPresenter(TypingSpeedModel typingSpeedModel, TypingSpeedView typingSpeedView)
        {
            _typingSpeedModel = typingSpeedModel;
            _typingSpeedView = typingSpeedView;
        }
        
        public void Initialize()
        {
            _typingSpeedModel.OnChangedTypingSpeed.Subscribe(typingSpeed =>
            {
                _typingSpeedView.EnqueueTypingSpeed(typingSpeed);
            }).AddTo(_compositeDisposable);
        }

        public void Dispose()
        {
            _compositeDisposable.Dispose();
        }
    }
}

(Reactive)Presenterの領域は以上です。


 ここからは、独自なりの設計思想である「Process」スクリプトの紹介です。

 Presenterが「Model→View」ならば、Processは「View→Model」といえるでしょう。またProcessは「View→View」もありえます。

GameStarterEventProcess.cs

 GameStarter.csのイベントを監視し、ゲームがスタートしてから一定時間経過後の処理を行うProcessスクリプトです。

using System;
using System.Collections.Generic;
using Enums;
using Models;
using UniRx;
using UnityEngine;
using VContainer;
using VContainer.Unity;
using Views;

namespace Processes.Events
{
    public class GameStarterEventProcess : IInitializable, IDisposable
    {
        private readonly ConvertHiraganaToRomanModel _convertHiraganaToRomanModel;
        private readonly CurrentTypingTextModel _currentTypingTextModel;
        private readonly GameStateModel _gameStateModel;
        private readonly TypingSpeedModel _typingSpeedModel;
        private readonly TypingTextStoreModel _typingTextStoreModel;
        private readonly NextTypingTextModel _nextTypingTextModel;

        private readonly GameStarter _gameStarter;

        private readonly CompositeDisposable _compositeDisposable = new();

        [Inject]
        public GameStarterEventProcess(ConvertHiraganaToRomanModel convertHiraganaToRomanModel,
            CurrentTypingTextModel currentTypingTextModel, GameStateModel gameStateModel,
            NextTypingTextModel nextTypingTextModel, TypingSpeedModel typingSpeedModel,
            TypingTextStoreModel typingTextStoreModel, GameStarter gameStarter)
        {
            _convertHiraganaToRomanModel = convertHiraganaToRomanModel;
            _currentTypingTextModel = currentTypingTextModel;
            _gameStateModel = gameStateModel;
            _nextTypingTextModel = nextTypingTextModel;
            _typingSpeedModel = typingSpeedModel;
            _typingTextStoreModel = typingTextStoreModel;

            _gameStarter = gameStarter;
        }

        public void Initialize()
        {
            _gameStarter.OnGameStart.Subscribe(_ =>
            {
                var isValid = false;
                if (SystemInfo.operatingSystem.Contains("Windows"))
                {
                    _currentTypingTextModel.SetOperatingSystemName(OperatingSystemName.Windows);
                    isValid = true;
                }

                if (SystemInfo.operatingSystem.Contains("Mac"))
                {
                    _currentTypingTextModel.SetOperatingSystemName(OperatingSystemName.Mac);
                    isValid = true;
                }

                if (isValid)
                {
                    _nextTypingTextModel.SetTypingText(_typingTextStoreModel.RandomTypingText);
                    
                    var currentTypingText = _typingTextStoreModel.RandomTypingText;
                    
                    _currentTypingTextModel.SetTitle(currentTypingText.title);
                    _currentTypingTextModel.SetCharacters(
                        _convertHiraganaToRomanModel.ConvertHiraganaToRoman(
                            new List<char>(currentTypingText.hiragana)));
                    
                    _typingSpeedModel.StartMeasure();
                    _gameStateModel.ChangeGameStateName(GameStateName.Typing);
                }
            }).AddTo(_compositeDisposable);
        }

        public void Dispose()
        {
            _compositeDisposable.Dispose();
        }
    }
}

デバイスがWindowsないしMacならゲームを開始させます。

KeyDetectorEventProcess.cs

KeyDetector.csを監視し、キーの入力処理を行うスクリプトです。

using System;
using System.Collections.Generic;
using Enums;
using Models;
using UniRx;
using VContainer;
using VContainer.Unity;
using Views;

namespace Processes.Events
{
    public class KeyDetectorEventProcess : IInitializable, IDisposable
    {
        private readonly ConvertHiraganaToRomanModel _convertHiraganaToRomanModel;
        private readonly CurrentTypingTextModel _currentTypingTextModel;
        private readonly GameStateModel _gameStateModel;
        private readonly NextTypingTextModel _nextTypingTextModel;
        private readonly TypingSpeedModel _typingSpeedModel;
        private readonly TypingTextStoreModel _typingTextStoreModel;

        private readonly KeyDetector _keyDetector;

        private readonly CompositeDisposable _compositeDisposable = new();

        [Inject]
        public KeyDetectorEventProcess(ConvertHiraganaToRomanModel convertHiraganaToRomanModel,
            CurrentTypingTextModel currentTypingTextModel, GameStateModel gameStateModel,
            NextTypingTextModel nextTypingTextModel, TypingSpeedModel typingSpeedModel,
            TypingTextStoreModel typingTextStoreModel, KeyDetector keyDetector)
        {
            _convertHiraganaToRomanModel = convertHiraganaToRomanModel;
            _currentTypingTextModel = currentTypingTextModel;
            _gameStateModel = gameStateModel;
            _nextTypingTextModel = nextTypingTextModel;
            _typingSpeedModel = typingSpeedModel;
            _typingTextStoreModel = typingTextStoreModel;

            _keyDetector = keyDetector;
        }

        public void Initialize()
        {
            _keyDetector.OnCharacterDetected.Subscribe(character =>
            {
                switch (_gameStateModel.GameStateName)
                {
                    case GameStateName.Typing:
                    {
                        switch (_currentTypingTextModel.TypeCharacter(character))
                        {
                            case TypeResult.Correct:
                            {
                                _typingSpeedModel.IncreaseTypeCount();
                                break;
                            }
                            case TypeResult.Incorrect:
                            {
                                break;
                            }
                            case TypeResult.Finished:
                            {
                                _typingSpeedModel.IncreaseTypeCount();
                                _typingSpeedModel.CalculateTypingSpeed();
                                _typingSpeedModel.StartMeasure();

                                var currentTypingText = _nextTypingTextModel.TypingText;
                                
                                _currentTypingTextModel.SetTitle(currentTypingText.title);
                                _currentTypingTextModel.SetCharacters(
                                    _convertHiraganaToRomanModel.ConvertHiraganaToRoman(
                                        new List<char>(currentTypingText.hiragana)));
                                
                                _currentTypingTextModel.ResetCharactersIndex();

                                _nextTypingTextModel.SetTypingText(_typingTextStoreModel.RandomTypingText);
                                break;
                            }
                        }

                        break;
                    }
                }
            }).AddTo(_compositeDisposable);
        }

        public void Dispose()
        {
            _compositeDisposable.Dispose();
        }
    }
}     

UpdateProcess.cs

毎フレーム処理をするスクリプトです。

using Enums;
using Models;
using UnityEngine;
using VContainer;
using VContainer.Unity;

namespace Processes
{
    public class UpdateProcess : ITickable
    {
        private readonly GameStateModel _gameStateModel;
        private readonly TypingSpeedModel _typingSpeedModel;

        [Inject]
        public UpdateProcess(GameStateModel gameStateModel, TypingSpeedModel typingSpeedModel)
        {
            _gameStateModel = gameStateModel;
            _typingSpeedModel = typingSpeedModel;
        }
        
        public void Tick()
        {
            switch (_gameStateModel.GameStateName)
            {
                case GameStateName.Typing:
                {
                    _typingSpeedModel.AddTime(Time.deltaTime);
                    break;
                }
            }
        }
    }
}

タイピングの速度の計測のために用いました。


 最後に、これらのコンポーネントの依存関係を解決するためのLifetimeScopeスクリプトを作成します。

GameLifetimeScope.cs

using Models;
using Presenters;
using Processes;
using Processes.Events;
using VContainer;
using VContainer.Unity;
using Views;

public class GameLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        // ------------------------------ Models ------------------------------

        builder.Register<ConvertHiraganaToRomanModel>(Lifetime.Singleton);
        builder.Register<CurrentTypingTextModel>(Lifetime.Singleton);
        builder.Register<GameStateModel>(Lifetime.Singleton);
        builder.Register<NextTypingTextModel>(Lifetime.Singleton);
        builder.Register<TypingSpeedModel>(Lifetime.Singleton);
        builder.Register<TypingTextStoreModel>(Lifetime.Singleton);

        // ------------------------------ Views ------------------------------

        builder.RegisterComponentInHierarchy<GameStarter>();
        builder.RegisterComponentInHierarchy<KeyDetector>();
        builder.RegisterComponentInHierarchy<NextTypingTextView>();
        builder.RegisterComponentInHierarchy<TypingSpeedView>();
        builder.RegisterComponentInHierarchy<TypingTextView>();
        
        // ------------------------------ Presenters ------------------------------

        builder.RegisterEntryPoint<NextTypingTextViewPresenter>();
        builder.RegisterEntryPoint<TypingSpeedViewPresenter>();
        builder.RegisterEntryPoint<TypingTextViewPresenter>();

        // ------------------------------ Processes ------------------------------

        builder.RegisterEntryPoint<GameStarterEventProcess>();
        builder.RegisterEntryPoint<KeyDetectorEventProcess>();

        builder.RegisterEntryPoint<UpdateProcess>();
    }
}

あとは適宜GameObjectを配置すれば完成です。

この記事が気に入ったらサポートをしてみませんか?