- Home /
How to implement SteamVR Laser Pointer
I'm relatively new to VR dev in Unity and I'm building a project based on the SteamVR plugin. I'm specifically asking for help using the SteamVR_LaserPointer.cs (pasted below).
My setup:
SteamVR Player pre-fab in scene
SteamVR_LaserPointer.cs script attached to each controller (which are children of the Player called LeftHand and RightHand)
I'm using an HTC Vive Pro
What I think I need to do is create a separate handler script that instantiates a SteamVR_LaserPointer and somehow uses the public virtual void methods (OnPointerIn, OnPointerClick, and OnPointer) to interact with what the laser pointer is colliding with... but I'm riding the struggle bus trying to figure that out. Has anyone successfully implemented this script before or have some pointers / references that you can point me to?
//======= Copyright (c) Valve Corporation, All rights reserved. ==== using UnityEngine; using System.Collections;
namespace Valve.VR.Extras { public class SteamVR_LaserPointer : MonoBehaviour { public SteamVR_Behaviour_Pose pose;
//public SteamVR_Action_Boolean interactWithUI = SteamVR_Input.__actions_default_in_InteractUI; public SteamVR_Action_Boolean interactWithUI = SteamVR_Input.GetBooleanAction("InteractUI"); public bool active = true; public Color color; public float thickness = 0.002f; public Color clickColor = Color.green; public GameObject holder; public GameObject pointer; bool isActive = false; public bool addRigidBody = false; public Transform reference; public event PointerEventHandler PointerIn; public event PointerEventHandler PointerOut; public event PointerEventHandler PointerClick; Transform previousContact = null; private void Start() { if (pose == null) pose = this.GetComponent<SteamVR_Behaviour_Pose>(); if (pose == null) Debug.LogError("No SteamVR_Behaviour_Pose component found on this object"); if (interactWithUI == null) Debug.LogError("No ui interaction action has been set on this component."); holder = new GameObject(); holder.transform.parent = this.transform; holder.transform.localPosition = Vector3.zero; holder.transform.localRotation = Quaternion.identity; pointer = GameObject.CreatePrimitive(PrimitiveType.Cube); pointer.transform.parent = holder.transform; pointer.transform.localScale = new Vector3(thickness, thickness, 100f); pointer.transform.localPosition = new Vector3(0f, 0f, 50f); pointer.transform.localRotation = Quaternion.identity; BoxCollider collider = pointer.GetComponent<BoxCollider>(); if (addRigidBody) { if (collider) { collider.isTrigger = true; } Rigidbody rigidBody = pointer.AddComponent<Rigidbody>(); rigidBody.isKinematic = true; } else { if (collider) { Object.Destroy(collider); } } Material newMaterial = new Material(Shader.Find("Unlit/Color")); newMaterial.SetColor("_Color", color); pointer.GetComponent<MeshRenderer>().material = newMaterial; } public virtual void OnPointerIn(PointerEventArgs e) { if (PointerIn != null) PointerIn(this, e); } public virtual void OnPointerClick(PointerEventArgs e) { if (PointerClick != null) PointerClick(this, e); } public virtual void OnPointerOut(PointerEventArgs e) { if (PointerOut != null) PointerOut(this, e); } private void Update() { if (!isActive) { isActive = true; this.transform.GetChild(0).gameObject.SetActive(true); } float dist = 100f; Ray raycast = new Ray(transform.position, transform.forward); RaycastHit hit; bool bHit = Physics.Raycast(raycast, out hit); if (previousContact && previousContact != hit.transform) { PointerEventArgs args = new PointerEventArgs(); args.fromInputSource = pose.inputSource; args.distance = 0f; args.flags = 0; args.target = previousContact; OnPointerOut(args); previousContact = null; } if (bHit && previousContact != hit.transform) { PointerEventArgs argsIn = new PointerEventArgs(); argsIn.fromInputSource = pose.inputSource; argsIn.distance = hit.distance; argsIn.flags = 0; argsIn.target = hit.transform; OnPointerIn(argsIn); previousContact = hit.transform; } if (!bHit) { previousContact = null; } if (bHit && hit.distance < 100f) { dist = hit.distance; } if (bHit && interactWithUI.GetStateUp(pose.inputSource)) { PointerEventArgs argsClick = new PointerEventArgs(); argsClick.fromInputSource = pose.inputSource; argsClick.distance = hit.distance; argsClick.flags = 0; argsClick.target = hit.transform; OnPointerClick(argsClick); } if (interactWithUI != null && interactWithUI.GetState(pose.inputSource)) { pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist); pointer.GetComponent<MeshRenderer>().material.color = clickColor; } else { pointer.transform.localScale = new Vector3(thickness, thickness, dist); pointer.GetComponent<MeshRenderer>().material.color = color; } pointer.transform.localPosition = new Vector3(0f, 0f, dist / 2f); } } public struct PointerEventArgs { public SteamVR_Input_Sources fromInputSource; public uint flags; public float distance; public Transform target; } public delegate void PointerEventHandler(object sender, PointerEventArgs e); }
Answer by valentin-simian · Apr 15, 2019 at 01:59 AM
I created a wrapper class around the pre-existing SteamVR_LaserPointer
using UnityEngine;
using UnityEngine.EventSystems;
using Valve.VR.Extras;
public class SteamVRLaserWrapper : MonoBehaviour
{
private SteamVR_LaserPointer steamVrLaserPointer;
private void Awake()
{
steamVrLaserPointer = gameObject.GetComponent<SteamVR_LaserPointer>();
steamVrLaserPointer.PointerIn += OnPointerIn;
steamVrLaserPointer.PointerOut += OnPointerOut;
steamVrLaserPointer.PointerClick += OnPointerClick;
}
private void OnPointerClick(object sender, PointerEventArgs e)
{
IPointerClickHandler clickHandler = e.target.GetComponent<IPointerClickHandler>();
if (clickHandler == null)
{
return;
}
clickHandler.OnPointerClick(new PointerEventData(EventSystem.current));
}
private void OnPointerOut(object sender, PointerEventArgs e)
{
IPointerExitHandler pointerExitHandler = e.target.GetComponent<IPointerExitHandler>();
if (pointerExitHandler == null)
{
return;
}
pointerExitHandler.OnPointerExit(new PointerEventData(EventSystem.current));
}
private void OnPointerIn(object sender, PointerEventArgs e)
{
IPointerEnterHandler pointerEnterHandler = e.target.GetComponent<IPointerEnterHandler>();
if (pointerEnterHandler == null)
{
return;
}
pointerEnterHandler.OnPointerEnter(new PointerEventData(EventSystem.current));
}
}
then, on any object with a UI element or a collider, just use regular Unity Interaction Events.
Hi, sorry to bother you here on this topic but im growing desperate and i was wondering if you could help me. I have 2 issues, the first one is directly related with this topic the second not so much.
1st one is i already had a laser script working but yours feels alot better in terms of "gameplay" but i cant seem to be able to interact with sliders, is there something that needs to be added?
then 2nd one is no matter what i do i cant seem to be able to turn off the laser. I want the laser to activated with the press of a button and then turn it off with the same button but it never turns off. Any tips?,I want to only have the laser active when its in pause mode or at the start of the app. However i cant seem to be able to turn the laser off no matter what i do. Any tips?
I would be super happy if you could help one of them!
Still can't figure how to set it up correctly. Any updates on how you got it working? Edit: Sorry for the bump
Answer by thampu_9 · Apr 02, 2019 at 07:14 PM
Attach this script to an object that you are trying to interact with. This script will tell you when the pointer is inside the object and when it goes outside.
public class LaserPointerHandler : MonoBehaviour { public SteamVR_LaserPointer laserPointer;
public bool selected;
// Start is called before the first frame update
void Start()
{
laserPointer.PointerIn += PointerInside;
laserPointer.PointerOut += PointerOutside;
selected = false;
}
// Update is called once per frame
void Update()
{
}
public void PointerInside(object sender, PointerEventArgs e)
{
if (e.target.name == this.gameObject.name && selected==false)
{
selected = true;
Debug.Log("pointer is inside this object" + e.target.name);
}
}
public void PointerOutside(object sender, PointerEventArgs e)
{
if (e.target.name == this.gameObject.name && selected == true)
{
selected = false;
Debug.Log("pointer is outside this object" + e.target.name);
}
}
public bool get_selected_value()
{
return selected;
}
}
I was trying to use S$$anonymous$$mVR_LaserPointer laserPointer script on Text UI element and it won't trigger PointerInside event. This element is in UI layer and I Tried changing it to Default but it still didn't work. Ground plane is the only thing that triggers this. So what to do?
Your answer
Follow this Question
Related Questions
picking and dropping of objects using the old steamvr laser pointer script 0 Answers
Unable to interact with imported assets 0 Answers
How do I switch levels when the user grabs an object in SteamVR? 0 Answers
Destroy object after time Only if raycast is colliding; 0 Answers
How to access menu button in vive by using steamvr player 1 Answer