/** * 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; 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 { [Tooltip("Is object dragging enabled")] public bool dragEnabled = true; [Tooltip("Move object back to its starting position when drag is cancelled")] [FormerlySerializedAs("returnToStartPos")] public bool returnOnCancelled = true; [Tooltip("Move object back to its starting position when drag is completed")] public bool returnOnCompleted = true; [Tooltip("Time object takes to return to its starting position")] public float returnDuration = 1f; [Tooltip("Mouse texture to use when hovering mouse over object")] public Texture2D hoverCursor; protected Vector3 startingPosition; protected bool updatePosition = false; protected Vector3 newPosition; protected Vector3 delta = Vector3.zero; protected virtual void Start() { // If the main camera doesn't already have a Physics2DRaycaster then add one automatically to // use UI raycasts for hit detection. This allows UI to block clicks on objects behind. if (Camera.main == null) return; var raycast = Camera.main.GetComponent(); if (raycast == null) { Camera.main.gameObject.AddComponent(); } } 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; } foreach (DragEntered handler in GetHandlers()) { handler.OnDragEntered(this, other); } foreach (DragCompleted handler in GetHandlers()) { handler.OnDragEntered(this, other); } } protected virtual void OnTriggerExit2D(Collider2D other) { if (!dragEnabled) { return; } foreach (DragExited handler in GetHandlers()) { handler.OnDragExited(this, other); } foreach (DragCompleted handler in GetHandlers()) { handler.OnDragExited(this, other); } } protected virtual T[] GetHandlers() where T : EventHandler { // TODO: Cache these object for faster lookup return GameObject.FindObjectsOfType(); } protected virtual void OnMouseEnter() { changeCursor(hoverCursor); } protected virtual void OnMouseExit() { SetMouseCursor.ResetMouseCursor(); } protected virtual void changeCursor(Texture2D cursorTexture) { if (!dragEnabled) { return; } Cursor.SetCursor(cursorTexture, Vector2.zero, CursorMode.Auto); } #region IBeginDragHandler implementation public void OnBeginDrag(PointerEventData eventData) { // 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; foreach (DragStarted handler in GetHandlers()) { handler.OnDragStarted(this); } } #endregion #region IDragHandler implementation public void OnDrag(PointerEventData eventData) { 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; } #endregion #region IEndHandler implementation public void OnEndDrag(PointerEventData eventData) { if (!dragEnabled) { return; } bool dragCompleted = false; DragCompleted[] handlers = GetHandlers(); foreach (DragCompleted handler in handlers) { if (handler.draggableObject == this) { if (handler.IsOverTarget()) { handler.OnDragCompleted(this); dragCompleted = true; if (returnOnCompleted) { LeanTween.move(gameObject, startingPosition, returnDuration).setEase(LeanTweenType.easeOutExpo); } } } } if (!dragCompleted) { foreach (DragCancelled handler in GetHandlers()) { handler.OnDragCancelled(this); } if (returnOnCancelled) { LeanTween.move(gameObject, startingPosition, returnDuration).setEase(LeanTweenType.easeOutExpo); } } } #endregion } }