【Unity】縦スクロールするViewに横スクロールするViewを格納する【ScrollView】

自分用のメモみたいなものです。
ScrollViewの使い方についてはGoogleで「Unity ScrollView」と検索すれば出てくると思います。

使用したUnityのバージョンは2017.3.1p4です。

スポンサーリンク

実現したいこと

今回実現したいのは、縦スクロールするViewの中に横スクロールするViewを格納することです。
何も考えずに縦スクロールのView(Vertical設定されたScrollView)に横スクロールのView(Horizontal設定されたScrollView)を格納すると、子オブジェクトとなるHorizontal設定されたScrollViewのイベントトリガーが発火してしまい、Vertical設定されたScrollViewが動作しません。

実現方法

横スクロールのView(Horizontal設定されたScrollView)に設定されているScrollRectコンポーネントを拡張し、タップの開始位置からどの方向にドラッグされたかで発火させるイベントトリガーを判断させます。

スクリプトは以下のような感じです。

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ChildScrollRect : ScrollRect
{

    private ScrollRect parentScrollRect;
    private Vector2 startPos;
    private bool eventFixed;
    private bool parentEvent;
    private bool parentEventInit;

    protected override void Start()
    {
        this.parentScrollRect = transform.parent.parent.parent.parent.GetComponent();
        base.Start();
    }

    public override void OnInitializePotentialDrag(PointerEventData eventData)
    {
        startPos = eventData.position;
        base.OnInitializePotentialDrag(eventData);
    }

    public override void OnBeginDrag(PointerEventData eventData)
    {
        base.OnBeginDrag(eventData);
    }

    public override void OnDrag(PointerEventData eventData)
    {
        if (!eventFixed)
        {
            Vector2 pos = eventData.position - startPos;
            float x = Mathf.Abs(pos.x);
            float y = Mathf.Abs(pos.y);
            if (x != y)
            {
                parentEvent = x < y; // 縦移動の方が多ければ親のイベントを発火
                eventFixed = true; // 親と子のどちらのイベントを実行するかをFix
            }
        }
        else
        {
            if (parentEvent)
            {
                if (!parentEventInit)
                {
                    // 親のイベント初期処理を実行
                    this.parentScrollRect.OnInitializePotentialDrag(eventData);
                    this.parentScrollRect.OnBeginDrag(eventData);
                    parentEventInit = true;
                }
                this.parentScrollRect.OnDrag(eventData);
            }
            else
            {
                base.OnDrag(eventData);
            }
        }

    }

    public override void OnEndDrag(PointerEventData eventData)
    {
        if (parentEvent)
        {
            this.parentScrollRect.OnEndDrag(eventData);
        }
        else
        {
            base.OnEndDrag(eventData);
        }
        // フラグ関連を初期化
        parentEvent = false;
        parentEventInit = false;
        eventFixed = false;
    }
}

parentScrollRectの取得については各々の環境に合わせて書き換えてください。
※SerializeFieldにしてもInspectorで設定できなかったので、多分Findか親参照で取得するしかないです。

最終的にHierarchyとInspectorはこんな感じになると思います。

動作はこんな感じです。