Unity3D UI,计算位置拖动项目?

现在,在Unity中拖动UI元素非常容易:制作一些UI项目。 添加组件 – >事件 – > 事件触发器 。 放在下面的脚本上。 单击以添加四个明显的触发器。 你完成了。

然而。

我完全迷失了指针坐标UI坐标之间的关系(如RectTransform中所见,等等)。

在下面的DragIt中:如何在手指下正确移动UI面板?

假设您有一个大面板,十个UIButton坐在面板上,按钮上有Dragster 。 RectTransform坐标和鼠标指针之间的关系是什么…

简而言之,如何在DragIt()下方移动其中一个按钮?

 /* modern Unity drag of UI element */ using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; using UnityEngine.EventSystems; public class Dragster:MonoBehaviour { public int index; // number each of your UI items static bool beingDragged = false; static int dragFrom; public void DragStart() { beingDragged = true; dragFrom = index; } public void DragIt() { ? ? WTF ? ? } public void DragEnd() { beingDragged = false; } public void DroppedBra() { Debig.Log("Drag: from/to " +dragFrom +" --> " +index); } } 

对于Draging的东西我只是这样做:

 using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; public class Draggable : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { public void OnBeginDrag(PointerEventData eventData) { } public void OnDrag(PointerEventData eventData) { //Debug.Log ("OnDrag"); this.transform.position = eventData.position; } public void OnEndDrag(PointerEventData eventData) { Debug.Log ("OnEndDrag"); } } 

我会让你的脚本实现拖动界面

public class Dragster:MonoBehaviour,IBeginDragHandler, IEndDragHandler, IDragHandler

这将使你的DragItfunction成为

 public void OnDrag(PointerEventData eventData) { transform.position += (Vector3)eventData.delta; } 

允许您访问该事件的增量(鼠标移动了多少)以便能够移动您的对象。

如果你仍然宁愿使用EventTrigger组件(不太喜欢的方式),你只需要将DragIt函数更改为DragIt(PointerEventData eventData)并在下拉列表中使用Dynamic EvenData选项来触发接收PointerEventData以访问delta信息


这里实际上是基于Uri和Colton代码拖放’UnityEngine.UI`项目的完整,完整的解决方案。 只需复制并粘贴即可。

令人惊叹的复制和粘贴Unity UI,wtt Colton&Uri的完美拖放:

 using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; public class UNCDraggable:MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler { public Image ghost; // note DON'T try to drag the actual item: it's not worth the hassle. // a problem arises where you can't have it on top (as you would want // visually), and still easily get the drops. always use a ghost. // even if you want the "original invisible" while dragging, // simply hide it and use a ghost. everything is tremendously // easier if you do not move the originals. void Awake() { ghost.raycastTarget = false; // (just in case you forgot to do that in the Editor) ghost.enabled = false; } public void OnBeginDrag(PointerEventData eventData) { ghost.transform.position = transform.position; ghost.enabled = true; } public void OnDrag(PointerEventData eventData) { ghost.transform.position += (Vector3)eventData.delta; } public void OnEndDrag(PointerEventData eventData) { ghost.enabled = false; } public void OnDrop(PointerEventData data) { GameObject fromItem = data.pointerDrag; if (data.pointerDrag == null) return; // (will never happen) UNCDraggable d = fromItem.GetComponent(); if (d == null) { // means something unrelated to our system was dragged from. // for example, just an unrelated scrolling area, etc. // simply completely ignore these. return; // note, if very unusually you have more than one "system" // of UNCDraggable items on the same screen, be careful to // distinguish them! Example solution, check parents are same. } Debug.Log ("dropped " + fromItem.name +" onto " +gameObject.name); // your code would look probably like this: YourThings fromThing = fromItem.GetComponent().info; YourThings untoThing = gameObject.GetComponent().info; yourBossyObject.dragHappenedFromTo(fromThing, untoThing); } } 

首先,这篇文章中的所有其他答案都运作良好。 我在这方面工作了很长时间,只想在这里发布。 它添加了一种方法来防止拖动其他不需要的UI对象。

我的官方目标是提供一种方法来实现这一点,而不使用bool beingDragged = false; 。 如果您这样做,您将无法知道正在拖动哪个ButtonImage

拖动UI

借助RectTransformUtility将Rectpoint中的Screenpoint转换为Local Point,然后使用Canvas.transform.TransformPoint找出子UI的确切位置。

 public Canvas parentCanvasOfImageToMove; Vector2 pos; RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvasOfImageToMove.transform as RectTransform, eventData.position, parentCanvasOfImageToMove.worldCamera, out pos); UIToMove.transform.position = parentCanvasOfImageToMove.transform.TransformPoint(pos); 

在其他答案中,拖拽代码看起来比其他拖拽代码更复杂,但它似乎在每个Canvas相机模式下都有效。

检测要拖动的对象

最简单的方法是创建一个全局变量,您可以使用该变量来保存哪个对象用户想要在OnBeginDrag函数中拖动,然后您可以在OnDrag拖动该对象。 调用OnEndDrag时将该对象设置为null。

 objectToBeDragged = eventData.pointerCurrentRaycast.gameObject; 

这必须在OnBeginDrag函数中完成一次,然后保存到全局变量中。

您无法在OnDrag函数中执行以下OnDrag

 if (eventData.pointerCurrentRaycast.gameObject == someOtherUI) { someOtherUI....drag } 

即使它被认为是有效的,但有时也没有。 它甚至在OnDrag期间有时返回null。 这就是必须在OnBeginDrag函数中完成的OnBeginDrag

检测并拖动按钮与图像

检测UI是否只是Image和拖动Image非常容易。

 objectToBeDragged = eventData.pointerCurrentRaycast.gameObject; Button tempButton = objectToBeDragged.GetComponent 

如果tempImage 不为 nulltempButtonnull那么这是一个Image。

检测UI是否只是一个Button并拖动Button 并不容易。 在侧面/边缘上单击Button将返回Button的名称,这很好。 但是大多数情况下,在Button中间点击Button会发生Button ,它返回Button的实例或名称,而是返回Text (子对象)。 您无法将文本作为按钮移动。 它不会工作。

 objectToBeDragged = eventData.pointerCurrentRaycast.gameObject; Button tempButton = objectToBeDragged.GetComponent 

如果tempText 不为 null,则获取GetComponentInParent的Image和Button组件的GetComponentInParent 。 如果Image不为null且Button不为null,那么它是一个Button

 if (tempText != null) { tempButton = tempText.GetComponentInParent 

下面是拖动UI图像/面板和按钮的完整脚本。 应该拖动的任何按钮都应该放在UIButtons数组中,任何应该拖动的Panel / Image都应该放在UIPanels数组中。 它将忽略不在Array中的其他UI。

 public class UIDRAGGER : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { public Canvas parentCanvasOfImageToMove; //10 UI Buttons (Assign in Editor) public Button[] UIButtons; //2 UI Panels/Images (Assign in Editor) public Image[] UIPanels; //Hold which Button or Image is selected private Button selectedButton; private Image selectedUIPanels; //Used to make sure that the UI is position exactly where mouse was clicked intead of the default center of the UI Vector3 moveOffset; //Used to decide which mode we are in. Button Drag or Image/Panel Mode private DragType dragType = DragType.NONE; void Start() { parentCanvasOfImageToMove = gameObject.GetComponent(); } //Checks if the Button passed in is in the array bool buttonIsAvailableInArray(Button button) { bool _isAValidButton = false; for (int i = 0; i < UIButtons.Length; i++) { if (UIButtons[i] == button) { _isAValidButton = true; break; } } return _isAValidButton; } //Checks if the Panel/Image passed in is in the array bool imageIsAvailableInArray(Image image) { bool _isAValidImage = false; for (int i = 0; i < UIPanels.Length; i++) { if (UIPanels[i] == image) { _isAValidImage = true; break; } } return _isAValidImage; } void selectButton(Button button, Vector3 currentPos) { //check if it is in the image array that is allowed to be moved if (buttonIsAvailableInArray(button)) { //Make the image the current selected image selectedButton = button; dragType = DragType.BUTTONS; moveOffset = selectedButton.transform.position - currentPos; } else { //Clear the selected Button selectedButton = null; dragType = DragType.NONE; } } void selectImage(Image image, Vector3 currentPos) { //check if it is in the image array that is allowed to be moved if (imageIsAvailableInArray(image)) { //Make the image the current selected image selectedUIPanels = image; dragType = DragType.IMAGES; moveOffset = selectedUIPanels.transform.position - currentPos; } else { //Clear the selected Button selectedUIPanels = null; dragType = DragType.NONE; } } public void OnBeginDrag(PointerEventData eventData) { GameObject tempObj = eventData.pointerCurrentRaycast.gameObject; if (tempObj == null) { return; } Button tempButton = tempObj.GetComponent