1
\$\begingroup\$

Been struggling with this one for a while. I've found a few other posts across Stack Exchange and the Unity forums that seem to have the same problem, but their solutions don't work for me (at least as far as I can tell).

I have a World Space canvas in my scene with a load of buttons as its children. Each button has an onClick function assigned.

Here are the settings for one of the buttons:

Button settings

And here are the settings for the canvas itself:

Canvas seytings

I also have an Event System with a Standalone Input Module with these settings:

Event system and input settings

By all accounts I've seen, this should be everything I need to interact with the buttons through the player camera. But when I look at the buttons, they don't change to the highlighted color, and pressing Mouse 0 (or Enter) doesn't click them.

(EDIT: Per DMGregory's comment, this is in a first-person view with an no visible cursor, and the reticle/center of the screen should behave like a cursor)

I feel like I'm missing something really obvious, but the documentation for this feature is so lacking I have no idea what it might be.

\$\endgroup\$
4
  • \$\begingroup\$ That should be everything needed. Is the error as well happening in a new scene with only a canvas and one button? Not the additional shader channels on your canvas, you dont even need an onclick function since the pressed state is working by itself \$\endgroup\$
    – Zibelas
    Commented Aug 10, 2023 at 19:12
  • \$\begingroup\$ When you say "look at the buttons", do you mean "position a visible mouse cursor over them"? Or are you using a first person camera with no visible cursor, and wanting the reticle / center of screen to behave like a cursor? It won't do that by default with these settings, so you'd need an extra step to fire an interaction ray through that reticle. \$\endgroup\$
    – DMGregory
    Commented Aug 10, 2023 at 19:26
  • \$\begingroup\$ @DMGregory - First person camera with no visible cursor. How would I go about actually triggering the events through that ray? \$\endgroup\$
    – rpatton583
    Commented Aug 10, 2023 at 22:52
  • \$\begingroup\$ I don't recall.off the top of my head. I'll have to experiment in the morning. In the meantime, edit your question to mention the reticle-based interaction and someone else may beat me to answering. 🙂 \$\endgroup\$
    – DMGregory
    Commented Aug 11, 2023 at 0:38

2 Answers 2

0
\$\begingroup\$

I think there should be a neater way to do this, but here's what I have working so far (based on a more involved example with curved UI here)....

We'll remove the usual StandaloneInputModule, which uses the mouse as the cursor position, and replace it with our own version that always treats the mouse as being in the center of the screen - at the reticle.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class FPSInputModule : StandaloneInputModule {
    protected override MouseState GetMousePointerEventData(int id = 0)
    {
        var reticlePosition = new Vector2(Screen.width/2, Screen.height/2);

        MouseState mouseState = new MouseState();

        PointerEventData leftData;
        var created = GetPointerData(kMouseLeftId, out leftData, true);

        leftData.Reset();

        leftData.delta = reticlePosition - leftData.position;
        leftData.position = reticlePosition;
        leftData.scrollDelta = Input.mouseScrollDelta;
        leftData.button = PointerEventData.InputButton.Left;
        eventSystem.RaycastAll(leftData, m_RaycastResultCache);
        var raycast = FindFirstRaycast(m_RaycastResultCache);
        leftData.pointerCurrentRaycast = raycast;
        m_RaycastResultCache.Clear();

        // copy the apropriate data into right and middle slots
        PointerEventData rightData;
        GetPointerData(kMouseRightId, out rightData, true);
        CopyFromTo(leftData, rightData);
        rightData.button = PointerEventData.InputButton.Right;

        PointerEventData middleData;
        GetPointerData(kMouseMiddleId, out middleData, true);
        CopyFromTo(leftData, middleData);
        middleData.button = PointerEventData.InputButton.Middle;

        mouseState.SetButtonState(PointerEventData.InputButton.Left, StateForMouseButton(0), leftData);
        mouseState.SetButtonState(PointerEventData.InputButton.Right, StateForMouseButton(1), rightData);
        mouseState.SetButtonState(PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData);

        return mouseState;
    }
}

This correctly handles clicking, but it's currently not working for hover. I think it's because as the camera rotates to bring the cursor over a new point in space, the cursor's positional delta is still zero - it's still in the center of the screen - so the event system doesn't realize it needs to re-evaluate the hover states of UI elements.

\$\endgroup\$
3
  • \$\begingroup\$ Maybe it's just too late in the evening for me to be working on this, but could you go into more detail about what's going on here? I assume you only need right- and middleData if you intend to use right- and middle-click? And interestingly, when I attempted to implement this, the raycast just hits the actual reticle object in the Overlay screen space before it can hit anything in the world space. \$\endgroup\$
    – rpatton583
    Commented Aug 12, 2023 at 3:36
  • \$\begingroup\$ Remember to turn off "raycast target" on that object. \$\endgroup\$
    – DMGregory
    Commented Aug 12, 2023 at 5:18
  • \$\begingroup\$ Oh yeah, duh. Everything seems to be working now (except the aforementioned hover issue, but I'll deal with that later). Thanks! \$\endgroup\$
    – rpatton583
    Commented Aug 18, 2023 at 23:48
0
\$\begingroup\$

I've struggled with this too. I found a kind of roundabout way to make it work, though.

So, the camera interacting with the canvas presumes it's overlaid the camera. None of the functionality works for world-space canvases. The documentation is lacking because they presume you want your UI elements to work directly on your camera with your cursor, not using your mouse position in the world, via your first-person camera

You need some sort of script to actually get that functionality back.

public GraphicRaycaster raycaster;
PointerEventData pointerData;
EventSystem eventSystem;

void Start()
{
    raycaster = GetComponent<GraphicRaycaster>();
    eventSystem = GetComponent<EventSystem>();
}

void Update()
{
    pointerData = new PointerEventData(eventSystem);
    pointerData.position = Input.mousePosition;
    //Create a list of Raycast Results
    List<RaycastResult> results = new List<RaycastResult>();

    //Raycast using the Graphics Raycaster and mouse click position
    raycaster.Raycast(pointerData, results);
    
    foreach (RaycastResult result in results)
       {
          Debug.Log("Hit " + result.gameObject.name);
       }

}

So basically you need to track pointer event data, and then theoretically what's here SHOULD let you tell you if you're hovering over a thing. You can swap that out by making the button change color on hit, by getting the buttons and then making them do what you want.

Then you can add (Input.GetMouseButtonDown(0)) and functionality after the fact to have them do things.

DMGregory's has the same basic idea but I think it's a bit more convoluted, depending on what exactly you're trying to accomplish

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .