[Unity]NGUI-UIScrollViewの縦横併用

前回は、nginxのパフォーマンス改善について御紹介しました。
今回は、ストリーミング再生から少し逸れますが、NGUI(Unityアセッツ)のUIScrollViewについてご紹介します。

Unityで人気のアセッツにNGUIというものがあります。
有料ですが、AssetsStoreで公開されており、便利な機能が多く用意されているアセッツです。

NGUIのコンポーネントの一部に、UIScrollViewというものが存在があります。
非常に簡単にスクロールUIを作成することが可能で、UIGridやUIDragScrollViewというコンポーネントを併せて使うことによってPageViewのようなUIもすぐに作ることが出来てしまいます。
ただ、過去に行った開発で縦と横のスクロールUIを併用するケースがあり、各方向のUIScrollViewを親子関係にしたところ、ドラッグイベントで衝突が起きてしまい、うまく動作しませんでした。
結果的に、実現はできましたが、スクリプトを使用して少し工夫する必要があったため、今回はそちらの方法をご紹介いたします。

使用したUnityのバージョンは、少し古いですが4.6.9です。
前提として、横方向にスライドするUIScrollViewの上に、縦方向にスライドするUIScrollViewを設置するオブジェクトを作ることとします。

まずは、横方向の身にスライドするUIScrollViewの作成です。

  1. EmptyなGameObjectを作成して、UIScrollViewコンポーネントをアタッチ
    ※MovementはHorizontalにします
  2. 作成したUIScrollView用オブジェクトと同階層に、新たにGameObjectを作成して、UIDragScrollViewコンポーネントをアタッチ
  3. GameObjectを新規作成して、UIGrid/UICenterOnChildコンポーネントをアタッチ
    ※こちらは任意となります
  4. 適当なオブジェクトをUIScrollViewの子供として追加します。
    ※UIGridを作成した方はUIGridオブジェクトの子供、作成していない方はUIScrollViewの子供として設定してください

各種オブジェクトの名前は適当に設定してください。

■横スライドのUIScrollView作成後の、ノードツリー
例では、PageというEmptyなGameObjectを作成し、それぞれを識別しやすいように色分けした背景を設置しています。

※MainSceneはUIRoot/UIPanel、Cameraは各オブジェクトを表示するためのUICameraです

この段階で横スクロール可能なUIScrollViewが作成できました。
次に、先ほど作ったオブジェクト(※例ではPage)内に、縦スライドのUIScrollViewを設置します。

  1. EmptyなGameObjectを作成して、UIScrollViewコンポーネントをアタッチし、オブジェクト内に配置
    ※MovementはVerticalにします
  2. 作成したUIScrollView用オブジェクトと同階層に、新たにGameObjectを作成して、UIDragScrollViewコンポーネントをアタッチ
  3. 適当なオブジェクトをUIScrollViewの子供として追加します。

■縦スライドのUIScrollView作成後の、ノードツリー

では、この状態で一度動かしてみてください。
おそらく、横スライドが動作せずに縦スライドだけが動かせる状態になっていると思います。
このままでは、縦横併用スライドを作ることが出来ないため、UIDragScrollViewを継承したオリジナルのコンポーネントを使用します。

■オリジナルUIDragScrollView(CustomDragScrollView.cs)

using UnityEngine;

public class CustomDragScrollView : UIDragScrollView
{
    enum DragState {
        NONE,
        HORIZONTAL,
        VERTICAl
    }

    private DragState _dragState = DragState.NONE;

    [SerializeField]
    private UIScrollView _parentScrollView = null;

    public void OnDragEnd()
    {
        //状態を元に戻す
        _dragState = DragState.NONE;
        if (_parentScrollView != null)
        {
            if (_parentScrollView.isDragging)
                _parentScrollView.Press(false);
        }
    }

    override protected void OnDrag(Vector2 delta)
    {
        if (_dragState != DragState.HORIZONTAL && Mathf.Abs(delta.x) > Mathf.Abs(delta.y))
        {
            //横のスクロールと判定
            _dragState = DragState.VERTICAl;
            if (_parentScrollView != null)
            {
                if (!_parentScrollView.isDragging)
                    _parentScrollView.Press(true);

                //ページスクロールビューにイベントを流す
                _parentScrollView.Drag();
            }
        }
        else if (_dragState != DragState.VERTICAl)
        {
            //縦のスクロールと判定
            _dragState = DragState.HORIZONTAL;

            //通常通り、縦スクロールイベントを実行
            base.OnDrag(delta);
        }
    }
}

上記のスクリプトを保存し、Assetsフォルダ内の適当な場所に保存してください。

先ほど作成した縦方向のUIScrollViewオブジェクトからUIScrollViewを削除し、保存したCustomDragScrollViewをアタッチします。
InspectorのCustomDragScrollView内に”Parent Scroll View”が表示されるので、そこに横方向のUIScrollViewオブジェクトを設定します。

これで縦横併用の設定は全て終了です。

ポイントは、OnDrag関数内の処理です。
NGUIのUIDragScrollViewクラスに用意されているOnDragイベントは、ドラッグが発生した際にタッチ座標の移動量を引数に実行されるため
その関数自体をオーバーライドし、NGUI標準の処理が実行される前に、縦横どちらのスライドを行うのかをハンドリングします。
deltaの型はVector2であるため、x/yの移動量を比較し、大きい値の方向をその時のスクロールイベントとして扱うようにしています。
今回の例では、このスクリプトと対になるUIScrollViewは縦方向のため、xの移動量が大きい時に_parentScrollViewのDrag関数を直接実行することによって、
縦方向のDragScrollViewで発生したドラッグイベントを、横方向のUIScrollViewにほうに流しています。
ただUIScrollViewのDrag関数は、ドラッグ状態の時以外は処理を行わないようになっているため、ドラッグ状態ではないときに一度だけPress関数を実行し、対象のUIScrollViewオブジェクトを強制的にドラッグ状態にすることで動作させています。
そして最後に、OnDragEnd関数でドラッグ終了を検知し、縦方向のUIScrollViewのisDraggingフラグを落として終了となります。

いかがでしょうか?
このようにコンポーネントを拡張することで、NGUIを使用することで便利になったUI作成の幅を更に広げることが出来ます。
他にもいろいろなコンポーネントを自作して、効率的なアプリ開発を目指してみてください。


弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*