web-dev-qa-db-fra.com

Interface utilisateur Unity3D, calcul de la position en faisant glisser un élément?

De nos jours, il est incroyablement facile de faire glisser des éléments d'interface utilisateur dans Unity: créez quelques éléments d'interface utilisateur. Ajouter un composant -> Événement -> déclencheur d'événement. Déposez sur le script ci-dessous. Cliquez pour ajouter les quatre déclencheurs évidents. Vous avez terminé.

Toutefois.

Je suis totalement perdu dans la relation entre coordonnées du pointeur et coordonnées UI (comme on le voit dans RectTransform et ainsi de suite).

Dans DragIt ci-dessous: comment diable déplacez-vous un panneau d'interface utilisateur correctement sous le doigt?

Disons que vous avez un grand panneau, avec dix UIButton assis dans le panneau avec Dragster sur les boutons. Quelle est la relation entre les coordonnées RectTransform et le pointeur de la souris ...

en bref, comment déplacez-vous l'un des boutons dans DragIt () ci-dessous?

/* 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);
        }
    }
13
Fattie

Pour faire glisser des trucs, je fais juste ceci:

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

    }
}
5
Uri Popov

Je ferais en sorte que votre script implémente les interfaces de glisser

public class Dragster:MonoBehaviour,IBeginDragHandler, IEndDragHandler, IDragHandler

Ce qui fera que votre fonction DragIt deviendra

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

vous donnant accès au delta de cet événement (combien la souris a bougé) pour pouvoir déplacer votre objet.

Si vous préférez toujours utiliser le composant EventTrigger (de manière moins préférée), il vous suffit de changer votre fonction DragIt en DragIt(PointerEventData eventData) et d'utiliser l'option Dynamic EvenData dans la liste déroulante du déclencheur pour recevoir le PointerEventData pour accéder aux informations delta


Voici en fait une solution totale et complète de glisser-déposer des éléments "UnityEngine.UI", basée sur le code d'Uri & Colton. Copiez et collez.

Incroyable copier-coller sans glisser-déposer parfait pour 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);
        }
    }
10
Colton White

Tout d'abord, toutes les autres réponses de ce post fonctionnent très bien. J'ai travaillé dessus pendant si longtemps et je voulais juste le poster ici. Il ajoute un moyen d'empêcher que d'autres objets d'interface utilisateur indésirables ne soient traînés.

Mon objectif officiel était de fournir un moyen de le faire sans utiliser bool beingDragged = false;. Vous ne saurez simplement pas qui Button ou Image est déplacé si vous le faites comme ça.

Glisser UI:

Convertissez Screenpoint en point local dans RectTransform à l'aide de RectTransformUtility puis utilisez Canvas.transform.TransformPoint pour savoir où se trouve exactement l'interface utilisateur enfant.

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

Le code de glissement semble plus compliqué que les autres codes de glissement dans d'autres réponses, mais il semble fonctionner dans tous les modes de la caméra Canvas.

Détecter quel objet est sur le point d'être glissé:

La façon la plus simple de procéder consiste à créer une variable globale que vous pouvez utiliser pour enregistrer l'objet que l'utilisateur souhaite faire glisser dans la fonction OnBeginDrag, puis vous pouvez faire glisser cet objet dans le OnDrag. Définissez cet objet sur null lorsque OnEndDrag est appelé.

objectToBeDragged = eventData.pointerCurrentRaycast.gameObject;

Cela doit être fait une fois dans la fonction OnBeginDrag puis enregistré dans une variable globale.

Vous ne pouvez pas faire ce qui suit dans la fonction OnDrag

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

Même si cela fonctionne, ce n'est pas toujours le cas. Il retourne même null parfois pendant OnDrag. C'est pourquoi cela doit être fait dans la fonction OnBeginDrag.

Détection et déplacement du bouton par rapport à l'image:

Détecter si l'interface utilisateur n'est qu'un Image et faire glisser un Image est très facile.

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

Si tempImage est pasnull et tempButton est null alors c'est une Image.

Détecter si l'interface utilisateur n'est qu'un Button et faire glisser un Button est PAS facile. Lorsqu'un bouton est cliqué sur côté/bord, le nom du Button est renvoyé, ce qui est correct. Mais la plupart du temps, un clic sur un Button se produit dans le milie du Button qui ne pas retourne l'instance ou le nom du bouton mais renvoie à la place le Text (objet enfant). Vous NE POUVEZ PAS déplacer un texte en tant que bouton. Ça ne marchera pas.

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

si tempText est pas null, obtenez GetComponentInParent du composant Image et Button du texte. Si le Image n'est pas nul et que le Button n'est pas nul alors c'est un Button.

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

Vous trouverez ci-dessous le script complet de déplacement de l'image/du panneau et du bouton de l'interface utilisateur. Tout bouton qui doit être glissé doit être placé dans le tableau UIButtons et tout panneau/image qui doit être glissé doit être placé dans le tableau UIPanels. Il ignorera les autres interfaces utilisateur qui ne sont pas dans le tableau.

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 };
}
7
Programmer