// Headset Controller Aware|Presence|70040
namespace VRTK
{
using UnityEngine;
///
/// Event Payload
///
/// The Raycast Hit struct of item that is obscuring the path to the controller.
/// The reference to the controller that is being or has been obscured or being or has been glanced.
public struct HeadsetControllerAwareEventArgs
{
public RaycastHit raycastHit;
public VRTK_ControllerReference controllerReference;
}
///
/// Event Payload
///
/// this object
///
public delegate void HeadsetControllerAwareEventHandler(object sender, HeadsetControllerAwareEventArgs e);
///
/// Determines whether the HMD is in line of sight to the controllers or if the headset is directly looking at one of the controllers.
///
///
/// **Script Usage:**
/// * Place the `VRTK_HeadsetControllerAware` script on any active scene GameObject.
///
///
/// `VRTK/Examples/029_Controller_Tooltips` displays tooltips that have been added to the controllers and are only visible when the controller is being looked at.
///
[AddComponentMenu("VRTK/Scripts/Presence/VRTK_HeadsetControllerAware")]
public class VRTK_HeadsetControllerAware : MonoBehaviour
{
[Tooltip("If this is checked then the left controller will be checked if items obscure it's path from the headset.")]
public bool trackLeftController = true;
[Tooltip("If this is checked then the right controller will be checked if items obscure it's path from the headset.")]
public bool trackRightController = true;
[Tooltip("The radius of the accepted distance from the controller origin point to determine if the controller is being looked at.")]
public float controllerGlanceRadius = 0.15f;
[Tooltip("A custom transform to provide the world space position of the right controller.")]
public Transform customRightControllerOrigin;
[Tooltip("A custom transform to provide the world space position of the left controller.")]
public Transform customLeftControllerOrigin;
[Tooltip("A custom raycaster to use when raycasting to find controllers.")]
public VRTK_CustomRaycast customRaycast;
///
/// Emitted when the controller is obscured by another object.
///
public event HeadsetControllerAwareEventHandler ControllerObscured;
///
/// Emitted when the controller is no longer obscured by an object.
///
public event HeadsetControllerAwareEventHandler ControllerUnobscured;
///
/// Emitted when the controller is seen by the headset view.
///
public event HeadsetControllerAwareEventHandler ControllerGlanceEnter;
///
/// Emitted when the controller is no longer seen by the headset view.
///
public event HeadsetControllerAwareEventHandler ControllerGlanceExit;
protected GameObject leftController;
protected GameObject rightController;
protected Transform headset;
protected bool leftControllerObscured = false;
protected bool rightControllerObscured = false;
protected bool leftControllerLastState = false;
protected bool rightControllerLastState = false;
protected bool leftControllerGlance = false;
protected bool rightControllerGlance = false;
protected bool leftControllerGlanceLastState = false;
protected bool rightControllerGlanceLastState = false;
public virtual void OnControllerObscured(HeadsetControllerAwareEventArgs e)
{
if (ControllerObscured != null)
{
ControllerObscured(this, e);
}
}
public virtual void OnControllerUnobscured(HeadsetControllerAwareEventArgs e)
{
if (ControllerUnobscured != null)
{
ControllerUnobscured(this, e);
}
}
public virtual void OnControllerGlanceEnter(HeadsetControllerAwareEventArgs e)
{
if (ControllerGlanceEnter != null)
{
ControllerGlanceEnter(this, e);
}
}
public virtual void OnControllerGlanceExit(HeadsetControllerAwareEventArgs e)
{
if (ControllerGlanceExit != null)
{
ControllerGlanceExit(this, e);
}
}
///
/// The LeftControllerObscured method returns the state of if the left controller is being obscured from the path of the headset.
///
/// Returns `true` if the path between the headset and the controller is obscured.
public virtual bool LeftControllerObscured()
{
return leftControllerObscured;
}
///
/// The RightControllerObscured method returns the state of if the right controller is being obscured from the path of the headset.
///
/// Returns `true` if the path between the headset and the controller is obscured.
public virtual bool RightControllerObscured()
{
return rightControllerObscured;
}
///
/// the LeftControllerGlanced method returns the state of if the headset is currently looking at the left controller or not.
///
/// Returns `true` if the headset can currently see the controller within the given radius threshold.
public virtual bool LeftControllerGlanced()
{
return leftControllerGlance;
}
///
/// the RightControllerGlanced method returns the state of if the headset is currently looking at the right controller or not.
///
/// Returns `true` if the headset can currently see the controller within the given radius threshold.
public virtual bool RightControllerGlanced()
{
return rightControllerGlance;
}
protected virtual void Awake()
{
VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this);
}
protected virtual void OnEnable()
{
headset = VRTK_DeviceFinder.HeadsetTransform();
leftController = VRTK_DeviceFinder.GetControllerLeftHand();
rightController = VRTK_DeviceFinder.GetControllerRightHand();
}
protected virtual void OnDisable()
{
leftController = null;
rightController = null;
}
protected virtual void OnDestroy()
{
VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this);
}
protected virtual void Update()
{
if (trackLeftController)
{
RayCastToController(leftController, customLeftControllerOrigin, ref leftControllerObscured, ref leftControllerLastState);
}
if (trackRightController)
{
RayCastToController(rightController, customRightControllerOrigin, ref rightControllerObscured, ref rightControllerLastState);
}
CheckHeadsetView(leftController, customLeftControllerOrigin, ref leftControllerGlance, ref leftControllerGlanceLastState);
CheckHeadsetView(rightController, customRightControllerOrigin, ref rightControllerGlance, ref rightControllerGlanceLastState);
}
protected virtual HeadsetControllerAwareEventArgs SetHeadsetControllerAwareEvent(RaycastHit raycastHit, VRTK_ControllerReference controllerReference)
{
HeadsetControllerAwareEventArgs e;
e.raycastHit = raycastHit;
e.controllerReference = controllerReference;
return e;
}
protected virtual void RayCastToController(GameObject controller, Transform customDestination, ref bool obscured, ref bool lastState)
{
obscured = false;
if (controller != null && controller.gameObject.activeInHierarchy)
{
Vector3 destination = (customDestination ? customDestination.position : controller.transform.position);
RaycastHit hitInfo;
if (VRTK_CustomRaycast.Linecast(customRaycast, headset.position, destination, out hitInfo, Physics.IgnoreRaycastLayer, QueryTriggerInteraction.Ignore))
{
obscured = true;
}
if (lastState != obscured)
{
ObscuredStateChanged(controller.gameObject, obscured, hitInfo);
}
lastState = obscured;
}
}
protected virtual void ObscuredStateChanged(GameObject controller, bool obscured, RaycastHit hitInfo)
{
VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(controller);
if (obscured)
{
OnControllerObscured(SetHeadsetControllerAwareEvent(hitInfo, controllerReference));
}
else
{
OnControllerUnobscured(SetHeadsetControllerAwareEvent(hitInfo, controllerReference));
}
}
protected virtual void CheckHeadsetView(GameObject controller, Transform customDestination, ref bool controllerGlance, ref bool controllerGlanceLastState)
{
controllerGlance = false;
if (controller != null && controller.gameObject.activeInHierarchy)
{
Vector3 controllerPosition = (customDestination ? customDestination.position : controller.transform.position);
float distanceFromHeadsetToController = Vector3.Distance(headset.position, controllerPosition);
Vector3 lookPoint = headset.position + (headset.forward * distanceFromHeadsetToController);
if (Vector3.Distance(controllerPosition, lookPoint) <= controllerGlanceRadius)
{
controllerGlance = true;
}
if (controllerGlanceLastState != controllerGlance)
{
GlanceStateChanged(controller.gameObject, controllerGlance);
}
controllerGlanceLastState = controllerGlance;
}
}
protected virtual void GlanceStateChanged(GameObject controller, bool glance)
{
RaycastHit emptyHit = new RaycastHit();
VRTK_ControllerReference controllerReference = VRTK_ControllerReference.GetControllerReference(controller);
if (glance)
{
OnControllerGlanceEnter(SetHeadsetControllerAwareEvent(emptyHit, controllerReference));
}
else
{
OnControllerGlanceExit(SetHeadsetControllerAwareEvent(emptyHit, controllerReference));
}
}
}
}