UGUIで多重スクロールを作る


多重スクロールしたい!

少し調べたら、みんな大好きテラシュールブログに書いていましたが、
ちょっと違う部分もあるので、アドベントカレンダーの記事とさせていただきます。

ためしにスクロールビューの中にスクロールビューを入れてみる

こうしてみると、親の方のスクロールビュー(以下、親)は反応せず、
子供の方のスクロールビュー(以下、子供)しか反応しなくなります。
これは、スクロールイベントが、子供で消費されてしまうため、
親にイベントが渡されないからです。

親スクロールビューにイベントを渡す

子供で消費されるイベントを、親にも渡せれば、親が反応してくれます。
そのため、スクロールビュー(ScrollRect)を拡張し、渡されたイベントを親に通知する仕組みを作ることが考えられます。
UnityのuGUIで階層構造を持ったスクロールビューを作る

既存のスクロールビューに追加で多重スクロールされるようにしたい

ここでScrollRectを拡張せずに、親へイベントを渡すことを考えてみます。

実は、子供で消費されてしまうイベントですが、
同ゲームオブジェクトにアタッチされているコンポーネントに関しては、
同じようにイベントを受け取ることができます。

ですので、子供が受け取っているイベントを別コンポーネント経由で親に渡せば、
既存のScrollRectを使って多重スクロールを実現することができます。

以下ソース

MultiScroller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using System.Linq;

public class MultiScroller : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler {
    [SerializeField]
    private IDragHandler[] m_parentDragHandlers;
    private IBeginDragHandler[] m_parentBeginDragHandlers;
    private IEndDragHandler[] m_parentEndDragHandlers;

    private bool isSelf = false;
    void Start () {
        m_parentDragHandlers = this.GetComponentsInParent<IDragHandler>().Where(p => !(p is MultiScroller)).ToArray();
        m_parentBeginDragHandlers = this.GetComponentsInParent<IBeginDragHandler>().Where(p => !(p is MultiScroller)).ToArray();
        m_parentEndDragHandlers = this.GetComponentsInParent<IEndDragHandler>().Where(p => !(p is MultiScroller)).ToArray();
    }

    public void OnDrag(PointerEventData ped)
    {
        foreach(var dr in m_parentDragHandlers)
        {
            dr.OnDrag(ped);
        }
    }
    public void OnBeginDrag(PointerEventData ped)
    {
        foreach(var dr in m_parentBeginDragHandlers)
        {
            dr.OnBeginDrag(ped);
        }
    }
    public void OnEndDrag(PointerEventData ped)
    {
        foreach(var dr in m_parentEndDragHandlers)
        {
            dr.OnEndDrag(ped);
        }
    }
}

※GetComponentでinterfaceを取得可能なので覚えておくと便利です。

これを使うとこうなります。

おまけ

この、別オブジェクトに直接イベントを渡してやる方式ですが、
応用すると、イベント周りの拡張がしやすくなります。