- Home /
Cursor.lockstate and OnPointerEnter not working together
Today we moved our project over from Unity 5.3.6 to 5.4.2, and so far we have only noticed one big issue. Locking the cursor used to center it and still trigger Pointer enters and exits on world space UI. But now according to the EventSystem in editor, the pointer position is set to -1,-1 and it does not trigger any enter or exit calls. I am currently thinking I will have to change how it is setup, even though it worked perfectly in 5.3.6. Has anyone else seen this?
It does sound more useful now since a locked cursor should probably not be able to do anything. But since you know you want to do something in the screen center can't you just implement center clicks yourself?
You can probably use EventSystem,current.RaycastAll with an EventData and the position set to screen center position. Then you can use ExecuteEvents.Execute to issue a click event on the GUI element you found.
Of course this might only work if you're talking about GUI elements. Otherwise a screen center Raycast and executing whatever you want might be it.
Try creating a custom Physics Raycaster and attach to the camera:
public class CustomPhysicsRaycaster : PhysicsRaycaster
{
public override void Raycast (PointerEventData eventData, List<RaycastResult> resultAppendList)
{
if (Cursor.lockState != CursorLock$$anonymous$$ode.Locked) {
base.Raycast (eventData, resultAppendList);
return;
}
Ray ray = eventCamera.ViewportPointToRay (new Vector3 (0.5f, 0.5f, 0f));
var hits = Physics.RaycastAll (ray, eventCamera.farClipPlane, event$$anonymous$$ask.value);
for (int i = 0; i < hits.Length; i++) {
var hit = hits [i];
var result = new RaycastResult ();
result.distance = hit.distance;
result.gameObject = hit.collider.gameObject;
result.index = i;
resultAppendList.Add (result);
}
}
}
Answer by RemiCarreira · Sep 20, 2018 at 11:46 AM
Hello, I have found a solution to avoid this problem (Still available in Unity 2018.2.6f).
Is not the best solution, but this can help.
Before calling the Process method of your Input Module, you can set the lock state of the cursor to None. To do this easily, you can create your custom input module like the code below.
using UnityEngine;
using UnityEngine.EventSystems;
public class CustomInputModule : StandaloneInputModule
{
// Current cursor lock state (memory cache)
private CursorLockMode _currentLockState = CursorLockMode.None;
/// <summary>
/// Process the current tick for the module.
/// </summary>
public override void Process()
{
_currentLockState = Cursor.lockState;
Cursor.lockState = CursorLockMode.None;
base.Process();
Cursor.lockState = _currentLockState;
}
}
Nice! This is such a clean and simple way of fixing it! Thanks!! :D
Answer by Bezzy · Dec 12, 2016 at 03:56 PM
I tried doing something like this, but it seemed to break in 5.5.
It seems that, if Cursor.lockstate is on, then although the custom raycaster fires, the processing of the move is discarded.
It seems like they changed this to ignore all pointer events while cursor is locked in 5.5.
I played around a bit with it and managed to get it to work. Use the Custom Physics Raycaster i posted before, in addition to replacing the default StandaloneInput$$anonymous$$odule on the EventSystem object with a custom one. See code below.
If you want to look more at the UI code, the relevant part can be found here: UI / UnityEngine.UI / EventSystem / Input$$anonymous$$odules / PointerInput$$anonymous$$odule.cs
I'm not sure this is the best workaround or how future proof it would be, though.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class CustomInput$$anonymous$$odule : StandaloneInput$$anonymous$$odule
{
protected override void Process$$anonymous$$ove (PointerEventData pointerEvent)
{
var targetGO = pointerEvent.pointerCurrentRaycast.gameObject;
HandlePointerExitAndEnter (pointerEvent, targetGO);
}
}
Haha, This is so funny. I literally was just co$$anonymous$$g back to show my fix and you've got the exact same one.
I'm going to post $$anonymous$$e just for fun.
//This is only necessary because in Unity 5.5, code was added to ignore mouse process when the cursor was locked.
namespace UnityEngine.EventSystems
{
[AddComponent$$anonymous$$enu("Event/Custom Standalone Input $$anonymous$$odule")]
public class InteractiveStandaloneInput$$anonymous$$odule : StandaloneInput$$anonymous$$odule
{
protected override void Process$$anonymous$$ove(PointerEventData pointerEvent)
{
GameObject newEnterTarget = pointerEvent.pointerCurrentRaycast.gameObject;
base.HandlePointerExitAndEnter(pointerEvent, newEnterTarget);
}
}
}
Oh and I previously made my own custom raycaster. I actually use a sphere cast because it can be slightly more forgiving in terms of user interface. Like, you don't have to be pixel perfect. It's as if every object has a forgiving radius to it. Slightly more expensive, but it's a singleton so, whatevs.
using System;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.EventSystems;
//I used IL2 to copy out the Physicsraycaster code and fix where the ray e$$anonymous$$ates from (in this case, always the center of the screen)
public class InteractionRaycaster : PhysicsRaycaster {
[Range(0.0f, 0.5f)]
public float RayRadius;
private readonly Vector2 _viewPos = new Vector2(0.5f, 0.5f);
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
if (!(this.eventCamera == null))
{
Ray ray = this.eventCamera.ViewportPointToRay(_viewPos);//this used to be the event position, but when the cursor is locked, that becomes -1,-1 so i fixed it
float distance = this.eventCamera.farClipPlane - this.eventCamera.nearClipPlane;
RaycastHit[] array = Physics.SphereCastAll(ray, RayRadius, distance, this.finalEvent$$anonymous$$ask);
if (array.Length > 1)
{
Array.Sort<RaycastHit>(array, (RaycastHit r1, RaycastHit r2) => r1.distance.CompareTo(r2.distance));
}
if (array.Length != 0)
{
int i = 0;
int num = array.Length;
while (i < num)
{
RaycastResult item = new RaycastResult
{
gameObject = array[i].collider.gameObject,
module = this,
distance = array[i].distance,
worldPosition = array[i].point,
worldNormal = array[i].normal,
screenPosition = _viewPos,
index = (float)resultAppendList.Count,//dunno why this is a float
sortingLayer = 0,
sortingOrder = 0
};
resultAppendList.Add(item);
i++;
}
}
}
}
}