/**
* 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
}
}
}