複数の UI (ユーザーインターフェース) のスクリーン間を移動することが必要なのはかなり一般的です。ここでは、各スクリーンを動かし制御するアニメーションやステートマシンを使用して、複数の UI スクリーンの移動を作成し管理する簡単な方法を学びます。
高レベルの考えは私達のそれぞれのスクリーンが アニメーターコントローラーと 2 つの アニメーションステート (Open and Closed)とブーリアン アニメーションパラメーター (Open)を持っているということです。スクリーン間の遷移には、あなたは現在開いているスクリーンを閉じ、望むひとつを開く必要があります。このプロセスを容易にするために、追跡をし、あらゆるすでに開いているスクリーンを閉じるように世話をする小さなクラス ScreenManager をつくります。遷移をトリガーするボタンは、ScreenManager に望むスクリーンを開けるように依頼する必要があります。
UI 要素のコントローラー/キーボードのナビゲーションをサポートする予定の場合、念頭に置くべき重要なことがいくつかあります。まず、画面外に選択可能な要素を置かないようにします。なぜなら、それにより、プレーヤーがオフ画面の要素を選択することが可能になってしまうからです。それを回避するには、すべてのオフ画面のヒエラルキーを無効にします。また、新しい画面を表示するとき、間違いなく要素が 1 つ選択されているように設定する必要があります。さもなければ、プレイヤーは新しい画面に移動することができません。これらの操作は、後述の ScreenManager クラスで詳しく説明します。
アニメーションコントローラーが画面遷移を行うための、もっとも一般的な最小限の設定を見てみましょう。コントローラーには、ブーリアンパラメーター (Open) と 2 つのステート (Open と Closed) が必要で、各ステートには、1 つだけキーフレームを持つアニメーションが必要です。このようにすると、ステートマシンは遷移ブレンドを行います。
今、両方のステート間の アニメーション遷移 を作成する必要があります。それではオープンからクローズへの移行から始め、適切に条件を設定してみましょう。パラメーターの Open が false に設定されているときには、Open から Closed に移動します。今度は Closed からOpen に遷移を作成し、パラメーター Open が true であるとき、Closed から Open に行くための条件を設定します。
上記のすべてのセットアップで、唯一不足しているものは、スクリーンアニメーターでオープンパラメーターを true に設定し、現在開いているスクリーンアニメーターの false をオープンにして遷移するようにすることです。そのために、私たちはそれを引き受ける スクリプトをつくります。
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
public class ScreenManager : MonoBehaviour {
//シーンの開始時に自動的に開く画面
public Animator initiallyOpen;
//現在開いているシーン
private Animator m_Open;
//遷移を制御するために使用するパラメーターのハッシュ
private int m_OpenParameterId;
//現在の画面を開く前に選択されたゲームオブジェクト
//画面を閉じるのに使用。すると、それを開いたボタンにもどります。
private GameObject m_PreviouslySelected;
//確認すべきアニメーターステートと遷移名
const string k_OpenTransitionName = "Open";
const string k_ClosedStateName = "Closed";
public void OnEnable()
{
//ハッシュを "Open" パラメーターにキャッシュし、Animator.SetBool に供給できます。
m_OpenParameterId = Animator.StringToHash (k_OpenTransitionName);
//設定すると、最初の画面が開きます。
if (initiallyOpen == null)
return;
OpenPanel(initiallyOpen);
}
//現在開いているパネルを閉じ、指定したものを開きます。
//さらに、ナビゲーションの処理と新しく選択した要素の設定もおこないます。
public void OpenPanel (Animator anim)
{
if (m_Open == anim)
return;
//新しい画面のヒエラルキーをアクティベートして、それを動かすことができます。
anim.gameObject.SetActive(true);
//画面を開くのに使用した現在選択中のボタンを保存します (CloseCurrent はそれを変更します)
var newPreviouslySelected = EventSystem.current.currentSelectedGameObject;
//画面を前方に移動
anim.transform.SetAsLastSibling();
CloseCurrent();
m_PreviouslySelected = newPreviouslySelected;
//新しい画面を設定し、開きます
m_Open = anim;
//画面を開くアニメーションを開始
m_Open.SetBool(m_OpenParameterId, true);
//新しく選択された要素として、新しい画面に設定します
GameObject go = FindFirstEnabledSelectable(anim.gameObject);
SetSelected(go);
}
//与えられたヒエラルキー内で最初の選択可能な要素を見つけます
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;
}
//現在開いている画面を閉じます。
//移動も処理します。
//現在の画面を開く前に使用した選択可能な要素に戻します。
public void CloseCurrent()
{
if (m_Open == null)
return;
//画面を閉じるアニメーションを始めます
m_Open.SetBool(m_OpenParameterId, false);
//現在の画面を開く前に使用した選択可能な要素に戻します。
SetSelected(m_PreviouslySelected);
//画面を閉じるアニメーションが終了するときに、ヒエラルキーを無効にするコルーチンを開始します。
StartCoroutine(DisablePanelDeleyed(m_Open));
//画面をすべて閉じます。
m_Open = null;
}
// 画面を閉じるアニメーションの終了を検知し、
//ヒエラルキーをディアクティベートするコルーチン。
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);
}
//指定したゲームオブジェクトを選択させます。
//マウス/タッチパネルを使用しているときは、実際には前に選択したものとして設定し、
//現在の選択には何も設定しません。
private void SetSelected(GameObject go)
{
//ゲームオブジェクトを選択します。
EventSystem.current.SetSelectedGameObject(go);
//たった今、キーボードを使っているのなら、それだけで十分です。
var standaloneInputModule = EventSystem.current.currentInputModule as StandaloneInputModule;
if (standaloneInputModule != null && standaloneInputModule.inputMode == StandaloneInputModule.InputMode.Buttons)
return;
//ポインターデバイスを使用しているので、何も選択する必要はありません。
//しかし、ユーザーがキーボードに切り替えると、提供されたゲームオブジェクトから移動を開始する必要があります。
//ここでは、現在の選択を nullに設定します。それにより、指定したゲームオブジェクトが EventSystem の最後の選択になります。
EventSystem.current.SetSelectedGameObject(null);
}
}
それではこのスクリプトについて説明しましょう。まず、新しいゲームオブジェクトを作成し、その名前を例えば ScreenManager と変更します。そしてそれにコンポーネントを追加します。それを最初の画面に指定し、シーンの開始時に開きます。
今度は、画面の終了部分のために、ボタン を作ってみましょう。画面遷移を起動するボタンを選択し、インスペクターで On Click () リストの下に新しいアクションを追加します。先ほどオブジェクトフィールドで作成した ScreenManager ゲームオブジェクトをドラッグし、ドロップダウンで ScreenManager->OpenPanel (Animator) を選択します。そして、ユーザーがボタンをクリックすると、開くようにしたいパネルをオブジェクトフィールドにドラッグ&ドロップします。
この技術は、それを動作させるために必要とする要件としてそれぞれのスクリーンにアニメータコントローラと共にオープンパラメータとクローズドステートが設定されているという利点があります。あなたのスクリーンか、または、あなたのステートマシンがどうのように構築されるかといった詳細とは完全に無関係です。そして、入れ子になったスクリーンとうまく動作し、ちょうど入れ子になったレベルごとに 1 つの ScreenManager が必要になります。
上記のセットアップしたステートマシンはクローズドのデフォルトのステートを持っていたので、このコントローラーを使用するスクリーンのすべては、クローズドで開始します。そういうわけで ScreenManager の上で、私たちは initiallyOpen のプロパティーを提供するので、あなたは示すべき最初のスクリーンが何であるかを指定することができます。