// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using Unity.Profiling; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input.UnityInput { [MixedRealityController( SupportedControllerType.TouchScreen, new[] { Handedness.Any })] public class UnityTouchController : BaseController { public UnityTouchController( TrackingState trackingState, Handedness controllerHandedness, IMixedRealityInputSource inputSource = null, MixedRealityInteractionMapping[] interactions = null) : base(trackingState, controllerHandedness, inputSource, interactions, new TouchScreenDefinition()) { } /// /// Time in seconds to determine if the contact registers as a tap or a hold /// public float MaxTapContactTime { get; set; } = 0.5f; /// /// The threshold a finger must move before starting a manipulation gesture. /// public float ManipulationThreshold { get; set; } = 5f; /// /// Current Touch Data for the Controller. /// public Touch TouchData { get; internal set; } /// /// Current Screen point ray for the Touch. /// public Ray ScreenPointRay { get; internal set; } /// /// The current lifetime of the Touch. /// public float Lifetime { get; private set; } = 0.0f; private bool isTouched; private MixedRealityInputAction holdingAction; private bool isHolding; private MixedRealityInputAction manipulationAction; private bool isManipulating; private MixedRealityPose lastPose = MixedRealityPose.ZeroIdentity; /// public override void SetupDefaultInteractions() { base.SetupDefaultInteractions(); if (CoreServices.InputSystem?.InputSystemProfile.GesturesProfile != null) { var gestures = CoreServices.InputSystem.InputSystemProfile.GesturesProfile.Gestures; for (int i = 0; i < gestures.Length; i++) { var gesture = gestures[i]; switch (gesture.GestureType) { case GestureInputType.Hold: holdingAction = gesture.Action; break; case GestureInputType.Manipulation: manipulationAction = gesture.Action; break; } } } } private bool isNewController = false; /// /// Start the touch. /// public void StartTouch() { // Indicate that this is a new controller. isNewController = true; } private static readonly ProfilerMarker UpdatePerfMarker = new ProfilerMarker("[MRTK] UnityTouchController.Update"); /// /// Update the touch data. /// public void Update() { using (UpdatePerfMarker.Auto()) { var inputSystem = CoreServices.InputSystem; if (inputSystem == null) { return; } if (isNewController) { isNewController = false; inputSystem.RaiseOnInputDown(InputSource, Handedness.None, Interactions[2].MixedRealityInputAction); inputSystem.RaisePointerDown(InputSource.Pointers[0], Interactions[2].MixedRealityInputAction); isTouched = true; inputSystem.RaiseGestureStarted(this, holdingAction); isHolding = true; } if (!isTouched) { return; } Lifetime += Time.deltaTime; if (TouchData.phase == TouchPhase.Moved) { Interactions[0].Vector2Data = TouchData.deltaPosition; if (Interactions[0].Changed) { inputSystem.RaisePositionInputChanged(InputSource, ControllerHandedness, Interactions[0].MixedRealityInputAction, TouchData.deltaPosition); } lastPose.Position = InputSource.Pointers[0].Position; lastPose.Rotation = InputSource.Pointers[0].Rotation; inputSystem.RaiseSourcePoseChanged(InputSource, this, lastPose); Interactions[1].PoseData = lastPose; if (Interactions[1].Changed) { inputSystem.RaisePoseInputChanged(InputSource, ControllerHandedness, Interactions[1].MixedRealityInputAction, lastPose); } if (!isManipulating) { if (Mathf.Abs(TouchData.deltaPosition.x) > ManipulationThreshold || Mathf.Abs(TouchData.deltaPosition.y) > ManipulationThreshold) { inputSystem?.RaiseGestureCanceled(this, holdingAction); isHolding = false; inputSystem?.RaiseGestureStarted(this, manipulationAction); isManipulating = true; } } else { inputSystem.RaiseGestureUpdated(this, manipulationAction, TouchData.deltaPosition); } // Send dragged event, to inform manipulation handlers. inputSystem.RaisePointerDragged(InputSource.Pointers[0], Interactions[1].MixedRealityInputAction); } } } private static readonly ProfilerMarker EndTouchPerfMarker = new ProfilerMarker("[MRTK] UnityTouchController.EndTouch"); /// /// End the touch. /// public void EndTouch() { using (EndTouchPerfMarker.Auto()) { var inputSystem = CoreServices.InputSystem; if (inputSystem == null) { return; } if (TouchData.phase == TouchPhase.Ended) { if (Lifetime < MaxTapContactTime) { if (isHolding) { inputSystem.RaiseGestureCanceled(this, holdingAction); isHolding = false; } if (isManipulating) { inputSystem.RaiseGestureCanceled(this, manipulationAction); isManipulating = false; } inputSystem.RaisePointerClicked(InputSource.Pointers[0], Interactions[2].MixedRealityInputAction, TouchData.tapCount); } if (isHolding) { inputSystem.RaiseGestureCompleted(this, holdingAction); isHolding = false; } if (isManipulating) { inputSystem.RaiseGestureCompleted(this, manipulationAction, TouchData.deltaPosition); isManipulating = false; } } if (isHolding) { inputSystem.RaiseGestureCompleted(this, holdingAction); isHolding = false; } Debug.Assert(!isHolding); if (isManipulating) { inputSystem.RaiseGestureCompleted(this, manipulationAction, TouchData.deltaPosition); isManipulating = false; } Debug.Assert(!isManipulating); inputSystem.RaiseOnInputUp(InputSource, Handedness.None, Interactions[2].MixedRealityInputAction); inputSystem.RaisePointerUp(InputSource.Pointers[0], Interactions[2].MixedRealityInputAction); Lifetime = 0.0f; isTouched = false; Interactions[1].PoseData = MixedRealityPose.ZeroIdentity; Interactions[0].Vector2Data = Vector2.zero; } } } }