Skip to content

Instantly share code, notes, and snippets.

@elringus
Created October 18, 2018 22:38
Show Gist options
  • Save elringus/58fb1395b3158d4975c5330765ee78a2 to your computer and use it in GitHub Desktop.
Save elringus/58fb1395b3158d4975c5330765ee78a2 to your computer and use it in GitHub Desktop.
#if LIVE2D_AVAILABLE
using Live2D.Cubism.Core;
using Live2D.Cubism.Framework;
using System.Collections.Generic;
using System.Linq;
using UnityCommon;
using UnityEngine;
namespace Naninovel
{
[RequireComponent(typeof(CubismModel))]
public class Live2DAppearanceController : MonoBehaviour
{
[System.Serializable]
public class ParametersMap : SerializableMap<string, float> { }
[System.Serializable]
public class AppearanceData
{
[Tooltip("Appearance to bind parameters for.")]
public string Appearance = string.Empty;
[Tooltip("List of parameters' name and value to bind.")]
public ParametersMap Params = default;
}
private struct ModifyRequest
{
public float SmoothedValue => Mathf.SmoothStep(startValue, targetValue, (Time.time - startTime) / normalizedDuration);
private readonly float targetValue;
private readonly float startValue;
private readonly float startTime;
private readonly float normalizedDuration;
public ModifyRequest (CubismParameter param, float targetValue, float duration)
{
this.targetValue = targetValue;
startValue = param.Value;
startTime = Time.time;
// In case we're starting the animation when the param is not at default value: reduce the duration accordingly.
var curValueProgress = 1f - ((targetValue - param.Value) / (targetValue - param.DefaultValue));
var curTimeProgress = InversedSmoothStep(curValueProgress);
normalizedDuration = duration * (1f - curTimeProgress);
print(normalizedDuration);
}
private static float InversedSmoothStep (float v) => v + (v - (v * v * (3f - 2f * v)));
}
[SerializeField] private List<AppearanceData> appearanceMap = default;
[SerializeField] private CubismParameterBlendMode blendMode = CubismParameterBlendMode.Override;
[SerializeField] private string headXAngleParamId = "PARAM_ANGLE_X";
private const float headXAngleDelta = 30f;
private static CubismParameter headXParam;
private CubismModel cubismModel;
private Dictionary<string, ModifyRequest> appearanceRequests = new Dictionary<string, ModifyRequest>();
private ModifyRequest lookDirRequest;
public void SetAppearance (string appearance, float duration)
{
appearanceRequests.Clear();
var appearanceData = appearanceMap?.FirstOrDefault(a => a.Appearance == appearance);
if (appearanceData is null) return;
foreach (var kv in appearanceData.Params)
appearanceRequests[kv.Key] = new ModifyRequest(cubismModel.Parameters.FindById(kv.Key), kv.Value, duration);
}
public void SetLookDirection (CharacterLookDirection lookDirection, float duration)
{
if (headXParam is null) return;
var targetValue = default(float);
switch (lookDirection)
{
case CharacterLookDirection.Center:
targetValue = 0f;
break;
case CharacterLookDirection.Left:
targetValue = -headXAngleDelta;
break;
case CharacterLookDirection.Right:
targetValue = headXAngleDelta;
break;
}
lookDirRequest = new ModifyRequest(headXParam, targetValue, duration);
}
private void Start ()
{
cubismModel = this.FindCubismModel();
headXParam = cubismModel.Parameters.FindById(headXAngleParamId);
}
private void LateUpdate ()
{
for (int i = 0; i < cubismModel.Parameters.Length; i++)
{
var param = cubismModel.Parameters[i];
var value = !appearanceRequests.ContainsKey(param.Id) ? param.DefaultValue : appearanceRequests[param.Id].SmoothedValue;
if (Mathf.Approximately(param.Value, value)) continue;
param.BlendToValue(blendMode, value);
}
if (headXParam != null) headXParam.BlendToValue(blendMode, lookDirRequest.SmoothedValue);
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment