Unity3D UI,用于拖动项目的位置计算



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

然而。

我完全迷失在指针坐标

UI坐标在下面的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()
{
? ? W T F ? ?
}
public void DragEnd()
{
beingDragged = false;
}
public void DroppedBra()
{
Debig.Log("Drag: from/to " +dragFrom +" --> " +index);
}
}

我会让您的脚本实现拖动接口

public class Dragster:MonoBehaviour,IBeginDragHandler, IEndDragHandler, IDragHandler

这将使您的DragIt功能成为

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

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

如果您仍然希望使用EventTrigger组件(不太喜欢的方式),您只需要将DragIt函数更改为DragIt(PointerEventData eventData),并使用下拉菜单中的Dynamic EvenData选项,使触发器接收PointerEventData以访问增量信息


这实际上是一个基于Uri&科尔顿密码。只需复制粘贴即可。

令人惊叹的复制和粘贴无需思考的完美拖放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<UNCDraggable>();
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<YourButtons>().info;
YourThings untoThing = gameObject.GetComponent<YourButtons>().info;

yourBossyObject.dragHappenedFromTo(fromThing, untoThing);
}
}

首先,这篇文章中的所有其他作者都工作得很好。我做了这么长时间,只是想把它发布在这里。它添加了一种防止其他不需要的UI对象被拖动的方法。

我的官方目标是提供一种不使用bool beingDragged = false;的方法。如果你这样做,你就不知道哪个ButtonImage被拖动了。

拖动UI

RectTransformUtility的帮助下,将Screenpoint转换为RectTransform中的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功能中执行以下操作

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

尽管它被认为是有效的,但有时却不起作用。它甚至有时在OnDrag期间返回null。这就是为什么它必须在OnBeginDrag函数中完成。

检测和拖动按钮与图像

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

objectToBeDragged  = eventData.pointerCurrentRaycast.gameObject;
Button tempButton = objectToBeDragged.GetComponent<Button>();
Image tempImage = objectToBeDragged.GetComponent<Image>();

如果tempImage而不是null,并且tempButtonnull,则这是图像。

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

objectToBeDragged  = eventData.pointerCurrentRaycast.gameObject;
Button tempButton = objectToBeDragged.GetComponent<Button>();
Image tempImage = objectToBeDragged.GetComponent<Image>();
Text tempText = objectToBeDragged.GetComponent<Text>();

如果tempText而不是null,则获取文本的图像和按钮组件的GetComponentInParent。如果Image不为空并且Button不为空,则它是Button

if (tempText != null)
{
tempButton = tempText.GetComponentInParent<Button>();
tempImage = tempText.GetComponentInParent<Image>();
if (tempButton != null && tempImage != null)
{
//This is a Button
}
}

以下是拖动UI图像/面板和按钮的完整脚本。应拖动的任何按钮都应放在UIButtons阵列中,应拖动的所有面板/图像都应放置在UIPanels阵列中。它将忽略不在数组中的其他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<Canvas>();
}
//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<Button>();
Image tempImage = tempObj.GetComponent<Image>();
Text tempText = tempObj.GetComponent<Text>();
//For Offeset Position
Vector2 pos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvasOfImageToMove.transform as RectTransform, eventData.position, parentCanvasOfImageToMove.worldCamera, out pos);

//Button must contain Text then Image and Button as parant
//Check if this is an image
if (tempButton == null || tempImage == null)
{
//Button not detected. Check if Button's text was detected
if (tempText != null)
{
//Text detected. Now Look for Button and Image in the text's parent Object
tempButton = tempText.GetComponentInParent<Button>();
tempImage = tempText.GetComponentInParent<Image>();
//Since child is text, check if parents are Button and Image
if (tempButton != null && tempImage != null)
{
//This is a Button
selectButton(tempButton, parentCanvasOfImageToMove.transform.TransformPoint(pos));
}
//Check if there is just an image
else if (tempImage != null)
{
//This is an Image
selectImage(tempImage, parentCanvasOfImageToMove.transform.TransformPoint(pos));
}
}
else
{
//This is an Image
selectImage(tempImage, parentCanvasOfImageToMove.transform.TransformPoint(pos));
}
}
//Check if there is just an image
else if (tempImage != null)
{
selectImage(tempImage, parentCanvasOfImageToMove.transform.TransformPoint(pos));
}
}
public void OnDrag(PointerEventData eventData)
{
Vector2 pos;
if (dragType == DragType.BUTTONS)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvasOfImageToMove.transform as RectTransform, eventData.position, parentCanvasOfImageToMove.worldCamera, out pos);
selectedButton.transform.position = parentCanvasOfImageToMove.transform.TransformPoint(pos) + moveOffset;
}
else if (dragType == DragType.IMAGES)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvasOfImageToMove.transform as RectTransform, eventData.position, parentCanvasOfImageToMove.worldCamera, out pos);
selectedUIPanels.transform.position = parentCanvasOfImageToMove.transform.TransformPoint(pos) + moveOffset;
}
}

public void OnEndDrag(PointerEventData eventData)
{
//Buttons
if (dragType == DragType.BUTTONS || dragType == DragType.IMAGES)
{
selectedButton = null;
selectedUIPanels = null;
dragType = DragType.NONE;
}
}
DragType getCurrentDragType()
{
return dragType;
}
private enum DragType { NONE, BUTTONS, IMAGES };
}

对于拖动东西,我只做以下操作:

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");

}
}

以下是相同的令人惊叹的URIPOPOV代码,有两个简单的功能,您在拖动时总是需要:

// allow dragging with two basic problem fixes:
// - limit drag to the parent box
// - don't "jump" based on where you hold down

100%测试:

请注意,您必须使用下面#programmer中的比例转换代码。

using UnityEngine;
using UnityEngine.EventSystems;
public class AmazingUPDrag : MonoBehaviour,
IBeginDragHandler, IDragHandler, IEndDragHandler
{
RectTransform rt;
Vector2 dragOffset = Vector2.zero;
Vector2 limits = Vector2.zero;
Canvas canvas;
void Awake()
{
rt = GetComponent<RectTransform>();
canvas = GetComponentInParent<Canvas>();
}
public void OnBeginDrag(PointerEventData eventData)
{
dragOffset = ActualPos(eventData.position) - (Vector2)transform.position;
limits = transform.parent.GetComponent<RectTransform>().rect.max;
limits.x = limits.x - 100f;
limits.y = limits.y - 100f;
}
public void OnDrag(PointerEventData eventData)
{
transform.position = ActualPos(eventData.position) - dragOffset;
var p = transform.localPosition;
if (p.x < -limits.x) { p.x = -limits.x; }
if (p.x > limits.x) { p.x = limits.x; }
if (p.y < -limits.y) { p.y = -limits.y; }
if (p.y > limits.y) { p.y = limits.y; }
transform.localPosition = p;
}
public void OnEndDrag(PointerEventData eventData)
{
dragOffset = Vector2.zero;
}
Vector2 ActualPos(Vector2 unityDelusionPos)
{
Vector2 p;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
(RectTransform)(canvas.transform),
unityDelusionPos,
canvas.worldCamera,
out p);
return canvas.transform.TransformPoint(p);
}

}

OK,所以第一个解决方案适用于UI元素(Overlay Canvas)来自@programmer的那个也适用于其他画布,但对我的使用来说有点开销。所以在这里,我提取了我认为你需要拖动你的";当前对象";

using UnityEngine.EventSystems;
public class SomeObject : MonoBehaviour, IBeginDragHandler, IDragHandler
{
private Canvas parentCanvas;
private Vector3 moveOffset;
void Start()
{
// get the "nearest Canvas"
parentCanvas = GetComponentsInParent<Canvas>()[0];
// you could get the Camera of this Canvas here as well and like @ina suggests add a null check for that and otherwise use Camera.Main
}
public void OnBeginDrag(PointerEventData eventData)
{
//For Offset Position so the object won't "jump" to the mouse/touch position
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvas.transform as RectTransform, eventData.position, parentCanvas.worldCamera, out Vector2 pos);
moveOffset = transform.position - parentCanvas.transform.TransformPoint(pos);
}
public void OnDrag(PointerEventData eventData)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvas.transform as RectTransform, eventData.position, parentCanvas.worldCamera, out Vector2 pos);
transform.position = parentCanvas.transform.TransformPoint(pos) + moveOffset;
}
}

代码是由从我的一个实际类中抓取的行组成的,所以它不是一个经过测试的脚本。当然你需要一些使用

最新更新