Search Unity

[Help] How would I do raycasthit using GvrLaserPointer.

Discussion in 'Daydream' started by mat9054, Jan 23, 2017.

  1. mat9054

    mat9054

    Joined:
    Nov 14, 2015
    Posts:
    42
    I am trying to do raycasthit from my daydream controller model in my scene in order to get transform.position where the raycast hit collider. I already tried using ray from controller transform toward direction (gvrcontroller.ori * vector3.forward). My problem is the will create a straight ray, whole the laser pointer is slightly incline.

    I have seen some raycast class in gvr sdk. My question is, is there any fonction in the classes provided in gvr sdk that will help me retrieve transform.postion where laser collides with a physics collider. If so, how would i implement it?
     
  2. dsternfeld

    dsternfeld

    Official Google Employee

    Joined:
    Jan 3, 2017
    Posts:
    72
    1. Use GvrPointerInputModule and GvrPointerPhysicsRaycaster to detect collision. You can see an example of this in the GvrDemo scene. There is also more information here in the section "The Google VR Pointer System".
    2. Create a script that inherits from one of the EventSystemHandlers (i.e. IPointerClickHandler). Place this script on the same object as the collider that you are trying to hit. Alternatively, you can use an EventTrigger.
    3. In the implemented function for the interface (i.e. OnPointerClick) you can access where the pointer hit the collider via the PointerEventData like this: eventData.pointerCurrentRaycast.worldPosition.
     
    guneyozsan and mat9054 like this.
  3. mat9054

    mat9054

    Joined:
    Nov 14, 2015
    Posts:
    42
    I manage to get pointer worldPosition using the above advice but I have another related question.

    First I will explain what I am trying to achieve. In my game I have a table which is the object with collider I am raycasting to. My goal is to have worldPosition where the ray hit the table collider, every frame I am pointing the table gameobject. I want to use this Vector3 in order to move another gameobject on the table so it follows the daydream laser pointer.

    The above example I great to get worldPosition but only when Click happens. What if I want this information everyframe that the ray hit the table collider?

    I looked into Supported Events and can't find anything related to pointer that will give my the information every frame. I've seen ISelectHandler - OnSelect but I dont think it is the right event for what I am trying to do.

    Sorry if this is basic stuff, I am quite new to C#/Unity, learning on my own and this is my first personal project so any help would be greatly appreciated. Hopefully my goal is clear enough.

    Thank you.
     
  4. dsternfeld

    dsternfeld

    Official Google Employee

    Joined:
    Jan 3, 2017
    Posts:
    72
    Good question!

    It sounds like what you want is a Hover event. As you've unfortunately seen, Unity doesn't have a Hover event built-in to it's event system.

    However, we've added our own hover event for purposes such as this. If you inherit from IGvrPointerHoverHandler instead of IPointerClickHandler (or in addition if you still want to detect when the table is clicked) then OnGvrPointerHover will be called every frame that the pointer is pointing at the table, and the worldPosition can be accessed in the same way.

    I hope that helps!

    -Dan
     
    mat9054 likes this.
  5. mat9054

    mat9054

    Joined:
    Nov 14, 2015
    Posts:
    42
    Exactly what I was looking for. Thank you very much for your assistance.
     
  6. sterlingcrispin

    sterlingcrispin

    Joined:
    Oct 23, 2013
    Posts:
    12
    Hey Dan

    One thing that I came across is that RaycastResult is generic and only includes things shared among all raycast results, for example GvrPointerPhysicsRaycaster's function for Raycast contains in it RaycastHit which includes lots of cool stuff that doesn't get passed into the list of results at the end.

    https://docs.unity3d.com/ScriptReference/RaycastHit.html

    I want access to the textureCoord but I can't seem to get at it with this PointerEventData , I have a really hacky solution but I'm interested if you have any clearer ideas --

    What I ended up doing was using EventTrigger to call a function in another script,
    Code (csharp):
    1.  
    2.     public void Foo(BaseEventData data){
    3.  
    4.         PointerEventData pointerData = data as PointerEventData;
    5.  
    6.         GvrPointerPhysicsRaycaster cast = pointerData.pointerCurrentRaycast.module as GvrPointerPhysicsRaycaster;
    7.         Vector2 texturecoord = Vector2.zero;
    8.         if(cast.rayhits != null){
    9.             foreach( RaycastHit element in cast.rayhits){
    10.                 if( element.collider.gameObject  == SomeGameObjectIcareAbout ){
    11.                     texturecoord = element.textureCoord;
    12.                 }
    13.             }
    14.         }
    15.    
    16.     }
    17.  
    18.  
    19.    
    and GvrPointerPhysicsRaycaster modified to include

    Code (csharp):
    1.  
    2.     public List<RaycastHit> rayhits;
    3.  
    and
    Code (csharp):
    1.  
    2. .....(around line 100 in Raycast()....
    3.  
    4.     rayhits = new List<RaycastHit>();
    5.     rayhits.Clear();
    6.     if (hits.Length != 0) {
    7.       for (int b = 0, bmax = hits.Length; b < bmax; ++b) {
    8.         rayhits.Add(hits[b]);
    9. ....
    10.  
     
    Last edited: Feb 9, 2017
    guneyozsan and fiixed like this.
  7. dsternfeld

    dsternfeld

    Official Google Employee

    Joined:
    Jan 3, 2017
    Posts:
    72
    I don't think that there is an elegant solution to this within the EventSystem api. I'd like to be able to extend RaycastResult, but that can't currently be done. You could work around the problem like this:


    RaycastResultExtraData.cs:

    Code (CSharp):
    1.  
    2. public class RaycastResultExtraData {
    3.   public Vector2 textureCoord;
    4.  
    5.   // Any other properties you want to expose.
    6. }
    7.  
    PointerEventDataExtra.cs

    Code (CSharp):
    1.  
    2. public class PointerEventDataExtra : PointerEventData {
    3.   public RaycastResultExtraData pointerCurrentRaycastExtra;
    4. }
    5.  
    Then, you would need to update GvrPointerInputModule to use PointerEventDataExtra. You would also need to modify GvrPointerPhysicsRaycaster and GvrPointerGraphicRaycaster to instantiate RaycastResultExtraData and assign it to the eventData. You'll have to be careful to make sure the RaycastResult hit matches the stored Extra Data.

    Then you would use it like this:

    Code (CSharp):
    1.  
    2. public void Foo(BaseEventData eventData) {
    3.   PointerEventDataExtra pointerEventData = eventData as PointerEventDataExtra;
    4.   if (pointerEventData != null && pointerEventData.pointerCurrentRaycastExtra != null) {
    5.     Vector2 textureCoord = pointerEventData.pointerCurrentRaycastExtra.textureCoord;
    6.   }
    7. }
    8.  
    I hope that helps!

    -Dan
     
  8. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    109
    Some of this is a little over my head. I think I have the same/similar question...

    I've been following these great Tutorials by GameGrind on YouTube (first one is here
    ).

    In this tutorial, he builds a script to allow an interaction with objects in your world based on a mouse click. I'm trying to reinterpret this simple game for GVR. I'm stuck on how to effectively swap out the Mouse for the GvrController. In GameGrind's example, he stores a Ray in interactionRay using Camera.main.ScreenPointToRay(Input.mousePosition). What is the equivalent to using the Gvr's ray data?

    Here's the code I've got, which works fine if I have the mouse over the game window, and us the game controller to click.


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.AI;
    5.  
    6. public class WorldInteraction : MonoBehaviour {
    7.  
    8.     NavMeshAgent playerAgent;
    9.  
    10.     void Start ()
    11.     {
    12.         playerAgent = GetComponent<NavMeshAgent> ();
    13.     }
    14.  
    15.     void Update ()
    16.     {
    17.         if (GvrController.ClickButtonDown)
    18.         {
    19. //            Debug.Log ("Clicked on controller.");
    20.             GetInteraction ();
    21.         }
    22.     }
    23.  
    24.  
    25.     void GetInteraction()
    26.     {
    27. //        Debug.Log ("GetInteraction has been intiated.");
    28.         Ray interactionRay = Camera.main.ScreenPointToRay (Input.mousePosition);  //  What do I replace here?
    29.         RaycastHit interactionInfo;
    30.  
    31.         if (Physics.Raycast(interactionRay, out interactionInfo, Mathf.Infinity))
    32.         {
    33.             GameObject interactedObject = interactionInfo.collider.gameObject;
    34.  
    35.             if(interactedObject.tag == "Interactable Object")
    36.             {
    37.                 Debug.Log ("Interactable object clicked, at point: " + interactionInfo.point);  
    38.             }
    39.             else
    40.             {
    41.                 Debug.Log ("Non-interactable object clicked, at point: " + interactionInfo.point);  
    42.             }
    43.         }
    44.     }
    45. }
     
  9. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    109
    Okay, I made some great progress today. I'm so much closer, but I'm not there yet.
    The game object with a NavMeshAgent is now moving around based on my point and click - it seems the aim is off.

    Using GvrPointerInputModule.Pointer.PointerTransform.transform.position I was able to create a Ray with the correct point of origin, and using GvrControllerInput.Orientation * Vector3.forward I was able to get my ray to rotate and move around - but the orientation of the controller and the laser it points is not the same angles. There's got to be some equivalent to the GvrPointerInputModule... except for the laser and its orientation. Right? Or does it have something to do with GvrArmModel??

    Here's what I've got as of today:

    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using UnityEngine.AI;
    4.  
    5. public class WorldInteraction : MonoBehaviour {
    6.  
    7.     NavMeshAgent playerAgent;
    8.  
    9.     void Start ()
    10.     {
    11.         playerAgent = GetComponent<NavMeshAgent> ();
    12.     }
    13.  
    14.     void Update ()
    15.     {
    16.         Vector3 rayOrigin = GvrPointerInputModule.Pointer.PointerTransform.transform.position;
    17.         Vector3 controlOri = GvrControllerInput.Orientation * Vector3.forward; // WHAT SHOULD I HAVE HERE??
    18.  
    19.         if (GvrController.ClickButtonDown)
    20.         {
    21. //            Debug.Log ("Clicked.");
    22.             GetInteraction (rayOrigin, controlOri, Mathf.Infinity);
    23.         }
    24.     }
    25.  
    26.  
    27.     void GetInteraction(Vector3 targetPosition, Vector3 direction, float length)
    28.     {
    29. //        Debug.Log ("GetInteraction has been intiated.");
    30.         Ray interactionRay = new Ray(targetPosition, direction);
    31.         RaycastHit interactionInfo;
    32.  
    33.         if (Physics.Raycast(interactionRay, out interactionInfo, length))
    34.         {
    35.             GameObject interactedObject = interactionInfo.collider.gameObject;
    36.  
    37.             if(interactedObject.tag == "Interactable Object")
    38.             {
    39.                 Debug.Log ("Interactable object clicked, at point: " + interactionInfo.point);  
    40.             }
    41.             else
    42.             {
    43.                 Debug.Log ("Non-interactable object clicked, at point: " + interactionInfo.point);  
    44.                 playerAgent.destination = interactionInfo.point;
    45.             }
    46.         }
    47.     }
    48. }
    49.  
     
  10. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    109
    Eureka! I figured it out!
    There is no need for me to create a new raycast -because GvrPointerInputModule.CurrentRaycastResult.gameObject gives me all the info I need. (or .worldPosition or .isValid). Here's my new code.

    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using UnityEngine.AI;
    4.  
    5. public class WorldInteraction : MonoBehaviour {
    6.  
    7.     NavMeshAgent playerAgent;
    8.  
    9.     void Start ()
    10.     {
    11.         playerAgent = GetComponent<NavMeshAgent> ();
    12.     }
    13.  
    14.     void Update ()
    15.     {
    16.         Vector3 endPosition = GvrPointerInputModule.CurrentRaycastResult.worldPosition;
    17.  
    18.         if (GvrController.ClickButtonDown && GvrPointerInputModule.CurrentRaycastResult.isValid)
    19.         {
    20. //            Debug.Log ("Clicked.");
    21.             GetInteraction (endPosition);
    22.         }
    23.     }
    24.  
    25.  
    26.     void GetInteraction(Vector3 endPosition)
    27.     {
    28. //        Debug.Log ("GetInteraction has been intiated.");
    29.  
    30.         if (GvrPointerInputModule.CurrentRaycastResult.gameObject.tag == "Interactable Object")
    31.             {
    32.             Debug.Log ("Interactable object clicked, at point: " + GvrPointerInputModule.CurrentRaycastResult.worldPosition);  
    33.             }
    34.             else
    35.             {
    36.             Debug.Log ("Non-interactable object clicked, at point: " + GvrPointerInputModule.CurrentRaycastResult.worldPosition);  
    37.             playerAgent.destination = GvrPointerInputModule.CurrentRaycastResult.worldPosition;
    38.             }
    39.     }
    40. }
    41.  
    It's the little wins, isn't it?
     
    solomonjohnson, Eubash and dsternfeld like this.
  11. dsternfeld

    dsternfeld

    Official Google Employee

    Joined:
    Jan 3, 2017
    Posts:
    72
    I'm glad you got it working!

    Here is an alternative way that you can make an object react when you click on it using Unity's event system:
    1. Add the EventTrigger script to an object that you want to have respond to click events.
    2. Add an event for "Pointer Click" and set it to call a function that handles the event in the inspector.
    OR create a script that implements the IPointerClickHandler interface and place it on an object that you want to have respond to click events.

    Code (CSharp):
    1. public class ClickResponder : MonoBehaviour, IPointerClickHandler {
    2.   public void OnPointerClick(PointerEventData eventData) {
    3.     Debug.Log("I have been clicked!");
    4.   }
    5. }
    Here is a tutorial that goes through how to use EventTrigger.

    Thanks,

    Dan
     
  12. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    109
    Woah!
    That's a cool alternative, Dan. Thank you for sharing!
     
    dsternfeld likes this.
  13. Eubash

    Eubash

    Joined:
    Dec 25, 2018
    Posts:
    1
    You are make my day! Thanks a lot to save me a lot of time.
     
    HeyBishop likes this.
  14. solomonjohnson

    solomonjohnson

    Joined:
    Jul 5, 2017
    Posts:
    2
     
    HeyBishop likes this.
  15. solomonjohnson

    solomonjohnson

    Joined:
    Jul 5, 2017
    Posts:
    2
    Thank you very much