// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.UI { /// /// Connects a ToolTip to a target /// Maintains that connection even if the target moves /// [ExecuteAlways] [AddComponentMenu("Scripts/MRTK/SDK/ToolTipConnector")] public class ToolTipConnector : MonoBehaviour { [SerializeField] [Tooltip("The GameObject to which the tooltip is connected")] private GameObject target; /// /// The GameObject to which the tooltip is connected /// public GameObject Target { get { return target; } set { target = value; } } [SerializeField] private ToolTip toolTip; [SerializeField] [Tooltip("The follow style of the tooltip connector")] private ConnectorFollowType connectorFollowType = ConnectorFollowType.AnchorOnly; /// /// The follow style of the tooltip connector /// public ConnectorFollowType ConnectorFollowingType { get { return connectorFollowType; } set { connectorFollowType = value; } } [SerializeField] [Tooltip("Is the connector pivot set manually or automatically?")] private ConnectorPivotMode pivotMode = ConnectorPivotMode.Manual; /// /// Is the connector pivot set manually or automatically? /// public ConnectorPivotMode PivotMode { get { return pivotMode; } set { pivotMode = value; } } [SerializeField] [Tooltip("The direction of the connector")] private ConnectorPivotDirection pivotDirection = ConnectorPivotDirection.North; /// /// The direction of the connector /// public ConnectorPivotDirection PivotDirection { get { return pivotDirection; } set { pivotDirection = value; } } [SerializeField] [Tooltip("orientation style for connector")] private ConnectorOrientType pivotDirectionOrient = ConnectorOrientType.OrientToObject; /// /// Orientation style for connector /// public ConnectorOrientType PivotDirectionOrient { get { return pivotDirectionOrient; } set { pivotDirectionOrient = value; } } [SerializeField] [Tooltip("The direction of the manual pivot.")] private Vector3 manualPivotDirection = Vector3.up; /// /// The direction of the manual pivot. /// public Vector3 ManualPivotDirection { get { return manualPivotDirection; } set { manualPivotDirection = value; } } [SerializeField] private Vector3 manualPivotLocalPosition = Vector3.up; /// /// getter/setter for local pivot position /// public Vector3 ManualPivotLocalPosition { get { return manualPivotLocalPosition; } set { manualPivotLocalPosition = value; } } [SerializeField] [Range(0f, 2f)] [Tooltip("Set Distance from object that Tooltip pivots around.")] private float pivotDistance = 0.25f; /// /// Set Distance from object that Tooltip pivots around. /// public float PivotDistance { get { return pivotDistance; } set { pivotDistance = Mathf.Min(2.0f, Mathf.Max(0, value)); } } private void OnEnable() { toolTip = gameObject.GetComponent(); if (toolTip == null) { Debug.LogWarning("This component only works with a ToolTip."); enabled = false; return; } UpdatePosition(); } private void UpdatePosition() { if (target == null) { return; } switch (connectorFollowType) { case ConnectorFollowType.AnchorOnly: default: // Set the position of the anchor to the target's position // And do nothing else toolTip.Anchor.transform.position = target.transform.position; break; case ConnectorFollowType.Position: // Move the entire tooltip transform while maintaining the anchor position offset toolTip.transform.position = target.transform.position; switch (PivotMode) { case ConnectorPivotMode.Automatic: Transform relativeTo = null; switch (PivotDirectionOrient) { case ConnectorOrientType.OrientToCamera: relativeTo = CameraCache.Main.transform; break; case ConnectorOrientType.OrientToObject: relativeTo = target.transform; break; } toolTip.PivotPosition = target.transform.position + GetDirectionFromPivotDirection( PivotDirection, ManualPivotDirection, relativeTo) * PivotDistance; break; case ConnectorPivotMode.LocalPosition: toolTip.PivotPosition = target.transform.position + target.transform.TransformPoint(manualPivotLocalPosition); break; case ConnectorPivotMode.Manual: // Do nothing break; } break; case ConnectorFollowType.PositionAndYRotation: // Set the transform of the entire tool tip // Set the pivot relative to target/camera toolTip.transform.position = target.transform.position; Vector3 eulerAngles = target.transform.eulerAngles; eulerAngles.x = 0f; eulerAngles.z = 0f; toolTip.transform.eulerAngles = eulerAngles; switch (PivotMode) { case ConnectorPivotMode.Automatic: Transform relativeTo = null; switch (PivotDirectionOrient) { case ConnectorOrientType.OrientToCamera: relativeTo = CameraCache.Main.transform; break; case ConnectorOrientType.OrientToObject: relativeTo = target.transform; break; } Vector3 localPosition = GetDirectionFromPivotDirection(PivotDirection, ManualPivotDirection, relativeTo) * PivotDistance; toolTip.PivotPosition = target.transform.position + localPosition; break; case ConnectorPivotMode.LocalPosition: toolTip.PivotPosition = target.transform.position + target.transform.TransformPoint(manualPivotLocalPosition); break; case ConnectorPivotMode.Manual: // Do nothing break; } break; case ConnectorFollowType.PositionAndXYRotation: // Set the transform of the entire tool tip // Set the pivot relative to target/camera toolTip.transform.position = target.transform.position; toolTip.transform.rotation = target.transform.rotation; switch (PivotMode) { case ConnectorPivotMode.Automatic: Transform relativeTo = null; switch (PivotDirectionOrient) { case ConnectorOrientType.OrientToCamera: relativeTo = CameraCache.Main.transform; break; case ConnectorOrientType.OrientToObject: relativeTo = target.transform; break; } toolTip.PivotPosition = target.transform.position + GetDirectionFromPivotDirection( PivotDirection, ManualPivotDirection, relativeTo) * PivotDistance; break; case ConnectorPivotMode.LocalPosition: toolTip.PivotPosition = target.transform.position + target.transform.TransformPoint(manualPivotLocalPosition); break; case ConnectorPivotMode.Manual: // Do nothing break; } break; } } private void Update() { UpdatePosition(); } /// /// Computes the director of the connector /// /// enum describing director of connector pivot /// is the pivot set manually /// Transform that describes the frame of reference of the pivot /// a vector describing the pivot direction in world space public static Vector3 GetDirectionFromPivotDirection(ConnectorPivotDirection pivotDirection, Vector3 manualPivotDirection, Transform relativeTo) { Vector3 dir = Vector3.zero; switch (pivotDirection) { case ConnectorPivotDirection.North: dir = Vector3.up; break; case ConnectorPivotDirection.Northeast: dir = Vector3.Lerp(Vector3.up, Vector3.right, 0.5f).normalized; break; case ConnectorPivotDirection.East: dir = Vector3.right; break; case ConnectorPivotDirection.Southeast: dir = Vector3.Lerp(Vector3.down, Vector3.right, 0.5f).normalized; break; case ConnectorPivotDirection.South: dir = Vector3.down; break; case ConnectorPivotDirection.Southwest: dir = Vector3.Lerp(Vector3.down, Vector3.left, 0.5f).normalized; break; case ConnectorPivotDirection.West: dir = Vector3.left; break; case ConnectorPivotDirection.Northwest: dir = Vector3.Lerp(Vector3.up, Vector3.left, 0.5f).normalized; break; case ConnectorPivotDirection.InFront: dir = Vector3.forward; break; case ConnectorPivotDirection.Manual: dir = manualPivotDirection.normalized; break; } return relativeTo.TransformDirection(dir); } } }