LoginSignup
1
6

More than 5 years have passed since last update.

スクロールビューのアイテムをドラッグ&ドロップできるようにする

Last updated at Posted at 2017-06-17

環境

Unity5.6.1f1

概要

水平スクロールビューのアイテム(の子供のイメージ等)をドラッグ&ドロップできるようにします

プログラム

シーンにはドロップ対象のエリアとなるImage等のGameObjectを配置しておく必要があります
ドロップ時にEventSystem.current.RaycastAllでドロップ対象のGameObjectを判定しています

Assets/DragItem.cs

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class DragItem : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    public enum EventState
    {
        None,
        BeginDrag,
        Drag,
        Drop,
    }
    public Action<DragItem,Transform> onUpdateState = null;
    public PointerEventData pointerEventData { get{ return _pointerEventData; } }
    public EventState eventState { get{ return _eventState; } }
    public Transform originalParent { get{ return _originalParent; } }

    [SerializeField] private Transform _draggingParent = null; //ドラッグ中はここの子になる
    [SerializeField] private float _upBasedBeginDragDegree = 70.0f; //ドラッグ選択判定の角度
    [SerializeField] private float _upBasedBeginDragDistance = 10.0f; //ドラッグ選択判定の距離

    private ScrollRect _scrollRectParent = null;
    private Transform _originalParent = null;
    private Vector3 _originalLocalPos;
    private PointerEventData _upDragPointerEventData = null;
    private PointerEventData _pointerEventData = null;
    private EventState _eventState = EventState.None;

    private void Start()
    {
        _scrollRectParent = GetComponentInParent<ScrollRect>();
    }

    void IPointerDownHandler.OnPointerDown( PointerEventData eventData )
    {
        _upDragPointerEventData = eventData;
    }

    void IPointerUpHandler.OnPointerUp( PointerEventData eventData )
    {
        if( _upDragPointerEventData != null && _upDragPointerEventData.pointerId == eventData.pointerId )
            _upDragPointerEventData = null;
        EndDrag( eventData );
    }

    void IBeginDragHandler.OnBeginDrag( PointerEventData eventData )
    {
        if( _scrollRectParent != null )
            _scrollRectParent.OnBeginDrag( eventData );
    }

    void IDragHandler.OnDrag( PointerEventData eventData )
    {
        if( _upDragPointerEventData != null && _upDragPointerEventData.pointerId == eventData.pointerId )
        {
            var vec = _upDragPointerEventData.position - _upDragPointerEventData.pressPosition;
            var lenSq = vec.sqrMagnitude;
            var dot = Vector2.Dot( vec.normalized, Vector2.up );
            var ac = Mathf.Acos( dot ) * Mathf.Rad2Deg;
            if( ac < _upBasedBeginDragDegree )
            {
                if( lenSq > ( _upBasedBeginDragDistance * _upBasedBeginDragDistance ) )
                {
                    StartDrag( _upDragPointerEventData );
                    _upDragPointerEventData = null;
                }
            }
        }

        var isUpdate = UpdateDrag( eventData );
        if( isUpdate == false )
        {
            if( _scrollRectParent != null )
                _scrollRectParent.OnDrag( eventData );
        }
    }

    void IEndDragHandler.OnEndDrag( PointerEventData eventData )
    {
        if( _scrollRectParent != null )
            _scrollRectParent.OnEndDrag( eventData );
    }

    public bool Reset()
    {
        _eventState = EventState.None;
        transform.SetParent( _originalParent, false );
        transform.localPosition = _originalLocalPos;
        return true;
    }

    private void StartDrag( PointerEventData eventData )
    {
        _eventState = EventState.BeginDrag;
        _pointerEventData = eventData;
        _originalParent = transform.parent;
        _originalLocalPos = transform.localPosition;
        transform.SetParent( _draggingParent, true );

        if( onUpdateState != null )
            onUpdateState.Invoke( this, null );
    }

    private bool UpdateDrag( PointerEventData eventData )
    {
        if( _pointerEventData == null || _pointerEventData.pointerId != eventData.pointerId )
            return false;

        _eventState = EventState.Drag;
//      transform.SetParent( _draggingParent );
//      transform.position = eventData.pressEventCamera.ScreenToWorldPoint( eventData.position );
        transform.SetParent( _draggingParent, false );
        Vector2 pos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle( _draggingParent as RectTransform, eventData.position, eventData.pressEventCamera, out pos );
        transform.localPosition = new Vector3( pos.x, pos.y, transform.position.z );

        var local = transform.localPosition;
        local.z = 0.0f;
        transform.localPosition = local;

        if( onUpdateState != null )
            onUpdateState.Invoke( this, null );
        return true;
    }

    private void EndDrag( PointerEventData eventData )
    {
        if( _pointerEventData == null || _pointerEventData.pointerId != eventData.pointerId )
            return;

        _eventState = EventState.Drop;
        var pointer = new PointerEventData( EventSystem.current );
        pointer.position = eventData.position;
        var results = new List<RaycastResult>();
        EventSystem.current.RaycastAll( pointer, results );
        Transform dropTarget = null;
        if( results.Count >= 2 )
        {
            for( int i = 0; i < results.Count; i++ )
            {
                var result = results[ i ];
                if( gameObject == result.gameObject )
                {
                    var targetIndex = i + 1;
                    if( targetIndex < results.Count )
                    {
                        Debug.Log( "targetIndex:" + targetIndex );
                        dropTarget = results[ targetIndex ].gameObject.transform;
                        break;
                    }
                    break;
                }
            }
        }

        if( onUpdateState != null )
            onUpdateState.Invoke( this, dropTarget );
        _pointerEventData = null;
    }
}

Assets/TestDragItem.cs

using UnityEngine;
using UnityEngine.UI;

public class DragItemTest : MonoBehaviour
{
    [SerializeField] private ScrollRect _scrollRect = null;
    [SerializeField] private GameObject _itemPrefab = null;

    private void Start()
    {
        var content = _scrollRect.content;
        for( int i = 0; i < 20; i++ )
        {
            var itemObj = Instantiate<GameObject>( _itemPrefab );
            itemObj.SetActive( true );

            itemObj.name = i.ToString();
            var text = itemObj.GetComponentInChildren<Text>();
            text.text = "Drag&Drop" + i;

            //itemObjの子供にImageのGameObjectがあり、そこにDragItemもセットされている想定
            var dragItem = itemObj.GetComponentInChildren<DragItem>();
            dragItem.onUpdateState = OnDragItemUpdateState;

            itemObj.transform.SetParent( content, false );
        }
        _itemPrefab.gameObject.SetActive( false );
    }

    private void OnDragItemUpdateState( DragItem dragItem, Transform dropTarget )
    {
        var state = dragItem.eventState;
        switch( state )
        {
            case DragItem.EventState.BeginDrag:
                Debug.Log( "BeginDrag:" + dragItem.name );
                break;
            case DragItem.EventState.Drag:
                break;
            case DragItem.EventState.Drop:
                if( dropTarget != null )
                {
                    Debug.Log( "Drop:" + dragItem.originalParent.name + " dropTarget:" + dropTarget.name );
                    var content = _scrollRect.content;
                    Destroy( dragItem.originalParent.gameObject );
                    Destroy( dragItem.gameObject );
                }
                else
                {
                    Debug.LogWarning( "Item None" );
                }
                break;
        }
    }

}
1
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
6