Files
StoryGame_Unity/Assets/Live2D/Cubism/Framework/Json/CubismModel3Json.cs

1052 lines
34 KiB
C#

/**
* 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 System;
using System.IO;
using Live2D.Cubism.Framework.MouthMovement;
using Live2D.Cubism.Framework.Physics;
using Live2D.Cubism.Framework.UserData;
using Live2D.Cubism.Framework.Pose;
using Live2D.Cubism.Framework.Expression;
using Live2D.Cubism.Framework.MotionFade;
using Live2D.Cubism.Framework.Raycasting;
using Live2D.Cubism.Rendering;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace Live2D.Cubism.Framework.Json
{
/// <summary>
/// Exposes moc3.json asset data.
/// </summary>
[Serializable]
// ReSharper disable once ClassCannotBeInstantiated
public sealed class CubismModel3Json
{
public static readonly string ModelCanvasName = "ModelCanvas";
#region Delegates
/// <summary>
/// Handles the loading of assets.
/// </summary>
/// <param name="assetType">The asset type to load.</param>
/// <param name="assetPath">The path to the asset.</param>
/// <returns></returns>
public delegate object LoadAssetAtPathHandler(Type assetType, string assetPath);
/// <summary>
/// Picks a <see cref="Material"/> for a <see cref="CubismDrawable"/>.
/// </summary>
/// <param name="sender">Event source.</param>
/// <param name="drawable">Drawable to pick for.</param>
/// <returns>Picked material.</returns>
public delegate Material DrawableMaterialPicker(CubismModel3Json sender, CubismDrawable drawable);
/// <summary>
/// Picks a <see cref="Texture2D"/> for a <see cref="CubismDrawable"/>.
/// </summary>
/// <param name="sender">Event source.</param>
/// <param name="drawable">Drawable to pick for.</param>
/// <returns>Picked texture.</returns>
public delegate Texture2D TexturePicker(CubismModel3Json sender, CubismDrawable drawable);
/// <summary>
/// Picks a <see cref="Material"/> for a <see cref="CubismOffscreen"/>.
/// </summary>
/// <param name="sender">Event source.</param>
/// <param name="offscreen">Offscreen to pick for.</param>
/// <returns></returns>
public delegate Material OffscreenMaterialPicker(CubismModel3Json sender, CubismOffscreen offscreen);
#endregion
#region Load Methods
/// <summary>
/// Loads a model.json asset.
/// </summary>
/// <param name="assetPath">The path to the asset.</param>
/// <returns>The <see cref="CubismModel3Json"/> on success; <see langword="null"/> otherwise.</returns>
public static CubismModel3Json LoadAtPath(string assetPath)
{
// Use default asset load handler.
return LoadAtPath(assetPath, BuiltinLoadAssetAtPath);
}
/// <summary>
/// Loads a model.json asset.
/// </summary>
/// <param name="assetPath">The path to the asset.</param>
/// <param name="loadAssetAtPath">Handler for loading assets.</param>
/// <returns>The <see cref="CubismModel3Json"/> on success; <see langword="null"/> otherwise.</returns>
public static CubismModel3Json LoadAtPath(string assetPath, LoadAssetAtPathHandler loadAssetAtPath)
{
// Load Json asset.
var modelJsonAsset = loadAssetAtPath(typeof(string), assetPath) as string;
// Return early in case Json asset wasn't loaded.
if (modelJsonAsset == null)
{
return null;
}
// Deserialize Json.
var modelJson = JsonUtility.FromJson<CubismModel3Json>(modelJsonAsset);
// Finalize deserialization.
modelJson.AssetPath = assetPath;
modelJson.LoadAssetAtPath = loadAssetAtPath;
// Set motion references.
var value = CubismJsonParser.ParseFromString(modelJsonAsset);
// Return early if there is no references.
if (!value.Get("FileReferences").GetMap(null).ContainsKey("Motions"))
{
return modelJson;
}
var motionGroupNames = value.Get("FileReferences").Get("Motions").KeySet().ToArray();
modelJson.FileReferences.Motions.GroupNames = motionGroupNames;
var motionGroupNamesCount = motionGroupNames.Length;
modelJson.FileReferences.Motions.Motions = new SerializableMotion[motionGroupNamesCount][];
for (var i = 0; i < motionGroupNamesCount; i++)
{
var motionGroup = value.Get("FileReferences").Get("Motions").Get(motionGroupNames[i]);
var motionCount = motionGroup.GetVector(null).ToArray().Length;
modelJson.FileReferences.Motions.Motions[i] = new SerializableMotion[motionCount];
var fadeInTime = -1.0f;
var fadeOutTime = -1.0f;
for (var j = 0; j < motionCount; j++)
{
// Reset fade time cache.
fadeInTime = -1.0f;
fadeOutTime = -1.0f;
if (motionGroup.Get(j).GetMap(null).ContainsKey("File"))
{
modelJson.FileReferences.Motions.Motions[i][j].File = motionGroup.Get(j).Get("File").toString();
}
if (motionGroup.Get(j).GetMap(null).ContainsKey("Sound"))
{
modelJson.FileReferences.Motions.Motions[i][j].Sound = motionGroup.Get(j).Get("Sound").toString();
}
if (motionGroup.Get(j).GetMap(null).ContainsKey("FadeInTime"))
{
fadeInTime = motionGroup.Get(j).Get("FadeInTime").ToFloat();
}
modelJson.FileReferences.Motions.Motions[i][j].FadeInTime = fadeInTime;
if (motionGroup.Get(j).GetMap(null).ContainsKey("FadeOutTime"))
{
fadeOutTime = motionGroup.Get(j).Get("FadeOutTime").ToFloat();
}
modelJson.FileReferences.Motions.Motions[i][j].FadeOutTime = fadeOutTime;
}
}
return modelJson;
}
#endregion
/// <summary>
/// Path to <see langword="this"/>.
/// </summary>
public string AssetPath { get; private set; }
/// <summary>
/// Method for loading assets.
/// </summary>
private LoadAssetAtPathHandler LoadAssetAtPath { get; set; }
#region Json Data
/// <summary>
/// The motion3.json format version.
/// </summary>
[SerializeField]
public int Version;
/// <summary>
/// The file references.
/// </summary>
[SerializeField]
public SerializableFileReferences FileReferences;
/// <summary>
/// Groups.
/// </summary>
[SerializeField]
public SerializableGroup[] Groups;
/// <summary>
/// Hit areas.
/// </summary>
[SerializeField]
public SerializableHitArea[] HitAreas;
#endregion
/// <summary>
/// The contents of the referenced moc3 asset.
/// </summary>
/// <remarks>
/// The contents isn't cached internally.
/// </remarks>
public byte[] Moc3
{
get
{
return LoadReferencedAsset<byte[]>(FileReferences.Moc);
}
}
/// <summary>
/// <see cref="CubismPose3Json"/> backing field.
/// </summary>
[NonSerialized]
private CubismPose3Json _pose3Json;
/// <summary>
/// The contents of pose3.json asset.
/// </summary>
public CubismPose3Json Pose3Json
{
get
{
if(_pose3Json != null)
{
return _pose3Json;
}
var jsonString = string.IsNullOrEmpty(FileReferences.Pose) ? null : LoadReferencedAsset<String>(FileReferences.Pose);
_pose3Json = CubismPose3Json.LoadFrom(jsonString);
return _pose3Json;
}
}
/// <summary>
/// <see cref="Expression3Jsons"/> backing field.
/// </summary>
[NonSerialized]
private CubismExp3Json[] _expression3Jsons;
/// <summary>
/// The referenced expression assets.
/// </summary>
/// <remarks>
/// The references aren't cached internally.
/// </remarks>
public CubismExp3Json[] Expression3Jsons
{
get
{
// Fail silently...
if(FileReferences.Expressions == null)
{
return null;
}
// Load expression only if necessary.
if (_expression3Jsons == null)
{
_expression3Jsons = new CubismExp3Json[FileReferences.Expressions.Length];
for (var i = 0; i < _expression3Jsons.Length; ++i)
{
var expressionJson = (string.IsNullOrEmpty(FileReferences.Expressions[i].File))
? null
: LoadReferencedAsset<string>(FileReferences.Expressions[i].File);
_expression3Jsons[i] = CubismExp3Json.LoadFrom(expressionJson);
}
}
return _expression3Jsons;
}
}
/// <summary>
/// The contents of physics3.json asset.
/// </summary>
public string Physics3Json
{
get
{
return string.IsNullOrEmpty(FileReferences.Physics) ? null : LoadReferencedAsset<string>(FileReferences.Physics);
}
}
public string UserData3Json
{
get
{
return string.IsNullOrEmpty(FileReferences.UserData) ? null : LoadReferencedAsset<string>(FileReferences.UserData);
}
}
/// <summary>
/// The contents of cdi3.json asset.
/// </summary>
public string DisplayInfo3Json
{
get
{
return string.IsNullOrEmpty(FileReferences.DisplayInfo) ? null : LoadReferencedAsset<string>(FileReferences.DisplayInfo);
}
}
/// <summary>
/// <see cref="Textures"/> backing field.
/// </summary>
[NonSerialized]
private Texture2D[] _textures;
/// <summary>
/// The referenced texture assets.
/// </summary>
/// <remarks>
/// The references aren't cached internally.
/// </remarks>
public Texture2D[] Textures
{
get
{
// Load textures only if necessary.
if (_textures == null)
{
_textures = new Texture2D[FileReferences.Textures.Length];
for (var i = 0; i < _textures.Length; ++i)
{
_textures[i] = LoadReferencedAsset<Texture2D>(FileReferences.Textures[i]);
}
}
return _textures;
}
}
#region Constructors
/// <summary>
/// Makes construction only possible through factories.
/// </summary>
private CubismModel3Json()
{
}
#endregion
/// <summary>
/// Instantiates a <see cref="CubismMoc">model source</see> and a <see cref="CubismModel">model</see> with the default texture set.
/// </summary>
/// <param name="shouldImportAsOriginalWorkflow">Should import as original workflow.</param>
/// <returns>The instantiated <see cref="CubismModel">model</see> on success; <see langword="null"/> otherwise.</returns>
public CubismModel ToModel(bool shouldImportAsOriginalWorkflow = false)
{
return ToModel(CubismBuiltinPickers.DrawableMaterialPicker, CubismBuiltinPickers.TexturePicker, CubismBuiltinPickers.OffscreenMaterialPicker, shouldImportAsOriginalWorkflow);
}
/// <summary>
/// Instantiates a <see cref="CubismMoc">model source</see> and a <see cref="CubismModel">model</see>.
/// </summary>
/// <param name="pickDrawableMaterial">The material mapper to use.</param>
/// <param name="pickTexture">The texture mapper to use.</param>
/// <param name="shouldImportAsOriginalWorkflow">Should import as original workflow.</param>
/// <returns>The instantiated <see cref="CubismModel">model</see> on success; <see langword="null"/> otherwise.</returns>
public CubismModel ToModel(DrawableMaterialPicker pickDrawableMaterial, TexturePicker pickTexture, OffscreenMaterialPicker pickOffscreenMaterial, bool shouldImportAsOriginalWorkflow = false)
{
// Initialize model source and instantiate it.
var mocAsBytes = Moc3;
if (mocAsBytes == null)
{
return null;
}
var moc = CubismMoc.CreateFrom(mocAsBytes);
var model = CubismModel.InstantiateFrom(moc);
if (model == null)
{
return null;
}
model.name = Path.GetFileNameWithoutExtension(FileReferences.Moc);
#if UNITY_EDITOR
// Add parameters and parts inspectors.
model.gameObject.AddComponent<CubismParametersInspector>().hideFlags = HideFlags.DontSaveInBuild;
model.gameObject.AddComponent<CubismPartsInspector>().hideFlags = HideFlags.DontSaveInBuild;
#endif
// Create renderers.
var rendererController = model.gameObject.AddComponent<CubismRenderController>();
var renderers = rendererController.Renderers;
var drawables = model.Drawables;
var offscreens = model.Offscreens;
var drawableRenderers = rendererController.DrawableRenderers;
var offscreenRenderers = rendererController.OffscreenRenderers;
if (renderers == null
|| drawables == null
|| drawableRenderers == null)
{
return null;
}
// Initialize materials.
for (var i = 0; i < drawableRenderers.Length; ++i)
{
var renderer = drawableRenderers[i];
renderer.Material = pickDrawableMaterial(this, drawables[i]);
renderer.ColorBlendType = drawables[i].ColorBlend;
renderer.AlphaBlendType = drawables[i].AlphaBlend;
}
for (var i = 0; i < offscreenRenderers?.Length; i++)
{
var renderer = offscreenRenderers[i];
renderer.Material = pickOffscreenMaterial(this, offscreens[i]);
renderer.ColorBlendType = offscreens[i].ColorBlend;
renderer.AlphaBlendType = offscreens[i].AlphaBlend;
}
// Initialize textures.
for (var i = 0; i < renderers.Length; ++i)
{
if (renderers[i].DrawObjectType != CubismModelTypes.DrawObjectType.Drawable)
{
continue;
}
renderers[i].MainTexture = pickTexture(this, drawables[i]);
}
if (model.Parts != null)
{
var parts = model.Parts;
// Create and initialize partColorsEditors.
for (int i = 0; i < parts.Length; i++)
{
var partColorsEditor = parts[i].gameObject.AddComponent<CubismPartColorsEditor>();
partColorsEditor.TryInitialize(model);
}
}
// Initialize drawables.
if (HitAreas != null)
{
for (var i = 0; i < HitAreas.Length; i++)
{
for (var j = 0; j < drawables.Length; j++)
{
if (drawables[j].Id == HitAreas[i].Id)
{
// Add components for hit judgement to HitArea target Drawables.
var hitDrawable = drawables[j].gameObject.AddComponent<CubismHitDrawable>();
hitDrawable.Name = HitAreas[i].Name;
drawables[j].gameObject.AddComponent<CubismRaycastable>();
break;
}
}
}
}
//Load from cdi3.json
var DisplayInfo3JsonAsString = DisplayInfo3Json;
var cdi3Json = CubismDisplayInfo3Json.LoadFrom(DisplayInfo3JsonAsString);
// Initialize groups.
var parameters = model.Parameters;
for (var i = 0; i < parameters.Length; ++i)
{
if (IsParameterInGroup(parameters[i], "EyeBlink"))
{
if (model.gameObject.GetComponent<CubismEyeBlinkController>() == null)
{
model.gameObject.AddComponent<CubismEyeBlinkController>();
}
parameters[i].gameObject.AddComponent<CubismEyeBlinkParameter>();
}
// Set up mouth parameters.
if (IsParameterInGroup(parameters[i], "LipSync"))
{
if (model.gameObject.GetComponent<CubismMouthController>() == null)
{
model.gameObject.AddComponent<CubismMouthController>();
}
parameters[i].gameObject.AddComponent<CubismMouthParameter>();
}
// Setting up the parameter name for display.
if (cdi3Json != null)
{
var cubismDisplayInfoParameterName = parameters[i].gameObject.AddComponent<CubismDisplayInfoParameterName>();
cubismDisplayInfoParameterName.Name = parameters[i].Id;
for (int j = 0; j < cdi3Json.Parameters.Length; j++)
{
if (cdi3Json.Parameters[j].Id == parameters[i].Id)
{
cubismDisplayInfoParameterName.Name = cdi3Json.Parameters[j].Name;
break;
}
}
cubismDisplayInfoParameterName.DisplayName = string.Empty;
}
}
if (cdi3Json != null)
{
// Setting up the part name for display.
// Initialize groups.
var parts = model.Parts;
for (var i = 0; i < parts.Length; i++)
{
var cubismDisplayInfoPartNames = parts[i].gameObject.AddComponent<CubismDisplayInfoPartName>();
cubismDisplayInfoPartNames.Name = parts[i].Id;
for (int j = 0; j < cdi3Json.Parts.Length; j++)
{
if (cdi3Json.Parts[j].Id == parts[i].Id)
{
cubismDisplayInfoPartNames.Name = cdi3Json.Parts[j].Name;
break;
}
}
cubismDisplayInfoPartNames.DisplayName = string.Empty;
}
// Get combined parameter information
var combinedParameters = cdi3Json.CombinedParameters;
if (combinedParameters != null)
{
// Parameters are always combined in pairs of two.
const int combinedParameterCount = 2;
// Set up CubismDisplayInfoCombinedParameterInfo component.
var combinedParameterInfo = model.gameObject.AddComponent<CubismDisplayInfoCombinedParameterInfo>();
combinedParameterInfo.CombinedParameters = new CubismDisplayInfo3Json.CombinedParameter[combinedParameters.Length];
for (var index = 0; index < combinedParameters.Length; index++)
{
// Skip if the combined parameter is invalid.
if (combinedParameters[index].Ids == null || combinedParameters[index].Ids.Length != combinedParameterCount)
{
Debug.LogWarning($"The data contains invalid CombinedParameters in {model.Moc.name}.cdi3.json.");
continue;
}
var combinedParameterIds = combinedParameters[index].Ids;
// Set CombinedParameter.
combinedParameterInfo.CombinedParameters[index] = new CubismDisplayInfo3Json.CombinedParameter
{
HorizontalParameterId = combinedParameterIds[0],
VerticalParameterId = combinedParameterIds[1]
};
}
}
}
// Add mask controller if required.
var anyMasked = false;
for (var i = 0; i < renderers.Length; i++)
{
var renderer = renderers[i];
switch (renderer.DrawObjectType)
{
case CubismModelTypes.DrawObjectType.Drawable:
if (renderer.Drawable.IsMasked)
{
anyMasked = true;
}
break;
case CubismModelTypes.DrawObjectType.Offscreen:
if (renderer.Offscreen.IsMasked)
{
anyMasked = true;
}
break;
default:
Debug.LogWarning($"Unknown draw object type {renderer.DrawObjectType} in {model.name}.model3.json.");
break;
}
if (anyMasked)
{
rendererController.HasMask = true;
rendererController.TryInitialize();
// Already have a mask controller, no need to add it again.
break;
}
}
// Add original workflow component if is original workflow.
if(shouldImportAsOriginalWorkflow)
{
// Add cubism update manager.
var updateManager = model.gameObject.GetComponent<CubismUpdateController>();
if(updateManager == null)
{
model.gameObject.AddComponent<CubismUpdateController>();
}
// Add parameter store.
var parameterStore = model.gameObject.GetComponent<CubismParameterStore>();
if(parameterStore == null)
{
parameterStore = model.gameObject.AddComponent<CubismParameterStore>();
}
// Add pose controller.
var poseController = model.gameObject.GetComponent<CubismPoseController>();
if(poseController == null)
{
poseController = model.gameObject.AddComponent<CubismPoseController>();
}
// Add expression controller.
var expressionController = model.gameObject.GetComponent<CubismExpressionController>();
if(expressionController == null)
{
expressionController = model.gameObject.AddComponent<CubismExpressionController>();
}
// Add fade controller.
var motionFadeController = model.gameObject.GetComponent<CubismFadeController>();
if(motionFadeController == null)
{
motionFadeController = model.gameObject.AddComponent<CubismFadeController>();
}
}
// Initialize physics if JSON exists.
var physics3JsonAsString = Physics3Json;
if (!string.IsNullOrEmpty(physics3JsonAsString))
{
var physics3Json = CubismPhysics3Json.LoadFrom(physics3JsonAsString);
var physicsController = model.gameObject.GetComponent<CubismPhysicsController>();
if (physicsController == null)
{
physicsController = model.gameObject.AddComponent<CubismPhysicsController>();
}
physicsController.Initialize(physics3Json.ToRig());
}
var userData3JsonAsString = UserData3Json;
if (!string.IsNullOrEmpty(userData3JsonAsString))
{
var userData3Json = CubismUserData3Json.LoadFrom(userData3JsonAsString);
var drawableBodies = userData3Json.ToBodyArray(CubismUserDataTargetType.ArtMesh);
for (var i = 0; i < drawables.Length; ++i)
{
var index = GetBodyIndexById(drawableBodies, drawables[i].Id);
if (index >= 0)
{
var tag = drawables[i].gameObject.GetComponent<CubismUserDataTag>();
if (tag == null)
{
tag = drawables[i].gameObject.AddComponent<CubismUserDataTag>();
}
tag.Initialize(drawableBodies[index]);
}
}
}
if (model.gameObject.GetComponent<Animator>() == null)
{
model.gameObject.AddComponent<Animator>();
}
// Make sure model is 'fresh'
model.ForceUpdateNow();
return model;
}
#region Helper Methods
/// <summary>
/// Type-safely loads an asset.
/// </summary>
/// <typeparam name="T">Asset type.</typeparam>
/// <param name="referencedFile">Path to asset.</param>
/// <returns>The asset on success; <see langword="null"/> otherwise.</returns>
private T LoadReferencedAsset<T>(string referencedFile) where T : class
{
var assetPath = Path.GetDirectoryName(AssetPath) + "/" + referencedFile;
return LoadAssetAtPath(typeof(T), assetPath) as T;
}
/// <summary>
/// Builtin method for loading assets.
/// </summary>
/// <param name="assetType">Asset type.</param>
/// <param name="assetPath">Path to asset.</param>
/// <returns>The asset on success; <see langword="null"/> otherwise.</returns>
private static object BuiltinLoadAssetAtPath(Type assetType, string assetPath)
{
// Explicitly deal with byte arrays.
if (assetType == typeof(byte[]))
{
#if UNITY_EDITOR
return File.ReadAllBytes(assetPath);
#else
var textAsset = Resources.Load(assetPath, typeof(TextAsset)) as TextAsset;
return (textAsset != null)
? textAsset.bytes
: null;
#endif
}
else if (assetType == typeof(string))
{
#if UNITY_EDITOR
return File.ReadAllText(assetPath);
#else
var textAsset = Resources.Load(assetPath, typeof(TextAsset)) as TextAsset;
return (textAsset != null)
? textAsset.text
: null;
#endif
}
#if UNITY_EDITOR
return AssetDatabase.LoadAssetAtPath(assetPath, assetType);
#else
return Resources.Load(assetPath, assetType);
#endif
}
/// <summary>
/// Checks whether the parameter is an eye blink parameter.
/// </summary>
/// <param name="parameter">Parameter to check.</param>
/// <param name="groupName">Name of group to query for.</param>
/// <returns><see langword="true"/> if parameter is an eye blink parameter; <see langword="false"/> otherwise.</returns>
private bool IsParameterInGroup(CubismParameter parameter, string groupName)
{
// Return early if groups aren't available...
if (Groups == null || Groups.Length == 0)
{
return false;
}
for (var i = 0; i < Groups.Length; ++i)
{
if (Groups[i].Name != groupName)
{
continue;
}
if(Groups[i].Ids != null)
{
for (var j = 0; j < Groups[i].Ids.Length; ++j)
{
if (Groups[i].Ids[j] == parameter.name)
{
return true;
}
}
}
}
return false;
}
/// <summary>
/// Get body index from body array by Id.
/// </summary>
/// <param name="bodies">Target body array.</param>
/// <param name="id">Id for find.</param>
/// <returns>Array index if Id found; -1 otherwise.</returns>
private int GetBodyIndexById(CubismUserDataBody[] bodies, string id)
{
for (var i = 0; i < bodies.Length; ++i)
{
if (bodies[i].Id == id)
{
return i;
}
}
return -1;
}
#endregion
#region Json Helpers
/// <summary>
/// File references data.
/// </summary>
[Serializable]
public struct SerializableFileReferences
{
/// <summary>
/// Relative path to the moc3 asset.
/// </summary>
[SerializeField]
public string Moc;
/// <summary>
/// Relative paths to texture assets.
/// </summary>
[SerializeField]
public string[] Textures;
/// <summary>
/// Relative path to the pose3.json.
/// </summary>
[SerializeField]
public string Pose;
/// <summary>
/// Relative path to the expression asset.
/// </summary>
[SerializeField]
public SerializableExpression[] Expressions;
/// <summary>
/// Relative path to the pose motion3.json.
/// </summary>
[SerializeField]
public SerializableMotions Motions;
/// <summary>
/// Relative path to the physics asset.
/// </summary>
[SerializeField]
public string Physics;
/// <summary>
/// Relative path to the user data asset.
/// </summary>
[SerializeField]
public string UserData;
/// <summary>
/// Relative path to the cdi3.json.
/// </summary>
[SerializeField]
public string DisplayInfo;
}
/// <summary>
/// Group data.
/// </summary>
[Serializable]
public struct SerializableGroup
{
/// <summary>
/// Target type.
/// </summary>
[SerializeField]
public string Target;
/// <summary>
/// Group name.
/// </summary>
[SerializeField]
public string Name;
/// <summary>
/// Referenced IDs.
/// </summary>
[SerializeField]
public string[] Ids;
}
/// <summary>
/// Expression data.
/// </summary>
[Serializable]
public struct SerializableExpression
{
/// <summary>
/// Expression Name.
/// </summary>
[SerializeField]
public string Name;
/// <summary>
/// Expression File.
/// </summary>
[SerializeField]
public string File;
/// <summary>
/// Expression FadeInTime.
/// </summary>
[SerializeField]
public float FadeInTime;
/// <summary>
/// Expression FadeOutTime.
/// </summary>
[SerializeField]
public float FadeOutTime;
}
/// <summary>
/// Motion data.
/// </summary>
[Serializable]
public struct SerializableMotions
{
/// <summary>
/// Motion group names.
/// </summary>
[SerializeField]
public string[] GroupNames;
/// <summary>
/// Motion groups.
/// </summary>
[SerializeField]
public SerializableMotion[][] Motions;
}
/// <summary>
/// Motion data.
/// </summary>
[Serializable]
public struct SerializableMotion
{
/// <summary>
/// File path.
/// </summary>
[SerializeField]
public string File;
/// <summary>
/// Sound path.
/// </summary>
[SerializeField]
public string Sound;
/// <summary>
/// Fade in time.
/// </summary>
[SerializeField]
public float FadeInTime;
/// <summary>
/// Fade out time.
/// </summary>
[SerializeField]
public float FadeOutTime;
}
/// <summary>
/// Hit Area.
/// </summary>
[Serializable]
public struct SerializableHitArea
{
/// <summary>
/// Hit area name.
/// </summary>
[SerializeField]
public string Name;
/// <summary>
/// Hit area id.
/// </summary>
[SerializeField]
public string Id;
}
#endregion
}
}