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!
Answer by streeetwalker · Apr 01, 2020 at 05:29 PM
@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.
Thanks a lot for the response. I actually didn't think of using a static variable yet. What I've done now is make the player a singleton. I'll put my solution in a separate reply and accept yours, as you said there is no way around having a variable somewhere (e.g. in the player class), I'll believe that and move on from trying to do it in another way. I would have liked to fix it via something like the "IsPointerOverGameObject()" code.
great! the Singleton was the "indirect hookup" I referred to. I wasn't sure if you wanted that .
I guess you could have done it with a IsPointerOverGameObject() kind of function, but it would messier than just setting a static boolean, or referencing a Singleton.
If a function similar to IsPointerOverGameObject() exists in Unity that also works for Drag I'd prefer to use that. The issue is that it doesn't work while dragging. Which to me is really strange as the position of the dragged object does change with the mouse (meaning it's always under the mouse).
Answer by Drislak · Apr 01, 2020 at 05:48 PM
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;
}
}
}
Your answer
Follow this Question
Related Questions
Move like 2D sonic in 3D space around in a spiral 1 Answer
Move player based on UI controls 0 Answers
Unity C# - How to check if random moving gameobject is within boundaries 0 Answers
Can't score points 1 Answer
How to Rotate player on Y Axis using Mouse position, but also Clamp the rotation distance? 0 Answers