この記事は2017年06月18日にqiitaに投稿した内容です。
環境
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;
}
}
}