// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using UnityEngine; namespace Microsoft.MixedReality.Toolkit.UI { /// /// Controls how the standard indeterminate loader moves and behaves over time. /// /// /// This loader is calculated dynamically based on Sine and Cosine /// [AddComponentMenu("Scripts/MRTK/SDK/LoaderController")] public class LoaderController : MonoBehaviour { /// /// Total strength of movement of the loading animation /// [SerializeField] [Tooltip("Total strength of movement of the loading animation")] private float amplitude = 0.05f; public float Amplitude { get => amplitude; set => amplitude = value; } /// /// How often happens /// [SerializeField] [Tooltip("How often 'amplitude' happens")] private float frequency = 3.0f; public float Frequency { get => frequency; set => frequency = value; } /// /// The base local position for the dots' parent /// [SerializeField] [Tooltip("The base local position for the dots' parent")] private float dotOffset = 0.05f; public float DotOffset { get => dotOffset; set => dotOffset = value; } /// /// The base scale unit for the dots' parent /// [SerializeField] [Tooltip("The base scale unit for the dots' parent")] private float dotSetScale = 0.06f; public float DotSetScale { get => dotSetScale; set => dotSetScale = value; } /// /// Use low frequency oscillation with the Sine calculation. /// [SerializeField] [Tooltip("Use low frequency oscillation with the Sine calculation.")] private bool lFOsin = false; public bool LFOsin { get => lFOsin; set => lFOsin = value; } /// /// Use low frequency oscillation with the Cosine calculation. /// [SerializeField] [Tooltip("Use low frequency oscillation with the Cosine calculation.")] private bool lFOcos = false; public bool LFOcos { get => lFOcos; set => lFOcos = value; } /// /// Low Frequency oscillation frequency /// [SerializeField] [Tooltip("Low frequency oscillation frequency")] private float lFOfreq = 1.0f; public float LFOfreq { get => lFOfreq; set => lFOfreq = value; } /// /// Low Frequency oscillation amplitude /// [SerializeField] [Tooltip("Low Frequency oscillation amplitude")] private float lFOamp = 0.1f; public float LFOamp { get => lFOamp; set => lFOamp = value; } /// /// Reverses dots' orbit rotation path /// [SerializeField] [Tooltip("Reverses dots' orbit rotation path")] private bool reverseOrbit = false; public bool ReverseOrbit { get => reverseOrbit; set => reverseOrbit = value; } /// /// Inverts dots' position in orbit /// [SerializeField] [Tooltip("Inverts dots' position in orbit")] private bool invertOrbitOffset = false; public bool InvertOrbitOffset { get => invertOrbitOffset; set => invertOrbitOffset = value; } /// /// Multiplier to dot's rotation calculation /// [SerializeField] [Tooltip("Multiplier to dot's rotation calculation")] private float dotSpinMultiplier = 0.3f; public float DotSpinMultiplier { get => dotSpinMultiplier; set => dotSpinMultiplier = value; } /// /// Multiplier to dot's scale calculation /// [SerializeField] [Tooltip("Multiplier to dot's scale calculation")] private float dotScaleMultipler = 0.0f; public float DotScaleMultipler { get => dotScaleMultipler; set => dotScaleMultipler = value; } /// /// When enabled, the dot scale uses Cosine to determine scale with including . /// Otherwise, it will use Sine. /// [SerializeField] [Tooltip("When enabled, the dot scale uses Cosine to determine scale with including 'dotSetScale'. Otherwise, it will use Sine.")] private bool sinCosSplitScale = false; public bool SinCosSplitScale { get => sinCosSplitScale; set => sinCosSplitScale = value; } /// /// Calculates the time cycle for the trigonometric functions /// private float Cycles { get { if (reverseOrbit) { return (frequency < 0.0f) ? (Time.time / 0.000001f) * -1.0f : (Time.time / frequency) * -1.0f; } else { return (frequency < 0.0f) ? Time.time / 0.000001f : Time.time / frequency; } } } private float degPerSec; private Vector3 parentNewPos = Vector3.zero; private Transform dot01; private Vector3 dot01NewPos = Vector3.zero; private Vector3 dot01NewScale = Vector3.zero; private Vector3 dot01NewRot = Vector3.zero; private Transform dot02; private Vector3 dot02NewPos = Vector3.zero; private Vector3 dot02NewScale = Vector3.zero; private Vector3 dot02NewRot = Vector3.zero; private const float tau = Mathf.PI * 2.0f; private void OnEnable() { if (dot01 == null) { dot01 = gameObject.transform.GetChild(0); } if (dot02 == null) { dot02 = gameObject.transform.GetChild(1); } } private void Update() { degPerSec = Time.deltaTime * 360f; AnimateParent(); AnimateDotTransforms(); } private void AnimateParent() { float cosX = Mathf.Cos(Cycles * tau) * amplitude; float sinY = Mathf.Sin(Cycles * tau) * amplitude; if (invertOrbitOffset == true) { cosX = -cosX; sinY = -sinY; } parentNewPos.Set(cosX, sinY, 0f); transform.localPosition = parentNewPos; if (lFOsin == true) { dotSpinMultiplier = Mathf.Sin(Time.time * lFOfreq) * lFOamp; } else if (lFOcos == true) { dotSpinMultiplier = Mathf.Cos(Time.time * lFOfreq) * lFOamp; } transform.Rotate(Vector3.forward * (degPerSec * dotSpinMultiplier)); } private void AnimateDotTransforms() { // SaveLocal dot groups' scale float sinScaleCalc = dotSetScale + Mathf.Sin(Cycles * tau / 2) * dotScaleMultipler; float cosScaleCalc = dotSetScale + Mathf.Cos(Cycles * tau / 2) * dotScaleMultipler; if (sinCosSplitScale == true) { dot01NewPos.Set(cosScaleCalc, cosScaleCalc, cosScaleCalc); } else { dot01NewPos.Set(sinScaleCalc, sinScaleCalc, sinScaleCalc); } dot01.localScale = dot01NewPos; dot02NewPos.Set(sinScaleCalc, sinScaleCalc, sinScaleCalc); dot02.localScale = dot02NewPos; // SaveLocal dot groups' position Offset from Parent-Null Center dot01NewPos.Set(dotOffset, dotOffset, 0f); dot02NewPos.Set(-dotOffset, -dotOffset, 0f); dot01.transform.localPosition = dot01NewPos; dot02.transform.localPosition = dot02NewPos; } } }