216 lines
6.9 KiB
C#
216 lines
6.9 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Threading.Tasks;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.UI
|
|
{
|
|
/// <summary>
|
|
/// This class manages how a GameObject rotates and/or scales
|
|
/// when activated as part of a Progress Indicator effect.
|
|
/// </summary>
|
|
[AddComponentMenu("Scripts/MRTK/SDK/ProgressIndicatorObjectDisplay")]
|
|
public class ProgressIndicatorObjectDisplay : MonoBehaviour, IProgressIndicator
|
|
{
|
|
/// <inheritdoc/>
|
|
public Transform MainTransform { get { return transform; } }
|
|
|
|
/// <inheritdoc/>
|
|
public ProgressIndicatorState State { get { return state; } }
|
|
|
|
/// <inheritdoc/>
|
|
public float Progress { set { progress = value; } }
|
|
|
|
/// <inheritdoc/>
|
|
public string Message { set { messageText.text = value; } }
|
|
|
|
[SerializeField]
|
|
private Transform scaleTargetObject = null;
|
|
[SerializeField]
|
|
private Transform rotateTargetObject = null;
|
|
|
|
[Header("How fast does object rotate?")]
|
|
[SerializeField]
|
|
private float rotationIncrement = 200;
|
|
|
|
[Header("Start scale of the object?")]
|
|
[SerializeField]
|
|
private float minScale = 1.0f;
|
|
|
|
[Header("Final scale of the object?")]
|
|
[SerializeField]
|
|
private float maxScale = 9.0f;
|
|
|
|
[Header("Should object rotate?")]
|
|
[SerializeField]
|
|
private bool rotationActive = false;
|
|
|
|
[Header("Rotation occurs about which axes?")]
|
|
[SerializeField]
|
|
private bool xAxisRotation = false;
|
|
[SerializeField]
|
|
private bool yAxisRotation = true;
|
|
[SerializeField]
|
|
private bool zAxisRotation = false;
|
|
|
|
[Header("Grow / Shrink curve on open / close")]
|
|
[SerializeField]
|
|
[Tooltip("Curve for opening animation. MUST end with value >= 1 or transition will not complete.")]
|
|
private AnimationCurve openCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
|
[SerializeField]
|
|
[Tooltip("Curve for opening animation. MUST end with value <= 0 or transition will not complete.")]
|
|
private AnimationCurve closeCurve = AnimationCurve.EaseInOut(0, 1, 1, 0);
|
|
|
|
[SerializeField]
|
|
private TextMeshPro messageText = null;
|
|
|
|
[SerializeField]
|
|
[Range(0f, 1f)]
|
|
private float progress;
|
|
|
|
private ProgressIndicatorState state = ProgressIndicatorState.Closed;
|
|
private float currentScale;
|
|
|
|
/// <inheritdoc/>
|
|
public async Task OpenAsync()
|
|
{
|
|
if (openCurve.length == 0)
|
|
{
|
|
Debug.LogWarning("Open curve length is zero - this may result in an infinite loop.");
|
|
}
|
|
|
|
float maxScale = openCurve.Evaluate(Mathf.Infinity);
|
|
if (maxScale < 1f)
|
|
{
|
|
Debug.LogWarning("Open curve value never reaches 1 - this may result in an infinite loop.");
|
|
}
|
|
|
|
if (state != ProgressIndicatorState.Closed)
|
|
{
|
|
throw new System.Exception("Can't open in state " + state);
|
|
}
|
|
|
|
gameObject.SetActive(true);
|
|
|
|
Reset();
|
|
|
|
state = ProgressIndicatorState.Opening;
|
|
|
|
float startTime = Time.unscaledTime;
|
|
float openScale = 0f;
|
|
while (openScale < 1 && isActiveAndEnabled)
|
|
{
|
|
openScale = openCurve.Evaluate(Time.unscaledTime - startTime);
|
|
scaleTargetObject.transform.localScale = Vector3.one * currentScale * openScale;
|
|
await Task.Yield();
|
|
}
|
|
|
|
state = ProgressIndicatorState.Open;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task CloseAsync()
|
|
{
|
|
if (closeCurve.length == 0)
|
|
{
|
|
Debug.LogWarning("Open curve length is zero - this may result in an infinite loop.");
|
|
}
|
|
|
|
float minScale = closeCurve.Evaluate(Mathf.Infinity);
|
|
if (minScale > 0)
|
|
{
|
|
Debug.LogWarning("Open curve value never reaches 0 - this may result in an infinite loop.");
|
|
}
|
|
|
|
if (state != ProgressIndicatorState.Open)
|
|
{
|
|
throw new System.Exception("Can't close in state " + state);
|
|
}
|
|
|
|
state = ProgressIndicatorState.Closing;
|
|
|
|
float startTime = Time.unscaledTime;
|
|
float closeScale = 1f;
|
|
while (closeScale > 0 && isActiveAndEnabled)
|
|
{
|
|
closeScale = closeCurve.Evaluate(Time.unscaledTime - startTime);
|
|
scaleTargetObject.transform.localScale = Vector3.one * currentScale * closeScale;
|
|
await Task.Yield();
|
|
}
|
|
|
|
state = ProgressIndicatorState.Closed;
|
|
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void CloseImmediate()
|
|
{
|
|
if (closeCurve.length == 0)
|
|
{
|
|
Debug.LogWarning("Open curve length is zero - this may result in an infinite loop.");
|
|
}
|
|
|
|
float minScale = closeCurve.Evaluate(Mathf.Infinity);
|
|
if (minScale > 0)
|
|
{
|
|
Debug.LogWarning("Open curve value never reaches 0 - this may result in an infinite loop.");
|
|
}
|
|
|
|
if (state != ProgressIndicatorState.Open)
|
|
{
|
|
throw new System.Exception("Can't close in state " + state);
|
|
}
|
|
|
|
scaleTargetObject.transform.localScale = Vector3.zero;
|
|
state = ProgressIndicatorState.Closed;
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task AwaitTransitionAsync()
|
|
{
|
|
while (isActiveAndEnabled)
|
|
{
|
|
switch (state)
|
|
{
|
|
case ProgressIndicatorState.Open:
|
|
case ProgressIndicatorState.Closed:
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
await Task.Yield();
|
|
}
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
currentScale = minScale;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (state == ProgressIndicatorState.Open)
|
|
{
|
|
// Only scale while we're not opening or closing
|
|
currentScale = Mathf.Lerp(minScale, maxScale, progress);
|
|
scaleTargetObject.localScale = Vector3.one * currentScale;
|
|
}
|
|
|
|
if (rotationActive)
|
|
{
|
|
float increment = Time.unscaledDeltaTime * rotationIncrement;
|
|
float xRotation = xAxisRotation ? increment : 0;
|
|
float yRotation = yAxisRotation ? increment : 0;
|
|
float zRotation = zAxisRotation ? increment : 0;
|
|
rotateTargetObject.Rotate(xRotation, yRotation, zRotation);
|
|
}
|
|
}
|
|
}
|
|
}
|