/** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ using Live2D.Cubism.Core; using UnityEngine; namespace Live2D.Cubism.Framework.HarmonicMotion { /// /// Holds data for controlling the output of simple harmonic motions. /// /// /// This type of motion can be very useful for faking breathing, for example. /// public sealed class CubismHarmonicMotionParameter : MonoBehaviour { /// /// Timescale channel. /// [SerializeField] public int Channel; /// /// Motion direction. /// [SerializeField] public CubismHarmonicMotionDirection Direction; /// /// Normalized origin of motion. /// /// /// The actual origin used for evaluating the motion depends limits of the . /// [SerializeField, Range(0f, 1f)] public float NormalizedOrigin = 0.5f; /// /// Normalized range of motion. /// /// /// The actual origin used for evaluating the motion depends limits of the . /// [SerializeField, Range(0f, 1f)] public float NormalizedRange = 0.5f; /// /// Duration of one motion cycle in seconds. /// [SerializeField, Range(0.01f, 10f)] public float Duration = 3f; /// /// if is initialized. /// private bool IsInitialized { get { return Mathf.Abs(ValueRange) >= Mathf.Epsilon; } } /// /// Initializes instance. /// private void Initialize() { // Initialize value fields. var parameter = GetComponent(); MaximumValue = parameter.MaximumValue; MinimumValue = parameter.MinimumValue; ValueRange = MaximumValue - MinimumValue; } #region Interface for Controller /// /// Cached . /// private float MaximumValue { get; set; } /// /// Cached . /// private float MinimumValue { get; set; } /// /// Range of and . /// private float ValueRange { get; set; } /// /// Current time. /// private float T { get; set; } /// /// Proceeds time. /// /// internal void Play(float[] channelTimescales) { T += (Time.deltaTime * channelTimescales[Channel]); // Make sure time stays within duration. while (T > Duration) { T -= Duration; } } /// /// Evaluates the parameter. /// /// Parameter value. internal float Evaluate() { // Lazily initialize. if (!IsInitialized) { Initialize(); } // Restore origin and range. var origin = MinimumValue + (NormalizedOrigin * ValueRange); var range = NormalizedRange * ValueRange; // Clamp the range so that it stays within the limits. Clamp(ref origin, ref range); // Return result. return origin + (range * Mathf.Sin(T * (2 * Mathf.PI) / Duration)); } #endregion #region Helper Methods /// /// Clamp origin and range based on . /// /// Origin to clamp. /// Range to clamp. private void Clamp(ref float origin, ref float range) { switch (Direction) { case CubismHarmonicMotionDirection.Left: { if ((origin - range) >= MinimumValue) { range /= 2; origin -= range; } else { range = (origin - MinimumValue) / 2f; origin = MinimumValue + range; NormalizedRange = (range * 2f)/ValueRange; } break; } case CubismHarmonicMotionDirection.Right: { if ((origin + range) <= MaximumValue) { range /= 2f; origin += range; } else { range = (MaximumValue - origin) / 2f; origin = MaximumValue - range; NormalizedRange = (range * 2f)/ValueRange; } break; } default: { break; } } // Clamp both range and NormalizedRange. if ((origin - range) < MinimumValue) { range = origin - MinimumValue; NormalizedRange = range / ValueRange; } else if ((origin + range) > MaximumValue) { range = MaximumValue - origin; NormalizedRange = range / ValueRange; } } #endregion } }