こちらはUnity Advent Calendar11日目の記事となります。
前日はtoru_inoueさんによるタイムラインGUI TimeFlowShiki をオープンソースにしてみたでした。
今回のお題
本来はSOOMLA LevelUpのAssetに関する記事を書こうと思っていたのですが、火曜日にAsset Storeを見たらSOOMLA LevelUpが消えているという衝撃的な事実が発覚したため、急遽内容を変更させていただきましたorz
最近Unity UIでメニュー画面を作る際、ページやダイアログなど、要素ごとにシーンを分離してシームレスにロードするパターンがお気に入りです。
Unity5.3から追加されたマルチシーンエディット機能の恩恵も受けられますので、その点でもオススメ出来るかもしれません。
メリット
各シーンにページを1つだけ配置するので、遷移が多くなりがちなメニュー画面も細分化して管理しやすくなります。
また、分業しやすくなりますので、複数人数での開発も向いてるかもです。
デメリット
シーンを読み込むため、PrefabをInstantiateするのに比べるとオーバーヘッドが多くなります。
ただ、古めのAndroid端末でもほとんど違和感は感じませんので、大量にコールしない限りは問題ありません。
あとはBuild Settingで大量のシーンを登録する必要が出てきますが、これはご愛嬌ということで。
実装してみよう
では、実装してみましょう。
iTweenをインポート
ページ遷移のアニメーションに使いますので、Asset StoreからiTweenをインポートします。
iTween大好き。イェー。
スクリプトを準備
下記3つのスクリプトを準備します。
ページ管理スクリプト
ページを管理するスクリプトで、メニューの基幹シーンに配置します。
とりあえずデモということで、ボタン制御処理も含めています。
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class Pager : MonoBehaviour
{
// インスペクターでGoToHogePageButtonを紐付ける
public Button goToHogePageButton;
// インスペクターでCloseButtonを紐付ける
public Button closeButton;
// ページのスタック
private List<PageAbstract> pages = new List<PageAbstract>();
private void Start()
{
// 各ボタンにリスナーを追加
goToHogePageButton.onClick.AddListener(OnGoToHogePageButtonClick);
closeButton.onClick.AddListener(OnCloseButtonClick);
// Closeボタンは隠しておく
closeButton.gameObject.SetActive(false);
}
// ページをスタックします
public void AddSubMenu(PageAbstract page)
{
if (pages.Count == 0)
{
// スタックが0→1になる時にcloseボタン表示
closeButton.gameObject.SetActive(true);
}
pages.Add(page);
}
// ページを閉じます
public void RemoveSubMenu(PageAbstract page)
{
if (pages.Contains(page))
{
pages.Remove(page);
}
Destroy(page.gameObject);
if (pages.Count == 0)
{
// スタックが0になる時にcloseボタン隠す
closeButton.gameObject.SetActive(false);
}
}
// GoToHogePageボタンを押したときの処理です
private void OnGoToHogePageButtonClick()
{
// HogePageを開く
Application.LoadLevelAdditiveAsync("HogePage");
}
// Closeボタンを押したときの処理です
private void OnCloseButtonClick()
{
// 一番上に積まれているページを閉じる
var surfaceSubMenu = pages.LastOrDefault();
if (null != surfaceSubMenu)
{
surfaceSubMenu.Hide();
}
}
}
ページ基幹クラス
ページの量産を楽にするため、ページ基幹クラスを準備します。
ページ遷移アニメーションなどの共通処理はここに書いておくイメージです。
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
abstract public class PageAbstract : MonoBehaviour
{
// フェードイン/アウトアニメーションの秒数
private const float FADE_TIME = 0.3f;
// アニメーション表示させたいコンテンツ。Canvas直下のPanelでOK
public GameObject contents;
// ページのマネージャー
protected Pager pager;
// フェードアニメーション時のAlpha値調整のために、CanvasにCanvasGroupコンポーネントをアタッチしてここにセットしておく
protected CanvasGroup canvasGroup;
protected virtual void Start()
{
canvasGroup = gameObject.AddComponent<CanvasGroup>();
// Pagerを探す
var pagerGameObject = GameObject.Find("Pager");
if (null != pagerGameObject)
{
pager = pagerGameObject.GetComponent<Pager>();
pager.AddSubMenu(this);
}
else
{
Debug.Log("Pager not found.");
}
Show();
}
// ページの表示アニメーション
protected virtual void Show()
{
iTween.ValueTo(gameObject, iTween.Hash(
"from", 0f,
"to", 1f,
"time", FADE_TIME,
"onupdate", "OnValueUpdate"
));
iTween.MoveFrom(contents, iTween.Hash(
"y", -Screen.height,
"time", FADE_TIME,
"easetype", iTween.EaseType.linear));
}
// ページの非表示アニメーション
public virtual void Hide()
{
iTween.ValueTo(gameObject, iTween.Hash(
"from", 1f,
"to", 0f,
"time", FADE_TIME,
"onupdate", "OnValueUpdate"
));
iTween.MoveTo(contents, iTween.Hash(
"y", -Screen.height,
"time", FADE_TIME,
"easetype", iTween.EaseType.linear,
"oncomplete", "OnHideComplete",
"oncompletetarget", gameObject));
}
private void OnValueUpdate(float value)
{
canvasGroup.alpha = value;
}
private void OnHideComplete()
{
pager.RemoveSubMenu(this);
}
}
ページのスクリプト
最後に、ページのスクリプトです。
先ほど作った基幹クラスを継承させます。
中身は空っぽでOKです。
using UnityEngine;
using System.Collections;
public class HogePage : PageAbstract
{
}
メニューのシーンを準備
続いて、メニューのシーンを準備します。
Canvasを2つ作り、ひとつは「MainCanvas」、もうひとつは「SurfaceCanvas」と名前を付けます。
SurfaceCanvasは常に前面に表示したいので、CanvasコンポーネントのSort Orderを1にします。
MainCanvasに、ページ呼び出し(ページ遷移)用の「GoToHogePageButton」を、
SurfaceCanvasに、ページを閉じるための「CloseButton」を配置します。
ページ管理オブジェクトを配置
空のゲームオブジェクトを作り、名前を「Pager」にします。
Pager.csスクリプトをアタッチし、インスペクターでGoToHogePageButtonとCloseButtonをひも付けしておきます。
ここまでの準備が出来たら、シーンを保存しましょう。
シーンの名前は「Menu」にしておきます。
ページのシーンを準備
ページ用に新しいシーンを作成します。
画面遷移したことがわかりやすいように、Panelを配置して真っ赤にでもしておきましょうか。
次に、CanvasにHogePageスクリプトをアタッチします。
HogePageスクリプトのContentsにはPanelをひも付けます。
シーンをロードしたときに邪魔になりますので、Main Camera、Directional Light、EventSystemをシーンから削除します。
最後に、シーンを保存しましょう。
名前は「HogePage」にしておきます。
Build Settingsにシーンを登録する
先ほど作ったMenuとHogePageシーンを登録しておきます。
実行してみよう
Menuシーンを開き、Editorで実行します。
上手くページ遷移できましたか?
ページをたくさん作って連続でページ遷移させると、ちゃんとスタックしていきます。
マルチシーンエディット機能の恩恵
Unity5.3をお使いであれば、MenuシーンとHogePageシーンを両方開いた状態で実行してみてください。
すると、HogePageがちゃんとページスタックに積まれた状態で開きます。
これってちょっとステキなことですよね。
まとめ
管理が楽でページをガンガン増やせるので、結構重宝してます。
バリバリな方々はもっと素晴らしいパターンを使われているかと思いますので、ぜひ教えてくださいましm(_ _)m