diff --git a/Assets/VRTK/Scripts/Abstractions/VRTK_WorldPointer.cs b/Assets/VRTK/Scripts/Abstractions/VRTK_WorldPointer.cs index 46cf04164..f8fe4f11f 100644 --- a/Assets/VRTK/Scripts/Abstractions/VRTK_WorldPointer.cs +++ b/Assets/VRTK/Scripts/Abstractions/VRTK_WorldPointer.cs @@ -35,12 +35,14 @@ public enum pointerVisibilityStates public Color pointerHitColor = new Color(0f, 0.5f, 0f, 1f); [Tooltip("The colour of the beam when it is not hitting a valid target. It can be set to a different colour for each controller.")] public Color pointerMissColor = new Color(0.8f, 0f, 0f, 1f); - [Tooltip("Determines when the pointer beam should be displayed.")] - public pointerVisibilityStates pointerVisibility = pointerVisibilityStates.On_When_Active; + [Tooltip("If this is checked then the pointer will be an extension of the controller and able to interact with Interactable Objects.")] + public bool interactWithObjects = false; [Tooltip("If this is checked then the pointer beam will be activated on first press of the pointer alias button and will stay active until the pointer alias button is pressed again. The destination set event is emitted when the beam is deactivated on the second button press.")] public bool holdButtonToActivate = true; [Tooltip("The time in seconds to delay the pointer beam being able to be active again. Useful for preventing constant teleportation.")] public float activateDelay = 0f; + [Tooltip("Determines when the pointer beam should be displayed.")] + public pointerVisibilityStates pointerVisibility = pointerVisibilityStates.On_When_Active; [Tooltip("The layers to ignore when raycasting.")] public LayerMask layersToIgnore = Physics.IgnoreRaycastLayer; @@ -51,6 +53,7 @@ public enum pointerVisibilityStates protected uint controllerIndex; protected VRTK_PlayAreaCursor playAreaCursor; protected Color currentPointerColor; + protected GameObject objectInteractor; private bool isActive; private bool destinationSetActive; @@ -124,12 +127,18 @@ protected override void OnEnable() pointerMaterial.color = pointerMissColor; playAreaCursor = (GetComponent() ?? GetComponent()); + + if (interactWithObjects) + { + CreateObjectInteractor(); + } } protected override void OnDisable() { base.OnDisable(); DisableBeam(); + Destroy(objectInteractor); destinationSetActive = false; pointerContactDistance = 0f; pointerContactTarget = null; @@ -142,6 +151,15 @@ protected override void OnDisable() protected virtual void Update() { + if (interactWithObjects && objectInteractor.activeInHierarchy) + { + UpdateObjectInteractor(); + } + } + + protected virtual void UpdateObjectInteractor() + { + objectInteractor.transform.position = destinationPosition; } protected virtual void InitPointer() @@ -228,6 +246,11 @@ protected virtual void PointerSet() protected virtual void TogglePointer(bool state) { + if (interactWithObjects) + { + objectInteractor.SetActive(state); + } + if (playAreaCursor) { playAreaCursor.SetHeadsetPositionCompensation(headsetPositionCompensation); @@ -294,6 +317,27 @@ protected virtual bool ValidDestination(Transform target, Vector3 destinationPos return (validNavMeshLocation && target && !(Utilities.TagOrScriptCheck(target.gameObject, invalidTagOrScriptListPolicy, invalidTargetWithTagOrClass))); } + protected virtual void CreateObjectInteractor() + { + objectInteractor = new GameObject(string.Format("[{0}]WorldPointer_ObjectIneractor_Holder", gameObject.name)); + objectInteractor.transform.SetParent(controller.transform); + objectInteractor.transform.localPosition = Vector3.zero; + objectInteractor.layer = LayerMask.NameToLayer("Ignore Raycast"); + Utilities.SetPlayerObject(objectInteractor, VRTK_PlayerObject.ObjectTypes.Pointer); + + var objectInteractorCollider = new GameObject(string.Format("[{0}]WorldPointer_ObjectIneractor_Collider", gameObject.name)); + objectInteractorCollider.transform.SetParent(objectInteractor.transform); + objectInteractorCollider.transform.localPosition = Vector3.zero; + objectInteractorCollider.layer = LayerMask.NameToLayer("Ignore Raycast"); + var tmpCollider = objectInteractorCollider.AddComponent(); + tmpCollider.isTrigger = true; + Utilities.SetPlayerObject(objectInteractorCollider, VRTK_PlayerObject.ObjectTypes.Pointer); + + var objectInteractorScale = 0.025f; + objectInteractor.transform.localScale = new Vector3(objectInteractorScale, objectInteractorScale, objectInteractorScale); + objectInteractor.SetActive(false); + } + private bool InvalidConstantBeam() { var equalToggleSet = controller.pointerToggleButton == controller.pointerSetButton; diff --git a/Assets/VRTK/Scripts/VRTK_InteractTouch.cs b/Assets/VRTK/Scripts/VRTK_InteractTouch.cs index 079b21da7..f5ca29a6e 100644 --- a/Assets/VRTK/Scripts/VRTK_InteractTouch.cs +++ b/Assets/VRTK/Scripts/VRTK_InteractTouch.cs @@ -59,6 +59,8 @@ public class VRTK_InteractTouch : MonoBehaviour private GameObject controllerCollisionDetector; private bool triggerRumble; private bool destroyColliderOnDisable; + private bool triggerIsColliding = false; + private bool triggerWasColliding = false; private Rigidbody touchRigidBody; private Object defaultColliderPrefab; private VRTK_ControllerEvents.ButtonAlias originalGrabAlias; @@ -263,6 +265,7 @@ private void CheckRumbleController(VRTK_InteractableObject touchedObjectScript) private void OnTriggerEnter(Collider collider) { + triggerIsColliding = true; AddActiveCollider(collider); var colliderInteractableObject = GetColliderInteractableObject(collider); @@ -275,8 +278,17 @@ private void OnTriggerEnter(Collider collider) } } + private void OnTriggerExit(Collider collider) + { + if (touchedObjectActiveColliders.Contains(collider)) + { + touchedObjectActiveColliders.Remove(collider); + } + } + private void OnTriggerStay(Collider collider) { + triggerIsColliding = true; AddActiveCollider(collider); var colliderInteractableObject = GetColliderInteractableObject(collider); @@ -291,7 +303,6 @@ private void OnTriggerStay(Collider collider) CleanupEndTouch(); return; } - StoreTouchedObjectColliders(collider); CheckButtonOverrides(touchedObjectScript); @@ -305,20 +316,14 @@ private void OnTriggerStay(Collider collider) } } - private void OnTriggerExit(Collider collider) - { - if (touchedObjectActiveColliders.Contains(collider)) - { - touchedObjectActiveColliders.Remove(collider); - } - } - private void LateUpdate() { - if (touchedObject != null && touchedObjectActiveColliders.Count == 0) + if (touchedObject != null && (touchedObjectActiveColliders.Count == 0 || (!triggerIsColliding && !triggerWasColliding))) { StopTouching(touchedObject); } + triggerWasColliding = triggerIsColliding; + triggerIsColliding = false; } private void CheckButtonOverrides(VRTK_InteractableObject touchedObjectScript) @@ -364,7 +369,6 @@ private void StopTouching(GameObject untouched) var untouchedObjectScript = untouched.GetComponent(); untouchedObjectScript.StopTouching(gameObject); - if (!untouchedObjectScript.IsTouched()) { untouchedObjectScript.ToggleHighlight(false); diff --git a/Assets/VRTK/Scripts/VRTK_SimplePointer.cs b/Assets/VRTK/Scripts/VRTK_SimplePointer.cs index 0d923dc58..cb4423288 100644 --- a/Assets/VRTK/Scripts/VRTK_SimplePointer.cs +++ b/Assets/VRTK/Scripts/VRTK_SimplePointer.cs @@ -105,6 +105,11 @@ protected override void InitPointer() base.InitPointer(); + if (showPointerTip && objectInteractor) + { + objectInteractor.transform.localScale = pointerTip.transform.localScale * 1.05f; + } + SetPointerTransform(pointerLength, pointerThickness); TogglePointer(false); } diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index b6803ea78..b3156fdf0 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -332,9 +332,10 @@ The play area collider does not work well with terrains as they are uneven and c * **Controller:** The controller that will be used to toggle the pointer. If the script is being applied onto a controller then this parameter can be left blank as it will be auto populated by the controller the script is on at runtime. * **Pointer Material:** The material to use on the rendered version of the pointer. If no material is selected then the default `WorldPointer` material will be used. - * **Pointer Visibility:** Determines when the pointer beam should be displayed. + * **Interact With Objects:** If this is checked then the pointer will be an extension of the controller and able to interact with Interactable Objects. * **Hold Button To Activate:** If this is checked then the pointer beam will be activated on first press of the pointer alias button and will stay active until the pointer alias button is pressed again. The destination set event is emitted when the beam is deactivated on the second button press. * **Activate Delay:** The time in seconds to delay the pointer beam being able to be active again. Useful for preventing constant teleportation. + * **Pointer Visibility:** Determines when the pointer beam should be displayed. * **Layers To Ignore:** The layers to ignore when raycasting. ### Class Variables