Detect UI under mouse during drag

Hi, I have a 3D world in which the player moves using the mouse. I’ve also created a UI that the player can interact with.


What I’m trying to achieve now is for the player to be able to drag icons. This is working completely fine, except one thing: when dragging the UI icon element the player moves to the targeted location during drag. To drag a UI element I’m using “IDragHandler” and “IEndDragHandler”.


I’ve already implemented “!EventSystem.current.IsPointerOverGameObject()” but this does not work for dragging. Which is strange to me as the position of the dragged object is under the mouse, it should detect it.

Does anyone have any idea what else I could try?


Note that:

  • I want the player to be able to drop the dragged UI icon anywhere on the screen and be able to get the World location of wherever he drops it
  • I’m trying to avoid giving the UI elements a link to my player (otherwise I could put a bool)

Any help is greatly appreciated, thanks a lot!

@Drislak, OK, I think I understand the problem. You state:

I’m trying to avoid giving the UI
elements a link to my player
(otherwise I could put a bool).

I take that to mean you understand the problem too. You have to have some way of notifying the player that you are in the act of dragging the UI element. You have to put a conditional check in your player movement script to test some bool (e.g. isDraggingIcon ) that you set when you start dragging an icon. There is no way around that.

So one way or another you need to connect the two operations. Seems like your question is how to do it.

One way you could do it, and I don’t know if this is best or not, (say the script name is “PayerController” for this example ) declare a static bool:

  public class PlayerController : MonoBehavior {
        public static bool isDraggingIcon = false;

        // and then in the movement function
       if( !isDraggingIcon ) {  // do all your movement

And then in the code where you drag and drop make sure to set

       PlayerController.isDraggingIcon = true 

when you start dragging, and false when you stop. This is fine to do it this way if you only have 1 instance of player.

Or you could put the static variable on the class that holds your icon drag and drop script, and check

     ! DragIconClass.isDraggingIcon in your player script.   

And again, as long as there is only 1 instance of your drag icon class, there is no problem.

There are other ways to share values between classes without hooking them up directly, but they all involve static variables or indirect hook ups.

Final result. What I’ve done now is make the player a singleton. However I don’t have experience with singletons in Unity so I don’t know how they might react within GameObjects. Here the code of both, in case you’d like to take a look at that:
using UnityEngine;
using UnityEngine.EventSystems;

namespace HiddenGameName .Characters {

    public sealed class Player : Character
    {
        private static Player _instance;
        public static Player instance { get => _instance; }

        [SerializeField] private Camera _playerCamera;
        
        private bool _usingUI = false;
        public bool usingUI { get => _usingUI; set => _usingUI = value; }
        
        void Awake()
        {
            if (_instance == null)
            {
                _instance = this;
                DontDestroyOnLoad(_instance);
            }
        }

        void Start()
        {
            if (!_playerCamera) _playerCamera = Camera.main;
        }

        void Update()
        {
            HandleMoveInput();
        }
        

        private void HandleMoveInput()
        {
            if (!EventSystem.current.IsPointerOverGameObject() && !_usingUI)
            {
                if (Input.GetAxis("Move") > 0)
                {
                    CalculateDestination();
                }
            }
        }

        private void CalculateDestination()
        {
            Ray ray = _playerCamera.ScreenPointToRay(Input.mousePosition);
            RaycastHit raycastHit;

            if (Physics.Raycast(ray, out raycastHit))
            {
                Interactable interactable = null;
                if (raycastHit.collider.TryGetComponent<Interactable>(out interactable))
                {
                    TargetInteractable(interactable);
                }
                else
                {
                    UntargetInteractable();
                    SetDestination(raycastHit.point);
                }
            }
            
        }
    }

}

&

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using Aedoren.Characters;

namespace HiddenGameName {

    public class ItemIcon : MonoBehaviour, IDragHandler, IEndDragHandler
    {
        private Image _image;
        
        private Sprite _icon;
        private Item _item;

        public Sprite icon { get => _icon; set => _icon = value; }
        public Item item { get => _item; set => _item = value; }
        
        void Start()
        {
            if (TryGetComponent<Image>(out _image))
            {
                _image.enabled = true;
            }
        }

        public void OnDrag(PointerEventData eventData)
        {
            transform.position = Input.mousePosition;
            Player.instance.usingUI = true;
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            transform.localPosition = Vector3.zero;
            Player.instance.usingUI = false;
        }
    }

}