Several students have tried to integrate our Inventory system into mobile games and found that the drag and drop interface simply didn’t work. You could drag the item but when you released, the item would dissappear.
I’ve been promising to look into this for a while, and after hours of experimentation and pouring through logcats, I believe I have found the issue and have a solution. The solution didn’t present itself until I realized that all button presses in the game were not blocking clicks into the game itself. In otherwords, EventSystem.current.IsPointerOverGameObject() was always returning false…
When dealing with the UI in general, in a mobile game, you have to supply EventSystem.current.IsPointerOverGameObject() with the handle of the touch that you’re checking. If you give it no parameter, it assumes you mean the mouse cursor, which of course does not exist in a mobile game. Events like GetMouseButtonDown() and GetMousebuttonUp() are simulated in mobile, but the Event System doesn’t convert this information for IsPointerOverGameObject().
So we’ll start with the solution for buttons and UI in general… in PlayerController.cs, in the method InteractWithUI()
private bool InteractWithUI()
{
if (Input.GetMouseButtonUp(0))
{
isDraggingUI = false;
}
if (EventSystem.current.IsPointerOverGameObject())
{
if (Input.GetMouseButtonDown(0))
{
isDraggingUI = true;
}
SetCursor(CursorType.UI);
return true;
}
if (isDraggingUI)
{
return true;
}
return false;
}
We’re going to leave most of this alone. Let’s let Unity simulate the MouseButtonUp and MouseButtonDown for now. It’s the check for EventSystem that we’re interested in.
#if UNITY_ANDROID
if(Input.touchCount>0 && EventSystem.current.IsPointerOverGameObject(Input.touches[0].fingerId))
#elif UNITY_IOS
if(Input.touchCount>0 && EventSystem.current.IsPointerOverGameObject(Input.touches[0].fingerId))
#else
if (EventSystem.current.IsPointerOverGameObject())
#endif
What this does is instruct the compiler to use a different if statement depending on the device being targetted. Note: Neither UNITY_ANDROID or UNITY_IOS will be valid compiler tags if their respective modules are not installed in your version of Unity! For the remainder of this lesson, I will only be using the UNITY_ANDROID tag, but the above reference demonstrats the use of both.
Unfortunately, there is no UNITY_MOBILE directive, so we have to do a separate line for each directive.
On to our Inventory system… In my game which I just converted to Android, dragging an inventory item from Inventory to anywhere causes the item to dissappear. Not only that, but it drops some cryptic hard to track down null reference exceptions in the LogCat which would crash your game if not in developer mode…
Scouring through the DragItem.CS, I noticed another IsPointerOverGameObject reference in OnEndDrag
if(!EventSystem.current.IsPointerOverGameObject())
{
container = parentCanvas.GetComponent<IDragDestination<T>>();
}
else
{
container = GetContainer(eventData);
}
My first attempt was to simply use the same fix I used in PlayerController, but this time, it did not work, I was still getting a null reference. It turns out that when the EndDrag has happened, there are no touches in Input.touches… I got more null references…
My final solution was to make a bool isOverGameObject; and make a modification to OnDrag
void IDragHandler.OnDrag(PointerEventData eventData)
{
transform.position = eventData.position;
#if UNITY_ANDROID
isOverGameObject = EventSystem.current.IsPointerOverGameObject(Input.touches[0].fingerId);
#endif
}
This is so we can use this bool in the EndDrag (the frame immediately before EndDrag will have isOverGameObject set properly)
#if UNITY_ANDROID
if(!isOverGameObject)
#else
if (!EventSystem.current.IsPointerOverGameObject())
#endif
Now, when you drag and drop, OnDrag should differentiate between being over a UI element or not.