Unity3D UI, расчет для перемещения объекта по позиции?
В наши дни невероятно легко перетаскивать элементы пользовательского интерфейса в Unity: создать несколько элементов пользовательского интерфейса. Добавить компонент -> Событие -> Триггер события. Оставьте на сценарий ниже. Нажмите, чтобы добавить четыре очевидных триггера. Вы сделали
Тем не мение.
Я полностью потерян в отношениях между координатами указателя и координатами пользовательского интерфейса (как видно в RectTransform и так далее).
В DragIt
ниже: как, черт возьми, вы правильно перемещаете панель интерфейса под пальцем?
Скажем, у вас есть одна большая панель с десятью 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);
}
}
4 ответа
Для 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
Который сделает ваш 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);
}
}
Прежде всего, все остальные вопросы в этом посте работают очень хорошо. Я так долго работал над этим и просто хотел опубликовать это здесь. Он добавляет способ предотвратить перетаскивание других нежелательных объектов пользовательского интерфейса.
Моей официальной целью было предоставить способ сделать это без использования bool beingDragged = false;
, Вы просто не будете знать, какие Button
или же Image
тащат, если вы делаете это так.
Перетаскивание пользовательского интерфейса:
Конвертировать Screenpoint в Local point в RectTransform с помощью RectTransformUtility
затем используйте Canvas.transform.TransformPoint
выяснить, где именно находится дочерний интерфейс.
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
называется.
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
Это должно быть сделано один раз в OnBeginDrag
Затем функция сохраняется в глобальной переменной.
Вы не можете сделать следующее в OnDrag
функция
if (eventData.pointerCurrentRaycast.gameObject == someOtherUI)
{
someOtherUI....drag
}
Хотя это и должно работать, иногда это не так. Он даже возвращает ноль иногда во время OnDrag
, Вот почему это должно быть сделано в OnBeginDrag
функция.
Обнаружение и перетаскивание кнопки против изображения:
Обнаружение, является ли пользовательский интерфейс просто Image
и перетаскивая Image
очень легко.
objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;
Button tempButton = objectToBeDragged.GetComponent<Button>();
Image tempImage = objectToBeDragged.GetComponent<Image>();
Если tempImage
не null
а также tempButton
является null
тогда это образ.
Обнаружение, является ли пользовательский интерфейс просто 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
не нуль, получить GetComponentInParent
компонента изображения и кнопки текста. Если Image
не является нулевым и Button
не является нулевым, то это Button
,
if (tempText != null)
{
tempButton = tempText.GetComponentInParent<Button>();
tempImage = tempText.GetComponentInParent<Image>();
if (tempButton != null && tempImage != null)
{
//This is a Button
}
}
Ниже приведен полный сценарий перетаскивания пользовательского интерфейса изображения / панели и кнопки. Любая кнопка, которую нужно перетащить, должна быть помещена в UIButtons
массив и любую панель / изображение, которое следует перетаскивать, следует поместить в UIPanels
массив. Он будет игнорировать другие интерфейсы, которых нет в массиве.
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 };
}
Хорошо, поэтому первое решение (я) работает для элементов пользовательского интерфейса (Overlay Canvas). Решение от @programmer также работает для Other 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;
}
}
Код состоит из строк, взятых из реального моего класса, поэтому это не проверенный сценарий. И, конечно, вам нужно немного использования