// FixedUpdate is called once per phsyics frame private void FixedUpdate() { // If a raycast is requested (button one on Oculus, constant for mouse) if (RaycastRequested()) { // Perform the raycast and store the result bool raycastHit = RaycastingMethod(out hit, maxRaycastDistance, layerMask); // If the raycast hit, if (raycastHit) { // See if the hit object has changed if (hit.collider.gameObject != curObj) { // Clean up hover, press events on previous object OnHoverEnd(); OnPressEnd(); // Update object curObj = hit.collider.gameObject; // Find the event manager to trigger on the new object curEvent = FindRaycastTrigger(hit); } // Get user press state Pressing = PressCondition(); // If the user is not pressing, we are hovering over the object Hovering = !Pressing; } else { // If we didn't hit a valid target, clean up hover, press events Hovering = false; Pressing = false; curEvent = null; curObj = null; } // TODO: If you hover over a different raycastable object immediately, this will not end the hover on the old object // This needs to track if the raycastHit object changes } else { Pressing = false; Hovering = false; } }
/// <summary> If raycastButton is pressed, attempt to raycast to valid target and activate valid raycastTrigger events on the target </summary> private void TryRaycastHit() { // If the user enables "raycast mode" by VR controller or by mouse bool raycastActive = OVRInput.Get(raycastButton, raycastController) || (mouseMode && Input.GetMouseButton(0)); if (raycastActive) { // Resolve raycast hit info for VR or mouse controller RaycastHit hit; bool rayHit = false; if (mouseMode) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); rayHit = Physics.Raycast(ray, out hit, 10f, layerMask); } else { RequestHandRenderState(true); // Enable raycasting hand Vector3 globalForward = transform.TransformDirection(relativeForward); // Find the direction of the raycast rayHit = Physics.Raycast(transform.position, globalForward, out hit, 10f, layerMask); } if (rayHit) { // If our raycast hits a raycastable target, // Are we raycasting to a new object, and does that object have any valid raycasting events? valid = ResolveCurrentTarget(hit); if (valid) { // If we have a valid target to hit, hitTriggered = false; holdTriggered = false; endTriggered = false; // Don't allow calling more than one hit, hold, or end event in a single frame DipointLinerendSetPositions(transform.position, hit.point); // Visualize our raycast as long as we have a valid target ResolveClickEvents(hit); // Call events according to trigger button system ResolveProximityEvents(hit); // Call events according to proximity distance activation system // If we actually activated any triggers this frame, change hand asthetics to reflect it if (holdTriggered) { RequestNewState(StateCode.HOLD); } else if (hitTriggered) { RequestNewState(StateCode.HIT); } else if (endTriggered) { RequestNewState(StateCode.END); } else { RequestNewState(StateCode.NULL); } // Othwerwise we are inactive } else { DontDrawLinerend(); } // If we don't have a valid target, don't draw the linerender } else { DontDrawLinerend(); } // If our raycast did NOT hit a raycastable target, don't draw the linerender } else { // If we aren't in "raycast mode" RequestHandRenderState(false); // Return to default hand // Turn off controller vibration & don't draw the raycast RequestNewState(StateCode.NULL); DontDrawLinerend(); if (touched) { touched = false; } // Allow proximity touches again if (clicked) { clicked = false; } // Allow trigger button activation again } #region Local_Functions // Are we raycasting to a new object, and does that object have any valid raycasting events? bool ResolveCurrentTarget(RaycastHit hit) { int newID = hit.collider.gameObject.GetInstanceID(); if (newID != activeObjectID) { // If we have found a new object, RaycastEventManager newTriggerManager = hit.collider.GetComponentInParent <RaycastEventManager>(); if (newTriggerManager != null) { // If it has a valid trigger, // Find whether or not there are valid hit, hold, and end events based on handedness if (rightHand) { validHitEvent = (newTriggerManager.rightTrigger.OnPress.GetPersistentEventCount() > 0) ? true : false; validHoldEvent = (newTriggerManager.rightTrigger.OnHoldPress.GetPersistentEventCount() > 0) ? true : false; validEndEvent = (newTriggerManager.rightTrigger.OnEndPress.GetPersistentEventCount() > 0) ? true : false; } else { validHitEvent = (newTriggerManager.leftTrigger.OnPress.GetPersistentEventCount() > 0) ? true : false; validHoldEvent = (newTriggerManager.leftTrigger.OnHoldPress.GetPersistentEventCount() > 0) ? true : false; validEndEvent = (newTriggerManager.leftTrigger.OnEndPress.GetPersistentEventCount() > 0) ? true : false; } if (validHitEvent || validHoldEvent || validEndEvent) { // If we have found some active event to activate, save it and mark this target as valid activeTriggerManager = newTriggerManager; activeObjectID = newID; return(true); } } // If the object doesn't have a RaycastTriggerManager, then we don't have a valid object return(false); } // Otherwise we have a previously valid object else { return(false); } } // Enable/disable static raycasting hand depending on raycast state void RequestHandRenderState(bool rURaycasting) { if (!mouseMode) { if (rURaycasting) { // If we're trying to use raycasting hands, if (defaultHandObject.activeSelf || !staticIndexRenderer.enabled) { // If our hand object isn't in raycasting mode yet, defaultHandObject.SetActive(false); // Disable the default Oculus hand staticIndexRenderer.enabled = true; // Enable the static hand mesh } } else { if (!defaultHandObject.activeSelf || staticIndexRenderer.enabled) { // If our hand object is still in raycasting mode, defaultHandObject.SetActive(true); // Enable default Oculus hand staticIndexRenderer.enabled = false; // Disable static hand mesh } } } } // Functionality for invoking trigger events with triggerButton void ResolveClickEvents(RaycastHit hit) { bool buttonPressed = false; if (mouseMode) { buttonPressed = true; } else if (OVRInput.Get(triggerButton, raycastController)) { buttonPressed = true; } if (buttonPressed) { // If we are holding down the relevant button if (!clicked) { // FIRST CLICK CASE: We have just pressed the index trigger all the way down on our controller for the first time recently. AttemptHitEvent(hit); clicked = true; } // We should attempt to call a hit event every frame that the button is held AttemptHoldEvent(hit); } else { // If we are not holding down the trigger if (clicked) { // If we were just clicking, and now we aren't, reset clicked and call the onEndEvent AttemptEndEvent(hit); clicked = false; } } } // Functionality for invoking trigger events for close-proximity raycast hits void ResolveProximityEvents(RaycastHit hit) { if (hit.distance < minHitDist) { // If we are within close enough to activate our trigger, if (!touched) { // & if we didn't just activate that trigger by being within distance already, touched = true; // Don't reactivate any triggers until we move far enough away. AttemptHitEvent(hit); } else { AttemptHoldEvent(hit); } // If we're still close, but have already trigegred the hit event, then we're holding } else if (hit.distance < hysteresisDist) { // If we are still within hysteresis range, don't allow further touches but continue calling hold events if (touched) { AttemptHoldEvent(hit); } // If we've already hit the object, then we can hold it } else { // If we are far enough away, then we are done with our proximity touch/hold if (touched) { touched = false; // Allow proximity touches again AttemptEndEvent(hit); } } } // Invoke valid hit, hold, or end events void AttemptHitEvent(RaycastHit hit) { if (validHitEvent) { // If the active object has a valid hit or hold event, activate the trigger and produce active aesthetics if (!hitTriggered) { // If we haven't already activated it, do that if (rightHand) { activeTriggerManager.rightTrigger.Press(hit); } else { activeTriggerManager.leftTrigger.Press(hit); } hitTriggered = true; } } } void AttemptHoldEvent(RaycastHit hit) { if (validHoldEvent) { // If we have an active hold event, if (!holdTriggered) { // If we haven't already activated it this frame, if (rightHand) { activeTriggerManager.rightTrigger.HoldPress(hit); } else { activeTriggerManager.leftTrigger.HoldPress(hit); } holdTriggered = true; } } } void AttemptEndEvent(RaycastHit hit) { if (validEndEvent) { if (!endTriggered) { if (rightHand) { activeTriggerManager.rightTrigger.EndPress(hit); } else { activeTriggerManager.leftTrigger.EndPress(hit); } endTriggered = true; } } } // Carry out changes for the current trigger state void RequestNewState(StateCode newState) { if (currentState != newState) { // If we have new changes to reflect, currentState = newState; // Store the new state, if (!mouseMode) { switch (currentState) { // Set the appropriate color and controller vibration case StateCode.NULL: LineRendSetEndpointColors(nullCol); OVRInput.SetControllerVibration(hapFreq, nullAmp, raycastController); break; case StateCode.HOLD: LineRendSetEndpointColors(holdCol); OVRInput.SetControllerVibration(hapFreq, holdAmp, raycastController); break; case StateCode.HIT: LineRendSetEndpointColors(hitCol); OVRInput.SetControllerVibration(hapFreq, hitAmp, raycastController); break; case StateCode.END: LineRendSetEndpointColors(endCol); OVRInput.SetControllerVibration(hapFreq, endAmp, raycastController); break; } } } } // Sets both ends of a 2-point line renderer to be the same color void LineRendSetEndpointColors(Color newColor) { if (lineRend != null) { lineRend.startColor = newColor; lineRend.endColor = newColor; } } // Sets the position of both ends of a 2-point line renderer void DipointLinerendSetPositions(Vector3 position0, Vector3 position1) { if (lineRend != null) { lineRend.SetPositions(new Vector3[2] { position0, position1 }); if (invisible) { invisible = false; } // Tell DontDrawLineRend() that there is a visible line render } } /// Don't draw the line renderer void DontDrawLinerend() { if (lineRend != null) { if (!invisible) { // If there is a visible line render, lineRend.SetPositions(new Vector3[2] { Vector3.zero, Vector3.zero }); // Draw both points to the same position invisible = true; // Don't bother again until we have a visible line render } } } #endregion }