public void ForceResetButton() { var oldState = CurrentButtonState; CurrentButtonState = InteractableState.Default; InteractableStateChanged?.Invoke(new InteractableStateArgs(this, null, CurrentButtonState, oldState, new ColliderZoneArgs(ContactCollider, Time.frameCount, null, InteractionType.Exit))); }
public override void UpdateCollisionDepth(InteractableTool interactableTool, InteractableCollisionDepth oldCollisionDepth, InteractableCollisionDepth newCollisionDepth) { bool isFarFieldTool = interactableTool.IsFarFieldTool; // if this is a near field tool and another tool already controls it, bail. if (!isFarFieldTool && _toolToState.Keys.Count > 0 && !_toolToState.ContainsKey(interactableTool)) { return; } var oldState = _currentButtonState; // ignore contact test if you are using the far field tool var currButtonDirection = transform.TransformDirection(_localButtonDirection); bool validContact = IsValidContact(interactableTool, currButtonDirection) || interactableTool.IsFarFieldTool; // in case tool enters contact zone first, we are in proximity as well bool toolIsInProximity = newCollisionDepth >= InteractableCollisionDepth.Proximity; bool toolInContactZone = newCollisionDepth == InteractableCollisionDepth.Contact; bool toolInActionZone = newCollisionDepth == InteractableCollisionDepth.Action; bool switchingStates = oldCollisionDepth != newCollisionDepth; if (switchingStates) { FireInteractionEventsOnDepth(oldCollisionDepth, interactableTool, InteractionType.Exit); FireInteractionEventsOnDepth(newCollisionDepth, interactableTool, InteractionType.Enter); } else { FireInteractionEventsOnDepth(newCollisionDepth, interactableTool, InteractionType.Stay); } var upcomingState = oldState; if (interactableTool.IsFarFieldTool) { upcomingState = toolInContactZone ? InteractableState.ContactState : toolInActionZone ? InteractableState.ActionState : InteractableState.Default; } else { // plane describing positive side of button var buttonZonePlane = new Plane(-currButtonDirection, _buttonPlaneCenter.position); // skip plane test if the boolean flag tells us not to test it bool onPositiveSideOfButton = !_makeSureToolIsOnPositiveSide || buttonZonePlane.GetSide(interactableTool.InteractionPosition); upcomingState = GetUpcomingStateNearField(oldState, newCollisionDepth, toolInActionZone, toolInContactZone, toolIsInProximity, validContact, onPositiveSideOfButton); } if (upcomingState != InteractableState.Default) { _toolToState[interactableTool] = upcomingState; } else { _toolToState.Remove(interactableTool); } // if using far field tool, the upcoming state is based // on the far field tool that has the greatest max state so far // (since there can be multiple far field tools interacting // with button) if (isFarFieldTool) { foreach (var toolState in _toolToState.Values) { if (upcomingState < toolState) { upcomingState = toolState; } } } if (oldState != upcomingState) { _currentButtonState = upcomingState; var interactionType = !switchingStates ? InteractionType.Stay : newCollisionDepth == InteractableCollisionDepth.None ? InteractionType.Exit : InteractionType.Enter; var CurrentCollider = _currentButtonState == InteractableState.ProximityState ? ProximityCollider : _currentButtonState == InteractableState.ContactState ? ContactCollider : _currentButtonState == InteractableState.ActionState ? ActionCollider : null; if (InteractableStateChanged != null) { InteractableStateChanged.Invoke(new InteractableStateArgs(this, interactableTool, _currentButtonState, oldState, new ColliderZoneArgs(CurrentCollider, Time.frameCount, interactableTool, interactionType))); } } }
public override void UpdateCollisionDepth(InteractableTool interactableTool, InteractableCollisionDepth oldCollisionDepth, InteractableCollisionDepth collisionDepth, InteractableTool collidingTool) { bool isFarFieldTool = interactableTool.IsFarFieldTool; // if this is a near field tool and another tool already controls it, bail. if (!isFarFieldTool && _toolToState.Keys.Count > 0 && !_toolToState.ContainsKey(interactableTool)) { return; } var oldState = _currentButtonState; // ignore contact test if you are using the far field tool var currButtonDirection = transform.TransformDirection(_localButtonDirection); bool validContact = IsValidContact(collidingTool, currButtonDirection) || collidingTool.IsFarFieldTool; // in case finger enters contact zone first, we are in proximity as well bool toolInActionZone = collisionDepth == InteractableCollisionDepth.Action; // plane describing positive side of button var buttonZonePlane = new Plane(-currButtonDirection, _buttonPlaneCenter.position); // skip plane test if the boolean flag tells us not to test it bool onPositiveSideOfButton = !_makeSureToolIsOnPositiveSide || buttonZonePlane.GetSide(collidingTool.InteractionPosition); bool switchingStates = oldCollisionDepth != collisionDepth; if (!switchingStates) { if (collisionDepth == InteractableCollisionDepth.Action) { if (OnStayInActionZone != null) { OnStayInActionZone.Invoke(); } } } var newState = oldState; if (collidingTool.IsFarFieldTool) { newState = toolInActionZone ? InteractableState.ActionState : InteractableState.Default; } else { switch (oldState) { case InteractableState.ActionState: if (!toolInActionZone) { newState = InteractableState.Default; if (OnExitActionZone != null) { OnExitActionZone.Invoke(); } } break; case InteractableState.Default: // test contact, action first then proximity (more important states // take precedence) if (validContact && collisionDepth == InteractableCollisionDepth.Action) { newState = InteractableState.ActionState; if (OnEnterActionZone != null) { OnEnterActionZone.Invoke(); } } break; } } if (newState != InteractableState.Default) { _toolToState[interactableTool] = newState; } else { _toolToState.Remove(interactableTool); } // far field tools depend on max state set if (isFarFieldTool) { foreach (var toolState in _toolToState.Values) { if (newState < toolState) { newState = toolState; } } } if (oldState != newState) { _currentButtonState = newState; var interactionType = !switchingStates ? InteractionType.Stay : collisionDepth == InteractableCollisionDepth.None ? InteractionType.Exit : InteractionType.Enter; if (InteractableStateChanged != null) { InteractableStateChanged.Invoke(new InteractableStateArgs(this, interactableTool, _currentButtonState, oldState, new ColliderZoneArgs(ContactCollider, Time.frameCount, collidingTool, interactionType))); } } }
public override void UpdateCollisionDepth(InteractableTool interactableTool, InteractableCollisionDepth oldCollisionDepth, InteractableCollisionDepth newCollisionDepth) { bool isFarFieldTool = interactableTool.IsFarFieldTool; // if this is a near field tool and another tool already controls it, bail. // (assuming we are not allowing multiple near field tools) bool testForSingleToolInteraction = !isFarFieldTool && !_allowMultipleNearFieldInteraction; if (testForSingleToolInteraction && _toolToState.Keys.Count > 0 && !_toolToState.ContainsKey(interactableTool)) { return; } var oldState = CurrentButtonState; // ignore contact test if you are using the far field tool var currButtonDirection = transform.TransformDirection(_localButtonDirection); bool validContact = IsValidContact(interactableTool, currButtonDirection) || interactableTool.IsFarFieldTool; // in case tool enters contact zone first, we are in proximity as well bool toolIsInProximity = newCollisionDepth >= InteractableCollisionDepth.Proximity; bool toolInContactZone = newCollisionDepth == InteractableCollisionDepth.Contact; bool toolInActionZone = newCollisionDepth == InteractableCollisionDepth.Action; bool switchingStates = oldCollisionDepth != newCollisionDepth; if (switchingStates) { FireInteractionEventsOnDepth(oldCollisionDepth, interactableTool, InteractionType.Exit); FireInteractionEventsOnDepth(newCollisionDepth, interactableTool, InteractionType.Enter); } else { FireInteractionEventsOnDepth(newCollisionDepth, interactableTool, InteractionType.Stay); } var upcomingState = oldState; if (interactableTool.IsFarFieldTool) { upcomingState = toolInContactZone ? InteractableState.ContactState : toolInActionZone ? InteractableState.ActionState : InteractableState.Default; } else { // plane describing positive side of button var buttonZonePlane = new Plane(-currButtonDirection, _buttonPlaneCenter.position); // skip plane test if the boolean flag tells us not to test it bool onPositiveSideOfButton = !_makeSureToolIsOnPositiveSide || buttonZonePlane.GetSide(interactableTool.InteractionPosition); upcomingState = GetUpcomingStateNearField(oldState, newCollisionDepth, toolInActionZone, toolInContactZone, toolIsInProximity, validContact, onPositiveSideOfButton); } if (upcomingState != InteractableState.Default) { _toolToState[interactableTool] = upcomingState; } else { _toolToState.Remove(interactableTool); } // far field tools depend on max state set // (or if proper flag is set for near field tools) bool setMaxStateForAllTools = isFarFieldTool || _allowMultipleNearFieldInteraction; if (setMaxStateForAllTools) { foreach (var toolState in _toolToState.Values) { if (upcomingState < toolState) { upcomingState = toolState; } } } if (oldState != upcomingState) { CurrentButtonState = upcomingState; var interactionType = !switchingStates ? InteractionType.Stay : newCollisionDepth == InteractableCollisionDepth.None ? InteractionType.Exit : InteractionType.Enter; ColliderZone currentCollider = null; switch (CurrentButtonState) { case InteractableState.ProximityState: currentCollider = ProximityCollider; break; case InteractableState.ContactState: currentCollider = ContactCollider; break; case InteractableState.ActionState: currentCollider = ActionCollider; break; default: currentCollider = null; break; } InteractableStateChanged?.Invoke(new InteractableStateArgs(this, interactableTool, CurrentButtonState, oldState, new ColliderZoneArgs(currentCollider, Time.frameCount, interactableTool, interactionType))); } }
public override void UpdateCollisionDepth(InteractableTool interactableTool, InteractableCollisionDepth oldCollisionDepth, InteractableCollisionDepth collisionDepth, InteractableTool collidingTool) { bool isFarFieldTool = interactableTool.IsFarFieldTool; // if this is a near field tool and another tool already controls it, bail. if (!isFarFieldTool && _toolToState.Keys.Count > 0 && !_toolToState.ContainsKey(interactableTool)) { return; } var oldState = _currentButtonState; // ignore contact test if you are using the far field tool var currButtonDirection = transform.TransformDirection(_localButtonDirection); bool validContact = IsValidContact(collidingTool, currButtonDirection) || collidingTool.IsFarFieldTool; // in case finger enters contact zone first, we are in proximity as well bool toolIsInProximity = collisionDepth >= InteractableCollisionDepth.Proximity; bool toolInContactZone = collisionDepth == InteractableCollisionDepth.Contact; bool toolInActionZone = collisionDepth == InteractableCollisionDepth.Action; // plane describing positive side of button var buttonZonePlane = new Plane(-currButtonDirection, _buttonPlaneCenter.position); // skip plane test if the boolean flag tells us not to test it bool onPositiveSideOfButton = !_makeSureToolIsOnPositiveSide || buttonZonePlane.GetSide(collidingTool.InteractionPosition); bool switchingStates = oldCollisionDepth != collisionDepth; if (switchingStates) { CallEventsOnOldDepth(oldCollisionDepth, collidingTool); CallEventsOnNewDepth(collisionDepth, collidingTool); } else { SustainEventsOnDepth(collisionDepth, collidingTool); } var newState = oldState; if (collidingTool.IsFarFieldTool) { newState = toolInContactZone ? InteractableState.ContactState : toolInActionZone ? InteractableState.ActionState : InteractableState.Default; } else { switch (oldState) { case InteractableState.ActionState: if (!toolInActionZone) { // if retreating from action, can go back into action state even if contact // is not legal (i.e. tool/finger retracts) if (toolInContactZone) { newState = InteractableState.ContactState; } else if (toolIsInProximity) { newState = InteractableState.ProximityState; } else { newState = InteractableState.Default; } } break; case InteractableState.ContactState: if (collisionDepth < InteractableCollisionDepth.Contact) { newState = toolIsInProximity ? InteractableState.ProximityState : InteractableState.Default; } // can only go to action state if contact is legal // if tool goes into contact state due to proper movement, but does not maintain // that movement throughout (i.e. a tool/finger presses downwards initially but // moves in random directions afterwards), then don't go into action else if (toolInActionZone) { newState = InteractableState.ActionState; } break; case InteractableState.ProximityState: if (collisionDepth < InteractableCollisionDepth.Proximity) { newState = InteractableState.Default; } else if (collisionDepth > InteractableCollisionDepth.Proximity) { newState = collisionDepth == InteractableCollisionDepth.Action ? InteractableState.ActionState : InteractableState.ContactState; } break; case InteractableState.Default: // test contact, action first then proximity (more important states // take precedence) if (validContact && onPositiveSideOfButton && collisionDepth > InteractableCollisionDepth.Proximity) { newState = collisionDepth == InteractableCollisionDepth.Action ? InteractableState.ActionState : InteractableState.ContactState; } else if (toolIsInProximity) { newState = InteractableState.ProximityState; } break; } } if (newState != InteractableState.Default) { _toolToState[interactableTool] = newState; } else { _toolToState.Remove(interactableTool); } // far field tools depend on max state set if (isFarFieldTool) { foreach (var toolState in _toolToState.Values) { if (newState < toolState) { newState = toolState; } } } if (oldState != newState) { _currentButtonState = newState; var interactionType = !switchingStates ? InteractionType.Stay : collisionDepth == InteractableCollisionDepth.None ? InteractionType.Exit : InteractionType.Enter; if (InteractableStateChanged != null) { InteractableStateChanged.Invoke(new InteractableStateArgs(this, interactableTool, _currentButtonState, oldState, new ColliderZoneArgs(ContactCollider, Time.frameCount, collidingTool, interactionType))); } } }