Add Animator As Code

This commit is contained in:
Haï~
2022-02-27 20:16:34 +01:00
parent 16199f7614
commit 58c51926a7
47 changed files with 18297 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
.idea
Library
UnityPackageManager
Temp
Logs
Packages
ProjectSettings
*.unitypackage
Assets/Plugins
Assets/Scenes
Assets/SerializedUdonPrograms
Assets/Udon
Assets/VRChat Examples
Assets/VRCSDK
Assets/Plugins.meta
Assets/Scenes.meta
Assets/SerializedUdonPrograms.meta
Assets/Udon.meta
Assets/VRChat Examples.meta
Assets/VRCSDK.meta
obj
cfe-project.sln
UnityEditorTests.csproj
VRC.Udon.csproj
VRC.Udon.Editor.csproj
VRC.Udon.Serialization.OdinSerializer.csproj
VRCSDK.ShaderStripping.csproj
Assembly-CSharp.csproj
Assembly-CSharp-Editor.csproj
cfe-project.sln.DotSettings.user
VRC.SDKBase.Editor.BuildPipeline.csproj
VRC.SDKBase.Editor.ShaderStripping.csproj
LICENSE.meta
README.md.meta
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dd0da3b061b24fdcaa56edcfe0ed41ae
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+112
View File
@@ -0,0 +1,112 @@
using System;
using AnimatorAsCode.V0;
using UnityEditor;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using AnimatorController = UnityEditor.Animations.AnimatorController;
namespace AnimatorAsCodeFramework.Examples
{
public static class AacExample
{
public static TemplateGenOptions Options()
{
return TemplateGenOptions.Defaults();
}
public struct TemplateGenOptions
{
public static TemplateGenOptions Defaults()
{
return new TemplateGenOptions()
{
WriteDefaults = false
};
}
public TemplateGenOptions WriteDefaultsOff()
{
WriteDefaults = false;
return this;
}
public TemplateGenOptions WriteDefaultsOn()
{
WriteDefaults = true;
return this;
}
public bool WriteDefaults;
}
public static void InspectorTemplate(Editor editor, SerializedObject serializedObj, string propName, Action createFn, Action removeFnOptional = null)
{
var prop = serializedObj.FindProperty(propName);
if (prop.stringValue.Trim() == "")
{
prop.stringValue = GUID.Generate().ToString();
serializedObj.ApplyModifiedProperties();
}
editor.DrawDefaultInspector();
if (GUILayout.Button("Create"))
{
createFn.Invoke();
}
if (removeFnOptional != null && GUILayout.Button("Remove"))
{
removeFnOptional.Invoke();
}
}
/// <summary>
/// Creates an AAC base with default options (write defaults OFF). This function is provided as an example on how to invoke AAC internals.
/// </summary>
/// <param name="systemName">Prefix for layer names</param>
/// <param name="avatar">Playable layers of this avatar to modify</param>
/// <param name="assetContainer">Animation assets will be generated as sub-assets of that asset container</param>
/// <param name="assetKey">Animation assets will be generated with this name in order to clean up previously generated assets of the same system</param>
/// <param name="options">Some options, such as whether Write Defaults is ON or OFF</param>
/// <returns>The AAC base.</returns>
public static AacFlBase AnimatorAsCode(string systemName, VRCAvatarDescriptor avatar, AnimatorController assetContainer, string assetKey)
{
return AnimatorAsCode(systemName, avatar, assetContainer, assetKey, TemplateGenOptions.Defaults());
}
/// <summary>
/// Creates an AAC base. This function is provided as an example on how to invoke AAC internals.
/// </summary>
/// <param name="systemName">Prefix for layer names</param>
/// <param name="avatar">Playable layers of this avatar to modify</param>
/// <param name="assetContainer">Animation assets will be generated as sub-assets of that asset container</param>
/// <param name="assetKey">Animation assets will be generated with this name in order to clean up previously generated assets of the same system</param>
/// <param name="options">Some options, such as whether Write Defaults is ON or OFF</param>
/// <returns>The AAC base.</returns>
public static AacFlBase AnimatorAsCode(string systemName, VRCAvatarDescriptor avatar, AnimatorController assetContainer, string assetKey, TemplateGenOptions options)
{
var aac = AacV0.Create(new AacConfiguration
{
SystemName = systemName,
// In the examples, we consider the avatar to be also the animator root.
AvatarDescriptor = avatar,
// You can set the animator root to be different than the avatar descriptor,
// if you want to apply an animator to a different avatar without redefining
// all of the game object references which were relative to the original avatar.
AnimatorRoot = avatar.transform,
// DefaultValueRoot is currently unused in AAC. It is added here preemptively
// in order to define an avatar root to sample default values from.
// The intent is to allow animators to be created with Write Defaults OFF,
// but mimicking the behaviour of Write Defaults ON by automatically
// sampling the default value from the scene relative to the transform
// defined in DefaultValueRoot.
DefaultValueRoot = avatar.transform,
AssetContainer = assetContainer,
AssetKey = assetKey,
DefaultsProvider = new AacDefaultsProvider(writeDefaults: options.WriteDefaults)
});
aac.ClearPreviousAssets();
return aac;
}
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b6c6740568e64846b6b18720f7315ca4
timeCreated: 1645751901
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bbcd8ae9108f27f44aacfe48a78785d4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9100000
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: e5874b9d3baa9584c88aae7527592af0
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 1
seamlessCubemap: 1
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 2
aniso: 0
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 2
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 100
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3aab8cb479c1da147b4fec0a8fe34236
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9100000
userData:
assetBundleName:
assetBundleVariant:
+77
View File
@@ -0,0 +1,77 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AacExampleMaterial
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.698
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 0.48689637, g: 0.11609437, b: 0.037962936, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c3542dfecd687604fb8d0b0e6a5a6318
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
+56
View File
@@ -0,0 +1,56 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -340790334, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
m_Name: AacExampleMenu
m_EditorClassIdentifier:
controls:
- name: Item
icon: {fileID: 0}
type: 102
parameter:
name: EnableItem
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
- name: Accesories
icon: {fileID: 0}
type: 102
parameter:
name: EnableAccessories
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
- name: Thing
icon: {fileID: 0}
type: 102
parameter:
name: AccessoryThing
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
- name: Wedge Amount
icon: {fileID: 0}
type: 203
parameter:
name:
value: 1
style: 0
subMenu: {fileID: 0}
subParameters:
- name: WedgeAmount
labels: []
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cde459092e0783540b3837521d750413
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+31
View File
@@ -0,0 +1,31 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1506855854, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
m_Name: AacExampleParams
m_EditorClassIdentifier:
parameters:
- name: EnableItem
valueType: 2
saved: 1
defaultValue: 0
- name: EnableAccessories
valueType: 2
saved: 1
defaultValue: 0
- name: AccessoryThing
valueType: 2
saved: 1
defaultValue: 0
- name: WedgeAmount
valueType: 1
saved: 1
defaultValue: 0
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 599b4ef18dd7b2b41ae63050dc68cf49
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
+102
View File
@@ -0,0 +1,102 @@
fileFormatVersion: 2
guid: 63527d990aefbfd4889bdd854f8389e2
ModelImporter:
serializedVersion: 19301
internalIDToNameTable: []
externalObjects:
- first:
type: UnityEngine:Material
assembly: UnityEngine.CoreModule
name: Material.001
second: {fileID: 2100000, guid: c3542dfecd687604fb8d0b0e6a5a6318, type: 2}
materials:
materialImportMode: 1
materialName: 0
materialSearch: 1
materialLocation: 1
animations:
legacyGenerateAnimations: 4
bakeSimulation: 0
resampleCurves: 1
optimizeGameObjects: 0
motionNodeName:
rigImportErrors:
rigImportWarnings:
animationImportErrors:
animationImportWarnings:
animationRetargetingWarnings:
animationDoRetargetingWarnings: 0
importAnimatedCustomProperties: 0
importConstraints: 0
animationCompression: 1
animationRotationError: 0.5
animationPositionError: 0.5
animationScaleError: 0.5
animationWrapMode: 0
extraExposedTransformPaths: []
extraUserProperties: []
clipAnimations: []
isReadable: 0
meshes:
lODScreenPercentages: []
globalScale: 1
meshCompression: 0
addColliders: 0
useSRGBMaterialColor: 1
sortHierarchyByName: 1
importVisibility: 1
importBlendShapes: 1
importCameras: 1
importLights: 1
fileIdsGeneration: 2
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
keepQuads: 0
weldVertices: 1
preserveHierarchy: 0
skinWeightsMode: 0
maxBonesPerVertex: 4
minBoneWeight: 0.001
meshOptimizationFlags: -1
indexFormat: 0
secondaryUVAngleDistortion: 8
secondaryUVAreaDistortion: 15.000001
secondaryUVHardAngle: 88
secondaryUVPackMargin: 4
useFileScale: 1
tangentSpace:
normalSmoothAngle: 60
normalImportMode: 0
tangentImportMode: 3
normalCalculationMode: 4
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
blendShapeNormalImportMode: 1
normalSmoothingSource: 0
referencedClips: []
importAnimation: 1
humanDescription:
serializedVersion: 3
human: []
skeleton: []
armTwist: 0.5
foreArmTwist: 0.5
upperLegTwist: 0.5
legTwist: 0.5
armStretch: 0.05
legStretch: 0.05
feetSpacing: 0
globalScale: 1
rootMotionBoneName:
hasTranslationDoF: 0
hasExtraRoot: 0
skeletonHasParents: 1
lastHumanDescriptionAvatarSource: {instanceID: 0}
autoGenerateAvatarMappingIfUnspecified: 1
animationType: 2
humanoidOversampling: 1
avatarSetup: 0
additionalBone: 0
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 152a25406238928428795a65c5473ecb
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+68
View File
@@ -0,0 +1,68 @@
using System.Linq;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using UnityEditor;
using UnityEditor.Animations;
namespace AnimatorAsCodeFramework.Examples
{
public class GenExample0_ToggleGo : MonoBehaviour
{
public VRCAvatarDescriptor avatar;
public AnimatorController assetContainer;
public string assetKey;
public GameObject item;
}
[CustomEditor(typeof(GenExample0_ToggleGo), true)]
public class GenExample0_ToggleGoEditor : Editor
{
private const string SystemName = "Example 0";
public override void OnInspectorGUI()
{
AacExample.InspectorTemplate(this, serializedObject, "assetKey", Create, Remove);
}
private void Create()
{
var my = (GenExample0_ToggleGo) target;
// The avatar is used here:
// - to find the FX playable layer animator, where a new layer will be created.
// - to resolve the relative animation path to the item.
// The generated animation files are stored in the asset container.
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey, AacExample.Options().WriteDefaultsOff());
// Create a layer in the FX animator.
// Additional layers can be created in the FX animator (see later in the manual).
var fx = aac.CreateMainFxLayer();
// The first created state is the default one connected to the "Entry" node.
// States are automatically placed on the grid (see later in the manual).
var hidden = fx.NewState("Hidden")
// Animation assets are generated as sub-assets of the asset container.
// The animation path to my.skinnedMesh is relative to my.avatar
.WithAnimation(aac.NewClip().Toggling(my.item, false));
var shown = fx.NewState("Shown")
.WithAnimation(aac.NewClip().Toggling(my.item, true));
// Creates a Bool parameter in the FX layer.
// Parameters are added to the Animator if a parameter with the same name
// does not exist yet.
var itemParam = fx.BoolParameter("EnableItem");
// Transitions are created with a set of default values
// That can be changed in the Generator settings (see later in the manual).
hidden.TransitionsTo(shown).When(itemParam.IsTrue());
shown.TransitionsTo(hidden).When(itemParam.IsFalse());
}
private void Remove()
{
var my = (GenExample0_ToggleGo) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
aac.RemoveAllMainLayers();
}
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be5bb2bad8914be8a6a86373d48e4574
timeCreated: 1645746140
+76
View File
@@ -0,0 +1,76 @@
using System.Linq;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using UnityEditor;
using UnityEditor.Animations;
namespace AnimatorAsCodeFramework.Examples
{
public class GenExample1_ToggleSmr : MonoBehaviour
{
public VRCAvatarDescriptor avatar;
public AnimatorController assetContainer;
public string assetKey;
public SkinnedMeshRenderer skinnedMesh;
}
[CustomEditor(typeof(GenExample1_ToggleSmr), true)]
public class GenExample1_ToggleSmrEditor : Editor
{
private const string SystemName = "Example 1";
public override void OnInspectorGUI()
{
AacExample.InspectorTemplate(this, serializedObject, "assetKey", Create, Remove);
}
private void Create()
{
var my = (GenExample1_ToggleSmr) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
var fx = aac.CreateMainFxLayer();
var hidden = fx.NewState("Hidden")
// The runtime type of my.skinnedMesh is used within the animation.
// In this case, the "SkinnedMeshRenderer" component is disabled.
.WithAnimation(aac.NewClip().TogglingComponent(my.skinnedMesh, false));
var shown = fx.NewState("Shown")
.WithAnimation(aac.NewClip().TogglingComponent(my.skinnedMesh, true));
// This creates two Bool parameters in the animator.
// The resulting value can be used in conditions.
var accessoriesParams = fx.BoolParameters("EnableAccessories", "AccessoryThing");
// The following line creates one transition.
// The conditions are "EnableAccessories is true" and "AccessoryThing is true"
hidden.TransitionsTo(shown).When(accessoriesParams.AreTrue());
// The following line creates two transitions:
// - The first transition is "EnableAccessories is false"
// - The second transition is "AccessoryThing is false"
shown.TransitionsTo(hidden).When(accessoriesParams.IsAnyFalse());
if (false)
{
// Alternatively, you can use the long way:
var enableAccessoriesParam = fx.BoolParameter("EnableAccessories");
var thingParam = fx.BoolParameter("ThingParam");
hidden.TransitionsTo(shown).When(enableAccessoriesParam.IsTrue()).And(thingParam.IsTrue());
// - The first transition:
shown.TransitionsTo(hidden).When(enableAccessoriesParam.IsFalse())
// - The second transition:
.Or().When(thingParam.IsFalse());
}
}
private void Remove()
{
var my = (GenExample1_ToggleSmr) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
aac.RemoveAllMainLayers();
}
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea8b0c32e2e54e4883f5b113609cb670
timeCreated: 1645744548
+55
View File
@@ -0,0 +1,55 @@
using System.Linq;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using UnityEditor;
using UnityEditor.Animations;
namespace AnimatorAsCodeFramework.Examples
{
public class GenExample2_Animate : MonoBehaviour
{
public VRCAvatarDescriptor avatar;
public AnimatorController assetContainer;
public string assetKey;
public SkinnedMeshRenderer wedgeMesh;
}
[CustomEditor(typeof(GenExample2_Animate), true)]
public class GenExample1_BlushEditor : Editor
{
private const string SystemName = "Example 2";
public override void OnInspectorGUI()
{
AacExample.InspectorTemplate(this, serializedObject, "assetKey", Create, Remove);
}
private void Create()
{
var my = (GenExample2_Animate) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
var fx = aac.CreateMainFxLayer();
fx.NewState("Motion")
.WithAnimation(aac.NewClip().Animating(clip =>
{
clip.Animates(my.wedgeMesh, "blendShape.Wedge").WithFrameCountUnit(keyframes =>
keyframes.Easing(0, 100f).Easing(28, 0).Easing(29, 0).Easing(30, 0).Easing(31, 0).Easing(32, 0).Easing(60, 100f)
);
clip.Animates(my.wedgeMesh, "material._Metallic").WithFrameCountUnit(keyframes =>
keyframes.Constant(0, 1f).Constant(28, 0).Constant(60, 0)
);
}))
.MotionTime(fx.FloatParameter("WedgeAmount"));
}
private void Remove()
{
var my = (GenExample2_Animate) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
aac.RemoveAllMainLayers();
}
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b09d955c545f45428d3c5cfb9b214117
timeCreated: 1645746969
+148
View File
@@ -0,0 +1,148 @@
using System.Linq;
using AnimatorAsCode.V0;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using UnityEditor;
using UnityEditor.Animations;
namespace AnimatorAsCodeFramework.Examples
{
public class GenExample3_Gesturing : MonoBehaviour
{
public VRCAvatarDescriptor avatar;
public AnimatorController assetContainer;
public string assetKey;
public SkinnedMeshRenderer iconMesh;
}
[CustomEditor(typeof(GenExample3_Gesturing), true)]
public class GenExample3_GesturingEditor : Editor
{
private const string SystemName = "Example 3";
private GenExample3_Gesturing my;
private AacFlBase aac;
private const string MaxBlendshape = "blendShape.KPIN_A_Major_Max";
private const string MinBlendshape = "blendShape.KPIN_A_Major_Min";
public override void OnInspectorGUI()
{
AacExample.InspectorTemplate(this, serializedObject, "assetKey", Create, Remove);
}
private void Create()
{
my = (GenExample3_Gesturing) target;
aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
CreateMainLayer();
CreateSupportingLayer();
}
private void Remove()
{
var my = (GenExample3_Gesturing) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
aac.RemoveAllMainLayers();
aac.RemoveAllSupportingLayers("Detection");
}
private void CreateMainLayer()
{
var layer = aac.CreateMainFxLayer();
var dirtyCheckParameter = layer.BoolParameter("AAC_INTERNAL_GesturingIcon_DirtyCheck");
// ### Create states
var lackOfChangeDetected = layer.NewState("Animate To NoChange")
.WithAnimation(IconAppears());
// By default, states have an animation that animates a dummy object for 1 frame.
var noChange = layer.NewState("NoChange", 1, 0).RightOf();
var changeDetected = layer.NewState("Animate To Changing").Under()
.WithAnimation(IconDisappears());
// This creates a clip that animates a dummy object for 1.5f seconds.
var changing = layer.NewState("Changing", 0, 1).LeftOf()
.WithAnimation(aac.DummyClipLasting(1.5f, AacFlUnit.Seconds));
// When this state is entered, the parameter is driven to the value of false.
var stillChanging = layer.NewState("Still Changing", 0, 2).Under()
.Drives(dirtyCheckParameter, false);
// ------
// ### Create transitions
lackOfChangeDetected.TransitionsTo(changeDetected).AfterAnimationIsAtLeastAtPercent(0.7f).When(dirtyCheckParameter.IsTrue());
// The transition duration is 30% of the animation duration.
lackOfChangeDetected.TransitionsTo(noChange).AfterAnimationFinishes().WithTransitionDurationPercent(0.3f);
noChange.TransitionsTo(changeDetected).When(dirtyCheckParameter.IsTrue());
// By using AfterAnimationFinishes, the transition will trigger after the animation
// for the icon appearing finishes.
changeDetected.TransitionsTo(changing).AfterAnimationFinishes();
changing.TransitionsTo(stillChanging).When(dirtyCheckParameter.IsTrue());
// By using AfterAnimationFinishes, the transition will trigger after 1.5 seconds,
// which is the length of the animation in Changing.
changing.TransitionsTo(lackOfChangeDetected).AfterAnimationFinishes();
// The transition will immediately happen upon entering, by using Exit time set to 0.
stillChanging.AutomaticallyMovesTo(changing);
}
private void CreateSupportingLayer()
{
// Create an additional FX layer.
var layer = aac.CreateSupportingFxLayer("Detection");
var reevaluating = layer.NewState("Reevaluating", -1, 0);
foreach (var left in Enumerable.Range(0, 8))
{
foreach (var right in Enumerable.Range(0, 8))
{
var state = layer.NewState($"Gesture {left} {right}", left, right)
// When this state is entered, the parameter is driven to the value of true.
.Drives(layer.BoolParameter("AAC_INTERNAL_GesturingIcon_DirtyCheck"), true);
reevaluating.TransitionsTo(state)
// Use ".Av3" to access VRChat standard parameters.
// Accessing these parameters will create the corresponding parameter in the animator.
.When(layer.Av3().GestureLeft.IsEqualTo(left))
.And(layer.Av3().GestureRight.IsEqualTo(right));
state.TransitionsTo(reevaluating)
.When(layer.Av3().GestureLeft.IsNotEqualTo(left))
.Or()
.When(layer.Av3().GestureRight.IsNotEqualTo(right));
}
}
}
private AacFlClip IconAppears()
{
return aac.NewClip().Animating(clip =>
{
clip.Animates(my.iconMesh, MaxBlendshape)
.WithFrameCountUnit(keyframes => keyframes.Easing(0, 1.001f).Easing(10, 0f));
clip.Animates(my.iconMesh, MinBlendshape)
.WithOneFrame(0f);
});
}
private AacFlClip IconDisappears()
{
return aac.NewClip().Animating(clip =>
{
clip.Animates(my.iconMesh, MaxBlendshape)
.WithFrameCountUnit(keyframes => keyframes.Easing(0, 0f).Easing(30, 0f));
clip.Animates(my.iconMesh, MinBlendshape)
.WithFrameCountUnit(keyframes => keyframes.Easing(0, 0f).Easing(10, 100f));
});
}
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a0bd105e311c440ca03e6f6c46f830fc
timeCreated: 1645755767
@@ -0,0 +1,52 @@
using System.Linq;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using UnityEditor;
using UnityEditor.Animations;
namespace AnimatorAsCodeFramework.Examples
{
public class GenExampleManual_PlacingStates : MonoBehaviour
{
public VRCAvatarDescriptor avatar;
public AnimatorController assetContainer;
public string assetKey;
}
[CustomEditor(typeof(GenExampleManual_PlacingStates), true)]
public class GenExampleManual_PlacingStatesEditor : Editor
{
private const string SystemName = "Example Manual - Placing States";
public override void OnInspectorGUI()
{
AacExample.InspectorTemplate(this, serializedObject, "assetKey", Create, Remove);
}
private void Create()
{
var my = (GenExampleManual_PlacingStates) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
var fx = aac.CreateMainFxLayer();
var init = fx.NewState("Init"); // This is the first state. By default it is at (0, 0)
var a = fx.NewState("A"); // This will be placed under Init.
var b = fx.NewState("B"); // This will be placed under A.
var c = fx.NewState("C").RightOf(a); // This will be placed right of A.
var d = fx.NewState("D"); // This will be placed under C.
var alternate = fx.NewState("Alternate").Over(c); // This will be placed over C.
// This will be placed next to Alternate: 2 blocks over, and 1 to the right.
var reset = fx.NewState("Reset").Shift(alternate, 1, -2);
}
private void Remove()
{
var my = (GenExampleManual_PlacingStates) target;
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
aac.RemoveAllMainLayers();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f4c992e0dc0e4f74a36f386346991dbc
timeCreated: 1645800049
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8743b020a8e44294871ab5782040e999
timeCreated: 1645907951
+3
View File
@@ -0,0 +1,3 @@
{
"name": "AnimatorAsCodeFramework"
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 21dee7794a6343ccbdcd48b92f941953
timeCreated: 1645911687
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6084a9de238746a28ba45b77d85d0dc7
timeCreated: 1645908045
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6ab1a9c322945d5449fea4130317f605
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+632
View File
@@ -0,0 +1,632 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using Random = UnityEngine.Random;
// ReSharper disable once CheckNamespace
namespace AnimatorAsCode.V0
{
public static class AacV0
{
public static AacFlBase Create(AacConfiguration configuration)
{
return new AacFlBase(configuration);
}
internal static AnimatorController AnimatorOf(VRCAvatarDescriptor ad, VRCAvatarDescriptor.AnimLayerType animLayerType)
{
return (AnimatorController) ad.baseAnimationLayers.First(it => it.type == animLayerType).animatorController;
}
internal static AnimationClip NewClip(AacConfiguration component, string suffix)
{
return RegisterClip(component, suffix, new AnimationClip());
}
internal static AnimationClip RegisterClip(AacConfiguration component, string suffix, AnimationClip clip)
{
clip.name = "zAutogenerated__" + component.AssetKey + "__" + suffix + "_" + Random.Range(0, Int32.MaxValue); // FIXME animation name conflict
clip.hideFlags = HideFlags.None;
AssetDatabase.AddObjectToAsset(clip, component.AssetContainer);
return clip;
}
internal static BlendTree NewBlendTreeAsRaw(AacConfiguration component, string suffix)
{
var clip = new BlendTree();
clip.name = "zAutogenerated__" + component.AssetKey + "__" + suffix + "_" + Random.Range(0, Int32.MaxValue); // FIXME animation name conflict
clip.hideFlags = HideFlags.None;
AssetDatabase.AddObjectToAsset(clip, component.AssetContainer);
return clip;
}
internal static EditorCurveBinding Binding(AacConfiguration component, Type type, Transform transform, string propertyName)
{
return new EditorCurveBinding
{
path = ResolveRelativePath(component.AnimatorRoot, transform),
type = type,
propertyName = propertyName
};
}
internal static AnimationCurve OneFrame(float desiredValue)
{
return AnimationCurve.Constant(0f, 1 / 60f, desiredValue);
}
internal static AnimationCurve ConstantSeconds(float seconds, float desiredValue)
{
return AnimationCurve.Constant(0f, seconds, desiredValue);
}
internal static string ResolveRelativePath(Transform avatar, Transform item)
{
if (item.parent != avatar && item.parent != null)
{
return ResolveRelativePath(avatar, item.parent) + "/" + item.name;
}
return item.name;
}
internal static EditorCurveBinding ToSubBinding(EditorCurveBinding binding, string suffix)
{
return new EditorCurveBinding {path = binding.path, type = binding.type, propertyName = binding.propertyName + "." + suffix};
}
}
public struct AacConfiguration
{
public string SystemName;
public VRCAvatarDescriptor AvatarDescriptor;
public Transform AnimatorRoot;
public Transform DefaultValueRoot;
public AnimatorController AssetContainer;
public string AssetKey;
public IAacDefaultsProvider DefaultsProvider;
}
public struct AacFlLayer
{
private readonly AnimatorController _animatorController;
private readonly AacConfiguration _configuration;
private readonly string _fullLayerName;
private readonly AacStateMachine _stateMachine;
internal AacFlLayer(AnimatorController animatorController, AacConfiguration configuration, AacStateMachine stateMachine, string fullLayerName)
{
_animatorController = animatorController;
_configuration = configuration;
_fullLayerName = fullLayerName;
_stateMachine = stateMachine;
}
public AacFlState NewState(string name)
{
var lastState = _stateMachine.LastStatePosition();
var state = _stateMachine.NewState(name, 0, 0).Shift(lastState, 0, 1);
return state;
}
public AacFlState NewState(string name, int x, int y)
{
return _stateMachine.NewState(name, x, y);
}
public AacFlTransition AnyTransitionsTo(AacFlState destination)
{
return _stateMachine.AnyTransitionsTo(destination);
}
public AacFlEntryTransition EntryTransitionsTo(AacFlState destination)
{
return _stateMachine.EntryTransitionsTo(destination);
}
public AacFlBoolParameter BoolParameter(string parameterName) => _stateMachine.BackingAnimator().BoolParameter(parameterName);
public AacFlBoolParameter TriggerParameterAsBool(string parameterName) => _stateMachine.BackingAnimator().TriggerParameter(parameterName);
public AacFlFloatParameter FloatParameter(string parameterName) => _stateMachine.BackingAnimator().FloatParameter(parameterName);
public AacFlIntParameter IntParameter(string parameterName) => _stateMachine.BackingAnimator().IntParameter(parameterName);
public AacFlBoolParameterGroup BoolParameters(params string[] parameterNames) => _stateMachine.BackingAnimator().BoolParameters(parameterNames);
public AacFlBoolParameterGroup TriggerParametersAsBools(params string[] parameterNames) => _stateMachine.BackingAnimator().TriggerParameters(parameterNames);
public AacFlFloatParameterGroup FloatParameters(params string[] parameterNames) => _stateMachine.BackingAnimator().FloatParameters(parameterNames);
public AacFlIntParameterGroup IntParameters(params string[] parameterNames) => _stateMachine.BackingAnimator().IntParameters(parameterNames);
public AacFlBoolParameterGroup BoolParameters(params AacFlBoolParameter[] parameters) => _stateMachine.BackingAnimator().BoolParameters(parameters);
public AacFlBoolParameterGroup TriggerParametersAsBools(params AacFlBoolParameter[] parameters) => _stateMachine.BackingAnimator().TriggerParameters(parameters);
public AacFlFloatParameterGroup FloatParameters(params AacFlFloatParameter[] parameters) => _stateMachine.BackingAnimator().FloatParameters(parameters);
public AacFlIntParameterGroup IntParameters(params AacFlIntParameter[] parameters) => _stateMachine.BackingAnimator().IntParameters(parameters);
public AacAv3 Av3() => new AacAv3(_stateMachine.BackingAnimator());
public void OverrideValue(AacFlBoolParameter toBeForced, bool value)
{
var parameters = _animatorController.parameters;
foreach (var param in parameters)
{
if (param.name == toBeForced.Name)
{
param.defaultBool = value;
}
}
_animatorController.parameters = parameters;
}
public void OverrideValue(AacFlFloatParameter toBeForced, float value)
{
var parameters = _animatorController.parameters;
foreach (var param in parameters)
{
if (param.name == toBeForced.Name)
{
param.defaultFloat = value;
}
}
_animatorController.parameters = parameters;
}
public void OverrideValue(AacFlIntParameter toBeForced, int value)
{
var parameters = _animatorController.parameters;
foreach (var param in parameters)
{
if (param.name == toBeForced.Name)
{
param.defaultInt = value;
}
}
_animatorController.parameters = parameters;
}
public AacFlLayer WithAvatarMask(AvatarMask avatarMask)
{
var finalFullLayerName = _fullLayerName;
_animatorController.layers = _animatorController.layers
.Select(layer =>
{
if (layer.name == finalFullLayerName)
{
layer.avatarMask = avatarMask;
}
return layer;
})
.ToArray();
return this;
}
public void WithAvatarMaskNoTransforms()
{
ResolveAvatarMask(new Transform[0]);
}
public void ResolveAvatarMask(Transform[] paths)
{
// FIXME: Fragile
var avatarMask = new AvatarMask();
avatarMask.name = "zAutogenerated__" + _configuration.AssetKey + "_" + _fullLayerName + "__AvatarMask";
avatarMask.hideFlags = HideFlags.None;
if (paths.Length == 0)
{
avatarMask.transformCount = 1;
avatarMask.SetTransformActive(0, false);
avatarMask.SetTransformPath(0, "_ignored");
}
else
{
avatarMask.transformCount = paths.Length;
for (var index = 0; index < paths.Length; index++)
{
var transform = paths[index];
avatarMask.SetTransformActive(index, true);
avatarMask.SetTransformPath(index, AacV0.ResolveRelativePath(_configuration.AnimatorRoot, transform));
}
}
for (int i = 0; i < (int) AvatarMaskBodyPart.LastBodyPart; i++)
{
avatarMask.SetHumanoidBodyPartActive((AvatarMaskBodyPart) i, false);
}
AssetDatabase.AddObjectToAsset(avatarMask, _animatorController);
WithAvatarMask(avatarMask);
}
}
public class AacFlBase
{
private readonly AacConfiguration _configuration;
internal AacFlBase(AacConfiguration configuration)
{
_configuration = configuration;
}
public AacFlClip NewClip()
{
var clip = AacV0.NewClip(_configuration, Guid.NewGuid().ToString());
return new AacFlClip(_configuration, clip);
}
public AacFlClip CopyClip(AnimationClip originalClip)
{
var newClip = UnityEngine.Object.Instantiate(originalClip);
var clip = AacV0.RegisterClip(_configuration, Guid.NewGuid().ToString(), newClip);
return new AacFlClip(_configuration, clip);
}
public BlendTree NewBlendTreeAsRaw()
{
return AacV0.NewBlendTreeAsRaw(_configuration, Guid.NewGuid().ToString());
}
public AacFlClip NewClip(string name)
{
var clip = AacV0.NewClip(_configuration, name);
return new AacFlClip(_configuration, clip);
}
public AacFlClip DummyClipLasting(float numberOf, AacFlUnit unit)
{
var dummyClip = AacV0.NewClip(_configuration, $"D({numberOf} {Enum.GetName(typeof(AacFlUnit), unit)})");
var duration = unit == AacFlUnit.Frames ? numberOf / 60f : numberOf;
return new AacFlClip(_configuration, dummyClip)
.Animating(clip => clip.Animates("_ignored", typeof(GameObject), "m_IsActive")
.WithUnit(unit, keyframes => keyframes.Constant(0, 0f).Constant(duration, 0f)));
}
public void RemoveAllMainLayers()
{
var layerName = _configuration.SystemName;
RemoveLayerOnAllControllers(_configuration.DefaultsProvider.ConvertLayerName(layerName));
}
public void RemoveAllSupportingLayers(string suffix)
{
var layerName = _configuration.SystemName;
RemoveLayerOnAllControllers(_configuration.DefaultsProvider.ConvertLayerNameWithSuffix(layerName, suffix));
}
private void RemoveLayerOnAllControllers(string layerName)
{
var layers = _configuration.AvatarDescriptor.baseAnimationLayers.Select(layer => layer.animatorController).Where(layer => layer != null).Distinct().ToList();
foreach (var customAnimLayer in layers)
{
new AacAnimatorRemoval((AnimatorController) customAnimLayer).RemoveLayer(_configuration.DefaultsProvider.ConvertLayerName(layerName));
}
}
public AacFlLayer CreateMainFxLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.FX);
public AacFlLayer CreateMainGestureLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Gesture);
public AacFlLayer CreateMainActionLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Action);
public AacFlLayer CreateMainIdleLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Additive);
public AacFlLayer CreateMainLocomotionLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Base);
public AacFlLayer CreateMainAv3Layer(VRCAvatarDescriptor.AnimLayerType animLayerType) => DoCreateMainLayerOnController(animLayerType);
public AacFlLayer CreateSupportingFxLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.FX, suffix);
public AacFlLayer CreateSupportingGestureLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Gesture, suffix);
public AacFlLayer CreateSupportingActionLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Action, suffix);
public AacFlLayer CreateSupportingIdleLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Additive, suffix);
public AacFlLayer CreateSupportingLocomotionLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Base, suffix);
public AacFlLayer CreateSupportingAv3Layer(VRCAvatarDescriptor.AnimLayerType animLayerType, string suffix) => DoCreateSupportingLayerOnController(animLayerType, suffix);
public AacFlLayer CreateMainArbitraryControllerLayer(AnimatorController controller) => DoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerName(_configuration.SystemName));
public AacFlLayer CreateSupportingArbitraryControllerLayer(AnimatorController controller, string suffix) => DoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerNameWithSuffix(_configuration.SystemName, suffix));
public AacFlLayer CreateFirstArbitraryControllerLayer(AnimatorController controller) => DoCreateLayer(controller, controller.layers[0].name);
private AacFlLayer DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType animType)
{
var animator = AacV0.AnimatorOf(_configuration.AvatarDescriptor, animType);
var layerName = _configuration.DefaultsProvider.ConvertLayerName(_configuration.SystemName);
return DoCreateLayer(animator, layerName);
}
private AacFlLayer DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType animType, string suffix)
{
var animator = AacV0.AnimatorOf(_configuration.AvatarDescriptor, animType);
var layerName = _configuration.DefaultsProvider.ConvertLayerNameWithSuffix(_configuration.SystemName, suffix);
return DoCreateLayer(animator, layerName);
}
private AacFlLayer DoCreateLayer(AnimatorController animator, string layerName)
{
var ag = new AacAnimatorGenerator(animator, CreateEmptyClip().Clip, _configuration.DefaultsProvider);
var machine = ag.CreateOrClearLayerAtSameIndex(layerName, 1f);
return new AacFlLayer(animator, _configuration, machine, layerName);
}
private AacFlClip CreateEmptyClip()
{
var emptyClip = DummyClipLasting(1, AacFlUnit.Frames);
return emptyClip;
}
public AacVrcAssetLibrary VrcAssets()
{
return new AacVrcAssetLibrary();
}
public void ClearPreviousAssets()
{
var allSubAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(_configuration.AssetContainer));
foreach (var subAsset in allSubAssets)
{
if (subAsset.name.StartsWith($"zAutogenerated__{_configuration.AssetKey}__")
&& (subAsset is AnimationClip || subAsset is BlendTree || subAsset is AvatarMask))
{
AssetDatabase.RemoveObjectFromAsset(subAsset);
}
}
}
}
public class AacAv3
{
private readonly AacBackingAnimator _backingAnimator;
internal AacAv3(AacBackingAnimator backingAnimator)
{
_backingAnimator = backingAnimator;
}
// ReSharper disable InconsistentNaming
public AacFlBoolParameter IsLocal => _backingAnimator.BoolParameter("IsLocal");
public AacFlEnumIntParameter<Av3Viseme> Viseme => _backingAnimator.EnumParameter<Av3Viseme>("Viseme");
public AacFlEnumIntParameter<Av3Gesture> GestureLeft => _backingAnimator.EnumParameter<Av3Gesture>("GestureLeft");
public AacFlEnumIntParameter<Av3Gesture> GestureRight => _backingAnimator.EnumParameter<Av3Gesture>("GestureRight");
public AacFlFloatParameter GestureLeftWeight => _backingAnimator.FloatParameter("GestureLeftWeight");
public AacFlFloatParameter GestureRightWeight => _backingAnimator.FloatParameter("GestureRightWeight");
public AacFlFloatParameter AngularY => _backingAnimator.FloatParameter("AngularY");
public AacFlFloatParameter VelocityX => _backingAnimator.FloatParameter("VelocityX");
public AacFlFloatParameter VelocityY => _backingAnimator.FloatParameter("VelocityY");
public AacFlFloatParameter VelocityZ => _backingAnimator.FloatParameter("VelocityZ");
public AacFlFloatParameter Upright => _backingAnimator.FloatParameter("Upright");
public AacFlBoolParameter Grounded => _backingAnimator.BoolParameter("Grounded");
public AacFlBoolParameter Seated => _backingAnimator.BoolParameter("Seated");
public AacFlBoolParameter AFK => _backingAnimator.BoolParameter("AFK");
public AacFlIntParameter TrackingType => _backingAnimator.IntParameter("TrackingType");
public AacFlIntParameter VRMode => _backingAnimator.IntParameter("VRMode");
public AacFlBoolParameter MuteSelf => _backingAnimator.BoolParameter("MuteSelf");
public AacFlBoolParameter InStation => _backingAnimator.BoolParameter("InStation");
public AacFlFloatParameter Voice => _backingAnimator.FloatParameter("Voice");
// ReSharper restore InconsistentNaming
public IAacFlCondition ItIsRemote() => IsLocal.IsFalse();
public IAacFlCondition ItIsLocal() => IsLocal.IsTrue();
public enum Av3Gesture
{
// Specify all the values explicitly because they should be dictated by VRChat, not enumeration order.
Neutral = 0,
Fist = 1,
HandOpen = 2,
Fingerpoint = 3,
Victory = 4,
RockNRoll = 5,
HandGun = 6,
ThumbsUp = 7
}
public enum Av3Viseme
{
// Specify all the values explicitly because they should be dictated by VRChat, not enumeration order.
// ReSharper disable InconsistentNaming
sil = 0,
pp = 1,
ff = 2,
th = 3,
dd = 4,
kk = 5,
ch = 6,
ss = 7,
nn = 8,
rr = 9,
aa = 10,
e = 11,
ih = 12,
oh = 13,
ou = 14
// ReSharper restore InconsistentNaming
}
}
public class AacVrcAssetLibrary
{
public AvatarMask LeftHandAvatarMask()
{
return AssetDatabase.LoadAssetAtPath<AvatarMask>("Assets/VRCSDK/Examples3/Animation/Masks/vrc_Hand Left.mask");
}
public AvatarMask RightHandAvatarMask()
{
return AssetDatabase.LoadAssetAtPath<AvatarMask>("Assets/VRCSDK/Examples3/Animation/Masks/vrc_Hand Right.mask");
}
public AnimationClip ProxyForGesture(AacAv3.Av3Gesture gesture, bool masculine)
{
return AssetDatabase.LoadAssetAtPath<AnimationClip>("Assets/VRCSDK/Examples3/Animation/ProxyAnim/" + ResolveProxyFilename(gesture, masculine));
}
private static string ResolveProxyFilename(AacAv3.Av3Gesture gesture, bool masculine)
{
switch (gesture)
{
case AacAv3.Av3Gesture.Neutral: return masculine ? "proxy_hands_idle.anim" : "proxy_hands_idle2.anim";
case AacAv3.Av3Gesture.Fist: return "proxy_hands_fist.anim";
case AacAv3.Av3Gesture.HandOpen: return "proxy_hands_open.anim";
case AacAv3.Av3Gesture.Fingerpoint: return "proxy_hands_point.anim";
case AacAv3.Av3Gesture.Victory: return "proxy_hands_peace.anim";
case AacAv3.Av3Gesture.RockNRoll: return "proxy_hands_rock.anim";
case AacAv3.Av3Gesture.HandGun: return "proxy_hands_gun.anim";
case AacAv3.Av3Gesture.ThumbsUp: return "proxy_hands_thumbs_up.anim";
default:
throw new ArgumentOutOfRangeException(nameof(gesture), gesture, null);
}
}
}
public class AacAnimatorRemoval
{
private readonly AnimatorController _animatorController;
public AacAnimatorRemoval(AnimatorController animatorController)
{
_animatorController = animatorController;
}
public void RemoveLayer(string layerName)
{
var index = FindIndexOf(layerName);
if (index == -1) return;
_animatorController.RemoveLayer(index);
}
private int FindIndexOf(string layerName)
{
return _animatorController.layers.ToList().FindIndex(layer => layer.name == layerName);
}
}
public class AacAnimatorGenerator
{
private readonly AnimatorController _animatorController;
private readonly AnimationClip _emptyClip;
private readonly IAacDefaultsProvider _defaultsProvider;
internal AacAnimatorGenerator(AnimatorController animatorController, AnimationClip emptyClip, IAacDefaultsProvider defaultsProvider)
{
_animatorController = animatorController;
_emptyClip = emptyClip;
_defaultsProvider = defaultsProvider;
}
internal void CreateParamsAsNeeded(params AacFlParameter[] parameters)
{
foreach (var parameter in parameters)
{
switch (parameter)
{
case AacFlIntParameter _:
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Int);
break;
case AacFlFloatParameter _:
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Float);
break;
case AacFlBoolParameter _:
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Bool);
break;
}
}
}
internal void CreateTriggerParamsAsNeeded(params AacFlBoolParameter[] parameters)
{
foreach (var parameter in parameters)
{
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Trigger);
}
}
private void CreateParamIfNotExists(string paramName, AnimatorControllerParameterType type)
{
if (_animatorController.parameters.FirstOrDefault(param => param.name == paramName) == null)
{
_animatorController.AddParameter(paramName, type);
}
}
// DEPRECATED: This causes the editor window to glitch by deselecting, which is jarring for experimentation
internal AacStateMachine CreateOrRemakeLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null)
{
var originalIndexToPreserveOrdering = FindIndexOf(layerName);
if (originalIndexToPreserveOrdering != -1)
{
_animatorController.RemoveLayer(originalIndexToPreserveOrdering);
}
AddLayerWithWeight(layerName, weightWhenCreating, maskWhenCreating);
if (originalIndexToPreserveOrdering != -1)
{
var items = _animatorController.layers.ToList();
var last = items[items.Count - 1];
items.RemoveAt(items.Count - 1);
items.Insert(originalIndexToPreserveOrdering, last);
_animatorController.layers = items.ToArray();
}
var layer = TryGetLayer(layerName);
var machinist = new AacStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider);
return machinist
.WithAnyStatePosition(0, 7)
.WithEntryPosition(0, -1)
.WithExitPosition(7, -1);
}
internal AacStateMachine CreateOrClearLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null)
{
var originalIndexToPreserveOrdering = FindIndexOf(layerName);
if (originalIndexToPreserveOrdering != -1)
{
foreach (var childAnimatorStateMachine in _animatorController.layers[originalIndexToPreserveOrdering].stateMachine.stateMachines)
{
childAnimatorStateMachine.stateMachine.states = new ChildAnimatorState[0];
childAnimatorStateMachine.stateMachine.entryTransitions = new AnimatorTransition[0];
childAnimatorStateMachine.stateMachine.anyStateTransitions = new AnimatorStateTransition[0];
}
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.stateMachines = new ChildAnimatorStateMachine[0];
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.states = new ChildAnimatorState[0];
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.entryTransitions = new AnimatorTransition[0];
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.anyStateTransitions = new AnimatorStateTransition[0];
}
else
{
_animatorController.AddLayer(_animatorController.MakeUniqueLayerName(layerName));
originalIndexToPreserveOrdering = _animatorController.layers.Length - 1;
}
var layers = _animatorController.layers;
layers[originalIndexToPreserveOrdering].avatarMask = maskWhenCreating;
layers[originalIndexToPreserveOrdering].defaultWeight = weightWhenCreating;
_animatorController.layers = layers;
var layer = TryGetLayer(layerName);
var machinist = new AacStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider);
return machinist
.WithAnyStatePosition(0, 7)
.WithEntryPosition(0, -1)
.WithExitPosition(7, -1);
}
private int FindIndexOf(string layerName)
{
return _animatorController.layers.ToList().FindIndex(layer1 => layer1.name == layerName);
}
private AnimatorControllerLayer TryGetLayer(string layerName)
{
return _animatorController.layers.FirstOrDefault(it => it.name == layerName);
}
private void AddLayerWithWeight(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating)
{
_animatorController.AddLayer(_animatorController.MakeUniqueLayerName(layerName));
var mutatedLayers = _animatorController.layers;
mutatedLayers[mutatedLayers.Length - 1].defaultWeight = weightWhenCreating;
mutatedLayers[mutatedLayers.Length - 1].avatarMask = maskWhenCreating;
_animatorController.layers = mutatedLayers;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbedba2d4d2730b43b9063eccba33706
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,58 @@
using UnityEditor.Animations;
using UnityEngine;
// ReSharper disable once CheckNamespace
namespace AnimatorAsCode.V0
{
public interface IAacDefaultsProvider
{
void ConfigureState(AnimatorState state, AnimationClip emptyClip);
void ConfigureTransition(AnimatorStateTransition transition);
string ConvertLayerName(string systemName);
string ConvertLayerNameWithSuffix(string systemName, string suffix);
Vector2 Grid();
}
public class AacDefaultsProvider : IAacDefaultsProvider
{
private readonly bool _writeDefaults;
public AacDefaultsProvider(bool writeDefaults = false)
{
_writeDefaults = writeDefaults;
}
public virtual void ConfigureState(AnimatorState state, AnimationClip emptyClip)
{
state.motion = emptyClip;
state.writeDefaultValues = _writeDefaults;
}
public virtual void ConfigureTransition(AnimatorStateTransition transition)
{
transition.duration = 0;
transition.hasExitTime = false;
transition.exitTime = 0;
transition.hasFixedDuration = true;
transition.offset = 0;
transition.interruptionSource = TransitionInterruptionSource.None;
transition.orderedInterruption = true;
transition.canTransitionToSelf = false;
}
public virtual string ConvertLayerName(string systemName)
{
return systemName;
}
public virtual string ConvertLayerNameWithSuffix(string systemName, string suffix)
{
return $"{systemName}__{suffix}";
}
public Vector2 Grid()
{
return new Vector2(250, 70);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe75277803d79b44091640b28b412243
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+451
View File
@@ -0,0 +1,451 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
// ReSharper disable once CheckNamespace
namespace AnimatorAsCode.V0
{
public readonly struct AacFlClip
{
private readonly AacConfiguration _component;
public AnimationClip Clip { get; }
public AacFlClip(AacConfiguration component, AnimationClip clip)
{
_component = component;
Clip = clip;
}
public AacFlClip Looping()
{
var settings = AnimationUtility.GetAnimationClipSettings(Clip);
settings.loopTime = true;
AnimationUtility.SetAnimationClipSettings(Clip, settings);
return this;
}
public AacFlClip NonLooping()
{
var settings = AnimationUtility.GetAnimationClipSettings(Clip);
settings.loopTime = false;
AnimationUtility.SetAnimationClipSettings(Clip, settings);
return this;
}
public AacFlClip Animating(Action<AacFlEditClip> action)
{
action.Invoke(new AacFlEditClip(_component, Clip));
return this;
}
public AacFlClip Toggling(GameObject[] gameObjectsWithNulls, bool value)
{
var defensiveObjects = gameObjectsWithNulls.Where(o => o != null); // Allow users to remove an item in the middle of the array
foreach (var component in defensiveObjects)
{
var binding = AacV0.Binding(_component, typeof(GameObject), component.transform, "m_IsActive");
AnimationUtility.SetEditorCurve(Clip, binding, AacV0.OneFrame(value ? 1f : 0f));
}
return this;
}
public AacFlClip BlendShape(SkinnedMeshRenderer renderer, string blendShapeName, float value)
{
var binding = AacV0.Binding(_component, typeof(SkinnedMeshRenderer), renderer.transform, $"blendShape.{blendShapeName}");
AnimationUtility.SetEditorCurve(Clip, binding, AacV0.OneFrame(value));
return this;
}
public AacFlClip BlendShape(SkinnedMeshRenderer[] rendererWithNulls, string blendShapeName, float value)
{
var defensiveObjects = rendererWithNulls.Where(o => o != null); // Allow users to remove an item in the middle of the array
foreach (var component in defensiveObjects)
{
var binding = AacV0.Binding(_component, typeof(SkinnedMeshRenderer), component.transform, $"blendShape.{blendShapeName}");
AnimationUtility.SetEditorCurve(Clip, binding, AacV0.OneFrame(value));
}
return this;
}
public AacFlClip Scaling(GameObject[] gameObjectsWithNulls, Vector3 scale)
{
var defensiveObjects = gameObjectsWithNulls.Where(o => o != null); // Allow users to remove an item in the middle of the array
foreach (var component in defensiveObjects)
{
AnimationUtility.SetEditorCurve(Clip, AacV0.Binding(_component, typeof(Transform), component.transform, "m_LocalScale.x"), AacV0.OneFrame(scale.x));
AnimationUtility.SetEditorCurve(Clip, AacV0.Binding(_component, typeof(Transform), component.transform, "m_LocalScale.y"), AacV0.OneFrame(scale.y));
AnimationUtility.SetEditorCurve(Clip, AacV0.Binding(_component, typeof(Transform), component.transform, "m_LocalScale.z"), AacV0.OneFrame(scale.z));
}
return this;
}
public AacFlClip Toggling(GameObject gameObject, bool value)
{
var binding = AacV0.Binding(_component, typeof(GameObject), gameObject.transform, "m_IsActive");
AnimationUtility.SetEditorCurve(Clip, binding, AacV0.OneFrame(value ? 1f : 0f));
return this;
}
public AacFlClip TogglingComponent(Component[] componentsWithNulls, bool value)
{
var defensiveComponents = componentsWithNulls.Where(o => o != null); // Allow users to remove an item in the middle of the array
foreach (var component in defensiveComponents)
{
var binding = AacV0.Binding(_component, component.GetType(), component.transform, "m_Enabled");
AnimationUtility.SetEditorCurve(Clip, binding, AacV0.OneFrame(value ? 1f : 0f));
}
return this;
}
public AacFlClip TogglingComponent(Component component, bool value)
{
var binding = AacV0.Binding(_component, component.GetType(), component.transform, "m_Enabled");
AnimationUtility.SetEditorCurve(Clip, binding, AacV0.OneFrame(value ? 1f : 0f));
return this;
}
public AacFlClip SwappingMaterial(Renderer renderer, int slot, Material material)
{
var binding = AacV0.Binding(_component, renderer.GetType(), renderer.transform, $"m_Materials.Array.data[{slot}]");
AnimationUtility.SetObjectReferenceCurve(Clip, binding, new[] {
new ObjectReferenceKeyframe { time = 0f, value = material },
new ObjectReferenceKeyframe { time = 1/60f, value = material }
});
return this;
}
public AacFlClip SwappingMaterial(ParticleSystem particleSystem, int slot, Material material)
{
var binding = AacV0.Binding(_component, typeof(ParticleSystemRenderer), particleSystem.transform, $"m_Materials.Array.data[{slot}]");
AnimationUtility.SetObjectReferenceCurve(Clip, binding, new[] {
new ObjectReferenceKeyframe { time = 0f, value = material },
new ObjectReferenceKeyframe { time = 1/60f, value = material }
});
return this;
}
}
public readonly struct AacFlEditClip
{
private readonly AacConfiguration _component;
public AnimationClip Clip { get; }
public AacFlEditClip(AacConfiguration component, AnimationClip clip)
{
_component = component;
Clip = clip;
}
public AacFlSettingCurve Animates(string path, Type type, string propertyName)
{
var binding = new EditorCurveBinding
{
path = path,
type = type,
propertyName = propertyName
};
return new AacFlSettingCurve(Clip, new[] {binding});
}
public AacFlSettingCurve Animates(Transform transform, Type type, string propertyName)
{
var binding = AacV0.Binding(_component, type, transform, propertyName);
return new AacFlSettingCurve(Clip, new[] {binding});
}
public AacFlSettingCurve Animates(GameObject gameObject)
{
var binding = AacV0.Binding(_component, typeof(GameObject), gameObject.transform, "m_IsActive");
return new AacFlSettingCurve(Clip, new[] {binding});
}
public AacFlSettingCurve Animates(Component anyComponent, string property)
{
var binding = Internal_BindingFromComponent(anyComponent, property);
return new AacFlSettingCurve(Clip, new[] {binding});
}
public AacFlSettingCurve Animates(Component[] anyComponents, string property)
{
var that = this;
var bindings = anyComponents
.Select(anyComponent => that.Internal_BindingFromComponent(anyComponent, property))
.ToArray();
return new AacFlSettingCurve(Clip, bindings);
}
public AacFlSettingCurve AnimatesAnimator(AacFlParameter floatParameter)
{
var binding = new EditorCurveBinding
{
path = "",
type = typeof(Animator),
propertyName = floatParameter.Name
};
return new AacFlSettingCurve(Clip, new[] {binding});
}
public AacFlSettingCurveColor AnimatesColor(Component anyComponent, string property)
{
var binding = Internal_BindingFromComponent(anyComponent, property);
return new AacFlSettingCurveColor(Clip, new[] {binding});
}
public AacFlSettingCurveColor AnimatesColor(Component[] anyComponents, string property)
{
var that = this;
var bindings = anyComponents
.Select(anyComponent => that.Internal_BindingFromComponent(anyComponent, property))
.ToArray();
return new AacFlSettingCurveColor(Clip, bindings);
}
public EditorCurveBinding BindingFromComponent(Component anyComponent, string propertyName)
{
return Internal_BindingFromComponent(anyComponent, propertyName);
}
private EditorCurveBinding Internal_BindingFromComponent(Component anyComponent, string propertyName)
{
return AacV0.Binding(_component, anyComponent.GetType(), anyComponent.transform, propertyName);
}
}
public class AacFlSettingCurve
{
private readonly AnimationClip _clip;
private readonly EditorCurveBinding[] _bindings;
public AacFlSettingCurve(AnimationClip clip, EditorCurveBinding[] bindings)
{
_clip = clip;
_bindings = bindings;
}
public void WithOneFrame(float desiredValue)
{
foreach (var binding in _bindings)
{
AnimationUtility.SetEditorCurve(_clip, binding, AacV0.OneFrame(desiredValue));
}
}
public void WithFixedSeconds(float seconds, float desiredValue)
{
foreach (var binding in _bindings)
{
AnimationUtility.SetEditorCurve(_clip, binding, AacV0.ConstantSeconds(seconds, desiredValue));
}
}
public void WithSecondsUnit(Action<AacFlSettingKeyframes> action)
{
InternalWithUnit(AacFlUnit.Seconds, action);
}
public void WithFrameCountUnit(Action<AacFlSettingKeyframes> action)
{
InternalWithUnit(AacFlUnit.Frames, action);
}
public void WithUnit(AacFlUnit unit, Action<AacFlSettingKeyframes> action)
{
InternalWithUnit(unit, action);
}
private void InternalWithUnit(AacFlUnit unit, Action<AacFlSettingKeyframes> action)
{
var mutatedKeyframes = new List<Keyframe>();
var builder = new AacFlSettingKeyframes(unit, mutatedKeyframes);
action.Invoke(builder);
foreach (var binding in _bindings)
{
AnimationUtility.SetEditorCurve(_clip, binding, new AnimationCurve(mutatedKeyframes.ToArray()));
}
}
}
public class AacFlSettingCurveColor
{
private readonly AnimationClip _clip;
private readonly EditorCurveBinding[] _bindings;
public AacFlSettingCurveColor(AnimationClip clip, EditorCurveBinding[] bindings)
{
_clip = clip;
_bindings = bindings;
}
public void WithOneFrame(Color desiredValue)
{
foreach (var binding in _bindings)
{
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "r"), AacV0.OneFrame(desiredValue.r));
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "g"), AacV0.OneFrame(desiredValue.g));
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "b"), AacV0.OneFrame(desiredValue.b));
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "a"), AacV0.OneFrame(desiredValue.a));
}
}
public void WithKeyframes(AacFlUnit unit, Action<AacFlSettingKeyframesColor> action)
{
var mutatedKeyframesR = new List<Keyframe>();
var mutatedKeyframesG = new List<Keyframe>();
var mutatedKeyframesB = new List<Keyframe>();
var mutatedKeyframesA = new List<Keyframe>();
var builder = new AacFlSettingKeyframesColor(unit, mutatedKeyframesR, mutatedKeyframesG, mutatedKeyframesB, mutatedKeyframesA);
action.Invoke(builder);
foreach (var binding in _bindings)
{
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "r"), new AnimationCurve(mutatedKeyframesR.ToArray()));
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "g"), new AnimationCurve(mutatedKeyframesG.ToArray()));
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "b"), new AnimationCurve(mutatedKeyframesB.ToArray()));
AnimationUtility.SetEditorCurve(_clip, AacV0.ToSubBinding(binding, "a"), new AnimationCurve(mutatedKeyframesA.ToArray()));
}
}
}
public class AacFlSettingKeyframes
{
private readonly AacFlUnit _unit;
private readonly List<Keyframe> _mutatedKeyframes;
public AacFlSettingKeyframes(AacFlUnit unit, List<Keyframe> mutatedKeyframes)
{
_unit = unit;
_mutatedKeyframes = mutatedKeyframes;
}
public AacFlSettingKeyframes Easing(float timeInUnit, float value)
{
_mutatedKeyframes.Add(new Keyframe(AsSeconds(timeInUnit), value, 0, 0));
return this;
}
public AacFlSettingKeyframes Constant(float timeInUnit, float value)
{
_mutatedKeyframes.Add(new Keyframe(AsSeconds(timeInUnit), value, 0, float.PositiveInfinity));
return this;
}
public AacFlSettingKeyframes Linear(float timeInUnit, float value)
{
float valueEnd = value;
float valueStart = _mutatedKeyframes.Count == 0 ? value : _mutatedKeyframes.Last().value;
float timeEnd = AsSeconds(timeInUnit);
float timeStart = _mutatedKeyframes.Count == 0 ? value : _mutatedKeyframes.Last().time;
float num = (float) (((double) valueEnd - (double) valueStart) / ((double) timeEnd - (double) timeStart));
// FIXME: This can cause NaN tangents which messes everything
// return new AnimationCurve(new Keyframe[2]
// {
// new Keyframe(timeStart, valueStart, 0.0f, num),
// new Keyframe(timeEnd, valueEnd, num, 0.0f)
// });
if (_mutatedKeyframes.Count > 0)
{
var lastKeyframe = _mutatedKeyframes.Last();
lastKeyframe.outTangent = num;
_mutatedKeyframes[_mutatedKeyframes.Count - 1] = lastKeyframe;
}
_mutatedKeyframes.Add(new Keyframe(AsSeconds(timeInUnit), value, num, 0.0f));
return this;
}
private float AsSeconds(float timeInUnit)
{
switch (_unit)
{
case AacFlUnit.Frames:
return timeInUnit / 60f;
case AacFlUnit.Seconds:
return timeInUnit;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public class AacFlSettingKeyframesColor
{
private AacFlSettingKeyframes _r;
private AacFlSettingKeyframes _g;
private AacFlSettingKeyframes _b;
private AacFlSettingKeyframes _a;
public AacFlSettingKeyframesColor(AacFlUnit unit, List<Keyframe> mutatedKeyframesR, List<Keyframe> mutatedKeyframesG, List<Keyframe> mutatedKeyframesB, List<Keyframe> mutatedKeyframesA)
{
_r = new AacFlSettingKeyframes(unit, mutatedKeyframesR);
_g = new AacFlSettingKeyframes(unit, mutatedKeyframesG);
_b = new AacFlSettingKeyframes(unit, mutatedKeyframesB);
_a = new AacFlSettingKeyframes(unit, mutatedKeyframesA);
}
public AacFlSettingKeyframesColor Easing(int frame, Color value)
{
_r.Easing(frame, value.r);
_g.Easing(frame, value.g);
_b.Easing(frame, value.b);
_a.Easing(frame, value.a);
return this;
}
public AacFlSettingKeyframesColor Linear(float frame, Color value)
{
_r.Linear(frame, value.r);
_g.Linear(frame, value.g);
_b.Linear(frame, value.b);
_a.Linear(frame, value.a);
return this;
}
public AacFlSettingKeyframesColor Constant(int frame, Color value)
{
_r.Constant(frame, value.r);
_g.Constant(frame, value.g);
_b.Constant(frame, value.b);
_a.Constant(frame, value.a);
return this;
}
}
public enum AacFlUnit
{
Frames, Seconds
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fec00a00ea40dce4eb7e0adf7a1696a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+225
View File
@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Animations;
using static AnimatorAsCode.V0.AacFlConditionSimple;
using static UnityEditor.Animations.AnimatorConditionMode;
// ReSharper disable once CheckNamespace
namespace AnimatorAsCode.V0
{
class AacFlConditionSimple : IAacFlCondition
{
private readonly Action<AacFlCondition> _action;
public AacFlConditionSimple(Action<AacFlCondition> action)
{
_action = action;
}
public static AacFlConditionSimple Just(Action<AacFlCondition> action)
{
return new AacFlConditionSimple(action);
}
public static AacFlConditionSimple ForEach(string[] subjects, Action<string, AacFlCondition> action)
{
return new AacFlConditionSimple(condition =>
{
foreach (var subject in subjects)
{
action.Invoke(subject, condition);
}
});
}
public void ApplyTo(AacFlCondition appender)
{
_action.Invoke(appender);
}
}
public abstract class AacFlParameter
{
public string Name { get; }
protected AacFlParameter(string name)
{
Name = name;
}
}
public class AacFlFloatParameter : AacFlParameter
{
internal static AacFlFloatParameter Internally(string name) => new AacFlFloatParameter(name);
protected AacFlFloatParameter(string name) : base(name) { }
public IAacFlCondition IsGreaterThan(float other) => Just(condition => condition.Add(Name, Greater, other));
public IAacFlCondition IsLessThan(float other) => Just(condition => condition.Add(Name, Less, other));
}
public class AacFlIntParameter : AacFlParameter
{
internal static AacFlIntParameter Internally(string name) => new AacFlIntParameter(name);
protected AacFlIntParameter(string name) : base(name) { }
public IAacFlCondition IsGreaterThan(int other) => Just(condition => condition.Add(Name, Greater, other));
public IAacFlCondition IsLessThan(int other) => Just(condition => condition.Add(Name, Less, other));
public IAacFlCondition IsEqualTo(int other) => Just(condition => condition.Add(Name, AnimatorConditionMode.Equals, other));
public IAacFlCondition IsNotEqualTo(int other) => Just(condition => condition.Add(Name, NotEqual, other));
}
public class AacFlEnumIntParameter<TEnum> : AacFlIntParameter where TEnum : Enum
{
internal static AacFlEnumIntParameter<TInEnum> Internally<TInEnum>(string name) where TInEnum : Enum => new AacFlEnumIntParameter<TInEnum>(name);
protected AacFlEnumIntParameter(string name) : base(name)
{
}
public IAacFlCondition IsEqualTo(TEnum other) => IsEqualTo((int)(object)other);
public IAacFlCondition IsNotEqualTo(TEnum other) => IsNotEqualTo((int)(object)other);
}
public class AacFlBoolParameter : AacFlParameter
{
internal static AacFlBoolParameter Internally(string name) => new AacFlBoolParameter(name);
protected AacFlBoolParameter(string name) : base(name) { }
public IAacFlCondition IsTrue() => Just(condition => condition.Add(Name, If, 0));
public IAacFlCondition IsFalse() => Just(condition => condition.Add(Name, IfNot, 0));
public IAacFlCondition IsEqualTo(bool other) => Just(condition => condition.Add(Name, other ? If : IfNot, 0));
public IAacFlCondition IsNotEqualTo(bool other) => Just(condition => condition.Add(Name, other ? IfNot : If, 0));
}
public class AacFlFloatParameterGroup
{
internal static AacFlFloatParameterGroup Internally(params string[] names) => new AacFlFloatParameterGroup(names);
private readonly string[] _names;
private AacFlFloatParameterGroup(params string[] names) { _names = names; }
public List<AacFlBoolParameter> ToList() => _names.Select(AacFlBoolParameter.Internally).ToList();
public IAacFlCondition AreGreaterThan(float other) => ForEach(_names, (name, condition) => condition.Add(name, Greater, other));
public IAacFlCondition AreLesserThan(float other) => ForEach(_names, (name, condition) => condition.Add(name, Less, other));
}
public class AacFlIntParameterGroup
{
internal static AacFlIntParameterGroup Internally(params string[] names) => new AacFlIntParameterGroup(names);
private readonly string[] _names;
private AacFlIntParameterGroup(params string[] names) { _names = names; }
public List<AacFlBoolParameter> ToList() => _names.Select(AacFlBoolParameter.Internally).ToList();
public IAacFlCondition AreGreaterThan(float other) => ForEach(_names, (name, condition) => condition.Add(name, Greater, other));
public IAacFlCondition AreLesserThan(float other) => ForEach(_names, (name, condition) => condition.Add(name, Less, other));
public IAacFlCondition AreEqualTo(float other) => ForEach(_names, (name, condition) => condition.Add(name, AnimatorConditionMode.Equals, other));
public IAacFlCondition AreNotEqualTo(float other) => ForEach(_names, (name, condition) => condition.Add(name, NotEqual, other));
}
public class AacFlBoolParameterGroup
{
internal static AacFlBoolParameterGroup Internally(params string[] names) => new AacFlBoolParameterGroup(names);
private readonly string[] _names;
private AacFlBoolParameterGroup(params string[] names) { _names = names; }
public List<AacFlBoolParameter> ToList() => _names.Select(AacFlBoolParameter.Internally).ToList();
public IAacFlCondition AreTrue() => ForEach(_names, (name, condition) => condition.Add(name, If, 0));
public IAacFlCondition AreFalse() => ForEach(_names, (name, condition) => condition.Add(name, IfNot, 0));
public IAacFlCondition AreEqualTo(bool other) => ForEach(_names, (name, condition) => condition.Add(name, other ? If : IfNot, 0));
/// is true when all of the following conditions are met:
/// <ul>
/// <li>all of the parameters in the group must be false except for the parameter defined in exceptThisMustBeTrue if it is present in the group.</li>
/// <li>the parameter defined in exceptThisMustBeTrue must be true.</li>
/// </ul>
public IAacFlCondition AreFalseExcept(AacFlBoolParameter exceptThisMustBeTrue)
{
var group = new AacFlBoolParameterGroup(exceptThisMustBeTrue.Name);
return AreFalseExcept(group);
}
public IAacFlCondition AreFalseExcept(params AacFlBoolParameter[] exceptTheseMustBeTrue)
{
var group = new AacFlBoolParameterGroup(exceptTheseMustBeTrue.Select(parameter => parameter.Name).ToArray());
return AreFalseExcept(group);
}
public IAacFlCondition AreFalseExcept(AacFlBoolParameterGroup exceptTheseMustBeTrue) => Just(condition =>
{
foreach (var name in _names.Where(name => !exceptTheseMustBeTrue._names.Contains(name)))
{
condition.Add(name, IfNot, 0);
}
foreach (var name in exceptTheseMustBeTrue._names)
{
condition.Add(name, If, 0);
}
});
public IAacFlCondition AreTrueExcept(AacFlBoolParameter exceptThisMustBeFalse)
{
var group = new AacFlBoolParameterGroup(exceptThisMustBeFalse.Name);
return AreTrueExcept(group);
}
public IAacFlCondition AreTrueExcept(params AacFlBoolParameter[] exceptTheseMustBeFalse)
{
var group = new AacFlBoolParameterGroup(exceptTheseMustBeFalse.Select(parameter => parameter.Name).ToArray());
return AreTrueExcept(group);
}
public IAacFlCondition AreTrueExcept(AacFlBoolParameterGroup exceptTheseMustBeFalse) => Just(condition =>
{
foreach (var name in _names.Where(name => !exceptTheseMustBeFalse._names.Contains(name)))
{
condition.Add(name, If, 0);
}
foreach (var name in exceptTheseMustBeFalse._names)
{
condition.Add(name, IfNot, 0);
}
});
public IAacFlOrCondition IsAnyTrue()
{
return IsAnyEqualTo(true);
}
public IAacFlOrCondition IsAnyFalse()
{
return IsAnyEqualTo(false);
}
private IAacFlOrCondition IsAnyEqualTo(bool value)
{
return new AacFlBoolParameterIsAnyOrCondition(_names, value);
}
}
internal class AacFlBoolParameterIsAnyOrCondition : IAacFlOrCondition
{
private readonly string[] _names;
private readonly bool _value;
public AacFlBoolParameterIsAnyOrCondition(string[] names, bool value)
{
_names = names;
_value = value;
}
public List<AacFlTransitionContinuation> ApplyTo(AacFlNewTransitionContinuation firstContinuation)
{
var pendingContinuations = new List<AacFlTransitionContinuation>();
var newContinuation = firstContinuation;
for (var index = 0; index < _names.Length; index++)
{
var name = _names[index];
var pendingContinuation = newContinuation.When(AacFlBoolParameter.Internally(name).IsEqualTo(_value));
pendingContinuations.Add(pendingContinuation);
if (index < _names.Length - 1)
{
newContinuation = pendingContinuation.Or();
}
}
return pendingContinuations;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3efdfabd1ad83ef48a287268320facf8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+939
View File
@@ -0,0 +1,939 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Animations;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using VRC.SDKBase;
// ReSharper disable once CheckNamespace
namespace AnimatorAsCode.V0
{
internal class AacBackingAnimator
{
private readonly AacAnimatorGenerator _generator;
public AacBackingAnimator(AacAnimatorGenerator animatorGenerator)
{
_generator = animatorGenerator;
}
public AacFlBoolParameter BoolParameter(string parameterName)
{
var result = AacFlBoolParameter.Internally(parameterName);
_generator.CreateParamsAsNeeded(result);
return result;
}
public AacFlBoolParameter TriggerParameter(string parameterName)
{
var result = AacFlBoolParameter.Internally(parameterName);
_generator.CreateTriggerParamsAsNeeded(result);
return result;
}
public AacFlFloatParameter FloatParameter(string parameterName)
{
var result = AacFlFloatParameter.Internally(parameterName);
_generator.CreateParamsAsNeeded(result);
return result;
}
public AacFlIntParameter IntParameter(string parameterName)
{
var result = AacFlIntParameter.Internally(parameterName);
_generator.CreateParamsAsNeeded(result);
return result;
}
public AacFlEnumIntParameter<TEnum> EnumParameter<TEnum>(string parameterName) where TEnum : Enum
{
var result = AacFlEnumIntParameter<TEnum>.Internally<TEnum>(parameterName);
_generator.CreateParamsAsNeeded(result);
return result;
}
public AacFlBoolParameterGroup BoolParameters(params string[] parameterNames)
{
var result = AacFlBoolParameterGroup.Internally(parameterNames);
_generator.CreateParamsAsNeeded(result.ToList().ToArray());
return result;
}
public AacFlBoolParameterGroup TriggerParameters(params string[] parameterNames)
{
var result = AacFlBoolParameterGroup.Internally(parameterNames);
_generator.CreateTriggerParamsAsNeeded(result.ToList().ToArray());
return result;
}
public AacFlFloatParameterGroup FloatParameters(params string[] parameterNames)
{
var result = AacFlFloatParameterGroup.Internally(parameterNames);
_generator.CreateParamsAsNeeded(result.ToList().ToArray());
return result;
}
public AacFlIntParameterGroup IntParameters(params string[] parameterNames)
{
var result = AacFlIntParameterGroup.Internally(parameterNames);
_generator.CreateParamsAsNeeded(result.ToList().ToArray());
return result;
}
public AacFlBoolParameterGroup BoolParameters(params AacFlBoolParameter[] parameters)
{
var result = AacFlBoolParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray());
_generator.CreateParamsAsNeeded(parameters);
return result;
}
public AacFlBoolParameterGroup TriggerParameters(params AacFlBoolParameter[] parameters)
{
var result = AacFlBoolParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray());
_generator.CreateTriggerParamsAsNeeded(parameters);
return result;
}
public AacFlFloatParameterGroup FloatParameters(params AacFlFloatParameter[] parameters)
{
var result = AacFlFloatParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray());
_generator.CreateParamsAsNeeded(parameters);
return result;
}
public AacFlIntParameterGroup IntParameters(params AacFlIntParameter[] parameters)
{
var result = AacFlIntParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray());
_generator.CreateParamsAsNeeded(parameters);
return result;
}
}
internal class AacStateMachine
{
private readonly AnimatorStateMachine _machine;
private readonly AnimationClip _emptyClip;
private readonly AacBackingAnimator _backingAnimator;
private readonly IAacDefaultsProvider _defaultsProvider;
private readonly float _gridShiftX;
private readonly float _gridShiftY;
public AacStateMachine(AnimatorStateMachine machine, AnimationClip emptyClip, AacBackingAnimator backingAnimator, IAacDefaultsProvider defaultsProvider)
{
_machine = machine;
_emptyClip = emptyClip;
_backingAnimator = backingAnimator;
_defaultsProvider = defaultsProvider;
var grid = defaultsProvider.Grid();
_gridShiftX = grid.x;
_gridShiftY = grid.y;
}
internal AacBackingAnimator BackingAnimator()
{
return _backingAnimator;
}
public AacStateMachine WithEntryPosition(int x, int y)
{
_machine.entryPosition = GridPosition(x, y);
return this;
}
public AacStateMachine WithExitPosition(int x, int y)
{
_machine.exitPosition = GridPosition(x, y);
return this;
}
public AacStateMachine WithAnyStatePosition(int x, int y)
{
_machine.anyStatePosition = GridPosition(x, y);
return this;
}
public AacFlState NewState(string name, int x, int y)
{
var state = _machine.AddState(name, GridPosition(x, y));
_defaultsProvider.ConfigureState(state, _emptyClip);
return new AacFlState(state, _machine, _defaultsProvider);
}
public AacFlTransition AnyTransitionsTo(AacFlState destination)
{
return AnyTransition(destination, _machine);
}
public AacFlEntryTransition EntryTransitionsTo(AacFlState destination)
{
return EntryTransition(destination, _machine);
}
private AacFlTransition AnyTransition(AacFlState destination, AnimatorStateMachine animatorStateMachine)
{
return new AacFlTransition(ConfigureTransition(animatorStateMachine.AddAnyStateTransition(destination.State)), animatorStateMachine, null, destination.State);
}
private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition)
{
_defaultsProvider.ConfigureTransition(transition);
return transition;
}
private AacFlEntryTransition EntryTransition(AacFlState destination, AnimatorStateMachine animatorStateMachine)
{
return new AacFlEntryTransition(animatorStateMachine.AddEntryTransition(destination.State), animatorStateMachine, null, destination.State);
}
internal Vector3 LastStatePosition()
{
return _machine.states.Length > 0 ? _machine.states.Last().position : Vector3.zero;
}
private Vector3 GridPosition(int x, int y)
{
return new Vector3(x * _gridShiftX , y * _gridShiftY, 0);
}
}
public class AacFlState
{
public readonly AnimatorState State;
private readonly AnimatorStateMachine _machine;
private readonly IAacDefaultsProvider _defaultsProvider;
private readonly AacBackingAnimator _backingAnimator;
private VRCAvatarParameterDriver _driver;
private VRCAnimatorTrackingControl _tracking;
private VRCAnimatorLocomotionControl _locomotionControl;
public AacFlState(AnimatorState state, AnimatorStateMachine machine, IAacDefaultsProvider defaultsProvider)
{
State = state;
_machine = machine;
_defaultsProvider = defaultsProvider;
}
public AacFlState LeftOf(AacFlState otherState) => MoveNextTo(otherState, -1, 0);
public AacFlState RightOf(AacFlState otherState) => MoveNextTo(otherState, 1, 0);
public AacFlState Over(AacFlState otherState) => MoveNextTo(otherState, 0, -1);
public AacFlState Under(AacFlState otherState) => MoveNextTo(otherState, 0, 1);
public AacFlState LeftOf() => MoveNextTo(null, -1, 0);
public AacFlState RightOf() => MoveNextTo(null, 1, 0);
public AacFlState Over() => MoveNextTo(null, 0, -1);
public AacFlState Under() => MoveNextTo(null, 0, 1);
public AacFlState Shift(AacFlState otherState, int shiftX, int shiftY) => MoveNextTo(otherState, shiftX, shiftY);
private AacFlState MoveNextTo(AacFlState otherStateOrSecondToLastWhenNull, int x, int y)
{
if (otherStateOrSecondToLastWhenNull == null)
{
var other = _machine.states[_machine.states.Length - 2];
Shift(other.position, x, y);
return this;
}
else
{
var other = _machine.states.First(animatorState => animatorState.state == otherStateOrSecondToLastWhenNull.State);
Shift(other.position, x, y);
return this;
}
}
public AacFlState Shift(Vector3 otherPosition, int shiftX, int shiftY)
{
var states = _machine.states;
for (var index = 0; index < states.Length; index++)
{
var childAnimatorState = states[index];
if (childAnimatorState.state == State)
{
var cms = childAnimatorState;
cms.position = otherPosition + new Vector3(shiftX * _defaultsProvider.Grid().x, shiftY * _defaultsProvider.Grid().y, 0);
states[index] = cms;
break;
}
}
_machine.states = states;
return this;
}
public AacFlState WithAnimation(Motion clip)
{
State.motion = clip;
return this;
}
public AacFlState WithAnimation(AacFlClip clip)
{
State.motion = clip.Clip;
return this;
}
public AacFlTransition TransitionsTo(AacFlState destination)
{
return new AacFlTransition(ConfigureTransition(State.AddTransition(destination.State)), _machine, State, destination.State);
}
public AacFlTransition TransitionsFromAny()
{
return new AacFlTransition(ConfigureTransition(_machine.AddAnyStateTransition(State)), _machine, null, State);
}
public AacFlEntryTransition TransitionsFromEntry()
{
return new AacFlEntryTransition(_machine.AddEntryTransition(State), _machine, null, State);
}
public AacFlState AutomaticallyMovesTo(AacFlState destination)
{
var transition = ConfigureTransition(State.AddTransition(destination.State));
transition.hasExitTime = true;
return this;
}
public AacFlTransition Exits()
{
return new AacFlTransition(ConfigureTransition(State.AddExitTransition()), _machine, State, null);
}
private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition)
{
_defaultsProvider.ConfigureTransition(transition);
return transition;
}
public AacFlState Drives(AacFlIntParameter parameter, int value)
{
CreateDriverBehaviorIfNotExists();
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
type = VRC_AvatarParameterDriver.ChangeType.Set,
name = parameter.Name, value = value
});
return this;
}
public AacFlState Drives(AacFlFloatParameter parameter, float value)
{
CreateDriverBehaviorIfNotExists();
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
type = VRC_AvatarParameterDriver.ChangeType.Set,
name = parameter.Name, value = value
});
return this;
}
public AacFlState DrivingIncreases(AacFlFloatParameter parameter, float additiveValue)
{
CreateDriverBehaviorIfNotExists();
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
type = VRC_AvatarParameterDriver.ChangeType.Add,
name = parameter.Name, value = additiveValue
});
return this;
}
public AacFlState DrivingDecreases(AacFlFloatParameter parameter, float positiveValueToDecreaseBy)
{
CreateDriverBehaviorIfNotExists();
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
type = VRC_AvatarParameterDriver.ChangeType.Add,
name = parameter.Name, value = -positiveValueToDecreaseBy
});
return this;
}
public AacFlState DrivingRandomizesLocally(AacFlFloatParameter parameter, float min, float max)
{
CreateDriverBehaviorIfNotExists();
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
type = VRC_AvatarParameterDriver.ChangeType.Random,
name = parameter.Name, valueMin = min, valueMax = max
});
_driver.localOnly = true;
return this;
}
public AacFlState DrivingRandomizesLocally(AacFlIntParameter parameter, int min, int max)
{
CreateDriverBehaviorIfNotExists();
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
type = VRC_AvatarParameterDriver.ChangeType.Random,
name = parameter.Name, valueMin = min, valueMax = max
});
_driver.localOnly = true;
return this;
}
public AacFlState Drives(AacFlBoolParameter parameter, bool value)
{
CreateDriverBehaviorIfNotExists();
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
name = parameter.Name, value = value ? 1 : 0
});
return this;
}
public AacFlState Drives(AacFlBoolParameterGroup parameters, bool value)
{
CreateDriverBehaviorIfNotExists();
foreach (var parameter in parameters.ToList())
{
_driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter
{
name = parameter.Name, value = value ? 1 : 0
});
}
return this;
}
public AacFlState DrivingLocally()
{
CreateDriverBehaviorIfNotExists();
_driver.localOnly = true;
return this;
}
private void CreateDriverBehaviorIfNotExists()
{
if (_driver != null) return;
_driver = State.AddStateMachineBehaviour<VRCAvatarParameterDriver>();
_driver.parameters = new List<VRC_AvatarParameterDriver.Parameter>();
}
public AacFlState WithWriteDefaultsSetTo(bool shouldWriteDefaults)
{
State.writeDefaultValues = shouldWriteDefaults;
return this;
}
public AacFlState PrintsToLogUsingTrackingBehaviour(string value)
{
CreateTrackingBehaviorIfNotExists();
_tracking.debugString = value;
return this;
}
public AacFlState TrackingTracks(TrackingElement element)
{
CreateTrackingBehaviorIfNotExists();
SettingElementTo(element, VRC_AnimatorTrackingControl.TrackingType.Tracking);
return this;
}
public AacFlState TrackingAnimates(TrackingElement element)
{
CreateTrackingBehaviorIfNotExists();
SettingElementTo(element, VRC_AnimatorTrackingControl.TrackingType.Animation);
return this;
}
public AacFlState TrackingSets(TrackingElement element, VRC_AnimatorTrackingControl.TrackingType trackingType)
{
CreateTrackingBehaviorIfNotExists();
SettingElementTo(element, trackingType);
return this;
}
public AacFlState LocomotionEnabled()
{
CreateLocomotionBehaviorIfNotExists();
_locomotionControl.disableLocomotion = false;
return this;
}
public AacFlState LocomotionDisabled()
{
CreateLocomotionBehaviorIfNotExists();
_locomotionControl.disableLocomotion = true;
return this;
}
public AacFlState MotionTime(AacFlFloatParameter floatParam)
{
State.timeParameterActive = true;
State.timeParameter = floatParam.Name;
return this;
}
private void SettingElementTo(TrackingElement element, VRC_AnimatorTrackingControl.TrackingType target)
{
switch (element)
{
case TrackingElement.Head:
_tracking.trackingHead = target;
break;
case TrackingElement.LeftHand:
_tracking.trackingLeftHand = target;
break;
case TrackingElement.RightHand:
_tracking.trackingRightHand = target;
break;
case TrackingElement.Hip:
_tracking.trackingHip = target;
break;
case TrackingElement.LeftFoot:
_tracking.trackingLeftFoot = target;
break;
case TrackingElement.RightFoot:
_tracking.trackingRightFoot = target;
break;
case TrackingElement.LeftFingers:
_tracking.trackingLeftFingers = target;
break;
case TrackingElement.RightFingers:
_tracking.trackingRightFingers = target;
break;
case TrackingElement.Eyes:
_tracking.trackingEyes = target;
break;
case TrackingElement.Mouth:
_tracking.trackingMouth = target;
break;
default:
throw new ArgumentOutOfRangeException(nameof(element), element, null);
}
}
private void CreateTrackingBehaviorIfNotExists()
{
if (_tracking != null) return;
_tracking = State.AddStateMachineBehaviour<VRCAnimatorTrackingControl>();
}
private void CreateLocomotionBehaviorIfNotExists()
{
if (_locomotionControl != null) return;
_locomotionControl = State.AddStateMachineBehaviour<VRCAnimatorLocomotionControl>();
}
public enum TrackingElement
{
Head,
LeftHand,
RightHand,
Hip,
LeftFoot,
RightFoot,
LeftFingers,
RightFingers,
Eyes,
Mouth
}
public AacFlState WithSpeed(AacFlFloatParameter parameter)
{
State.speedParameter = parameter.Name;
State.speedParameterActive = true;
return this;
}
}
public class AacFlTransition : AacFlNewTransitionContinuation
{
private readonly AnimatorStateTransition _transition;
public AacFlTransition(AnimatorStateTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits)
{
_transition = transition;
}
public AacFlTransition WithSourceInterruption()
{
_transition.interruptionSource = TransitionInterruptionSource.Source;
return this;
}
public AacFlTransition WithTransitionDurationSeconds(float transitionDuration)
{
_transition.duration = transitionDuration;
return this;
}
public AacFlTransition WithOrderedInterruption()
{
_transition.orderedInterruption = true;
return this;
}
public AacFlTransition WithNoOrderedInterruption()
{
_transition.orderedInterruption = false;
return this;
}
public AacFlTransition WithTransitionToSelf()
{
_transition.canTransitionToSelf = true;
return this;
}
public AacFlTransition WithNoTransitionToSelf()
{
_transition.canTransitionToSelf = false;
return this;
}
public AacFlTransition AfterAnimationFinishes()
{
_transition.hasExitTime = true;
_transition.exitTime = 1;
return this;
}
public AacFlTransition AfterAnimationIsAtLeastAtPercent(float exitTimeNormalized)
{
_transition.hasExitTime = true;
_transition.exitTime = exitTimeNormalized;
return this;
}
public AacFlTransition WithTransitionDurationPercent(float transitionDurationNormalized)
{
_transition.hasFixedDuration = false;
_transition.duration = transitionDurationNormalized;
return this;
}
}
public class AacFlEntryTransition : AacFlNewTransitionContinuation
{
public AacFlEntryTransition(AnimatorTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits)
{
}
}
public interface IAacFlCondition
{
void ApplyTo(AacFlCondition appender);
}
public interface IAacFlOrCondition
{
List<AacFlTransitionContinuation> ApplyTo(AacFlNewTransitionContinuation firstContinuation);
}
public class AacFlCondition
{
private readonly AnimatorTransitionBase _transition;
public AacFlCondition(AnimatorTransitionBase transition)
{
_transition = transition;
}
public AacFlCondition Add(string parameter, AnimatorConditionMode mode, float threshold)
{
_transition.AddCondition(mode, threshold, parameter);
return this;
}
}
public class AacFlNewTransitionContinuation
{
public readonly AnimatorTransitionBase Transition;
private readonly AnimatorStateMachine _machine;
private readonly AnimatorState _sourceNullableIfAny;
private readonly AnimatorState _destinationNullableIfExits;
public AacFlNewTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits)
{
Transition = transition;
_machine = machine;
_sourceNullableIfAny = sourceNullableIfAny;
_destinationNullableIfExits = destinationNullableIfExits;
}
/// Adds a condition to the transition.
///
/// The settings of the transition can no longer be modified after this point.
/// <example>
/// <code>
/// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue())
/// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2))
/// .And(AacAv3.ItIsLocal())
/// .Or()
/// .When(_aac.BoolParameters(
/// my.myBoolParameterName,
/// my.myOtherBoolParameterName
/// ).AreTrue())
/// .And(AacAv3.ItIsRemote());
/// </code>
/// </example>
public AacFlTransitionContinuation When(IAacFlCondition action)
{
action.ApplyTo(new AacFlCondition(Transition));
return AsContinuationWithOr();
}
/// <summary>
/// Applies a series of conditions to this transition, but this series of conditions cannot include an Or operator.
/// </summary>
/// <param name="actionsWithoutOr"></param>
/// <returns></returns>
public AacFlTransitionContinuation When(Action<AacFlTransitionContinuationWithoutOr> actionsWithoutOr)
{
actionsWithoutOr(new AacFlTransitionContinuationWithoutOr(Transition));
return AsContinuationWithOr();
}
/// <summary>
/// Applies a series of conditions, and this series may contain Or operators. However, the result can not be followed by an And operator. It can only be an Or operator.
/// </summary>
/// <param name="actionsWithOr"></param>
/// <returns></returns>
public AacFlTransitionContinuationOnlyOr When(Action<AacFlNewTransitionContinuation> actionsWithOr)
{
actionsWithOr(this);
return AsContinuationOnlyOr();
}
/// <summary>
/// Applies a series of conditions, and this series may contain Or operators. All And operators that follow will apply to all the conditions generated by this series, until the next Or operator.
/// </summary>
/// <param name="actionsWithOr"></param>
/// <returns></returns>
public AacFlMultiTransitionContinuation When(IAacFlOrCondition actionsWithOr)
{
var pendingContinuations = actionsWithOr.ApplyTo(this);
return new AacFlMultiTransitionContinuation(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits, pendingContinuations);
}
public AacFlTransitionContinuation WhenConditions()
{
return AsContinuationWithOr();
}
private AacFlTransitionContinuation AsContinuationWithOr()
{
return new AacFlTransitionContinuation(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits);
}
private AacFlTransitionContinuationOnlyOr AsContinuationOnlyOr()
{
return new AacFlTransitionContinuationOnlyOr(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits);
}
}
public class AacFlTransitionContinuation : AacFlTransitionContinuationAbstractWithOr
{
public AacFlTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits)
{
}
/// Adds an additional condition to the transition that requires all preceding conditions to be true.
/// <example>
/// <code>
/// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue())
/// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2))
/// .And(AacAv3.ItIsLocal())
/// .Or()
/// .When(_aac.BoolParameters(
/// my.myBoolParameterName,
/// my.myOtherBoolParameterName
/// ).AreTrue())
/// .And(AacAv3.ItIsRemote());
/// </code>
/// </example>
public AacFlTransitionContinuation And(IAacFlCondition action)
{
action.ApplyTo(new AacFlCondition(Transition));
return this;
}
/// <summary>
/// Applies a series of conditions to this transition. The conditions cannot include an Or operator.
/// </summary>
/// <param name="actionsWithoutOr"></param>
/// <returns></returns>
public AacFlTransitionContinuation And(Action<AacFlTransitionContinuationWithoutOr> actionsWithoutOr)
{
actionsWithoutOr(new AacFlTransitionContinuationWithoutOr(Transition));
return this;
}
}
public class AacFlMultiTransitionContinuation : AacFlTransitionContinuationAbstractWithOr
{
private readonly List<AacFlTransitionContinuation> _pendingContinuations;
public AacFlMultiTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits, List<AacFlTransitionContinuation> pendingContinuations) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits)
{
_pendingContinuations = pendingContinuations;
}
/// Adds an additional condition to these transitions that requires all preceding conditions to be true.
/// <example>
/// <code>
/// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue())
/// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2))
/// .And(AacAv3.ItIsLocal())
/// .Or()
/// .When(_aac.BoolParameters(
/// my.myBoolParameterName,
/// my.myOtherBoolParameterName
/// ).AreTrue())
/// .And(AacAv3.ItIsRemote());
/// </code>
/// </example>
public AacFlMultiTransitionContinuation And(IAacFlCondition action)
{
foreach (var pendingContinuation in _pendingContinuations)
{
pendingContinuation.And(action);
}
return this;
}
/// <summary>
/// Applies a series of conditions to these transitions. The conditions cannot include an Or operator.
/// </summary>
/// <param name="actionsWithoutOr"></param>
/// <returns></returns>
public AacFlMultiTransitionContinuation And(Action<AacFlTransitionContinuationWithoutOr> actionsWithoutOr)
{
foreach (var pendingContinuation in _pendingContinuations)
{
pendingContinuation.And(actionsWithoutOr);
}
return this;
}
}
public class AacFlTransitionContinuationOnlyOr : AacFlTransitionContinuationAbstractWithOr
{
public AacFlTransitionContinuationOnlyOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits)
{
}
}
public abstract class AacFlTransitionContinuationAbstractWithOr
{
protected readonly AnimatorTransitionBase Transition;
private readonly AnimatorStateMachine _machine;
private readonly AnimatorState _sourceNullableIfAny;
private readonly AnimatorState _destinationNullableIfExits;
public AacFlTransitionContinuationAbstractWithOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits)
{
Transition = transition;
_machine = machine;
_sourceNullableIfAny = sourceNullableIfAny;
_destinationNullableIfExits = destinationNullableIfExits;
}
/// <summary>
/// Creates a new transition with identical settings but having no conditions defined yet.
/// </summary>
/// <example>
/// <code>
/// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue())
/// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2))
/// .And(AacAv3.ItIsLocal())
/// .Or()
/// .When(_aac.BoolParameters(
/// my.myBoolParameterName,
/// my.myOtherBoolParameterName
/// ).AreTrue())
/// .And(AacAv3.ItIsRemote());
/// </code>
/// </example>
public AacFlNewTransitionContinuation Or()
{
return new AacFlNewTransitionContinuation(NewTransitionFromTemplate(), _machine, _sourceNullableIfAny, _destinationNullableIfExits);
}
private AnimatorTransitionBase NewTransitionFromTemplate()
{
AnimatorTransitionBase newTransition;
if (Transition is AnimatorStateTransition templateStateTransition)
{
var stateTransition = NewTransition();
stateTransition.duration = templateStateTransition.duration;
stateTransition.offset = templateStateTransition.offset;
stateTransition.interruptionSource = templateStateTransition.interruptionSource;
stateTransition.orderedInterruption = templateStateTransition.orderedInterruption;
stateTransition.exitTime = templateStateTransition.exitTime;
stateTransition.hasExitTime = templateStateTransition.hasExitTime;
stateTransition.hasFixedDuration = templateStateTransition.hasFixedDuration;
stateTransition.canTransitionToSelf = templateStateTransition.canTransitionToSelf;
newTransition = stateTransition;
}
else
{
newTransition = _machine.AddEntryTransition(_destinationNullableIfExits);
}
return newTransition;
}
private AnimatorStateTransition NewTransition()
{
if (_sourceNullableIfAny == null)
{
return _machine.AddAnyStateTransition(_destinationNullableIfExits);
}
if (_destinationNullableIfExits == null)
{
return _sourceNullableIfAny.AddExitTransition();
}
return _sourceNullableIfAny.AddTransition(_destinationNullableIfExits);
}
}
public class AacFlTransitionContinuationWithoutOr
{
private readonly AnimatorTransitionBase _transition;
public AacFlTransitionContinuationWithoutOr(AnimatorTransitionBase transition)
{
_transition = transition;
}
public AacFlTransitionContinuationWithoutOr And(IAacFlCondition action)
{
action.ApplyTo(new AacFlCondition(_transition));
return this;
}
/// <summary>
/// Applies a series of conditions to this transition. The conditions cannot include an Or operator.
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public AacFlTransitionContinuationWithoutOr AndWhenever(Action<AacFlTransitionContinuationWithoutOr> action)
{
action(this);
return this;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0a803c4ceabee84b9081a8728fc4bf9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Haï~ (@vr_hai github.com/hai-vr)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1
View File
@@ -0,0 +1 @@