/** * 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 UnityEngine; namespace Live2D.Cubism.Framework { /// /// Automatic mouth movement. /// public sealed class CubismAutoEyeBlinkInput : MonoBehaviour { /// /// Mean time between eye blinks in seconds. /// [SerializeField, Range(1f, 10f)] public float Mean = 2.5f; /// /// Maximum deviation from in seconds. /// [SerializeField, Range(0.5f, 5f)] public float MaximumDeviation = 2f; /// /// Timescale. /// [SerializeField, Range(1f, 20f)] public float Timescale = 10f; /// /// Target controller. /// private CubismEyeBlinkController Controller { get; set; } /// /// Control over whether output should be evaluated. /// private Phase CurrentPhase { get; set; } /// /// Used for switching from to and back to . /// private float LastValue { get; set; } /// /// Totalized delta time [s]. /// private float UserTimeSeconds { get; set; } /// /// Time when the current state started [sec]. /// private float StateStartTimeSeconds { get; set; } /// /// Duration of eyelid closing motion [sec] /// private float ClosingSeconds { get; set; } /// /// Duration of eyelid closed state [sec] /// private float ClosedSeconds { get; set; } /// /// Duration of eyelid opening motion [sec] /// private float OpeningSeconds { get; set; } /// /// Next blinking time. /// private float NextBlinkingTime { get; set; } /// /// Resets the input. /// public void Reset() { CurrentPhase = Phase.Idling; NextBlinkingTime = Mean + Random.Range(-MaximumDeviation, MaximumDeviation); } /// /// Calculate the value when the eyes are closed. /// /// Eye closing value. private float UpdateEyeBlinkClosing() { var t = (UserTimeSeconds - StateStartTimeSeconds) / ClosingSeconds; if (t >= 1.0f) { CurrentPhase = Phase.ClosedEyes; StateStartTimeSeconds = UserTimeSeconds; } var value = 1.0f - t; return value; } /// /// Calculate the value when the eyes are closed. /// /// Eye closed value. private float UpdateEyeBlinkClosed() { var t = (UserTimeSeconds - StateStartTimeSeconds) / ClosedSeconds; if (t >= 1.0f) { CurrentPhase = Phase.OpeningEyes; StateStartTimeSeconds = UserTimeSeconds; } var value = 0.0f; return value; } /// /// Calculate the value when the eyes are opening. /// /// Eye opening value. private float UpdateEyeBlinkOpening() { var t = (UserTimeSeconds - StateStartTimeSeconds) / OpeningSeconds; if (t >= 1.0f) { t = 1.0f; CurrentPhase = Phase.Idling; NextBlinkingTime = Mean + Random.Range(-MaximumDeviation, MaximumDeviation); } var value = t; return value; } /// /// Calculate the value when the eyes are opened. /// /// Eye opened value. private float UpdateEyeBlinkIdling() { NextBlinkingTime -= Time.deltaTime; if (NextBlinkingTime < 0.0f) { CurrentPhase = Phase.ClosingEyes; StateStartTimeSeconds = UserTimeSeconds; } var value = 1.0f; return value; } /// /// Update eye blink. /// private void UpdateEyeBlink() { UserTimeSeconds += (Time.deltaTime * Timescale); var value = 0.0f; switch (CurrentPhase) { case Phase.ClosingEyes: { value = UpdateEyeBlinkClosing(); break; } case Phase.ClosedEyes: { value = UpdateEyeBlinkClosed(); break; } case Phase.OpeningEyes: { value = UpdateEyeBlinkOpening(); break; } case Phase.Idling: { value = UpdateEyeBlinkIdling(); break; } } Controller.EyeOpening = value; } /// /// Set the details of the blinking motion. /// /// Duration of eyelid closing motion [sec]. /// Duration of eyelid closed state [sec]. /// Duration of eyelid opening motion [sec]. public void SetBlinkingSettings(float closing, float closed, float opening) { ClosingSeconds = closing; ClosedSeconds = closed; OpeningSeconds = opening; } #region Unity Event Handling /// /// Called by Unity. Initializes input. /// private void Start() { Controller = GetComponent(); CurrentPhase = Phase.Idling; NextBlinkingTime = Mean + Random.Range(-MaximumDeviation, MaximumDeviation); SetBlinkingSettings(1.0f, 0.5f, 1.5f); } /// /// Called by Unity. Updates controller. /// /// /// Make sure this method is called after any animations are evaluated. /// private void LateUpdate() { // Fail silently. if (Controller == null) { return; } UpdateEyeBlink(); } #endregion /// /// Internal states. /// private enum Phase { /// /// Idle state. /// Idling, /// /// State when closing eyes. /// ClosingEyes, /// /// State when closed eyes. /// ClosedEyes, /// /// State when opening eyes. /// OpeningEyes } } }