private bool IsValidContact(InteractableTool collidingTool, Vector3 buttonDirection) { if (_contactTests == null || collidingTool.IsFarFieldTool) { return(true); } foreach (var contactTest in _contactTests) { switch (contactTest) { case ContactTest.BackwardsPress: if (!PassEntryTest(collidingTool, buttonDirection)) { return(false); } break; default: if (!PassPerpTest(collidingTool, buttonDirection)) { return(false); } break; } } return(true); }
public InteractableCollisionInfo(ColliderZone collider, InteractableCollisionDepth collisionDepth, InteractableTool collidingTool) { InteractableCollider = collider; CollisionDepth = collisionDepth; CollidingTool = collidingTool; }
private bool IsValidContact(InteractableTool collidingTool, Vector3 buttonDirection) { if (_contactTests == null || collidingTool.IsFarFieldTool) { return(true); } //foreach (var contactTest in _contactTests) //{ // switch (contactTest) // { // case ContactTest.BackwardsPress: // if (!PassEntryTest(collidingTool, buttonDirection)) // { // return false; // } // break; // default: // if (!PassPerpTest(collidingTool, buttonDirection)) // { // return false; // } // break; // } //} return(true); }
public void UnregisterInteractableTool(InteractableTool interactableTool) { if (interactableTool.IsRightHandedTool) { if (interactableTool.IsFarFieldTool) { _rightHandFarTools.Remove(interactableTool); } else { _rightHandNearTools.Remove(interactableTool); } } else { if (interactableTool.IsFarFieldTool) { _leftHandFarTools.Remove(interactableTool); } else { _leftHandNearTools.Remove(interactableTool); } } }
public ColliderZoneArgs(ColliderZone collider, float frameTime, InteractableTool collidingTool, InteractionType interactionType) { Collider = collider; FrameTime = frameTime; CollidingTool = collidingTool; InteractionT = interactionType; }
public InteractableStateArgs(Interactable interactable, InteractableTool tool, InteractableState newInteractableState, InteractableState oldState, ColliderZoneArgs colliderArgs) { Interactable = interactable; Tool = tool; NewInteractableState = newInteractableState; OldInteractableState = oldState; ColliderArgs = colliderArgs; }
/// <summary> /// If our collision information changed per frame, make note of it. Removed, added and remaining /// objects must get their proper events. /// </summary> /// <param name="oldCollisionMap">Previous collision information.</param> /// <param name="newCollisionMap">Current collision information.</param> private void UpdateUsingOldNewCollisionData(InteractableTool interactableTool, Dictionary<Interactable, InteractableCollisionInfo> oldCollisionMap, Dictionary<Interactable, InteractableCollisionInfo> newCollisionMap) { _addedInteractables.Clear(); _removedInteractables.Clear(); _remainingInteractables.Clear(); foreach (Interactable key in newCollisionMap.Keys) { if (!oldCollisionMap.ContainsKey(key)) { _addedInteractables.Add(key); } else { _remainingInteractables.Add(key); } } foreach (Interactable key in oldCollisionMap.Keys) { if (!newCollisionMap.ContainsKey(key)) { _removedInteractables.Add(key); } } // tell removed interactables that we are gone foreach (Interactable removedInteractable in _removedInteractables) { removedInteractable.UpdateCollisionDepth(interactableTool, oldCollisionMap[removedInteractable].CollisionDepth, InteractableCollisionDepth.None, oldCollisionMap[removedInteractable].CollidingTool); } // tell added interactable what state we are now in foreach (Interactable addedInteractableKey in _addedInteractables) { var addedInteractable = newCollisionMap[addedInteractableKey]; var collisionDepth = addedInteractable.CollisionDepth; addedInteractableKey.UpdateCollisionDepth(interactableTool, InteractableCollisionDepth.None, collisionDepth, newCollisionMap[addedInteractableKey].CollidingTool); } // remaining interactables must be updated foreach (Interactable remainingInteractableKey in _remainingInteractables) { var newDepth = newCollisionMap[remainingInteractableKey].CollisionDepth; var oldDepth = oldCollisionMap[remainingInteractableKey].CollisionDepth; remainingInteractableKey.UpdateCollisionDepth(interactableTool, oldDepth, newDepth, newCollisionMap[remainingInteractableKey].CollidingTool); } }
public void CrossingButtonStateChanged(InteractableStateArgs obj) { bool inActionState = obj.NewInteractableState == InteractableState.ActionState; if (inActionState) { ActivateTrainCrossing(); } _toolInteractingWithMe = obj.NewInteractableState > InteractableState.Default ? obj.Tool : null; }
/// <summary> /// Is tool entering button correctly? Check velocity and make sure that /// tool is not below action zone. /// </summary> private bool PassEntryTest(InteractableTool collidingTool, Vector3 buttonDirection) { var jointVelocityVector = collidingTool.Velocity.normalized; var dotProduct = Vector3.Dot(jointVelocityVector, buttonDirection); if (dotProduct < ENTRY_DOT_THRESHOLD) { return(false); } return(true); }
private void StartStopStateChanged(InteractableStateArgs obj) { bool inActionState = obj.NewInteractableState == InteractableState.ActionState; if (inActionState) { if (_bladesRotation.IsMoving) { _bladesRotation.SetMoveState(false, 0.0f); } else { _bladesRotation.SetMoveState(true, _maxSpeed); } } _toolInteractingWithMe = obj.NewInteractableState > InteractableState.Default ? obj.Tool : null; }
/// <summary> /// Is our tool pointing in opposite direction compared to button? /// </summary> private bool PassPerpTest(InteractableTool collidingTool, Vector3 buttonDirection) { // the "right" vector points along tool by default // if it's right hand, then flip that direction var toolDirection = collidingTool.ToolTransform.right; if (collidingTool.IsRightHandedTool) { toolDirection = -toolDirection; } var dotProduct = Vector3.Dot(toolDirection, buttonDirection); if (dotProduct < PERP_DOT_THRESHOLD) { return(false); } return(true); }
private void CallEventsOnOldDepth(InteractableCollisionDepth oldDepth, InteractableTool collidingTool) { switch (oldDepth) { case InteractableCollisionDepth.Action: OnActionZoneEvent(new ColliderZoneArgs(ActionCollider, Time.frameCount, collidingTool, InteractionType.Exit)); break; case InteractableCollisionDepth.Contact: OnContactZoneEvent(new ColliderZoneArgs(ContactCollider, Time.frameCount, collidingTool, InteractionType.Exit)); break; case InteractableCollisionDepth.Proximity: OnProximityZoneEvent(new ColliderZoneArgs(ProximityCollider, Time.frameCount, collidingTool, InteractionType.Exit)); break; } }
private void OnButtonStateChanged(InteractableStateArgs obj) { toolInteractingWithMe = obj.NewInteractableState > InteractableState.Default ? obj.Tool : null; }
public abstract void UpdateCollisionDepth(InteractableTool interactableTool, InteractableCollisionDepth oldCollisionDepth, InteractableCollisionDepth newCollisionDepth);
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 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 abstract void UpdateCollisionDepth(InteractableTool interactableTool, InteractableCollisionDepth oldCollisionDepth, InteractableCollisionDepth collisionDepth, InteractableTool collidingTool);
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))); } } }