Unity3D UI,计算拖动物品的位置?
Unity3D UI, calculation for position dragging an item?
如今,在 Unity 中拖动 UI 元素非常容易:制作一些 UI 项目。添加组件 -> 事件 -> 事件触发器。放在下面的脚本上。单击以添加四个明显的触发器。大功告成。
但是
我完全迷失在 指针坐标 和 UI 坐标 之间的关系中(如 RectTransform 等所示)上)。
在下面的 DragIt
中:你到底是怎么在手指下正确移动 UI 面板的?
假设您有一个大面板,面板中有十个 UI 按钮,按钮上有 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 以访问增量信息
这里实际上是一个完整的拖放“UnityEngine.UI”项目解决方案,基于 Uri 和 Colton 的代码。只需复制和粘贴即可。
用于 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);
}
}
对于拖动的东西,我只是这样做:
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);
}
}
首先,此 post 中的所有其他答案都非常有效。我为此工作了很长时间,只是想 post 在这里。它增加了一种防止其他不需要的 UI object 被拖来拖去的方法。
我的正式目标是提供一种无需使用 bool beingDragged = false;
即可完成此操作的方法。如果你那样做,你只是不知道 Button
或 Image
被拖动了。
拖动UI:
在 RectTransformUtility
的帮助下将屏幕点转换为 RectTransform 中的本地点,然后使用 Canvas.transform.TransformPoint
找出 child 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 相机模式下都有效。
正在检测哪个Object即将被拖动:
最简单的方法是创建一个全局变量,您可以使用它来保存 object 用户想要在 OnBeginDrag
函数中拖动,然后您可以拖动那个 object在 OnDrag
。在调用 OnEndDrag
时将 object 设置为 null。
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
这必须在 OnBeginDrag
函数中完成一次,然后保存到全局变量中。
您不能在 OnDrag
函数中执行以下操作
if (eventData.pointerCurrentRaycast.gameObject == someOtherUI)
{
someOtherUI....drag
}
即使它应该有效,但有时却无效。在 OnDrag
期间,它有时甚至 return 为空。这就是为什么它必须在 OnBeginDrag
函数中完成的原因。
检测并拖动按钮与图像:
检测 UI 是否只是一个 Image
并且拖动一个 Image
非常容易。
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
Button tempButton = objectToBeDragged.GetComponent<Button>();
Image tempImage = objectToBeDragged.GetComponent<Image>();
如果 tempImage
是 而不是 null
并且 tempButton
是 null
那么这是一个图像。
检测 UI 是否只是一个 Button
并且拖动 Button
不 容易。
当在 side/edge 上单击按钮时,Button
的名称是 returned,这很好。但大多数时候,Button
的点击发生在 Button
的 中间 ,而 而不是 return 按钮的实例或名称,而不是 returns Text
(Child Object)。您不能 将文本作为按钮移动。它不会工作。
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
Button tempButton = objectToBeDragged.GetComponent<Button>();
Image tempImage = objectToBeDragged.GetComponent<Image>();
Text tempText = objectToBeDragged.GetComponent<Text>();
如果 tempText
为 not 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
}
}
下面是拖动UIImage/Panel和Button的完整脚本。应拖动的任何 Button 应放在 UIButtons
数组中,应拖动的任何 Panel/Image 应放在 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 };
}
好的,所以第一个解决方案适用于 UI 个元素(覆盖 Canvas)
来自@programmer 的那个也适用于其他 Canvases,但对我的使用有一点开销。所以在这里我提取了我认为你需要拖动“当前对象”的最小值
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;
}
}
代码由从我的 class 中抓取的行组成,因此它不是经过测试的脚本。当然你需要一些使用
如今,在 Unity 中拖动 UI 元素非常容易:制作一些 UI 项目。添加组件 -> 事件 -> 事件触发器。放在下面的脚本上。单击以添加四个明显的触发器。大功告成。
但是
我完全迷失在 指针坐标 和 UI 坐标 之间的关系中(如 RectTransform 等所示)上)。
在下面的 DragIt
中:你到底是怎么在手指下正确移动 UI 面板的?
假设您有一个大面板,面板中有十个 UI 按钮,按钮上有 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 以访问增量信息
这里实际上是一个完整的拖放“UnityEngine.UI”项目解决方案,基于 Uri 和 Colton 的代码。只需复制和粘贴即可。
用于 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);
}
}
对于拖动的东西,我只是这样做:
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);
}
}
首先,此 post 中的所有其他答案都非常有效。我为此工作了很长时间,只是想 post 在这里。它增加了一种防止其他不需要的 UI object 被拖来拖去的方法。
我的正式目标是提供一种无需使用 bool beingDragged = false;
即可完成此操作的方法。如果你那样做,你只是不知道 Button
或 Image
被拖动了。
拖动UI:
在 RectTransformUtility
的帮助下将屏幕点转换为 RectTransform 中的本地点,然后使用 Canvas.transform.TransformPoint
找出 child 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 相机模式下都有效。
正在检测哪个Object即将被拖动:
最简单的方法是创建一个全局变量,您可以使用它来保存 object 用户想要在 OnBeginDrag
函数中拖动,然后您可以拖动那个 object在 OnDrag
。在调用 OnEndDrag
时将 object 设置为 null。
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
这必须在 OnBeginDrag
函数中完成一次,然后保存到全局变量中。
您不能在 OnDrag
函数中执行以下操作
if (eventData.pointerCurrentRaycast.gameObject == someOtherUI)
{
someOtherUI....drag
}
即使它应该有效,但有时却无效。在 OnDrag
期间,它有时甚至 return 为空。这就是为什么它必须在 OnBeginDrag
函数中完成的原因。
检测并拖动按钮与图像:
检测 UI 是否只是一个 Image
并且拖动一个 Image
非常容易。
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
Button tempButton = objectToBeDragged.GetComponent<Button>();
Image tempImage = objectToBeDragged.GetComponent<Image>();
如果 tempImage
是 而不是 null
并且 tempButton
是 null
那么这是一个图像。
检测 UI 是否只是一个 Button
并且拖动 Button
不 容易。
当在 side/edge 上单击按钮时,Button
的名称是 returned,这很好。但大多数时候,Button
的点击发生在 Button
的 中间 ,而 而不是 return 按钮的实例或名称,而不是 returns Text
(Child Object)。您不能 将文本作为按钮移动。它不会工作。
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
Button tempButton = objectToBeDragged.GetComponent<Button>();
Image tempImage = objectToBeDragged.GetComponent<Image>();
Text tempText = objectToBeDragged.GetComponent<Text>();
如果 tempText
为 not 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
}
}
下面是拖动UIImage/Panel和Button的完整脚本。应拖动的任何 Button 应放在 UIButtons
数组中,应拖动的任何 Panel/Image 应放在 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 };
}
好的,所以第一个解决方案适用于 UI 个元素(覆盖 Canvas) 来自@programmer 的那个也适用于其他 Canvases,但对我的使用有一点开销。所以在这里我提取了我认为你需要拖动“当前对象”的最小值
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;
}
}
代码由从我的 class 中抓取的行组成,因此它不是经过测试的脚本。当然你需要一些使用