Forum rules - please read before posting.

Trigger Hotspot interaction with raycast

Hi, I was wondering if there was a simple way to raycast to a hotspot from a point in worldspace, then trigger the interaction UI to appear (or just the interaction itself if context sensitive).

Use case - I'm using tracked controllers with a VR rig, so want to point the controller at the hotspot, raycast at it, then trigger interaction/UI. I've got raycast to UI using OnPointerSubmit, now just need hotspot triggers. If you like I can share codebase when done as a VR port of adventure creator. Adventure games + VR is awesome.

Comments

  • Regular Hotspot interaction occurs via the cursor, so you could try overriding the cursor position.  The included "World Space Cursor Example" component allows you to use an object in the scene to define the cursor's position - you could use that as a basis for your own.

    To trigger a Hotspot's interaction manually, you can call the Hotspot script's RunUseInteraction function.

    To turn on the Interaction menus, you call the PlayerMenus script's SetInteractionMenus function:

    AC.KickStarter.playerMenus.SetInteractionMenus (true);

    That will turn on the Interaction menu for the selected Hotspot.  However, there is a private function of the same name in the script that allows to to choose which Hotspot specifically it is turned on for.  I will see if this can be made public (or a variant of it) in the next update, but you can change it to public to use it yourself if you wish in the meantime.
  • Amazing thanks! I'm trying to avoid anything that takes over mouse position (had my own solution for that in the past but its not ideal).

    So I'm able to raycast and get the hotspot, and send Select() to it (highlighting it succesfully), however when I run EnableInteractionMenus with the hotspot found by raycast, it doesn't open the interaction menu. not sure if I'm missing steps or conditions that need to be met? I've also tried SetInteractionMenus set to public and set a bunch of debugs throughout it, and process seems to be same for mouseover compared to triggering the function via code so i'm a bit stumped why the interaction menu isn't appear.

    The basic routine is below (imagine there's already been a raycast that gets the hotspot).

    selectedHotspot = hit.collider.GetComponent<AC.Hotspot>();
    selectedHotspot.Select();
    AC.KickStarter.playerMenus.EnableInteractionMenus (selectedHotspot);

    Any light you can shine on it would be amazing thanks!
  • Further to this, I tried 'AC.KickStarter.playerMenus.SetInteractionMenus (true)', but it requires the hotspot, and a null (for inventory menu), which is essentailly what the EnableInteractionMenu does. I'm sure I'm missing something obvious, I've tried tracking the functions from StateHandler - PlayerInteraction - PlayerMenus but can't seem to crack what I'm missing!

    Thanks in advance!
  • EnableInteractionMenus was added since your first post in order to provide what you're trying to achieve, so the code you've posted should work.  Are you using the very latest AC?

    For it to work, you need a Menu with an Appear type of On Interaction.  Try it with the default interface by setting your Menu Manager asset file to Demo_MenuManager, and let us know if it works - the result will help us work out the next step.
  • edited January 2017
    hey thanks for getting back, yeah I've updated and dug the enableinteractionmenus function out, I've definitely got the hotspot set to appear on interaction,

    I've also tried a vanilla version (new project, newest AC, raycasting at hotspot sending "Select" and "EnableInteractionMenus") and getting same results. I've definitely got appear type to on interaction, 
    Aside from above is there any other conditions/variables/functions that affect the display of the menu?

    I'm going to publish my full code below in case there's something I'm missing. 
  • using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    using System.Collections.Generic;

    public class VRCursor : MonoBehaviour {

    //Reticle Management

    public Camera CameraFacing;
    private Vector3 originalScale;
    public GameObject LeftController;
    public bool mouseInput;
    public GameObject interactionMenu;
    public SteamVR_TrackedController thisController;

    public bool selectedSent = false;

    //Hotspot ray
    AC.Hotspot selectedHotspot;
    AC.Hotspot previousHotspot;

    //UI Ray

    int UIMask = 1 << 5;
    RaycastHit rayHit;
    public float rayThickness;
    public bool displayDebugRay;
    GameObject rayHitDelta;


    // Use this for initialization
    void Start () {
    originalScale = transform.localScale;
    thisController = LeftController.GetComponent<SteamVR_TrackedController>();
    }
    void Update () {
    HotspotRay();

    }



    // Update is called once per frame
    public void HotspotRay(){

    RaycastHit hit;
    float distance;

    if (Physics.Raycast (new Ray (LeftController.transform.position,
    LeftController.transform.rotation * Vector3.forward),
    out hit)) {

    distance = hit.distance;
    Debug.Log("hit");
    Debug.Log(hit.collider.gameObject);

    if(hit.collider.tag=="Hotspot"){

    selectedHotspot = hit.collider.GetComponent(); //gets hotspot component

    if(!selectedSent){//makes sure select only sent once

    selectedHotspot.Select();

    selectedSent = true; //one hitter

    AC.KickStarter.playerMenus.EnableInteractionMenus(selectedHotspot);

    previousHotspot = selectedHotspot; //record this as last rays to another hotspot
    }
    if(selectedSent){
    if(thisController.triggerPressed){
    selectedHotspot.RunUseInteraction();
    }
    }

    }

    if (hit.collider.tag!="Hotspot"&&selectedSent){ //if hotspot selected and stops hitting a hotspot

    selectedHotspot.Deselect();//sends deselect because no longer hitting hotspot

    selectedSent = false;//allows for select to be sent again on next collision

    AC.KickStarter.playerMenus.CloseInteractionMenus();//closes menus hypothetically

    }

    if (selectedHotspot != previousHotspot ){//sends deselect because hitting another hotspot

    selectedHotspot.Deselect();

    AC.KickStarter.playerMenus.CloseInteractionMenus();

    selectedSent = false;
    }

    } else {

    distance = CameraFacing.farClipPlane* 0.1f;

    if(selectedSent){

    selectedHotspot.Deselect();

    selectedSent = false;

    AC.KickStarter.playerMenus.CloseInteractionMenus();

    }

    }


    //Reticle position/management

    transform.position = LeftController.transform.position +
    LeftController.transform.rotation * Vector3.forward * distance;
    transform.LookAt (CameraFacing.transform.position);
    transform.Rotate (0.0f,180.0f, 0.0f);
    transform.localScale = originalScale * distance;

    if(mouseInput){
    Vector3 temp = Input.mousePosition;
    temp.z = distance; // Set this to be the distance you want the object to be placed in front of the camera.
    this.transform.position = Camera.main.ScreenToWorldPoint(temp);
    }

    }


    }

  • Thank you - I will go through it and get back to you.
  • cool, just to be clear, the script is put on a quad with a world cursor, and then left controller is linked to the steamvr controller, if you scrap the steamvr controller references, and then just manually link to the raycasting object you can fake test it without using controller (ie just raycast from a random object and manipulate in scene view. I'll actually post version that doesn't require VR component below.
  • using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    using System.Collections.Generic;

    public class VRCursor : MonoBehaviour {

    //Reticle Management

    public Camera CameraFacing;
    private Vector3 originalScale;
    public GameObject LeftController;
    public bool mouseInput;
    public GameObject interactionMenu;

    public bool selectedSent = false;

    //Hotspot ray
    AC.Hotspot selectedHotspot;
    AC.Hotspot previousHotspot;

    //UI Ray

    int UIMask = 1 << 5;
    RaycastHit rayHit;
    public float rayThickness;
    public bool displayDebugRay;
    GameObject rayHitDelta;


    // Use this for initialization
    void Start () {
    originalScale = transform.localScale;
    }
    void Update () {
    HotspotRay();

    }



    // Update is called once per frame
    public void HotspotRay(){

    RaycastHit hit;
    float distance;

    if (Physics.Raycast (new Ray (LeftController.transform.position,
    LeftController.transform.rotation * Vector3.forward),
    out hit)) {

    distance = hit.distance;
    Debug.Log("hit");
    Debug.Log(hit.collider.gameObject);

    if(hit.collider.tag=="Hotspot"){

    selectedHotspot = hit.collider.GetComponent(); //gets hotspot component

    if(!selectedSent){//makes sure select only sent once

    selectedHotspot.Select();

    selectedSent = true; //one hitter

    AC.KickStarter.playerMenus.EnableInteractionMenus(selectedHotspot);

    previousHotspot = selectedHotspot; //record this as last rays to another hotspot
    }


    }

    if (hit.collider.tag!="Hotspot"&&selectedSent){ //if hotspot selected and stops hitting a hotspot

    selectedHotspot.Deselect();//sends deselect because no longer hitting hotspot

    selectedSent = false;//allows for select to be sent again on next collision

    AC.KickStarter.playerMenus.CloseInteractionMenus();//closes menus hypothetically

    }

    if (selectedHotspot != previousHotspot ){//sends deselect because hitting another hotspot

    selectedHotspot.Deselect();

    AC.KickStarter.playerMenus.CloseInteractionMenus();

    selectedSent = false;
    }

    } else {

    distance = CameraFacing.farClipPlane* 0.1f;

    if(selectedSent){

    selectedHotspot.Deselect();

    selectedSent = false;

    AC.KickStarter.playerMenus.CloseInteractionMenus();

    }

    }


    //Reticle position/management

    transform.position = LeftController.transform.position +
    LeftController.transform.rotation * Vector3.forward * distance;
    transform.LookAt (CameraFacing.transform.position);
    transform.Rotate (0.0f,180.0f, 0.0f);
    transform.localScale = originalScale * distance;

    if(mouseInput){
    Vector3 temp = Input.mousePosition;
    temp.z = distance; // Set this to be the distance you want the object to be placed in front of the camera.
    this.transform.position = Camera.main.ScreenToWorldPoint(temp);
    }

    }


    }
  • I'm assuming you've done some extra work regarding your Hotspot's tags, but alternatively you could simply check for the presence of the Hotspot component.

    Look for the Close interactions with option in the Settings Manager and set it to Click Off Menu.  I think the issue was that AC was closing it instantly as the conditions to keep it open were not being met.

    I would also recommend you take a look at the "World Space Cursor Example" script included with AC, as it shows how you can manipulate the AC cursor to take the position of a real-world object.
  • Ahh amazing! They all sound like good pointers, I'll give it a go - I've said it before but if I get a good vanilla VR set up I'll share it with you for future work! (This is my little side project so progress can be slow sometimes :P)
  • edited January 2017
    You should probably use a property instead of a regular variable for your Nearest hotspot (that's what I do). That way you can run the necessary code inside the property whenever there's a change in the contents.

    something like:

    private Hotspot _nearestHotspot;

            public Hotspot NearestHotspot
            {
                get { return _nearestHotspot; }
                set
                {
                    if (value != _nearestHotspot)//if the value has changed
                    {
                        if (value != null) //if new value is not null
                        {
    DeselectHotspot(); // deselect old
                            _nearestHotspot = value; //update to new hotspot
                            SelectHotspot(); // select new hotspot
                        }
                        else //if null just deselect old
                        {
                            DeselectHotspot();
                        }
                    }

                    _nearestHotspot = value;
                }
            }

    Also you could probably look into using dialogue boxes instead of the regular hotspot title. I've found that it gives you a lot more power (specially if you already have code for positioning your UI). You'll have all the Dialogue options (including text scrolling :) ). In which case all you'll have to do is put the text on an examine or other interaction category and then run it in your select method:

    _nearestHotspot.RunUseInteraction(InteractionIconId);
  • Given the scenario brought up in this thread, I'll be adding options to enable/disable Interaction Menus via script only in the next release.  This'll mean you have the option of having complete control over when such Menus are shown - as opposed to having to "fight" AC for it.
  • amazing!  and thanks for tip alverik.


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.