Forum rules - please read before posting.

Render cursor behind UnityUI

AC: 1.72.4 Unity 2018.4.1f1

I'm looking to render the AC cursor behind a Unity UI based AC menu.

To have more control over how the cursor is rendered I've tried switching to a Unity UI based cursor using this guide: https://adventure-creator.fandom.com/wiki/Unity_UI-based_cursor

I've followed the guide closely and it doesn't seem to be working correctly, the cursor is not locked in the centre of the screen (this is a first person, 3D game) but the camera still moves, albeit with a delay behind the cursor. If I switch back to software rendering for the cursor, it is locked in centre.

The UI for the cursor is following mouse movements but the normal hardware cursor is also showing. The UI cursor is lagging slightly behind the hardware cursor and then the first person camera is lagging behind them both.

I've tried to capture a gif but the capture software doesn't capture the hardware cursor (which displays as well as the UI cursor), believe me it's there!

https://imgur.com/vx8Qogd

(excuse the huge cursor, that doesn't seem to scale with window size)

This all feels a bit like overkill, Is there a more straightforward method to make the AC cursor render behind a UI? Software rendering is perfectly suitable otherwise.

I appreciate this is a bit of a weird request that likely no one else has required!

Comments

  • If it's any help, here are my settings for the Canvas used for the cursor. The RawImage is a child of this canvas.

    https://imgur.com/VYNrdu9

  • edited December 2020

    Unfortunately, it is a complicated aspect.

    With Hardware rendering, your actual system's cursor is used - so this'll always be displayed on top of everything, no matter what.

    With Software rendering, AC relies on Unity's OnGUI system to render the cursor as a texture. This too is rendered last in Unity draw calls, along with other OnGUI elements such as "AC" menus.

    You're on the right track in that your cursor needs to rely on the same render method as your Menus, though if your Menus are rendered with AC, then you could rely on the Software cursor. Otherwise, using a Unity UI-based cursor is the way to go.

    However, the script on the wiki works by reading Unity's raw mouse position (ie. Input.mousePosition) which won't account for the "locked" state of the cursor that AC relies on.

    Try replacing the line:

    Vector2 _position = Input.mousePosition;
    

    with:

    Vector2 _position = KickStarter.playerInput.GetMousePosition ();
    

    That should cause it to rely on the AC cursor position instead.

  • Yup, that locks the UI in place. Unfortunately the hardware cursor iself still able to move around.

    I'm thinking maybe there is a better approach to this, I should probably explain my reasoning behind the whole thing.

    The end result I'm looking to achieve is to have an image sit behind the hotspot description text (a dark fade). My UI for the hotspot description is set to the 'On Hotspot' appear type.

    The thing preventing me from implementing this conventionally is that I also use hotspots to trigger actions without the player being aware, these hotspots have a single space character to prevent the description text from displaying. If I add an image to the hotspot UI this image displays when hovering these hotspots with a space for their label (but text does not), which breaks the illusion.

    As my game only uses the 'use' cursor icon, I was trying to work around this by using cursors to display the fade image and setting hotspots that shouldn't have a label to the Talk to cursor/icon (which doesn't have the fade cursor).

    Here's an example of what I'm aiming for, this is using software cursors and shows the cursor rendering in front of the text:

    https://imgur.com/JaSOKDK

  • edited December 2020

    Unfortunately the hardware cursor iself still able to move around.

    The script won't lock the Hardware cursor - but if you supply a transparent texture in the "Empty Texture" field, it should apply that to the Hardware cursor to make it invisible.

    I also use hotspots to trigger actions without the player being aware, these hotspots have a single space character to prevent the description text from displaying.

    An alternative to giving a Hotspot an "empty" label is to instead lock the "On Hotspot" menu when such a Hotspot is selected.

    If you hook into the OnHotspotSelect event, you can lock/unlock a given menu according to its tag.

    Something like:

    using UnityEngine;
    using AC;
    
    public class HideHotspotMenu : MonoBehaviour
    {
    
        private void OnEnable ()
        {
            EventManager.OnHotspotSelect += OnSelect;
            EventManager.OnHotspotDeselect += OnDeselect;
        }
    
        private void OnDisable ()
        {
            EventManager.OnHotspotSelect -= OnSelect;
            EventManager.OnHotspotDeselect -= OnDeselect;
        }
    
        private void OnSelect (Hotspot hotspot)
        {
            bool lockMenu = hotspot.gameObject.CompareTag ("InvisibleHotspot");
            PlayerMenus.GetMenuWithName ("Hotspot").isLocked = lockMenu;
        }
    
        private void OnDeselect (Hotspot hotspot)
        {
            PlayerMenus.GetMenuWithName ("Hotspot").isLocked = false;
        }
    
    }
    

    Placed in a scene, that will lock a menu named "Hotspot" if a Hotspot given the tag "InvisibleHotspot" is selected, and unlock it otherwise.

  • Awesome, thanks! I was just exploring this method by hacking together an example script in another thread.

    It works perfectly. Just to note there's a typo on that second private void, it should be OnDeselect.

    Thanks again Chris.

  • Actually, there's a couple - the OnDisable function needs to un-register the events.

    Updated above with both corrections.

  • edited December 2020

    Thanks again :smile:

    Sorry to keep this going, I'm using the method you provided to lock/unlock different hotspot descriptions based on their tag.

    There appears to be an issue with how I'm approaching this, I've noticed an edge case where if two hotspots overlap, it will display both Hotspot_UseUI and Hotspot_OpenUI when hovering from one to another (despite the two hotspots in question being tagged MicHotspot and UseHotspot). I assume this is because the select or deselect command isn't being run (as there isn't a gap between the two hotspots).

    Any tips?

    
    using UnityEngine;
    using AC;
    
    public class HideHotspotMenu : MonoBehaviour
    {
    
        private void OnEnable()
        {
            EventManager.OnHotspotSelect += OnSelect;
            EventManager.OnHotspotDeselect += OnDeselect;
        }
    
        private void OnDisable()
        {
            EventManager.OnHotspotSelect -= OnSelect;
            EventManager.OnHotspotDeselect -= OnDeselect;
        }
    
        private void OnSelect(Hotspot hotspot)
        {
            bool lockMenu = hotspot.gameObject.CompareTag("MicHotspot"); // lockMenu bool checks if tag is MicHotspot if it is lockMenu = true
            PlayerMenus.GetMenuWithName("Hotspot_Desc").isLocked = lockMenu;  // sets menu Hotspot_Desc to lockMenu bool value
            bool lockMenu2 = hotspot.gameObject.CompareTag("UseHotspot"); // lockMenu2 bool checks if tag is UseHotspot if it is lockMenu = true
            PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = !lockMenu2;  // sets menu Hotspot_UseUI to lockMenu2 bool value
            bool lockMenu3 = hotspot.gameObject.CompareTag("OpenHotspot"); // lockMenu3 bool checks if tag is OpenHotspot if it is lockMenu = true
            PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = !lockMenu3;  // sets menu Hotspot_OpenUI to lockMenu3 bool value
        }
    
        private void OnDeselect(Hotspot hotspot)
        {
            PlayerMenus.GetMenuWithName("Hotspot_Desc").isLocked = false;
            PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = false;
            PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = false;
        }
    
    }
    
  • edited December 2020

    Seem to be getting somewhere with this, although there is a frame or two where the menus display before being hidden when hovering the 'hidden' hotspot (MicHotspot).

    
    
    using UnityEngine;
    using AC;
    
    public class HideHotspotMenu : MonoBehaviour
    {
    
        private void OnEnable()
        {
            EventManager.OnHotspotSelect += OnSelect;
            EventManager.OnHotspotDeselect += OnDeselect;
        }
    
        private void OnDisable()
        {
            EventManager.OnHotspotSelect -= OnSelect;
            EventManager.OnHotspotDeselect -= OnDeselect;
        }
    
        private void OnSelect(Hotspot hotspot)
        {
            bool micHotspot = hotspot.gameObject.CompareTag("MicHotspot"); // lockMenu bool checks if tag is MicHotspot if it is lockMenu = true
            bool useHotspot = hotspot.gameObject.CompareTag("UseHotspot"); // lockMenu2 bool checks if tag is UseHotspot if it is lockMenu = true
            bool openHotspot = hotspot.gameObject.CompareTag("OpenHotspot"); // lockMenu3 bool checks if tag is OpenHotspot if it is lockMenu = true
            bool pickupHotspot = hotspot.gameObject.CompareTag("PickupHotspot"); // lockMenu3 bool checks if tag is PickupHotspot if it is lockMenu = true
            bool examineHotspot = hotspot.gameObject.CompareTag("ExamineHotspot"); // lockMenu3 bool checks if tag is ExamineHotspot if it is lockMenu = true
    
            if (micHotspot == true) {
                PlayerMenus.GetMenuWithName("Hotspot_Desc").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
            }
            if (useHotspot == true) {
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
            }
            if (openHotspot == true) {
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
            }
            if (pickupHotspot == true)
            {
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
            }
            if (examineHotspot == true)
            {
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").DisableUI();
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
            }
        }
    
        private void OnDeselect(Hotspot hotspot)
        {
            PlayerMenus.GetMenuWithName("Hotspot_Desc").isLocked = false;
            PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = false;  // sets menu Hotspot_UseUI to lockMenu2 bool value
            PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = false;  // sets menu Hotspot_OpenUI to lockMenu3 bool value
            PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = false;  // sets menu Hotspot_OpenUI to lockMenu3 bool value
            PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = false;  // sets menu Hotspot_OpenUI to lockMenu3 bool value
        }
    
    }
    
    
  • Your code blocks aren't conditional. If examineHotspot is true, then all the code above it will be ignored because the code that then follows overrides it all.

    You're also not unlocking anything in OnSelect, meaning things can only become locked.

    Instead, try calling this at the top of OnSelect:

    OnDeselect (null);
    

    That will cause all Menus to become unlocked (i.e. reset). Then, have each code block that follows be conditional upon each other using 'else' statements, i.e.:

    if (micHotspot)
    {
        // Do this
    } 
    else if (useHotspot)
    {
        // Or do this
    }
    else if (openHotspot)
    {
        // etc
    }
    

    If there is an issue with two Hotspots overlapping, I'll need to know more about your Interface and Hotspot settings in the Settings Manager.

  • Thanks Chris.

    The overlapping hotspot issue doesn't seem to be a problem now.

    However I'm finding when hovering off a hotspot all of the menus appear for a frame or two before disappearing.

    As far as I can tell, the issue seems to be related to the OnDeselect private void running just before whatever AC code handles the hotspot detection.

    I am able to mask this problem somewhat by setting the menu's transition type to Canvas Group Fade and setting a custom curve that keeps the menu hidden for the first 0.2 seconds before curving to visible over an additional 0.2 seconds. Giving the script time to run before the fade is completed.

    This solution isn't ideal but will suffice if this isn't something caused by my script.

    Thanks for your patience with this.

    The appear type is set to On Hotspot for all these menus.

    My hotspot settings:

    https://i.imgur.com/0eMisPm.png

    https://i.imgur.com/fl7T4ZS.png

    My current script:

    
    
    using UnityEngine;
    using AC;
    
    public class HideHotspotMenu : MonoBehaviour
    {
    
        private void OnEnable()
        {
            EventManager.OnHotspotSelect += OnSelect;
            EventManager.OnHotspotDeselect += OnDeselect;
        }
    
        private void OnDisable()
        {
            EventManager.OnHotspotSelect -= OnSelect;
            EventManager.OnHotspotDeselect -= OnDeselect;
        }
    
        private void OnSelect(Hotspot hotspot)
        {
            OnDeselect(null);
    
            bool micHotspot = hotspot.gameObject.CompareTag("MicHotspot");
            bool useHotspot = hotspot.gameObject.CompareTag("UseHotspot");
            bool openHotspot = hotspot.gameObject.CompareTag("OpenHotspot");
            bool pickupHotspot = hotspot.gameObject.CompareTag("PickupHotspot");
            bool examineHotspot = hotspot.gameObject.CompareTag("ExamineHotspot");
    
            if (micHotspot == true) {
                PlayerMenus.GetMenuWithName("Hotspot_Desc").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
            }
            else if (useHotspot == true) {
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
            }
            else if (openHotspot == true) {
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
            }
            else if (pickupHotspot == true)
            {
                PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
            }
            else if (examineHotspot == true)
            {
                PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
                PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
            }
        }
    
        private void OnDeselect(Hotspot hotspot)
        {
            PlayerMenus.GetMenuWithName("Hotspot_Desc").DisableUI();
            PlayerMenus.GetMenuWithName("Hotspot_UseUI").DisableUI();
            PlayerMenus.GetMenuWithName("Hotspot_OpenUI").DisableUI();
            PlayerMenus.GetMenuWithName("Hotspot_PickupUI").DisableUI();
            PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").DisableUI();
    
            PlayerMenus.GetMenuWithName("Hotspot_Desc").isLocked = false;
            PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = false;
            PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = false;
            PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = false;
            PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = false;
        }
    
    }
    
    

  • edited December 2020

    I'm finding when hovering off a hotspot all of the menus appear for a frame or two before disappearing.

    In that case, probably best to just get rid of the OnDeselect function / event completely.

    You'd then instead update the lock state of all menus explicitly in OnSelect, i.e.:

    else if (examineHotspot == true)
    {
        PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = true;
        PlayerMenus.GetMenuWithName("Hotspot_UseUI").isLocked = true;
        PlayerMenus.GetMenuWithName("Hotspot_OpenUI").isLocked = true;
        PlayerMenus.GetMenuWithName("Hotspot_PickupUI").isLocked = false;
        PlayerMenus.GetMenuWithName("Hotspot_ExamineUI").isLocked = false;
    }
    

    Doing it this way will keep the lock states of your Menus the same until you hover over another Hotspot. So, if you want to update them when hovering over / selecting inventory items, you'll need to incorporate that into the script as well - though exactly how would depend on your needs.

  • Worked a charm, thanks Chris!

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.