/// <summary> /// Initializes the GestureTouchesUtility singleton if needed and returns a valid instance. /// </summary> /// <returns>The instance of GestureTouchesUtility.</returns> private static GestureTouchesUtility _GetInstance() { if (s_Instance == null) { s_Instance = new GestureTouchesUtility(); } return(s_Instance); }
/// <summary> /// Helper function for creating one finger gestures when a touch begins. /// </summary> /// <typeparam name="createGestureFunction">Function to be executed to create the /// gesture.</param> protected internal void TryCreateOneFingerGestureOnTouchBegan( Func <Touch, T> createGestureFunction) { for (int i = 0; i < Input.touches.Length; i++) { Touch touch = Input.touches[i]; if (touch.phase == TouchPhase.Began && !GestureTouchesUtility.IsFingerIdRetained(touch.fingerId) && !GestureTouchesUtility.IsTouchOffScreenEdge(touch)) { T gesture = createGestureFunction(touch); gesture.onStart += OnStart; gesture.onFinished += OnFinished; m_Gestures.Add(gesture); } } }
private void TryCreateGestureTwoFingerGestureOnTouchBeganForTouchIndex( int touchIndex, Func <Touch, Touch, T> createGestureFunction) { if (Input.touches[touchIndex].phase != TouchPhase.Began) { return; } Touch touch = Input.touches[touchIndex]; if (GestureTouchesUtility.IsFingerIdRetained(touch.fingerId) || GestureTouchesUtility.IsTouchOffScreenEdge(touch)) { return; } for (int i = 0; i < Input.touches.Length; i++) { if (i == touchIndex) { continue; } // Prevents the same two touches from creating two gestures if both touches began on // the same frame. if (i < touchIndex && Input.touches[i].phase == TouchPhase.Began) { continue; } Touch otherTouch = Input.touches[i]; if (GestureTouchesUtility.IsFingerIdRetained(otherTouch.fingerId) || GestureTouchesUtility.IsTouchOffScreenEdge(otherTouch)) { continue; } T gesture = createGestureFunction(touch, otherTouch); gesture.onStart += OnStart; gesture.onFinished += OnFinished; m_Gestures.Add(gesture); } }
/// <summary> /// Calculates the best position to place an object in AR based on screen position. /// Could be used for tapping a location on the screen, dragging an object, or using a fixed /// cursor in the center of the screen for placing and moving objects. /// /// Objects are placed along the x/z of the grounding plane. When placed on an AR plane /// below the grounding plane, the object will drop straight down onto it in world space. /// This prevents the object from being pushed deeper into the scene when moving from a /// higher plane to a lower plane. When moving from a lower plane to a higher plane, this /// function returns a new groundingPlane to replace the old one. /// </summary> /// <returns>The best placement position.</returns> /// <param name="currentAnchorPosition">Position of the parent anchor, i.e., where the /// object is before translation starts.</param> /// <param name="screenPos">Location on the screen in pixels to place the object at.</param> /// <param name="groundingPlaneHeight">The starting height of the plane that the object is /// being placed along.</param> /// <param name="hoverOffset">How much should the object hover above the groundingPlane /// before it has been placed.</param> /// <param name="maxTranslationDistance">The maximum distance that the object can be /// translated.</param> /// <param name="translationMode">The translation mode, indicating the plane types allowed. /// </param> public static Placement GetBestPlacementPosition( Vector3 currentAnchorPosition, Vector2 screenPos, float groundingPlaneHeight, float hoverOffset, float maxTranslationDistance, TranslationMode translationMode) { Placement result = new Placement(); result.UpdatedGroundingPlaneHeight = groundingPlaneHeight; // Get the angle between the camera and the object's down direction. float angle = Vector3.Angle(Camera.main.transform.forward, Vector3.down); angle = 90.0f - angle; float touchOffsetRatio = Mathf.Clamp01(angle / 90.0f); float screenTouchOffset = touchOffsetRatio * k_MaxScreenTouchOffset; screenPos.y += GestureTouchesUtility.InchesToPixels(screenTouchOffset); float hoverRatio = Mathf.Clamp01(angle / 45.0f); hoverOffset *= hoverRatio; float distance = (Camera.main.transform.position - currentAnchorPosition).magnitude; float distanceHoverRatio = Mathf.Clamp01(distance / k_HoverDistanceThreshold); hoverOffset *= distanceHoverRatio; // The best estimate of the point in the plane where the object will be placed: Vector3 groundingPoint; // Get the ray to cast into the scene from the perspective of the camera. TrackableHit hit; if (Frame.Raycast( screenPos.x, screenPos.y, TrackableHitFlags.PlaneWithinBounds, out hit)) { if (hit.Trackable is DetectedPlane) { DetectedPlane plane = hit.Trackable as DetectedPlane; if (IsPlaneTypeAllowed(translationMode, plane.PlaneType)) { // Avoid detecting the back of existing planes. if ((hit.Trackable is DetectedPlane) && Vector3.Dot(Camera.main.transform.position - hit.Pose.position, hit.Pose.rotation * Vector3.up) < 0) { Debug.Log("Hit at back of the current DetectedPlane"); return(result); } // Don't allow hovering for vertical or horizontal downward facing planes. if (plane.PlaneType == DetectedPlaneType.Vertical || plane.PlaneType == DetectedPlaneType.HorizontalDownwardFacing) { // Limit the translation to maxTranslationDistance. groundingPoint = LimitTranslation( hit.Pose.position, currentAnchorPosition, maxTranslationDistance); result.PlacementPlane = hit; result.PlacementPosition = groundingPoint; result.HoveringPosition = groundingPoint; result.UpdatedGroundingPlaneHeight = groundingPoint.y; result.PlacementRotation = hit.Pose.rotation; return(result); } // Allow hovering for horizontal upward facing planes. if (plane.PlaneType == DetectedPlaneType.HorizontalUpwardFacing) { // Return early if the camera is pointing upwards. if (angle < 0f) { return(result); } // Limit the translation to maxTranslationDistance. groundingPoint = LimitTranslation( hit.Pose.position, currentAnchorPosition, maxTranslationDistance); // Find the hovering position by casting from the camera onto the // grounding plane and offsetting the result by the hover offset. result.HoveringPosition = groundingPoint + (Vector3.up * hoverOffset); // If the AR Plane is above the grounding plane, then the hit plane's // position is used to replace the current groundingPlane. Otherwise, // the hit is ignored because hits are only detected on lower planes by // casting straight downwards in world space. if (groundingPoint.y > groundingPlaneHeight) { result.PlacementPlane = hit; result.PlacementPosition = groundingPoint; result.UpdatedGroundingPlaneHeight = hit.Pose.position.y; result.PlacementRotation = hit.Pose.rotation; return(result); } } else { // Not supported plane type. return(result); } } else { // Plane type not allowed. return(result); } } else { // Hit is not a plane. return(result); } } // Return early if the camera is pointing upwards. if (angle < 0f) { return(result); } // If the grounding point is lower than the current gorunding plane height, or if the // raycast did not return a hit, then we extend the grounding plane to infinity, and do // a new raycast into the scene from the perspective of the camera. Ray cameraRay = Camera.main.ScreenPointToRay(screenPos); Plane groundingPlane = new Plane(Vector3.up, new Vector3(0.0f, groundingPlaneHeight, 0.0f)); // Find the hovering position by casting from the camera onto the grounding plane // and offsetting the result by the hover offset. float enter; if (groundingPlane.Raycast(cameraRay, out enter)) { groundingPoint = LimitTranslation( cameraRay.GetPoint(enter), currentAnchorPosition, maxTranslationDistance); result.HoveringPosition = groundingPoint + (Vector3.up * hoverOffset); } else { // If we can't successfully cast onto the groundingPlane, just return early. return(result); } // Cast straight down onto AR planes that are lower than the current grounding plane. if (Frame.Raycast( groundingPoint + (Vector3.up * k_DownRayOffset), Vector3.down, out hit, Mathf.Infinity, TrackableHitFlags.PlaneWithinBounds)) { result.PlacementPosition = hit.Pose.position; result.PlacementPlane = hit; result.PlacementRotation = hit.Pose.rotation; return(result); } return(result); }