// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input { /// /// A cursor that looks and acts more like the shell cursor. /// A two part cursor with visual feedback for all cursor states /// [AddComponentMenu("Scripts/MRTK/SDK/InteractiveMeshCursor")] public class InteractiveMeshCursor : BaseCursor { [SerializeField] [Tooltip("The ring or outer element")] private GameObject ring = null; [SerializeField] [Tooltip("Inner cursor element")] private GameObject dot = null; [SerializeField] [Tooltip("The scale factor to soften the distance scaling, we want the cursor to scale in the distance, but not disappear.")] private float distanceScaleFactor = 0.3f; [SerializeField] [Tooltip("The scale both elements will be at their default state")] private float defaultScale = 0.75f; [SerializeField] [Tooltip("The scale both elements will when pressed")] private float downScale = 0.5f; [SerializeField] [Tooltip("The scale both elements will a hand is visible")] private float upScale = 1; [SerializeField] [Tooltip("Time to scale between states")] private float scaleTime = 0.5f; /// /// internal state and element management /// private float timer = 0; private bool isVisible = true; private bool hasHover = false; private bool hasHand = false; private bool isDown = false; private float ringDotTargetScale; private Vector3 initialScale; private void Awake() { initialScale = transform.localScale; } /// /// Decide which element (ring or dot) should be visible and at what scale /// public override void OnCursorStateChange(CursorStateEnum state) { base.OnCursorStateChange(state); // the cursor state has changed, reset the animation timer if (hasHand != IsSourceDetected || isDown != IsPointerDown || hasHover != (TargetedObject != null)) { timer = 0; } hasHand = IsSourceDetected; isDown = IsPointerDown; hasHover = TargetedObject != null; ringDotTargetScale = defaultScale; bool showRing = false; switch (state) { case CursorStateEnum.None: break; case CursorStateEnum.Observe: break; case CursorStateEnum.ObserveHover: showRing = true; break; case CursorStateEnum.Interact: showRing = true; ringDotTargetScale = downScale; break; case CursorStateEnum.InteractHover: showRing = true; ringDotTargetScale = upScale; break; case CursorStateEnum.Select: ringDotTargetScale = upScale; break; case CursorStateEnum.Release: break; case CursorStateEnum.Contextual: break; } if (!isVisible) { return; } ring.SetActive(showRing); dot.SetActive(!showRing); // added observation of CursorModifier if (Pointer.CursorModifier != null && hasHover) { ElementVisibility(!Pointer.CursorModifier.GetCursorVisibility()); } } /// /// scale the cursor elements /// protected override void UpdateCursorTransform() { base.UpdateCursorTransform(); // animate scale of ring and dot if (timer < scaleTime) { timer += Time.deltaTime; if (timer > scaleTime) { timer = scaleTime; } Vector3 useScale = Vector3.one * Mathf.Lerp(defaultScale, ringDotTargetScale, timer / scaleTime); ring.transform.localScale = useScale; dot.transform.localScale = useScale; } // handle scale of main cursor go float distance = Vector3.Distance(CoreServices.InputSystem.GazeProvider.GazeOrigin, transform.position); float smoothScaling = 1 - DefaultCursorDistance * distanceScaleFactor; transform.localScale = initialScale * (distance * distanceScaleFactor + smoothScaling); } /// /// override the base class for custom visibility /// public override void SetVisibility(bool visible) { base.SetVisibility(visible); isVisible = visible; ElementVisibility(visible); if (visible) { OnCursorStateChange(CursorState); } } /// /// controls the visibility of cursor elements in one place /// private void ElementVisibility(bool visible) { if (ring != null) { ring.SetActive(visible); } if (dot != null) { dot.SetActive(visible); } } } }