スクリプトから UI 要素を作成する
Immediate Mode GUI (IMGUI)

画面遷移の作成

複数の UI (ユーザーインターフェース) のスクリーン間を移動することが必要なのはかなり一般的です。ここでは、各スクリーンを動かし制御するアニメーションやステートマシンを使用して、複数の UI スクリーンの移動を作成し管理する簡単な方法を学びます。

概要

高レベルの考えは私達のそれぞれのスクリーンが アニメーターコントローラーと 2 つの アニメーションステート (Open and Closed)とブーリアン アニメーションパラメーター (Open)を持っているということです。スクリーン間の遷移には、あなたは現在開いているスクリーンを閉じ、望むひとつを開く必要があります。このプロセスを容易にするために、追跡をし、あらゆるすでに開いているスクリーンを閉じるように世話をする小さなクラス ScreenManager をつくります。遷移をトリガーするボタンは、ScreenManager に望むスクリーンを開けるように依頼する必要があります。

ナビゲーションについて

あなたが UI 要素のコントローラー/キーボードのナビゲーションをサポートする予定の場合、それを念頭に置いて重要ないくつかのことがあります。これは、スプレーヤーがオフスクリーン要素を選択することができるので、すなわち、スクリーン外で選択可能な要素がないようにすることが重要です。私たちはすべてのオフスクリーンヒエラルキーをネガティブにすることでそれを行うことができます。私たちはまた、新しいスクリーンを選択したとして、それから要素を設定して表示されるときに確認する必要があり、さもなければ、プレイヤーは新しいスクリーンに移動することができません。私たちは下記の ScreenManager クラスですべてのそのようなことをケアします。

アニメーターコントローラーの設定

アニメーションコントローラーがスクリーン遷移を行うための、もっとも一般的な最小限の設定を見てみましょう。コントローラーは、ブール·パラメーター(オープン)と 2 つのステート(オープンとクローズ)が必要で、各ステートは、一つのキーフレームでアニメーションを持っていなければなりません。この方法でステートマシンに遷移ブレンドをやらせます。

オープンステートとアニメーション
オープンステートとアニメーション
クローズステーションとアニメーション
クローズステーションとアニメーション

今、私たちは両方のステート間の アニメーション遷移 を作成する必要があります。それではオープンからクローズへの移行から始め、適切に条件を設定してみましょう。オープンパラメーターが false に設定されているときにオープンからクローズに行きたいです。今、私たちはクローズドからオープンにトランジションを作成し、パラメーター・オープンが true であるとき、クローズドからオープンに行くための条件を設定してください。

クローズドからオープンへの移行
クローズドからオープンへの移行
オープンからクローズへの移行
オープンからクローズへの移行

スクリーンの管理

上記のすべてのセットアップで、唯一不足しているものは、スクリーンアニメーターでオープンパラメーターを true に設定し、現在開いているスクリーンアニメーターの false をオープンにして遷移するようにすることです。そのために、私たちはそれを引き受ける スクリプトをつくります。

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;

public class ScreenManager : MonoBehaviour {

    //Screen to open automatically at the start of the Scene
    public Animator initiallyOpen;

    //Currently Open Screen
    private Animator m_Open;

    //Hash of the parameter we use to control the transitions.
    private int m_OpenParameterId;

    //The GameObject Selected before we opened the current Screen.
    //Used when closing a Screen, so we can go back to the button that opened it.
    private GameObject m_PreviouslySelected;

    //Animator State and Transition names we need to check against.
    const string k_OpenTransitionName = "Open";
    const string k_ClosedStateName = "Closed";

    public void OnEnable()
    {
        //We cache the Hash to the "Open" Parameter, so we can feed to Animator.SetBool.
        m_OpenParameterId = Animator.StringToHash (k_OpenTransitionName);

        //If set, open the initial Screen now.
        if (initiallyOpen == null)
            return;
        OpenPanel(initiallyOpen);
    }

    //Closes the currently open panel and opens the provided one.
    //It also takes care of handling the navigation, setting the new Selected element.
    public void OpenPanel (Animator anim)
    {
        if (m_Open == anim)
            return;

        //Activate the new Screen hierarchy so we can animate it.
        anim.gameObject.SetActive(true);
        //Save the currently selected button that was used to open this Screen. (CloseCurrent will modify it)
        var newPreviouslySelected = EventSystem.current.currentSelectedGameObject;
        //Move the Screen to front.
        anim.transform.SetAsLastSibling();

        CloseCurrent();

        m_PreviouslySelected = newPreviouslySelected;

        //Set the new Screen as then open one.
        m_Open = anim;
        //Start the open animation
        m_Open.SetBool(m_OpenParameterId, true);

        //Set an element in the new screen as the new Selected one.
        GameObject go = FindFirstEnabledSelectable(anim.gameObject);
        SetSelected(go);
    }

    //Finds the first Selectable element in the providade hierarchy.
    static GameObject FindFirstEnabledSelectable (GameObject gameObject)
    {
        GameObject go = null;
        var selectables = gameObject.GetComponentsInChildren<Selectable> (true);
        foreach (var selectable in selectables) {
            if (selectable.IsActive () && selectable.IsInteractable ()) {
                go = selectable.gameObject;
                break;
            }
        }
        return go;
    }

    //Closes the currently open Screen
    //It also takes care of navigation.
    //Reverting selection to the Selectable used before opening the current screen.
    public void CloseCurrent()
    {
        if (m_Open == null)
            return;

        //Start the close animation.
        m_Open.SetBool(m_OpenParameterId, false);

        //Reverting selection to the Selectable used before opening the current screen.
        SetSelected(m_PreviouslySelected);
        //Start Coroutine to disable the hierarchy when closing animation finishes.
        StartCoroutine(DisablePanelDeleyed(m_Open));
        //No screen open.
        m_Open = null;
    }

    //Coroutine that will detect when the Closing animation is finished and it will deactivate the
    //hierarchy.
    IEnumerator DisablePanelDeleyed(Animator anim)
    {
        bool closedStateReached = false;
        bool wantToClose = true;
        while (!closedStateReached && wantToClose)
        {
            if (!anim.IsInTransition(0))
                closedStateReached = anim.GetCurrentAnimatorStateInfo(0).IsName(k_ClosedStateName);

            wantToClose = !anim.GetBool(m_OpenParameterId);

            yield return new WaitForEndOfFrame();
        }

        if (wantToClose)
            anim.gameObject.SetActive(false);
    }

    //Make the provided GameObject selected
    //When using the mouse/touch we actually want to set it as the previously selected and 
    //set nothing as selected for now.
    private void SetSelected(GameObject go)
    {
        //Select the GameObject.
        EventSystem.current.SetSelectedGameObject(go);

        //If we are using the keyboard right now, that's all we need to do.
        var standaloneInputModule = EventSystem.current.currentInputModule as StandaloneInputModule;
        if (standaloneInputModule != null && standaloneInputModule.inputMode == StandaloneInputModule.InputMode.Buttons)
            return;

        //Since we are using a pointer device, we don't want anything selected. 
        //But if the user switches to the keyboard, we want to start the navigation from the provided game object.
        //So here we set the current Selected to null, so the provided gameObject becomes the Last Selected in the EventSystem.
        EventSystem.current.SetSelectedGameObject(null);
    }
}

それではこのスクリプトを取り上げてみましょう。私たちは新しいゲームオブジェクトを作成することでこれを行い、例えばそれを ScreenManager と名前を変更することができ、そしてそれに上記コンポーネントを追加できます。あなたは、最初のスクリーンをそれに割り当てることができ、このスクリーンは、あなたのシーンの開始時に開きます。

今、最後の部分のために、ボタン を動かしてみましょう。スクリーン遷移を起動し、インスペクターで On Click () リストの下に新しいアクションを追加する必要があるボタンを選択します。先ほど ObjectField に作成した ScreenManager の GameObject をドラッグし、選択した ScreenManager->OpenPanel (Animator) の上にドロップダウンします。そして、ユーザーが ObjectField のボタンをクリックしたときに開いてほしいパネルをドラッグ&ドロップします。

ボタンのインスペクター
ボタンのインスペクター

注意

この技術は、それを動作させるために必要とする要件としてそれぞれのスクリーンにアニメータコントローラと共にオープンパラメータとクローズドステートが設定されているという利点があります。あなたのスクリーンか、または、あなたのステートマシンがどうのように構築されるかといった詳細とは完全に無関係です。そして、入れ子になったスクリーンとうまく動作し、ちょうど入れ子になったレベルごとに 1 つの ScreenManager が必要になります。

上記のセットアップしたステートマシンはクローズドのデフォルトのステートを持っていたので、このコントローラーを使用するスクリーンのすべては、クローズドで開始します。そういうわけで ScreenManager の上で、私たちは initiallyOpen のプロパティーを提供するので、あなたは示すべき最初のスクリーンが何であるかを指定することができます。

スクリプトから UI 要素を作成する
Immediate Mode GUI (IMGUI)