I have a script for dragging GameObjects around, but at the moment, whenever I start to drag on the object it jumps to the center itself onto the pointer. What I'd like to achieve is regardless of where I start to drag on the object it initiates drag at that point instead of jumping to the object center first. What do I need to modify in my OnDrag or OnBeginDrag method to achieve this?
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;
public class DragHandling : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
{
public float partScale;
[HideInInspector] public Transform placeholderParent = null;
[HideInInspector] public Transform parentToReturnTo = null;
[HideInInspector] public GameObject trashCan;
[HideInInspector] public GameObject partsPanel;
[HideInInspector] public GameObject partsWindow;
[HideInInspector] public GameObject buildBoard;
GameObject placeholder = null;
GameObject dragLayer;
Vector3 buildPanelScale;
Vector3 partsPanelScale = new Vector3(1.0f, 1.0f, 1.0f);
Vector3 startPosition;
// PolygonCollider2D collider;
void Start ()
{
dragLayer = GameObject.FindGameObjectWithTag("DragLayer");
buildBoard = GameObject.FindGameObjectWithTag("Board");
partsPanel = GameObject.FindGameObjectWithTag("Parts");
partsWindow = GameObject.FindGameObjectWithTag("PartsWindow");
trashCan = GameObject.FindGameObjectWithTag("Trash");
// collider = transform.GetComponent<PolygonCollider2D>();
}
#region IPointerClickHandler implementation
public void OnPointerClick (PointerEventData eventData)
{
if(transform.parent.gameObject == buildBoard)
transform.SetAsLastSibling();
}
#endregion
#region IBeginDragHandler implementation
public void OnBeginDrag (PointerEventData eventData)
{
// create placeholder gap and hold correct position in layout
placeholder = new GameObject();
placeholder.transform.SetParent(transform.parent);
placeholder.transform.SetSiblingIndex(transform.GetSiblingIndex());
parentToReturnTo = transform.parent; // store original parent location
placeholderParent = parentToReturnTo; // set placeholder gameobject transform
startPosition = transform.position;
GetComponent<CanvasGroup>().blocksRaycasts = false; // turn off image raycasting when dragging image in order to see what's behind the image
}
#endregion
#region IDragHandler implementation
public void OnDrag (PointerEventData eventData)
{
Vector3 mousePosition = new Vector3(eventData.position.x, eventData.position.y, 0);
transform.position = Input.mousePosition; // set object coordinates to mouse coordinates
if(transform.parent.gameObject == partsPanel)
transform.SetParent(dragLayer.transform); // pop object to draglayer to move object out of parts Panel
if(transform.parent.gameObject == buildBoard)
transform.SetParent(dragLayer.transform);
}
#endregion
#region IEndDragHandler implementation
public void OnEndDrag (PointerEventData eventData)
{
transform.SetParent(parentToReturnTo); // Snaps object back to orginal parent if dropped outside of a dropzone
transform.SetSiblingIndex(placeholder.transform.GetSiblingIndex()); // Returns card back to placeholder location
GetComponent<CanvasGroup>().blocksRaycasts = true; // turn Raycast back on
Destroy(placeholder); // kill the placeholder if object hits a drop zone or returns to parts panel
if(transform.parent.gameObject == buildBoard)
{
buildPanelScale = new Vector3(partScale, partScale, partScale);
transform.localScale = buildPanelScale;
transform.SetAsLastSibling(); // always place last piece on top
}
if(transform.parent.gameObject == partsPanel)
transform.localScale = partsPanelScale;
}
#endregion
}
I have never used any interface to implement this, But the solution should be the same as using with OnMouseDown, OnMouseUp and OnMouseDrag with the sprites. Try this with your current implementation
using UnityEngine;
using System.Collections;
public class Drag : MonoBehaviour {
private Vector3 offset = Vector3.zero;
void OnMouseDown () {
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
worldPos.z = transform.position.z;
offset = worldPos - transform.position;
}
void OnMouseDrag () {
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
worldPos.z = transform.position.z;
worldPos = worldPos - offset;
transform.position = worldPos;
}
void OnMouseUp () {
offset = Vector3.zero;
}
}
I have used this with an orthographic camera on Sprites. Hope this helps.
EDIT
Tried implementing the interfaces as in your code with UI elements. It is working as expected. Only thing is to set the canvas to ScreenSpace-Camera implement the code in the respective interfaces
your question is a bit confusing and if im correct you are saying that the object you start to drag keeps moving to the middle of screen , if you dont want that just update the dragged objects transform in OnEndDrag.
Related
I'm trying to make an instance at the mouse pos, but when it makes the instance, it's way off and is really far out from my mouse. How could I fix this? Here is my code:
using UnityEngine;
using System.Collections;
// note
// note
public class attack : MonoBehaviour
{
public Transform prefab;
void Update()
{
Vector3 mousePos = Input.mousePosition;
if (Input.GetMouseButtonDown(0))
Instantiate(prefab, mousePos, Quaternion.identity);
}
}
Input.mousePosition is in screen pixel space!
You most probably rather want to use Camera.ScreenToWorldSpace
public class attack : MonoBehaviour
{
public Transform prefab;
// you will need to figure this out
public float desiredDistanceInFrontOfCamera;
[SerializeFiel] private Camera _camera;
private void Awake()
{
if(!_camera) _camera = Camera.main;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Input.mousePosition;
mousePos.z = desiredDistanceInFrontOfCamera;
Vector3 spawnPos = _camera.ScreenToWorldPoint(mousePos);
Instantiate(prefab, spawnPos, Quaternion.identity);
}
}
}
So I have a script (For drag and drop inventory system) that was working fine yesterday, but this morning the Items being dragged no longer followed the mouse pointer at the crusors position but instead followed it with a at a distance. So upon clicking the item, nothing happened, once i start dragging the item x,y values just changed and placed itself a few centimeters above the crusor whilst still following the crusor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class DragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public static GameObject itemBeingDragged;
Vector2 startPosition;
Transform startParent;
public void OnBeginDrag ( PointerEventData eventData)
{
itemBeingDragged = gameObject;
startPosition = transform.position;
startParent = transform.parent;
GetComponent<CanvasGroup>().blocksRaycasts = false;
}
public void OnDrag (PointerEventData eventData)
{
transform.position = Input.mousePosition;
}
public void OnEndDrag (PointerEventData eventData)
{
itemBeingDragged = null;
GetComponent<CanvasGroup>().blocksRaycasts = true;
if (transform.parent == startParent)
{
transform.position = startPosition;
}
}
}
ยดยดยด
When you drag an item, the mouse can be anywhere inside the item. You should therefore calculate the delta between the mouse position and the position of the dragged item. You can then add this delta in the OnDrag() functions when setting a new position.
Vector3 delta;
public void OnBeginDrag(PointerEventData eventData)
{
itemBeingDragged = gameObject;
delta = itemBeingDragged.transform.position -
new Vector3(eventData.pressPosition.x, eventData.pressPosition.y, 0);
// ....
}
...
public void OnDrag(PointerEventData eventData)
{
transform.position = Input.mousePosition + delta;
}
Note that we should not use the Input.mousePosition in the OnBeginDrag() since the drag operation starts first after the mouse has moved a bit. We could of course calculate the delta as soon as the mouse button was pressed, and then we can use the Input.mousePosition. But, since you use the OnBeginDrag() we use the event's pressPosition which is the mousePosition when pressed.
I'm trying to implement a drag and drop system but I'm having a problem with the drag rate at the moment. Currently when I drag an object it only moves a tiny fraction of the amount that my pointer actually moves. How do I fix this?
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;
public class DragHandling : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
{
public float partScale;
[HideInInspector] public Transform placeholderParent = null;
[HideInInspector] public Transform parentToReturnTo = null;
[HideInInspector] public GameObject trashCan;
[HideInInspector] public GameObject partsPanel;
[HideInInspector] public GameObject partsWindow;
[HideInInspector] public GameObject buildBoard;
GameObject placeholder = null;
GameObject dragLayer;
Vector3 buildPanelScale;
Vector3 partsPanelScale = new Vector3(1.0f, 1.0f, 1.0f);
Vector3 startPosition;
private Vector3 offset = Vector3.zero;
void Start ()
{
dragLayer = GameObject.FindGameObjectWithTag("DragLayer");
buildBoard = GameObject.FindGameObjectWithTag("Board");
partsPanel = GameObject.FindGameObjectWithTag("Parts");
partsWindow = GameObject.FindGameObjectWithTag("PartsWindow");
trashCan = GameObject.FindGameObjectWithTag("Trash");
}
#region IPointerClickHandler implementation
public void OnPointerClick (PointerEventData eventData)
{
if(transform.parent.gameObject == buildBoard)
transform.SetAsLastSibling();
}
#endregion
#region IBeginDragHandler implementation
public void OnBeginDrag (PointerEventData eventData)
{
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
worldPos.z = transform.position.z;
offset = worldPos - transform.position;
// create placeholder gap and hold correct position in layout
placeholder = new GameObject();
placeholder.transform.SetParent(transform.parent);
placeholder.transform.SetSiblingIndex(transform.GetSiblingIndex());
parentToReturnTo = transform.parent; // store original parent location
placeholderParent = parentToReturnTo; // set placeholder gameobject transform
GetComponent<CanvasGroup>().blocksRaycasts = false; // turn off image raycasting when dragging image in order to see what's behind the image
}
#endregion
#region IDragHandler implementation
public void OnDrag (PointerEventData eventData)
{
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
worldPos.z = transform.position.z;
worldPos = worldPos - offset;
transform.position = worldPos;
if(transform.parent.gameObject == partsPanel)
transform.SetParent(dragLayer.transform); // pop object to draglayer to move object out of parts Panel
if(transform.parent.gameObject == buildBoard)
transform.SetParent(dragLayer.transform);
}
#endregion
#region IEndDragHandler implementation
public void OnEndDrag (PointerEventData eventData)
{
offset = Vector3.zero;
transform.SetParent(parentToReturnTo); // Snaps object back to orginal parent if dropped outside of a dropzone
transform.SetSiblingIndex(placeholder.transform.GetSiblingIndex()); // Returns card back to placeholder location
GetComponent<CanvasGroup>().blocksRaycasts = true; // turn Raycast back on
Destroy(placeholder); // kill the placeholder if object hits a drop zone or returns to parts panel
if(transform.parent.gameObject == buildBoard)
{
buildPanelScale = new Vector3(partScale, partScale, partScale);
transform.localScale = buildPanelScale;
transform.SetAsLastSibling(); // always place last piece on top
}
if(transform.parent.gameObject == partsPanel)
transform.localScale = partsPanelScale;
}
#endregion
}
Right off the back, you should use eventData.position instead of Input.mousePosition. ScreenToWorldPoint takes a Vector3 but Input.mousePosition is a Vector2. I also personally wouldn't use ScreenToWorldPoint for this purpose, id use a raycast, and use the RayCastHit hit.point for where to put the object as its being dragged, which would be alot more accurate for you.
Actually, since you are using Unity Canvas, you should use RectTransformUtility.ScreenPointToLocalPointInRectangle(...) and put in the parent rect or canvas for what you are trying to drag. It will give you the coordinates you want.
implementation:
RectTransform parentRect = (RectTransform)objectToBeDragged.transform.parent;
Vector2 posInParent;
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, eventData.position, Camera.main, out posInParent);
objectToBeDragged.localPosition = posInParent;
I also just realized another problem you might be experiencing is that you are using transform.position instead of transform.localPosition not saying for sure thats the problem but sometimes that can really bite you in the ass.
I'm building a game upon which objects can be dragged around and arranged to build different types of animals. My question is, is there a way to define the draggable area of each sprite beyond just a simple rectangle? Many of the graphics in my sprites are elliptical and triangular in nature which leaves a lot of clear space around the actual image. I'd like to make just the graphic within the rectangular bounds of my sprite draggable. How can this be done?
Here are some of the graphics to illustrate what I mean by irregular shapes.
So when you draw a bounding box around these shapes there is a lot of clear space that I don't want to be draggable or clickable. I already have a script that handles dragging, but it makes the entire sprite draggable, how do I make just the 2D Polygon Collider accept the drag event?
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;
public class DragHandling : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
{
Vector3 partsPanelScale = new Vector3(1.0f, 1.0f, 1.0f);
public float partScale;
// public float partRotate;
Vector3 buildPanelScale;
// Vector3 buildPanelRotate;
[HideInInspector] public Transform placeholderParent = null;
[HideInInspector] public Transform parentToReturnTo = null;
GameObject placeholder = null;
[HideInInspector] public GameObject animalPart;
[HideInInspector] public GameObject trashCan;
[HideInInspector] public GameObject partsPanel;
[HideInInspector] public GameObject partsWindow;
[HideInInspector] public GameObject buildBoard;
[HideInInspector] public GameObject horns;
[HideInInspector] public GameObject heads;
[HideInInspector] public GameObject ears;
[HideInInspector] public GameObject necks;
[HideInInspector] public GameObject bodies;
[HideInInspector] public GameObject legs;
[HideInInspector] public GameObject tails;
[HideInInspector] public GameObject more;
GameObject dragLayer;
void Start ()
{
dragLayer = GameObject.FindGameObjectWithTag("DragLayer");
buildBoard = GameObject.FindGameObjectWithTag("Board");
partsPanel = GameObject.FindGameObjectWithTag("Parts");
partsWindow = GameObject.FindGameObjectWithTag("PartsWindow");
trashCan = GameObject.FindGameObjectWithTag("Trash");
horns = GameObject.FindGameObjectWithTag("AnimalPart-Horns");
heads = GameObject.FindGameObjectWithTag("AnimalPart-Heads");
ears = GameObject.FindGameObjectWithTag("AnimalPart-Ears");
necks = GameObject.FindGameObjectWithTag("AnimalPart-Necks");
bodies = GameObject.FindGameObjectWithTag("AnimalPart-Bodies");
legs = GameObject.FindGameObjectWithTag("AnimalPart-Legs");
tails = GameObject.FindGameObjectWithTag("AnimalPart-Tails");
more = GameObject.FindGameObjectWithTag("AnimalPart-More");
}
#region IPointerClickHandler implementation
public void OnPointerClick (PointerEventData eventData)
{
if(transform.parent.gameObject == buildBoard)
{
transform.SetAsLastSibling();
}
}
#endregion
#region IBeginDragHandler implementation
public void OnBeginDrag (PointerEventData eventData)
{
// create placeholder gap and hold correct position in layout
placeholder = new GameObject();
placeholder.transform.SetParent(transform.parent);
placeholder.transform.SetSiblingIndex(transform.GetSiblingIndex());
/*
if(transform.parent.gameObject == partsPanel)
{
partsPanelScale = transform.localScale;
}
*/
parentToReturnTo = transform.parent; // store current parent location
placeholderParent = parentToReturnTo; // set placeholder gameobject transform
GetComponent<CanvasGroup>().blocksRaycasts = false; // turn off image raycasting when dragging image in order to see what's behind the image
}
#endregion
#region IDragHandler implementation
float distance = 0;
public void OnDrag (PointerEventData eventData)
{
Vector3 mousePosition = new Vector3(eventData.position.x, eventData.position.y, distance);
// Vector3 objPosition = Camera.main.ViewportToScreenPoint(mousePosition);
transform.position = mousePosition; // set object coordinates to mouse coordinates
if(transform.parent.gameObject == partsPanel)
{
transform.SetParent(dragLayer.transform); // pop object to draglayer to move object out of parts Panel
}
if(transform.parent.gameObject == buildBoard)
{
transform.SetParent(dragLayer.transform);
// Constrain drag to boundaries of buildBoard Code
}
}
#endregion
#region IEndDragHandler implementation
public void OnEndDrag (PointerEventData eventData)
{
transform.SetParent(parentToReturnTo); // Snaps object back to orginal parent if dropped outside of a dropzone
transform.SetSiblingIndex(placeholder.transform.GetSiblingIndex()); // Returns card back to placeholder location
GetComponent<CanvasGroup>().blocksRaycasts = true; // turn Raycast back on
Destroy(placeholder); // kill the placeholder if object hits a drop zone or returns to parts panel
if(transform.parent.gameObject == buildBoard)
{
// Debug.Log ("Your sprite is now on the " + transform.parent.name);
buildPanelScale = new Vector3(partScale, partScale, partScale);
transform.localScale = buildPanelScale;
transform.SetAsLastSibling();
}
if(transform.parent.gameObject == partsPanel)
{
// transform.SetParent(buildBoard.transform);
transform.localScale = partsPanelScale;
}
}
#endregion
}
If you're using a BoxCollider2D as the default colliderfor your puzzle pieces, you could change that out to PolygonCollider2D instead. It automatically generates the collider for your Sprite (IIRC, it uses the alpha channel), and you can also further customize it by adding vertices to the collider's mesh.
I'm using Unity 5, withthe new uGUI system and I'm having a terrible time getting pointer events to ignore the clear space around my sprites and register only the image area. I've attached images and code to show where I'm at. I cannot get the this to work.
Ultimately, the thing I'm trying to achieve is to just have the click event interact with the image rather than the entire image sprite (everything in the rectangular bounding box).
My image texture type is Sprite (2D and UI). I made sure the space around my image is indeed transparent, yet the clear space around the image is still registering a click.
What am I doing wrong here? Need help!
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;
public class DragHandling : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
{
public float partScale;
[HideInInspector] public Transform placeholderParent = null;
[HideInInspector] public Transform parentToReturnTo = null;
[HideInInspector] public GameObject trashCan;
[HideInInspector] public GameObject partsPanel;
[HideInInspector] public GameObject partsWindow;
[HideInInspector] public GameObject buildBoard;
GameObject placeholder = null;
GameObject dragLayer;
Vector3 buildPanelScale;
Vector3 partsPanelScale = new Vector3(1.0f, 1.0f, 1.0f);
Vector3 startPosition;
void Start ()
{
dragLayer = GameObject.FindGameObjectWithTag("DragLayer");
buildBoard = GameObject.FindGameObjectWithTag("Board");
partsPanel = GameObject.FindGameObjectWithTag("Parts");
partsWindow = GameObject.FindGameObjectWithTag("PartsWindow");
trashCan = GameObject.FindGameObjectWithTag("Trash");
}
#region IPointerClickHandler implementation
public void OnPointerClick (PointerEventData eventData)
{
if(transform.parent.gameObject == buildBoard)
transform.SetAsLastSibling();
}
#endregion
#region IBeginDragHandler implementation
public void OnBeginDrag (PointerEventData eventData)
{
// create placeholder gap and hold correct position in layout
placeholder = new GameObject();
placeholder.transform.SetParent(transform.parent);
placeholder.transform.SetSiblingIndex(transform.GetSiblingIndex());
parentToReturnTo = transform.parent; // store original parent location
placeholderParent = parentToReturnTo; // set placeholder gameobject transform
startPosition = transform.position;
GetComponent<CanvasGroup>().blocksRaycasts = false; // turn off image raycasting when dragging image in order to see what's behind the image
}
#endregion
#region IDragHandler implementation
public void OnDrag (PointerEventData eventData)
{
Vector3 mousePosition = new Vector3(eventData.position.x, eventData.position.y, 0);
transform.position = Input.mousePosition; // set object coordinates to mouse coordinates
if(transform.parent.gameObject == partsPanel)
transform.SetParent(dragLayer.transform); // pop object to draglayer to move object out of parts Panel
if(transform.parent.gameObject == buildBoard)
transform.SetParent(dragLayer.transform);
}
#endregion
#region IEndDragHandler implementation
public void OnEndDrag (PointerEventData eventData)
{
transform.SetParent(parentToReturnTo); // Snaps object back to orginal parent if dropped outside of a dropzone
transform.SetSiblingIndex(placeholder.transform.GetSiblingIndex()); // Returns card back to placeholder location
GetComponent<CanvasGroup>().blocksRaycasts = true; // turn Raycast back on
Destroy(placeholder); // kill the placeholder if object hits a drop zone or returns to parts panel
if(transform.parent.gameObject == buildBoard)
{
buildPanelScale = new Vector3(partScale, partScale, partScale);
transform.localScale = buildPanelScale;
transform.SetAsLastSibling(); // always place last piece on top
}
if(transform.parent.gameObject == partsPanel)
transform.localScale = partsPanelScale;
}
#endregion
}