tomo_manaのブログ

tomo_manaのブログ

Unityで一からゲームを作る方法を紹介しています。

WebGL#1 WebGLでゲームを作り、はてなブログに掲載 (Unity 2019.4.4f1)

最近、難題に突き当たってしまい、すっかり更新頻度が落ちてしまったので、もう少し基礎的な技術だけを使って、具体的にゲームを作ってみようと思います。

ゲームは、WebGL で作ります。WebGLJavaScript ベースのため、無料で公開する限り、iPhoneandroid のようにストアへの登録しなくても公開できるのがメリットです。尚、はてなブログでは JavaScript 実行ファイル(.js)を直接置くことができないので、Githubをファイル置き場にします。

<目次>

ゲームの条件

簡単な売買をしながら、財務諸表の変化を見られるゲームを作ります。
こちらはできるだけ初歩的な技術(No Code)で作ります。
(詳細は、ゲームを公開する記事に記載していきます)

WebGL

Unityで、WebGLビルド環境での出力はこちらを参考にしました。
alterbo.jp

Githubへの登録

WebGL実行環境Githubへのアカウント作成とレポジトリへの登録はこちらを参考にしました。
prog-8.com

確認テスト

以下のサイトで公開します。
tomo-mana.hatenablog.com

404 Not Found

最初リンクを貼った時、 404 Not Found が表示されました(涙)

これは、リンクを貼ったディレクトリの直下にindex.htmlが含まれていない場合に表示されていました。プロジェクトのルートフォルダにindex.htmlが無い場合、index.htmlがあるフォルダの直上フォルダのアドレスを指定することで、ちゃんと表示されるようになりました。

f:id:tomo_mana:20210722094305p:plain

財務諸表を学べるゲーム (WebGL)

財務諸表を学べるゲーム(表示テスト)
※このページは随時更新していきます

(今はHello Worldだけ表示されます)
tomo-mana.github.io

<ゲームの条件>
●スモールスタート(課題を小さくする練習を兼ねる)
●シングルシーン
●最小画面構成

Unity学習#34-2 EventSystemとInputSystemUIInputModule (Unity 2019.4.4f1)

第12回でInputSystemについて、第13回でEventSystemについて、それぞれ調べました。複数シーンでゲームを構成する場合は、もう少し深掘りが必要なことが分かったので、再度いくつか疑問に思っていたことについて調べました。


全体として知りたいのは、EventSystemからPlayerInput経由でメッセージを受け取るまでの流れです。

今回は、EventSystem と InputSystem 用のコンポーネント InputSystemUIInputModule との関係について。

特に、(1) 複数のシーンでゲームが構成されている場合、EventSystemはシーン毎に置いても正しく動作するのか。また、(2) InputManager から InputSystem に変える時に StandaloneInputModule から InputSystemUIInputModule に置き換えましたが、この二つはどんな繋がりがあるのか。EventSystemはそもそも何をしていて、InputSysmteUIInputModule は何をしているのか。この2つについて調べました。

動機は、シーンが複数ある場合に、EventSystem をどのように配置したら良いかを知りたかったからでした。

結論

こんな感じでした。

(1) EventSystem は、シーン毎に一つずつあっても正常に動作します排他制御)。
(2) EventSystem は StandaloneInputModule と InputSystemUIInputModule の共通ベースクラス BaseInputModule を参照しています。また、InputSystemUIInputModule は、入力イベントの「状態管理」しています。EventSystem は BaseInputModule の排他制御をしています。


以下は EventSystem と InputSystemUIInputModule(BaseInputModule) の関係を表した図です。

f:id:tomo_mana:20210622003533j:plain
イメージ

EventSystem

まず、EventSystem が何をしているか。
EventSystem は、InputManager または InputSystem による入力監視処理が、一つだけ呼び出されるようにしています(排他制御)。また、EventSystem は現在選択されているゲームオブジェクト(SelectedGameObject)を保持する箱でもあり、選択が変更されたときにゲームオブジェクトに変更を伝えます(ISelectHandler、IDeselectHandler)。

EventSystem同士の連携(リスト化・排他制御

同一ゲーム内に複数のEventSystem があった場合、現在有効な EventSystem 以外は動かないようになっています。現在有効な EventSystem は、シングルトンである current です。(EventSystem.current)

f:id:tomo_mana:20210619225547p:plain
EventSystem管理(シングルトン構造)

この current と、現在アクセス可能な EventSystem たちを管理するシングルトンのリスト m_EventSystems があります。この EventSystemのリスト は、EventSystem コンポーネントを持つゲームオブジェクトがアクティブになった時に登録され、非アクティブになった時に登録解除されます。先述の current は、このリストの先頭にいる EventSystem です。

f:id:tomo_mana:20210619225635p:plain
EventSystemへの登録と解除、有効化

この リスト は private なので、EventSystem の外から リスト を直接書き変えることはできません。その代わりに、シーンが切り替わった時など、EventSystemを切り替える場合は、current にこれから使いたい EventSystem をセットします。これで、current にセットした EventSystem は、このリストの先頭になります。

InputManager と InputSystem の互換性

従来の入力システムである InputManager と、新しい入力システムである InputSystem 間の互換性は、両者の上位クラス BaseInputModule を用意することで吸収しています。

f:id:tomo_mana:20210619231232p:plain
EventSystemとBaseInputModule

このBaseInputModuleを継承したクラスは、入力が確定した時に、IEventSystemHandler を持つゲームオブジェクトにメッセージを伝える役割を果たします。また、入力が確定したことを調べるため、入力状態を管理(トレース)しています。例えばボタンを押した/離した、ドラッグした/離した、などです。
尚、InputSystemUIInputModule は、Navigation と呼ばれる仕組みを一緒に管理しています。これは経路探索などで使用できるようですが、まだ調べていないので、以下のリンクを参考に貼っておきます。
【Unity】Navigationを使って経路探索させる - Qiita

EventSystem と InputSystemUIInputModule の関係

EventSystem の役目

EventSystem は、InputSystemUIInputModule にとって、タイマーのような役目を果たします。

f:id:tomo_mana:20210619233914p:plain
EventSystemからのトリガ(Update)

EventSystem は、それ自身がコンポーネントのため、MonoBehavior(UIBehavior) を継承しています。MonoBehavior を継承するクラスは、UnityシステムからのUpdate()イベントを受信します。EventSystem は、このUpdate() のタイミング毎に、現在有効なInputSystemUIInputModule に、入力状態の確認(Process())を依頼します。

InputSystemUIInputModule 自身もコンポーネント(BaseInputModule が UIBehaviorを継承)のため、Update() を受け取れそうなものですが、EventSystem からのキックを待って動くようにできています。これは EventSystem側 で、BaseInputModule を排他制御させたいためと思われます。

EventSystem -> InputSystemUIInputModule

尚、EventSystem が InputSystemUIInputModule を一方的に参照している(親子)関係です。(が、InputSystemUIInputModule も、EventSystem にメッセージを投げることがあります)

f:id:tomo_mana:20210619233238p:plain
EventSystemとInputSystemUIInputModule


EventSystem から見て、現在有効なInputSystemUIInputModule は、リスト(m_CurrentInputModule)に保存されています。これはシングルトンではなく、各 EventSystemコンポーネント が持っています。

f:id:tomo_mana:20210619232916p:plain
EventSystem内のBaseInputModule群

EventSystem は、自分と同じゲームオブジェクト内の全ての InputSystemUIInputModule を認識します。ただし、複数の InputSystemUIInputModule があっても、有効な InputSystemUIInputModule は一つだけです。

ちなみに、このリストは GetComponent() で取得してくるだけで、登録順の入れ替えなどは特にしていないようですので、できれば EventSystem と同一ゲームオブジェクト内に BaseInputModule は一つだけ存在しているのが無難です。


これまでの内容を繋げると、冒頭の図になります。

f:id:tomo_mana:20210622003533j:plain


これで、複数シーンが起動していても、有効なEventSystems は常に一つであることが担保されています。また、有効な InputSystemUIInputModule も一つだけ、ということになります。

EventSystem <- InputSystemUIInputModule

EventSystem が InputSystemUIInputModule を一方的に参照している(親子)関係ですが、InputSystemUIInputModule も、EventSystem にメッセージを投げることがあります。それは、InputSystemUIInputModule が、ゲームオブジェクトの選択状態が変わったことを察知した時です。

InputSystemUIInputModule は、EventSystem.SetSelectedGameObject() を利用して、EventSystem にゲームオブジェクトの選択状態の変更を伝えます。その意味で、EventSystem と InputSystemUIInputModule は連携して動きます。


(以上)

Unity学習#34 複数シーン構成でのイベントシステムの切り替え (Unity 2019.4.4f1)

戦闘シーンの実装にあたって、イベントシステムはこれまでと同じ作り方でよいか迷ったので、先にゲームを複数シーンで構成する時の、イベントシステムの切り替えについて調べました。

複数シーンでのイベントシステムの切り替え

EventSystem を切り替えるには、EventSystem.current に、現在選択されているシーンの EventSystem を代入します

通常は、加算シーンで立ち上げておいて、アクティブなシーンを切り替えるタイミングで、EventSystem を切り替えます。この時、Camera(Transform系) と Canvas(RectTransform系)も併せて無効化します。また、EventSystem と連動して動く PlayerInput も無効化します。

コード

以下は、EventSystem、Camera、Canvas、PlayerInput がいずれも階層の一番上(Root)に属している場合のコードです。

static void ActivateSceneObjects(Scene scene)
{
    ForceSceneActivateState(scene, true);
}
static void DeactivateSceneObjects(Scene scene)
{
    ForceSceneActivateState(scene, false);
}
static void ForceSceneActivateState(Scene scene, bool state)
{
    GameObject[] gameObjects = scene.GetRootGameObjects();
    
    // カメラ
    foreach( GameObject g in gameObjects ){
        if( g.GetComponent<Camera>() != null ){
            g.SetActive(state);
            break;
        }
    }

    // イベントシステム
    foreach( GameObject g in gameObjects ){
        if( g.GetComponent<EventSystem>() != null ){
            g.SetActive(state);
            if( state == true ){
                // イベントシステムの切り替え
                EventSystem.current = g.GetComponent<EventSystem>();
            }
            break;
        }
    }

    // プレイヤーインプット
    foreach( GameObject g in gameObjects ){
        if( g.GetComponent<PlayerInput>() != null ){
            g.SetActive(state);
            break;
        }
    }

    // キャンバス
    foreach( GameObject g in gameObjects ){
        if( g.GetComponent<Canvas>() != null ){
            g.SetActive(state);
            break;
        }
    }
}

関連記事

EventSystem と InputSystemUIInputModule の関係

※作成中

EventSystem、InputSystemUIInputModule と PlayerInput

※作成中

InputUser と InputDevice/Control

※作成中

(以上)