// This code is part of the Fungus library (http://fungusgames.com) maintained by Chris Gregan (http://twitter.com/gofungus).
// It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE)
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using System.Collections.Generic;
namespace Fungus
{
///
/// Detects drag and drop interactions on a Game Object, and sends events to all Flowchart event handlers in the scene.
/// The Game Object must have Collider2D & RigidBody components attached.
/// The Collider2D must have the Is Trigger property set to true.
/// The RigidBody would typically have the Is Kinematic property set to true, unless you want the object to move around using physics.
/// Use in conjunction with the Drag Started, Drag Completed, Drag Cancelled, Drag Entered & Drag Exited event handlers.
///
public class Draggable2D : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{
[Tooltip("Is object dragging enabled")]
[SerializeField] protected bool dragEnabled = true;
[Tooltip("Move object back to its starting position when drag is cancelled")]
[FormerlySerializedAs("returnToStartPos")]
[SerializeField] protected bool returnOnCancelled = true;
[Tooltip("Move object back to its starting position when drag is completed")]
[SerializeField] protected bool returnOnCompleted = true;
[Tooltip("Time object takes to return to its starting position")]
[SerializeField] protected float returnDuration = 1f;
[Tooltip("Mouse texture to use when hovering mouse over object")]
[SerializeField] protected Texture2D hoverCursor;
[Tooltip("Use the UI Event System to check for drag events. Clicks that hit an overlapping UI object will be ignored. Camera must have a PhysicsRaycaster component, or a Physics2DRaycaster for 2D colliders.")]
[SerializeField] protected bool useEventSystem;
protected Vector3 startingPosition;
protected bool updatePosition = false;
protected Vector3 newPosition;
protected Vector3 delta = Vector3.zero;
#region DragCompleted handlers
protected List dragCompletedHandlers = new List();
public void RegisterHandler(DragCompleted handler)
{
dragCompletedHandlers.Add(handler);
}
public void UnregisterHandler(DragCompleted handler)
{
if(dragCompletedHandlers.Contains(handler))
{
dragCompletedHandlers.Remove(handler);
}
}
#endregion
protected virtual void LateUpdate()
{
// iTween will sometimes override the object position even if it should only be affecting the scale, rotation, etc.
// To make sure this doesn't happen, we force the position change to happen in LateUpdate.
if (updatePosition)
{
transform.position = newPosition;
updatePosition = false;
}
}
protected virtual void OnTriggerEnter2D(Collider2D other)
{
if (!dragEnabled)
{
return;
}
var eventDispatcher = FungusManager.Instance.EventDispatcher;
eventDispatcher.Raise(new DragEntered.DragEnteredEvent(this, other));
}
protected virtual void OnTriggerExit2D(Collider2D other)
{
if (!dragEnabled)
{
return;
}
var eventDispatcher = FungusManager.Instance.EventDispatcher;
eventDispatcher.Raise(new DragExited.DragExitedEvent(this, other));
}
protected virtual void DoBeginDrag()
{
// Offset the object so that the drag is anchored to the exact point where the user clicked it
float x = Input.mousePosition.x;
float y = Input.mousePosition.y;
delta = Camera.main.ScreenToWorldPoint(new Vector3(x, y, 10f)) - transform.position;
delta.z = 0f;
startingPosition = transform.position;
var eventDispatcher = FungusManager.Instance.EventDispatcher;
eventDispatcher.Raise(new DragStarted.DragStartedEvent(this));
}
protected virtual void DoDrag()
{
if (!dragEnabled)
{
return;
}
float x = Input.mousePosition.x;
float y = Input.mousePosition.y;
float z = transform.position.z;
newPosition = Camera.main.ScreenToWorldPoint(new Vector3(x, y, 10f)) - delta;
newPosition.z = z;
updatePosition = true;
}
protected virtual void DoEndDrag()
{
if (!dragEnabled)
{
return;
}
var eventDispatcher = FungusManager.Instance.EventDispatcher;
bool dragCompleted = false;
for (int i = 0; i < dragCompletedHandlers.Count; i++)
{
var handler = dragCompletedHandlers[i];
if (handler != null && handler.DraggableObject == this)
{
if (handler.IsOverTarget())
{
dragCompleted = true;
eventDispatcher.Raise(new DragCompleted.DragCompletedEvent(this));
}
}
}
if (!dragCompleted)
{
eventDispatcher.Raise(new DragCancelled.DragCancelledEvent(this));
if (returnOnCancelled)
{
LeanTween.move(gameObject, startingPosition, returnDuration).setEase(LeanTweenType.easeOutExpo);
}
}
else if(returnOnCompleted)
{
LeanTween.move(gameObject, startingPosition, returnDuration).setEase(LeanTweenType.easeOutExpo);
}
}
protected virtual void DoPointerEnter()
{
ChangeCursor(hoverCursor);
}
protected virtual void DoPointerExit()
{
SetMouseCursor.ResetMouseCursor();
}
protected virtual void ChangeCursor(Texture2D cursorTexture)
{
if (!dragEnabled)
{
return;
}
Cursor.SetCursor(cursorTexture, Vector2.zero, CursorMode.Auto);
}
#region Legacy OnMouseX methods
protected virtual void OnMouseDown()
{
if (!useEventSystem)
{
DoBeginDrag();
}
}
protected virtual void OnMouseDrag()
{
if (!useEventSystem)
{
DoDrag();
}
}
protected virtual void OnMouseUp()
{
if (!useEventSystem)
{
DoEndDrag();
}
}
protected virtual void OnMouseEnter()
{
if (!useEventSystem)
{
DoPointerEnter();
}
}
protected virtual void OnMouseExit()
{
if (!useEventSystem)
{
DoPointerExit();
}
}
#endregion
#region Public members
///
/// Is object drag and drop enabled.
///
/// true if drag enabled; otherwise, false.
public virtual bool DragEnabled { get { return dragEnabled; } set { dragEnabled = value; } }
#endregion
#region IBeginDragHandler implementation
public void OnBeginDrag(PointerEventData eventData)
{
if (useEventSystem)
{
DoBeginDrag();
}
}
#endregion
#region IDragHandler implementation
public void OnDrag(PointerEventData eventData)
{
if (useEventSystem)
{
DoDrag();
}
}
#endregion
#region IEndDragHandler implementation
public void OnEndDrag(PointerEventData eventData)
{
if (useEventSystem)
{
DoEndDrag();
}
}
#endregion
#region IPointerEnterHandler implementation
public void OnPointerEnter(PointerEventData eventData)
{
if (useEventSystem)
{
DoPointerEnter();
}
}
#endregion
#region IPointerExitHandler implementation
public void OnPointerExit(PointerEventData eventData)
{
if (useEventSystem)
{
DoPointerExit();
}
}
#endregion
}
}