mixedreality/com.microsoft.mixedreality..../SDK/Features/UX/Scripts/ProgressIndicators/ProgressIndicatorObjectDisp...

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);
}
}
}
}