imported animancer and rainbow folder

This commit is contained in:
2025-10-01 20:11:27 +02:00
parent c51c07d19b
commit 8a84bd459f
1649 changed files with 689172 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 07fd09f3df0ed1b4a876daf641301455
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac7b5777c4040094a9e5e2dae7be67b7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,529 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &-5087839347003138994
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Body-URP
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _ALPHATEST_ON
- _NORMALMAP
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
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: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
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}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 1
- _AlphaToMask: 1
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _Metallic: 0.5
- _Mode: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.1
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Body-Builtin
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _ALPHATEST_ON
- _NORMALMAP
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
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: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
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_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _Metallic: 0.5
- _Mode: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
--- !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: 11500000, guid: 8231c727c20d639459e01298ffacd6c6, type: 3}
m_Name: AnimancerHumanoid-Body-Variants
m_EditorClassIdentifier:
_Material: {fileID: 2100000, guid: cac1be5495752be49a0c8b893d7c6865, type: 2}
_Variants:
- {fileID: -5087839347003138994}
- {fileID: 8308403988194906132}
- {fileID: 2100000}
--- !u!21 &8308403988194906132
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Body-HDRP
m_Shader: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _ALPHATEST_ON
- _DISABLE_SSR_TRANSPARENT
- _NORMALMAP
- _NORMALMAP_TANGENT_SPACE
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2475
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses:
- TransparentDepthPrepass
- TransparentDepthPostpass
- TransparentBackface
- RayTracingPrepass
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _AnisotropyMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BaseColorMap:
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BentNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BentNormalMapOS:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _CoatMaskMap:
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}
- _DetailMap:
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}
- _EmissiveColorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _HeightMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _IridescenceMaskMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _IridescenceThicknessMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MaskMap:
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}
- _NormalMap:
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _NormalMapOS:
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}
- _SpecularColorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SubsurfaceMaskMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TangentMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TangentMapOS:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ThicknessMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TransmissionMaskMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TransmittanceColorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AORemapMax: 1
- _AORemapMin: 0
- _ATDistance: 1
- _AddPrecomputedVelocity: 0
- _AlbedoAffectEmissive: 0
- _AlphaCutoff: 0.5
- _AlphaCutoffEnable: 1
- _AlphaCutoffPostpass: 0.5
- _AlphaCutoffPrepass: 0.5
- _AlphaCutoffShadow: 0.5
- _AlphaDstBlend: 0
- _AlphaRemapMax: 1
- _AlphaRemapMin: 0
- _AlphaSrcBlend: 1
- _Anisotropy: 0
- _BlendMode: 0
- _BumpScale: 1
- _CoatMask: 0
- _CullMode: 2
- _CullModeForward: 2
- _Cutoff: 0.5
- _DepthOffsetEnable: 0
- _DetailAlbedoScale: 1
- _DetailNormalMapScale: 1
- _DetailNormalScale: 1
- _DetailSmoothnessScale: 1
- _DiffusionProfile: 0
- _DiffusionProfileHash: 0
- _DisplacementLockObjectScale: 1
- _DisplacementLockTilingScale: 1
- _DisplacementMode: 0
- _DoubleSidedEnable: 0
- _DoubleSidedGIMode: 0
- _DoubleSidedNormalMode: 1
- _DstBlend: 0
- _EmissiveColorMode: 1
- _EmissiveExposureWeight: 1
- _EmissiveIntensity: 1
- _EmissiveIntensityUnit: 0
- _EnableBlendModePreserveSpecularLighting: 1
- _EnableFogOnTransparent: 1
- _EnableGeometricSpecularAA: 0
- _EnergyConservingSpecularColor: 1
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _HeightAmplitude: 0.02
- _HeightCenter: 0.5
- _HeightMapParametrization: 0
- _HeightMax: 1
- _HeightMin: -1
- _HeightOffset: 0
- _HeightPoMAmplitude: 2
- _HeightTessAmplitude: 2
- _HeightTessCenter: 0.5
- _InvTilingScale: 1
- _Ior: 1.5
- _IridescenceMask: 1
- _IridescenceThickness: 1
- _LinkDetailsWithBase: 0
- _MaterialID: 1
- _Metallic: 0.21763763
- _MetallicRemapMax: 0.21763763
- _MetallicRemapMin: 0
- _Mode: 1
- _NormalMapSpace: 0
- _NormalScale: 1
- _ObjectSpaceUVMapping: 0
- _ObjectSpaceUVMappingEmissive: 0
- _OcclusionStrength: 1
- _OpaqueCullMode: 2
- _PPDLodThreshold: 5
- _PPDMaxSamples: 15
- _PPDMinSamples: 5
- _PPDPrimitiveLength: 1
- _PPDPrimitiveWidth: 1
- _Parallax: 0.02
- _RayTracing: 0
- _ReceivesSSR: 1
- _ReceivesSSRTransparent: 0
- _RefractionModel: 0
- _Smoothness: 0.1
- _SmoothnessRemapMax: 0.1
- _SmoothnessRemapMin: 0
- _SmoothnessTextureChannel: 0
- _SpecularAAScreenSpaceVariance: 0.1
- _SpecularAAThreshold: 0.2
- _SpecularHighlights: 1
- _SpecularOcclusionMode: 1
- _SrcBlend: 1
- _StencilRef: 0
- _StencilRefDepth: 8
- _StencilRefGBuffer: 10
- _StencilRefMV: 40
- _StencilWriteMask: 6
- _StencilWriteMaskDepth: 9
- _StencilWriteMaskGBuffer: 15
- _StencilWriteMaskMV: 41
- _SubsurfaceMask: 1
- _SupportDecals: 1
- _SurfaceType: 0
- _TexWorldScale: 1
- _TexWorldScaleEmissive: 1
- _Thickness: 1
- _TransmissionEnable: 1
- _TransmissionMask: 1
- _TransparentBackfaceEnable: 0
- _TransparentCullMode: 2
- _TransparentDepthPostpassEnable: 0
- _TransparentDepthPrepassEnable: 0
- _TransparentSortPriority: 0
- _TransparentWritingMotionVec: 0
- _TransparentZWrite: 0
- _UVBase: 0
- _UVDetail: 0
- _UVEmissive: 0
- _UVSec: 0
- _UseEmissiveIntensity: 0
- _UseShadowThreshold: 0
- _ZTestDepthEqualForOpaque: 3
- _ZTestGBuffer: 3
- _ZTestTransparent: 4
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _BaseColorMap_MipInfo: {r: 0, g: 0, b: 0, a: 0}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _DiffusionProfileAsset: {r: 0, g: 0, b: 0, a: 0}
- _DoubleSidedConstants: {r: 1, g: 1, b: -1, a: 0}
- _EmissionColor: {r: 1, g: 1, b: 1, a: 1}
- _EmissiveColor: {r: 0, g: 0, b: 0, a: 1}
- _EmissiveColorLDR: {r: 0, g: 0, b: 0, a: 1}
- _InvPrimScale: {r: 1, g: 1, b: 0, a: 0}
- _IridescenceThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _ThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
- _TransmittanceColor: {r: 1, g: 1, b: 1, a: 1}
- _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0}
- _UVMappingMask: {r: 1, g: 0, b: 0, a: 0}
- _UVMappingMaskEmissive: {r: 1, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 337e4811ec2d4a848b80babd997d4be5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Body-Variants.asset
uploadId: 795566

View File

@@ -0,0 +1,139 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-2036435769910986465
MonoBehaviour:
m_ObjectHideFlags: 11
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: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Body
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _ALPHATEST_ON
- _NORMALMAP
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
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: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
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}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 1
- _AlphaToMask: 1
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _Metallic: 0.5
- _Mode: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.1
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: cac1be5495752be49a0c8b893d7c6865
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Body.mat
uploadId: 795566

View File

@@ -0,0 +1,529 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Face-Builtin
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _ALPHATEST_ON
- _NORMALMAP
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
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: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
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_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _Metallic: 0.5
- _Mode: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
--- !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: 11500000, guid: 8231c727c20d639459e01298ffacd6c6, type: 3}
m_Name: AnimancerHumanoid-Face-Variants
m_EditorClassIdentifier:
_Material: {fileID: 2100000, guid: 6535c7c5fb8a8934689cbde1388be193, type: 2}
_Variants:
- {fileID: 1581502679226286254}
- {fileID: 6540744349012763745}
- {fileID: 2100000}
--- !u!21 &1581502679226286254
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Face-URP
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _ALPHATEST_ON
- _NORMALMAP
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
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: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
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}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 1
- _AlphaToMask: 1
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _Metallic: 0.5
- _Mode: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.1
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
--- !u!21 &6540744349012763745
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Face-HDRP
m_Shader: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _ALPHATEST_ON
- _DISABLE_SSR_TRANSPARENT
- _NORMALMAP
- _NORMALMAP_TANGENT_SPACE
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2475
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses:
- TransparentDepthPrepass
- TransparentDepthPostpass
- TransparentBackface
- RayTracingPrepass
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _AnisotropyMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BaseColorMap:
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BentNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BentNormalMapOS:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _CoatMaskMap:
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}
- _DetailMap:
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}
- _EmissiveColorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _HeightMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _IridescenceMaskMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _IridescenceThicknessMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MaskMap:
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}
- _NormalMap:
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _NormalMapOS:
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}
- _SpecularColorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SubsurfaceMaskMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TangentMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TangentMapOS:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ThicknessMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TransmissionMaskMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _TransmittanceColorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AORemapMax: 1
- _AORemapMin: 0
- _ATDistance: 1
- _AddPrecomputedVelocity: 0
- _AlbedoAffectEmissive: 0
- _AlphaCutoff: 0.5
- _AlphaCutoffEnable: 1
- _AlphaCutoffPostpass: 0.5
- _AlphaCutoffPrepass: 0.5
- _AlphaCutoffShadow: 0.5
- _AlphaDstBlend: 0
- _AlphaRemapMax: 1
- _AlphaRemapMin: 0
- _AlphaSrcBlend: 1
- _Anisotropy: 0
- _BlendMode: 0
- _BumpScale: 1
- _CoatMask: 0
- _CullMode: 2
- _CullModeForward: 2
- _Cutoff: 0.5
- _DepthOffsetEnable: 0
- _DetailAlbedoScale: 1
- _DetailNormalMapScale: 1
- _DetailNormalScale: 1
- _DetailSmoothnessScale: 1
- _DiffusionProfile: 0
- _DiffusionProfileHash: 0
- _DisplacementLockObjectScale: 1
- _DisplacementLockTilingScale: 1
- _DisplacementMode: 0
- _DoubleSidedEnable: 0
- _DoubleSidedGIMode: 0
- _DoubleSidedNormalMode: 1
- _DstBlend: 0
- _EmissiveColorMode: 1
- _EmissiveExposureWeight: 1
- _EmissiveIntensity: 1
- _EmissiveIntensityUnit: 0
- _EnableBlendModePreserveSpecularLighting: 1
- _EnableFogOnTransparent: 1
- _EnableGeometricSpecularAA: 0
- _EnergyConservingSpecularColor: 1
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _HeightAmplitude: 0.02
- _HeightCenter: 0.5
- _HeightMapParametrization: 0
- _HeightMax: 1
- _HeightMin: -1
- _HeightOffset: 0
- _HeightPoMAmplitude: 2
- _HeightTessAmplitude: 2
- _HeightTessCenter: 0.5
- _InvTilingScale: 1
- _Ior: 1.5
- _IridescenceMask: 1
- _IridescenceThickness: 1
- _LinkDetailsWithBase: 0
- _MaterialID: 1
- _Metallic: 0.21763763
- _MetallicRemapMax: 0.21763763
- _MetallicRemapMin: 0
- _Mode: 1
- _NormalMapSpace: 0
- _NormalScale: 1
- _ObjectSpaceUVMapping: 0
- _ObjectSpaceUVMappingEmissive: 0
- _OcclusionStrength: 1
- _OpaqueCullMode: 2
- _PPDLodThreshold: 5
- _PPDMaxSamples: 15
- _PPDMinSamples: 5
- _PPDPrimitiveLength: 1
- _PPDPrimitiveWidth: 1
- _Parallax: 0.02
- _RayTracing: 0
- _ReceivesSSR: 1
- _ReceivesSSRTransparent: 0
- _RefractionModel: 0
- _Smoothness: 0.1
- _SmoothnessRemapMax: 0.1
- _SmoothnessRemapMin: 0
- _SmoothnessTextureChannel: 0
- _SpecularAAScreenSpaceVariance: 0.1
- _SpecularAAThreshold: 0.2
- _SpecularHighlights: 1
- _SpecularOcclusionMode: 1
- _SrcBlend: 1
- _StencilRef: 0
- _StencilRefDepth: 8
- _StencilRefGBuffer: 10
- _StencilRefMV: 40
- _StencilWriteMask: 6
- _StencilWriteMaskDepth: 9
- _StencilWriteMaskGBuffer: 15
- _StencilWriteMaskMV: 41
- _SubsurfaceMask: 1
- _SupportDecals: 1
- _SurfaceType: 0
- _TexWorldScale: 1
- _TexWorldScaleEmissive: 1
- _Thickness: 1
- _TransmissionEnable: 1
- _TransmissionMask: 1
- _TransparentBackfaceEnable: 0
- _TransparentCullMode: 2
- _TransparentDepthPostpassEnable: 0
- _TransparentDepthPrepassEnable: 0
- _TransparentSortPriority: 0
- _TransparentWritingMotionVec: 0
- _TransparentZWrite: 0
- _UVBase: 0
- _UVDetail: 0
- _UVEmissive: 0
- _UVSec: 0
- _UseEmissiveIntensity: 0
- _UseShadowThreshold: 0
- _ZTestDepthEqualForOpaque: 3
- _ZTestGBuffer: 3
- _ZTestTransparent: 4
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _BaseColorMap_MipInfo: {r: 0, g: 0, b: 0, a: 0}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _DiffusionProfileAsset: {r: 0, g: 0, b: 0, a: 0}
- _DoubleSidedConstants: {r: 1, g: 1, b: -1, a: 0}
- _EmissionColor: {r: 1, g: 1, b: 1, a: 1}
- _EmissiveColor: {r: 0, g: 0, b: 0, a: 1}
- _EmissiveColorLDR: {r: 0, g: 0, b: 0, a: 1}
- _InvPrimScale: {r: 1, g: 1, b: 0, a: 0}
- _IridescenceThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _ThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
- _TransmittanceColor: {r: 1, g: 1, b: 1, a: 1}
- _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0}
- _UVMappingMask: {r: 1, g: 0, b: 0, a: 0}
- _UVMappingMaskEmissive: {r: 1, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 36e0a2860b54531479d1a601cb1c61ff
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Face-Variants.asset
uploadId: 795566

View File

@@ -0,0 +1,139 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AnimancerHumanoid-Face
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _ALPHATEST_ON
- _NORMALMAP
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2450
stringTagMap:
RenderType: TransparentCutout
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
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: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
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}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 1
- _AlphaToMask: 1
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0.1
- _GlossyReflections: 1
- _Metallic: 0.5
- _Mode: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.1
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &949205788524907297
MonoBehaviour:
m_ObjectHideFlags: 11
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: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 6535c7c5fb8a8934689cbde1388be193
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Face.mat
uploadId: 795566

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: f976ca0fb1329b44a8bc3dcca706751a
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid.prefab
uploadId: 795566

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7e53ced3af95a6d44bd6c409fd8b29e5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 KiB

View File

@@ -0,0 +1,134 @@
fileFormatVersion: 2
guid: 7a55677d26cde6e439d31127b134e95b
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
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
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
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: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Body-Albedo.png
uploadId: 795566

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

View File

@@ -0,0 +1,134 @@
fileFormatVersion: 2
guid: 222dcdea0e5223c44a4021c4bc04bcf1
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 0
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
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
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: 1
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Body-Normal.png
uploadId: 795566

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 KiB

View File

@@ -0,0 +1,134 @@
fileFormatVersion: 2
guid: c644b15de86a4f043bfb7c162d1f0b66
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
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
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
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: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Face-Albedo.png
uploadId: 795566

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,134 @@
fileFormatVersion: 2
guid: 9dcc4e0c655862c45938e956f19ed59c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 0
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
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
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: 1
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Face-Normal.png
uploadId: 795566

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,113 @@
fileFormatVersion: 2
guid: d4e06a71fc03595429cac47cd385c4c1
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 5
mipmaps:
mipMapMode: 0
enableMipMap: 0
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
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -1
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
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: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Icon.png
uploadId: 795566

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b3bfb48a622b34b48a451f1ef2c43fe8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1cc25f23cb30b8c41ba4bb4390768a6c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] Displays the <see cref="AnimancerSettings"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/AnimancerSettingsTool
[Serializable]
public class AnimancerSettingsTool : AnimancerToolsWindow.Tool
{
/************************************************************************************************************************/
/// <inheritdoc/>
public override int DisplayOrder => int.MaxValue;
/// <inheritdoc/>
public override string Name => "Animancer Settings";
/// <inheritdoc/>
public override string Instructions => null;
/// <inheritdoc/>
public override string HelpURL
=> $"{Strings.DocsURLs.APIDocumentation}.{nameof(Editor)}/{nameof(AnimancerSettings)}";
/************************************************************************************************************************/
[NonSerialized] private readonly CachedEditor SettingsEditor = new();
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnDisable()
{
base.OnDisable();
SettingsEditor.Dispose();
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
var settings = AnimancerSettings.Instance;
if (settings == null)
return;
AnimancerSettings.Editor.HideNextInfo = true;
SettingsEditor.GetEditor(settings).OnInspectorGUI();
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 809481e9287856440bbcfa5e886163d4
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/AnimancerSettingsTool.cs
uploadId: 795566

View File

@@ -0,0 +1,279 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// An <see cref="EditorWindow"/> with various utilities for managing sprites and generating animations.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
/// Animancer Tools</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/AnimancerToolsWindow
///
public sealed partial class AnimancerToolsWindow : EditorWindow
{
/************************************************************************************************************************/
/// <summary>The display name of this window.</summary>
public const string Name = "Animancer Tools";
/// <summary>The singleton instance of this window.</summary>
public static AnimancerToolsWindow Instance { get; private set; }
[SerializeReference] private List<Tool> _Tools;
[SerializeField] private Vector2 _Scroll;
[SerializeField] private int _CurrentTool = -1;
/************************************************************************************************************************/
private SerializedObject _SerializedObject;
private SerializedObject SerializedObject
=> _SerializedObject ??= new(this);
/// <summary>Returns the <see cref="SerializedProperty"/> which represents the specified `tool`.</summary>
public SerializedProperty FindSerializedPropertyForTool(Tool tool)
{
var index = _Tools.IndexOf(tool);
var property = SerializedObject.FindProperty(nameof(_Tools));
return property.GetArrayElementAtIndex(index);
}
/************************************************************************************************************************/
private void OnEnable()
{
titleContent = new(Name);
Instance = this;
InitializeTools();
Undo.undoRedoPerformed += Repaint;
OnSelectionChange();
}
/************************************************************************************************************************/
private void InitializeTools()
{
AnimancerEditorUtilities.InstantiateDerivedTypes(ref _Tools);
for (int i = 0; i < _Tools.Count; i++)
_Tools[i].OnEnable(i);
}
/************************************************************************************************************************/
private int IndexOfTool(Type type)
{
for (int i = 0; i < _Tools.Count; i++)
if (_Tools[i].GetType() == type)
return i;
return -1;
}
/************************************************************************************************************************/
private void OnDisable()
{
Undo.undoRedoPerformed -= Repaint;
for (int i = 0; i < _Tools.Count; i++)
_Tools[i].OnDisable();
if (_SerializedObject != null)
{
_SerializedObject.Dispose();
_SerializedObject = null;
}
}
/************************************************************************************************************************/
private void OnSelectionChange()
{
for (int i = 0; i < _Tools.Count; i++)
_Tools[i].OnSelectionChanged();
Repaint();
}
/************************************************************************************************************************/
private void OnGUI()
{
EditorGUIUtility.labelWidth = Mathf.Min(EditorGUIUtility.labelWidth, position.width * 0.5f);
EditorGUIUtility.wideMode = true;
_Scroll = GUILayout.BeginScrollView(_Scroll);
GUILayout.BeginVertical();
GUILayout.EndVertical();
for (int i = 0; i < _Tools.Count; i++)
_Tools[i].DoGUI();
GUILayout.EndScrollView();
}
/************************************************************************************************************************/
/// <summary>Causes the <see cref="Instance"/> to redraw its GUI.</summary>
public static new void Repaint()
{
if (Instance != null)
((EditorWindow)Instance).Repaint();
}
/// <summary>Calls <see cref="Undo.RecordObject(Object, string)"/> for this window.</summary>
public static void RecordUndo()
=> Undo.RecordObject(Instance, Name);
/************************************************************************************************************************/
/// <summary>Calls <see cref="EditorGUI.BeginChangeCheck"/>.</summary>
public static void BeginChangeCheck()
=> EditorGUI.BeginChangeCheck();
/// <summary>Calls <see cref="EditorGUI.EndChangeCheck"/> and <see cref="RecordUndo"/> if it returned true.</summary>
public static bool EndChangeCheck()
{
if (!EditorGUI.EndChangeCheck())
return false;
RecordUndo();
return true;
}
/// <summary>Calls <see cref="EndChangeCheck"/> and sets the <c>field = value</c> if it returned true.</summary>
public static bool EndChangeCheck<T>(ref T field, T value)
{
if (!EndChangeCheck())
return false;
field = value;
return true;
}
/************************************************************************************************************************/
/// <summary>Creates and initializes a new <see cref="ReorderableList"/>.</summary>
public static ReorderableList CreateReorderableList<T>(
List<T> list,
string name,
ReorderableList.ElementCallbackDelegate drawElementCallback,
bool showFooter = false)
{
var reorderableList = new ReorderableList(list, typeof(T))
{
drawHeaderCallback = (area) => GUI.Label(area, name),
drawElementCallback = drawElementCallback,
elementHeight = AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing,
};
if (!showFooter)
{
reorderableList.footerHeight = 0;
reorderableList.displayAdd = false;
reorderableList.displayRemove = false;
}
return reorderableList;
}
/************************************************************************************************************************/
/// <summary>Creates and initializes a new <see cref="ReorderableList"/> for <see cref="Sprite"/>s.</summary>
public static ReorderableList CreateReorderableObjectList<T>(
List<T> objects,
string name,
bool showFooter = false)
where T : Object
{
var reorderableList = CreateReorderableList(objects, name, (area, index, isActive, isFocused) =>
{
area.y = Mathf.Ceil(area.y + AnimancerGUI.StandardSpacing * 0.5f);
area.height = AnimancerGUI.LineHeight;
BeginChangeCheck();
var obj = AnimancerGUI.DoObjectFieldGUI(area, "", objects[index], false);
if (EndChangeCheck())
{
objects[index] = obj;
}
}, showFooter);
if (showFooter)
{
reorderableList.onAddCallback = (list) => list.list.Add(null);
}
return reorderableList;
}
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="ReorderableList"/> for <see cref="string"/>s.</summary>
public static ReorderableList CreateReorderableStringList(
List<string> strings,
string name,
Func<Rect, int, string> doElementGUI)
{
return CreateReorderableList(strings, name, (area, index, isActive, isFocused) =>
{
area.y = Mathf.Ceil(area.y + AnimancerGUI.StandardSpacing * 0.5f);
area.height = AnimancerGUI.LineHeight;
BeginChangeCheck();
var str = doElementGUI(area, index);
if (EndChangeCheck())
{
strings[index] = str;
}
});
}
/// <summary>Creates a new <see cref="ReorderableList"/> for <see cref="string"/>s.</summary>
public static ReorderableList CreateReorderableStringList(
List<string> strings,
string name)
{
return CreateReorderableStringList(strings, name, (area, index) =>
{
return EditorGUI.TextField(area, strings[index]);
});
}
/************************************************************************************************************************/
/// <summary>Opens the <see cref="AnimancerToolsWindow"/>.</summary>
[MenuItem(Strings.AnimancerToolsMenuPath)]
public static void Open()
=> GetWindow<AnimancerToolsWindow>();
/// <summary>Opens the <see cref="AnimancerToolsWindow"/> showing the specified `tool`.</summary>
public static void Open(Type toolType)
{
var window = GetWindow<AnimancerToolsWindow>();
window._CurrentTool = AnimancerEditorUtilities.IndexOfType(window._Tools, toolType);
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 92f977efd2d1e4c49ada1f068a739289
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/AnimancerToolsWindow.cs
uploadId: 795566

View File

@@ -0,0 +1,101 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// A base <see cref="AnimancerToolsWindow.Tool"/> for modifying <see cref="AnimationClip"/>s.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
/// Animancer Tools</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/AnimationModifierTool
///
[Serializable]
public abstract class AnimationModifierTool : AnimancerToolsWindow.Tool
{
/************************************************************************************************************************/
[SerializeField]
private AnimationClip _Animation;
/// <summary>The currently selected <see cref="AnimationClip"/> asset.</summary>
public AnimationClip Animation => _Animation;
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnEnable(int index)
{
base.OnEnable(index);
OnAnimationChanged();
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnSelectionChanged()
{
if (Selection.activeObject is AnimationClip animation)
{
_Animation = animation;
OnAnimationChanged();
}
}
/************************************************************************************************************************/
/// <summary>Called whenever the selected <see cref="Animation"/> changes.</summary>
protected virtual void OnAnimationChanged() { }
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
AnimancerToolsWindow.BeginChangeCheck();
var animation = AnimancerGUI.DoObjectFieldGUI("Animation", _Animation, false);
if (AnimancerToolsWindow.EndChangeCheck(ref _Animation, animation))
OnAnimationChanged();
}
/************************************************************************************************************************/
/// <summary>Calls <see cref="AnimancerToolsWindow.Tool.SaveModifiedAsset"/> on the animation.</summary>
protected bool SaveAs()
{
AnimancerGUI.Deselect();
if (SaveModifiedAsset(
"Save Modified Animation",
"Where would you like to save the new animation?",
_Animation,
Modify))
{
_Animation = null;
OnAnimationChanged();
return true;
}
else return false;
}
/************************************************************************************************************************/
/// <summary>Override this to apply the desired modifications to the `animation` before it is saved.</summary>
protected virtual void Modify(AnimationClip animation) { }
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 4ab2916f8d63d1d4e9471536b78698a3
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/AnimationModifierTool.cs
uploadId: 795566

View File

@@ -0,0 +1,611 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using static Animancer.Editor.AnimancerGUI;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// A <see cref="SpriteModifierTool"/> for generating <see cref="AnimationClip"/>s from <see cref="Sprite"/>s.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/generate-sprite-animations">
/// Generate Sprite Animations</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/GenerateSpriteAnimationsTool
///
[Serializable]
public class GenerateSpriteAnimationsTool : SpriteModifierTool
{
/************************************************************************************************************************/
#region Tool
/************************************************************************************************************************/
[NonSerialized] private List<string> _Names;
[NonSerialized] private Dictionary<string, List<Sprite>> _NameToSprites;
[NonSerialized] private ReorderableList _Display;
[NonSerialized] private bool _NamesAreDirty;
[NonSerialized] private double _PreviewStartTime;
[NonSerialized] private long _PreviewFrameIndex;
[NonSerialized] private bool _RequiresRepaint;
/************************************************************************************************************************/
/// <inheritdoc/>
public override int DisplayOrder => 3;
/// <inheritdoc/>
public override string Name => "Generate Sprite Animations";
/// <inheritdoc/>
public override string HelpURL => Strings.DocsURLs.GenerateSpriteAnimations;
/// <inheritdoc/>
public override string Instructions
{
get
{
if (Sprites.Count == 0)
return "Select the Sprites you want to generate animations from.";
return "Configure the animation settings then click Generate.";
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnEnable(int index)
{
base.OnEnable(index);
_Names = new();
_NameToSprites = new();
_Display = AnimancerToolsWindow.CreateReorderableList(
_Names,
"Animations to Generate",
DrawDisplayElement);
_Display.elementHeightCallback = CalculateDisplayElementHeight;
_PreviewStartTime = EditorApplication.timeSinceStartup;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnSelectionChanged()
{
_NameToSprites.Clear();
_Names.Clear();
_NamesAreDirty = true;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
var property = GenerateSpriteAnimationsSettings.SerializedProperty;
property.serializedObject.Update();
using (var label = PooledGUIContent.Acquire("Settings"))
EditorGUILayout.PropertyField(property, label, true);
property.serializedObject.ApplyModifiedProperties();
GenerateSpriteAnimationsSettings.Instance.FillDefaults();
var sprites = Sprites;
if (_NamesAreDirty)
{
_NamesAreDirty = false;
GatherNameToSprites(sprites, _NameToSprites);
_Names.AddRange(_NameToSprites.Keys);
}
using (new EditorGUI.DisabledScope(true))
{
var previewCurrentTime = EditorApplication.timeSinceStartup - _PreviewStartTime;
_PreviewFrameIndex = (long)(previewCurrentTime * GenerateSpriteAnimationsSettings.FrameRate);
_Display.DoLayoutList();
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
GUI.enabled = sprites.Count > 0;
if (GUILayout.Button("Generate"))
{
Deselect();
GenerateAnimationsBySpriteName(sprites);
}
}
GUILayout.EndHorizontal();
}
EditorGUILayout.HelpBox("This function is also available via:" +
"\n• The 'Assets/Create/Animancer' menu." +
"\n• The Context Menu in the top right of the Inspector for Sprite and Texture assets",
MessageType.Info);
if (_RequiresRepaint)
{
_RequiresRepaint = false;
AnimancerToolsWindow.Repaint();
}
}
/************************************************************************************************************************/
/// <summary>Calculates the height of an animation to generate.</summary>
private float CalculateDisplayElementHeight(int index)
{
if (_NameToSprites.Count <= 0 || _Names.Count <= 0)
return 0;
var lineCount = _NameToSprites[_Names[index]].Count + 3;
return (LineHeight + StandardSpacing) * lineCount;
}
/************************************************************************************************************************/
/// <summary>Draws the details of an animation to generate.</summary>
private void DrawDisplayElement(Rect area, int index, bool isActive, bool isFocused)
{
area.y = Mathf.Ceil(area.y + StandardSpacing * 0.5f);
area.height = LineHeight;
DrawAnimationHeader(ref area, index, out var sprites);
DrawAnimationBody(ref area, sprites);
}
/************************************************************************************************************************/
/// <summary>Draws the name and preview of an animation to generate.</summary>
private void DrawAnimationHeader(ref Rect area, int index, out List<Sprite> sprites)
{
var width = area.width;
var previewSize = 3 * LineHeight + 2 * StandardSpacing;
var previewArea = StealFromRight(ref area, previewSize, StandardSpacing);
previewArea.height = previewSize;
// Name.
var name = _Names[index];
AnimancerToolsWindow.BeginChangeCheck();
name = EditorGUI.TextField(area, name);
if (AnimancerToolsWindow.EndChangeCheck())
{
_Names[index] = name;
}
NextVerticalArea(ref area);
// Frame Count.
sprites = _NameToSprites[name];
var frame = (int)(_PreviewFrameIndex % sprites.Count);
var enabled = GUI.enabled;
GUI.enabled = false;
EditorGUI.TextField(area, $"Frame {frame} / {sprites.Count}");
NextVerticalArea(ref area);
// Preview Time.
GUI.enabled = true;
var beforeControlID = GUIUtility.GetControlID(FocusType.Passive);
var newFrame = EditorGUI.IntSlider(area, frame, 0, sprites.Count);
var afterControlID = GUIUtility.GetControlID(FocusType.Passive);
var hotControl = GUIUtility.hotControl;
if (newFrame != frame ||
(hotControl > beforeControlID && hotControl < afterControlID))
{
_PreviewStartTime = EditorApplication.timeSinceStartup;
_PreviewStartTime -= newFrame / GenerateSpriteAnimationsSettings.FrameRate;
_PreviewFrameIndex = newFrame;
frame = newFrame % sprites.Count;
}
GUI.enabled = enabled;
NextVerticalArea(ref area);
area.width = width;
// Preview.
DrawSprite(previewArea, sprites[frame]);
_RequiresRepaint = true;
}
/************************************************************************************************************************/
/// <summary>Draws the sprite contents of an animation to generate.</summary>
private void DrawAnimationBody(ref Rect area, List<Sprite> sprites)
{
var previewFrame = (int)(_PreviewFrameIndex % sprites.Count);
for (int i = 0; i < sprites.Count; i++)
{
var sprite = sprites[i];
var fieldArea = area;
var thumbnailArea = StealFromLeft(
ref fieldArea,
fieldArea.height,
StandardSpacing);
AnimancerToolsWindow.BeginChangeCheck();
sprite = DoObjectFieldGUI(fieldArea, "", sprite, false);
if (AnimancerToolsWindow.EndChangeCheck())
{
sprites[i] = sprite;
}
if (i == previewFrame)
EditorGUI.DrawRect(fieldArea, new(0.25f, 1, 0.25f, 0.1f));
DrawSprite(thumbnailArea, sprite);
NextVerticalArea(ref area);
}
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Methods
/************************************************************************************************************************/
/// <summary>Uses <see cref="GatherNameToSprites"/> and creates new animations from those groups.</summary>
private static void GenerateAnimationsBySpriteName(List<Sprite> sprites)
{
if (sprites.Count == 0)
return;
sprites.Sort(NaturalCompare);
var nameToSprites = new Dictionary<string, List<Sprite>>();
GatherNameToSprites(sprites, nameToSprites);
var pathToSprites = new Dictionary<string, List<Sprite>>();
var message = StringBuilderPool.Instance.Acquire()
.Append("Do you wish to generate the following animations?");
const int MaxLines = 25;
var line = 0;
foreach (var nameToSpriteGroup in nameToSprites)
{
var path = AssetDatabase.GetAssetPath(nameToSpriteGroup.Value[0]);
path = Path.GetDirectoryName(path);
path = Path.Combine(path, nameToSpriteGroup.Key + ".anim");
pathToSprites.Add(path, nameToSpriteGroup.Value);
if (++line <= MaxLines)
{
message.AppendLine()
.Append("- ")
.Append(path)
.Append(" (")
.Append(nameToSpriteGroup.Value.Count)
.Append(" frames)");
}
}
if (line > MaxLines)
{
message.AppendLine()
.Append("And ")
.Append(line - MaxLines)
.Append(" others.");
}
if (!EditorUtility.DisplayDialog("Generate Sprite Animations?", message.ReleaseToString(), "Generate", "Cancel"))
return;
foreach (var pathToSpriteGroup in pathToSprites)
CreateAnimation(pathToSpriteGroup.Key, pathToSpriteGroup.Value.ToArray());
AssetDatabase.SaveAssets();
}
/************************************************************************************************************************/
private static char[] _Numbers, _TrimOther;
/// <summary>Groups the `sprites` by name into the `nameToSptires`.</summary>
private static void GatherNameToSprites(List<Sprite> sprites, Dictionary<string, List<Sprite>> nameToSprites)
{
for (int i = 0; i < sprites.Count; i++)
{
var sprite = sprites[i];
var name = sprite.name;
// Remove numbers from the end.
_Numbers ??= new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
name = name.TrimEnd(_Numbers);
// Then remove other characters from the end.
_TrimOther ??= new char[] { ' ', '_', '-' };
name = name.TrimEnd(_TrimOther);
// Doing both at once would turn "Attack2-0" (Attack 2 Frame 0) into "Attack" (losing the number).
if (!nameToSprites.TryGetValue(name, out var spriteGroup))
{
spriteGroup = new();
nameToSprites.Add(name, spriteGroup);
}
// Add the sprite to the group if it's not a duplicate.
if (spriteGroup.Count == 0 || spriteGroup[^1] != sprite)
spriteGroup.Add(sprite);
}
}
/************************************************************************************************************************/
/// <summary>Creates and saves a new <see cref="AnimationClip"/> that plays the `sprites`.</summary>
private static void CreateAnimation(string path, params Sprite[] sprites)
{
var frameRate = GenerateSpriteAnimationsSettings.FrameRate;
var hierarchyPath = GenerateSpriteAnimationsSettings.HierarchyPath;
var type = GenerateSpriteAnimationsSettings.TargetType.Type ?? typeof(SpriteRenderer);
var property = GenerateSpriteAnimationsSettings.PropertyName;
if (string.IsNullOrWhiteSpace(property))
property = "m_Sprite";
var clip = new AnimationClip
{
frameRate = frameRate,
};
var spriteKeyFrames = new ObjectReferenceKeyframe[sprites.Length];
for (int i = 0; i < spriteKeyFrames.Length; i++)
{
spriteKeyFrames[i] = new()
{
time = i / (float)frameRate,
value = sprites[i]
};
}
var spriteBinding = EditorCurveBinding.PPtrCurve(hierarchyPath, type, property);
AnimationUtility.SetObjectReferenceCurve(clip, spriteBinding, spriteKeyFrames);
AssetDatabase.CreateAsset(clip, path);
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Menu Functions
/************************************************************************************************************************/
private const string GenerateAnimationsBySpriteNameFunctionName = "Generate Animations By Sprite Name";
/************************************************************************************************************************/
/// <summary>Should <see cref="GenerateAnimationsBySpriteName()"/> be enabled or greyed out?</summary>
[MenuItem(Strings.CreateMenuPrefix + GenerateAnimationsBySpriteNameFunctionName, validate = true)]
private static bool ValidateGenerateAnimationsBySpriteName()
{
var selection = Selection.objects;
for (int i = 0; i < selection.Length; i++)
{
var selected = selection[i];
if (selected is Sprite || selected is Texture)
return true;
}
return false;
}
/// <summary>Calls <see cref="GenerateAnimationsBySpriteName(List{Sprite})"/> with the selected <see cref="Sprite"/>s.</summary>
[MenuItem(
itemName: Strings.CreateMenuPrefix + GenerateAnimationsBySpriteNameFunctionName,
priority = Strings.AssetMenuOrder + 8)]
private static void GenerateAnimationsBySpriteName()
{
var sprites = new List<Sprite>();
var selection = Selection.objects;
for (int i = 0; i < selection.Length; i++)
{
var selected = selection[i];
if (selected is Sprite sprite)
{
sprites.Add(sprite);
}
else if (selected is Texture2D texture)
{
sprites.AddRange(LoadAllSpritesInTexture(texture));
}
}
GenerateAnimationsBySpriteName(sprites);
}
/************************************************************************************************************************/
private static List<Sprite> _CachedSprites;
/// <summary>
/// Returns a list of <see cref="Sprite"/>s which will be passed into
/// <see cref="GenerateAnimationsBySpriteName(List{Sprite})"/> by <see cref="EditorApplication.delayCall"/>.
/// </summary>
private static List<Sprite> GetCachedSpritesToGenerateAnimations()
{
if (_CachedSprites == null)
return _CachedSprites = new();
// Delay the call in case multiple objects are selected.
if (_CachedSprites.Count == 0)
{
EditorApplication.delayCall += () =>
{
GenerateAnimationsBySpriteName(_CachedSprites);
_CachedSprites.Clear();
};
}
return _CachedSprites;
}
/************************************************************************************************************************/
/// <summary>
/// Adds the <see cref="MenuCommand.context"/> to the <see cref="GetCachedSpritesToGenerateAnimations"/>.
/// </summary>
[MenuItem("CONTEXT/" + nameof(Sprite) + GenerateAnimationsBySpriteNameFunctionName)]
private static void GenerateAnimationsFromSpriteByName(MenuCommand command)
{
GetCachedSpritesToGenerateAnimations().Add((Sprite)command.context);
}
/************************************************************************************************************************/
/// <summary>Should <see cref="GenerateAnimationsFromTextureBySpriteName"/> be enabled or greyed out?</summary>
[MenuItem("CONTEXT/" + nameof(TextureImporter) + GenerateAnimationsBySpriteNameFunctionName, validate = true)]
private static bool ValidateGenerateAnimationsFromTextureBySpriteName(MenuCommand command)
{
var importer = (TextureImporter)command.context;
var sprites = LoadAllSpritesAtPath(importer.assetPath);
return sprites.Length > 0;
}
/// <summary>
/// Adds all <see cref="Sprite"/> sub-assets of the <see cref="MenuCommand.context"/> to the
/// <see cref="GetCachedSpritesToGenerateAnimations"/>.
/// </summary>
[MenuItem("CONTEXT/" + nameof(TextureImporter) + GenerateAnimationsBySpriteNameFunctionName)]
private static void GenerateAnimationsFromTextureBySpriteName(MenuCommand command)
{
var cachedSprites = GetCachedSpritesToGenerateAnimations();
var importer = (TextureImporter)command.context;
cachedSprites.AddRange(LoadAllSpritesAtPath(importer.assetPath));
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
/************************************************************************************************************************/
#region Settings
/************************************************************************************************************************/
/// <summary>[Editor-Only] Settings for <see cref="GenerateSpriteAnimationsTool"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/GenerateSpriteAnimationsSettings
[Serializable, InternalSerializableType]
public class GenerateSpriteAnimationsSettings : AnimancerSettingsGroup
{
/************************************************************************************************************************/
/// <summary>Gets or creates an instance.</summary>
public static GenerateSpriteAnimationsSettings Instance
=> AnimancerSettingsGroup<GenerateSpriteAnimationsSettings>.Instance;
/// <summary>The <see cref="UnityEditor.SerializedProperty"/> representing the <see cref="Instance"/>.</summary>
public static SerializedProperty SerializedProperty
=> Instance.GetSerializedProperty(null);
/************************************************************************************************************************/
/// <inheritdoc/>
public override string DisplayName
=> "Generate Sprite Animations Tool";
/// <inheritdoc/>
public override int Index
=> 6;
/************************************************************************************************************************/
[SerializeField]
[Tooltip("The frame rate to use for new animations")]
private float _FrameRate = 12;
/// <summary>The frame rate to use for new animations.</summary>
public static ref float FrameRate
=> ref Instance._FrameRate;
/************************************************************************************************************************/
[SerializeField]
[Tooltip("The Transform Hierarchy path from the Animator to the object being animated" +
" using forward slashes '/' between each object name")]
private string _HierarchyPath;
/// <summary>The Transform Hierarchy path from the <see cref="Animator"/> to the object being animated.</summary>
public static ref string HierarchyPath
=> ref Instance._HierarchyPath;
/************************************************************************************************************************/
[SerializeField]
[Tooltip("The type of component being animated. Defaults to " + nameof(SpriteRenderer) + " if not set." +
" Use the type picker on the right or drag and drop a component onto it to set this field.")]
private SerializableTypeReference _TargetType = new(typeof(SpriteRenderer));
/// <summary>The type of component being animated. Defaults to <see cref="SpriteRenderer"/> if not set.</summary>
public static ref SerializableTypeReference TargetType
=> ref Instance._TargetType;
/************************************************************************************************************************/
/// <summary>The default value for <see cref="PropertyName"/>.</summary>
public const string DefaultPropertyName = "m_Sprite";
[SerializeField]
[Tooltip("The path of the property being animated. Defaults to " + DefaultPropertyName + " if not set.")]
private string _PropertyName = DefaultPropertyName;
/// <summary>The path of the property being animated.</summary>
public static ref string PropertyName
=> ref Instance._PropertyName;
/************************************************************************************************************************/
/// <summary>Reverts any empty values to their defaults.</summary>
public void FillDefaults()
{
if (string.IsNullOrWhiteSpace(_TargetType.QualifiedName))
_TargetType = new(typeof(SpriteRenderer));
if (string.IsNullOrWhiteSpace(_PropertyName))
_PropertyName = DefaultPropertyName;
}
/************************************************************************************************************************/
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 435f0cce717bfc544bbb5dc56db55763
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/GenerateSpriteAnimationsTool.cs
uploadId: 795566

View File

@@ -0,0 +1,251 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
using System;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// A <see cref="SpriteModifierTool"/> for modifying <see cref="Sprite"/> detauls.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/modify-sprites">
/// Modify Sprites</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/ModifySpritesTool
///
[Serializable]
public class ModifySpritesTool : SpriteModifierTool
{
/************************************************************************************************************************/
[SerializeField] private OffsetRectMode _RectMode;
[SerializeField] private Rect _RectOffset;
[SerializeField] private bool _SetPivot;
[SerializeField] private Vector2 _Pivot;
[SerializeField] private bool _SetAlignment;
[SerializeField] private SpriteAlignment _Alignment;
[SerializeField] private bool _SetBorder;
[SerializeField] private RectOffset _Border;
[SerializeField] private bool _ShowDetails;
/************************************************************************************************************************/
private enum OffsetRectMode { None, Add, Subtract }
private static readonly string[] OffsetRectModes = { "None", "Add", "Subtract" };
private SerializedProperty _SerializedProperty;
/************************************************************************************************************************/
/// <inheritdoc/>
public override int DisplayOrder => 1;
/// <inheritdoc/>
public override string Name => "Modify Sprites";
/// <inheritdoc/>
public override string HelpURL => Strings.DocsURLs.ModifySprites;
/// <inheritdoc/>
public override string Instructions
{
get
{
if (Sprites.Count == 0)
return "Select the Sprites you want to modify.";
if (!IsValidModification())
return "The current Rect Offset would move some Sprites outside the texture bounds.";
return "Enter the desired modifications and click Apply.";
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnEnable(int index)
{
base.OnEnable(index);
_SerializedProperty = AnimancerToolsWindow.Instance.FindSerializedPropertyForTool(this);
_SerializedProperty = _SerializedProperty.FindPropertyRelative(nameof(_RectMode));
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
base.DoBodyGUI();
var area = AnimancerGUI.LayoutSingleLineRect();
area.xMin += 4;
using (var label = PooledGUIContent.Acquire("Offset Rects", null))
area = EditorGUI.PrefixLabel(area, label);
AnimancerToolsWindow.BeginChangeCheck();
var selected = (OffsetRectMode)GUI.Toolbar(area, (int)_RectMode, OffsetRectModes);
AnimancerToolsWindow.EndChangeCheck(ref _RectMode, selected);
using (var property = _SerializedProperty.Copy())
{
property.serializedObject.Update();
var depth = property.depth;
while (property.Next(false) && property.depth >= depth)
{
EditorGUILayout.PropertyField(property, true);
}
property.serializedObject.ApplyModifiedProperties();
}
GUI.enabled = false;
for (int i = 0; i < Sprites.Count; i++)
{
if (_ShowDetails)
GUILayout.BeginVertical(GUI.skin.box);
var sprite = Sprites[i] = AnimancerGUI.DoObjectFieldGUI("", Sprites[i], false);
if (_ShowDetails)
{
if (_RectMode != OffsetRectMode.None)
EditorGUILayout.RectField("Rect", sprite.rect);
if (_SetPivot)
EditorGUILayout.Vector2Field("Pivot", sprite.pivot);
if (_SetBorder)
EditorGUILayout.Vector4Field("Border", sprite.border);
GUILayout.EndVertical();
}
}
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
GUI.enabled = Sprites.Count > 0 && IsValidModification();
if (GUILayout.Button("Apply"))
{
AnimancerGUI.Deselect();
AskAndApply();
}
}
GUILayout.EndHorizontal();
}
/************************************************************************************************************************/
private bool IsValidModification()
{
switch (_RectMode)
{
default:
case OffsetRectMode.None:
return true;
case OffsetRectMode.Add:
case OffsetRectMode.Subtract:
break;
}
var offset = GetOffset();
var sprites = Sprites;
for (int i = 0; i < sprites.Count; i++)
{
var sprite = sprites[i];
var rect = Add(sprite.rect, offset);
if (rect.xMin < 0 ||
rect.yMin < 0 ||
rect.xMax >= sprite.texture.width ||
rect.xMax >= sprite.texture.height)
{
return false;
}
}
return true;
}
/************************************************************************************************************************/
private Rect GetOffset()
{
return _RectMode switch
{
OffsetRectMode.Add
=> _RectOffset,
OffsetRectMode.Subtract
=> new(-_RectOffset.x, -_RectOffset.y, -_RectOffset.width, -_RectOffset.height),
_
=> throw new InvalidOperationException($"Can't {nameof(GetOffset)} when the mode is {_RectMode}."),
};
}
private static Rect Add(Rect a, Rect b)
{
a.x += b.x;
a.y += b.y;
a.width += b.width;
a.height += b.height;
return a;
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override string AreYouSure => "Are you sure you want to modify the borders of these Sprites?";
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void Modify(SpriteDataEditor data, int index, Sprite sprite)
{
switch (_RectMode)
{
default:
case OffsetRectMode.None:
break;
case OffsetRectMode.Add:
case OffsetRectMode.Subtract:
var rect = data.GetRect(index);
rect = Add(rect, GetOffset());
data.SetRect(index, rect);
break;
}
if (_SetPivot)
data.SetPivot(index, _Pivot);
if (_SetAlignment)
data.SetAlignment(index, _Alignment);
if (_SetBorder)
data.SetBorder(index, new(_Border.left, _Border.bottom, _Border.right, _Border.top));
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 296afd430bc64e5419309c1f569fe899
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/ModifySpritesTool.cs
uploadId: 795566

View File

@@ -0,0 +1,534 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// A <see cref="AnimancerToolsWindow.Tool"/> for packing multiple <see cref="Texture2D"/>s into a single image.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/pack-textures">
/// Pack Textures</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/PackTexturesTool
///
[Serializable]
public class PackTexturesTool : AnimancerToolsWindow.Tool
{
/************************************************************************************************************************/
[SerializeField] private List<Object> _AssetsToPack;
[SerializeField] private int _Padding;
[SerializeField] private int _MaximumSize = 8192;
[NonSerialized] private ReorderableList _TexturesDisplay;
/************************************************************************************************************************/
/// <inheritdoc/>
public override int DisplayOrder => 0;
/// <inheritdoc/>
public override string Name => "Pack Textures";
/// <inheritdoc/>
public override string HelpURL => Strings.DocsURLs.PackTextures;
/// <inheritdoc/>
public override string Instructions
{
get
{
if (_AssetsToPack.Count == 0)
return "Add the texture, sprites, and folders you want to pack to the list.";
return "Set the other details then click Pack and it will ask where you want to save the combined texture.";
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnEnable(int index)
{
base.OnEnable(index);
_AssetsToPack ??= new();
_TexturesDisplay = AnimancerToolsWindow.CreateReorderableObjectList(_AssetsToPack, "Textures", true);
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
GUILayout.BeginVertical();
_TexturesDisplay.DoLayoutList();
GUILayout.EndVertical();
HandleDragAndDropIntoList(GUILayoutUtility.GetLastRect(), _AssetsToPack, overwrite: false);
RemoveDuplicates(_AssetsToPack);
AnimancerToolsWindow.BeginChangeCheck();
var padding = EditorGUILayout.IntField("Padding", _Padding);
AnimancerToolsWindow.EndChangeCheck(ref _Padding, padding);
AnimancerToolsWindow.BeginChangeCheck();
var maximumSize = EditorGUILayout.IntField("Maximum Size", _MaximumSize);
maximumSize = Math.Max(maximumSize, 16);
AnimancerToolsWindow.EndChangeCheck(ref _MaximumSize, maximumSize);
#if !UNITY_IMAGE_CONVERSION
EditorGUILayout.HelpBox(
"This feature requires Unity's Built-in Image Conversion module." +
"\n1. Click here to open the Package Manager." +
"\n2. Open the Packages menu and select 'Built-in'." +
"\n3. Select the 'Image Conversion' module and Enable it.",
MessageType.Error);
if (AnimancerGUI.TryUseClickEventInLastRect())
EditorApplication.ExecuteMenuItem("Window/Package Manager");
#endif
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
GUI.enabled = _AssetsToPack.Count > 0;
if (GUILayout.Button("Clear"))
{
AnimancerGUI.Deselect();
AnimancerToolsWindow.RecordUndo();
_AssetsToPack.Clear();
}
#if !UNITY_IMAGE_CONVERSION
var enabled = GUI.enabled;
GUI.enabled = false;
#endif
if (GUILayout.Button("Pack"))
{
#if UNITY_IMAGE_CONVERSION
AnimancerGUI.Deselect();
Pack();
#endif
}
#if !UNITY_IMAGE_CONVERSION
GUI.enabled = enabled;
#endif
}
GUILayout.EndHorizontal();
}
/************************************************************************************************************************/
/// <summary>Removes any items from the `list` that are the same as earlier items.</summary>
private static void RemoveDuplicates<T>(IList<T> list)
{
for (int i = list.Count - 1; i >= 0; i--)
{
var item = list[i];
if (item == null)
continue;
for (int j = 0; j < i; j++)
{
if (item.Equals(list[j]))
{
list.RemoveAt(i);
break;
}
}
}
}
/************************************************************************************************************************/
#if UNITY_IMAGE_CONVERSION
/************************************************************************************************************************/
/// <summary>Combines the <see cref="_AssetsToPack"/> into a new texture and saves it.</summary>
private void Pack()
{
var textures = GatherTextures();
if (textures.Count == 0 ||
!MakeTexturesReadable(textures))
return;
var path = GetCommonDirectory(_AssetsToPack);
path = EditorUtility.SaveFilePanelInProject("Save Packed Texture", "PackedTexture", "png",
"Where would you like to save the packed texture?", path);
if (string.IsNullOrEmpty(path))
return;
try
{
const string ProgressTitle = "Packing Textures";
EditorUtility.DisplayProgressBar(ProgressTitle, "Gathering", 0);
var tightSprites = GatherTightSprites();
EditorUtility.DisplayProgressBar(ProgressTitle, "Packing", 0.1f);
var packedTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
var tightTextures = new Texture2D[tightSprites.Count];
var index = 0;
foreach (var sprite in tightSprites)
tightTextures[index++] = sprite.texture;
var packedUVs = packedTexture.PackTextures(tightTextures, _Padding, _MaximumSize);
EditorUtility.DisplayProgressBar(ProgressTitle, "Encoding", 0.4f);
var bytes = packedTexture.EncodeToPNG();
if (bytes == null)
return;
EditorUtility.DisplayProgressBar(ProgressTitle, "Writing", 0.5f);
File.WriteAllBytes(path, bytes);
AssetDatabase.Refresh();
var importer = GetTextureImporter(path);
importer.maxTextureSize = Math.Max(packedTexture.width, packedTexture.height);
importer.textureType = TextureImporterType.Sprite;
importer.spriteImportMode = SpriteImportMode.Multiple;
var data = new SpriteDataEditor(importer)
{
SpriteCount = 0
};
CopyCompressionSettings(importer, textures);
EditorUtility.SetDirty(importer);
importer.SaveAndReimport();
// Use the UV coordinates to set up sprites for the new texture.
EditorUtility.DisplayProgressBar(ProgressTitle, "Generating Sprites", 0.7f);
data.SpriteCount = tightSprites.Count;
index = 0;
foreach (var sprite in tightSprites)
{
var rect = packedUVs[index];
rect.x *= packedTexture.width;
rect.y *= packedTexture.height;
rect.width *= packedTexture.width;
rect.height *= packedTexture.height;
var spriteRect = rect;
spriteRect.x += spriteRect.width * sprite.rect.x / sprite.texture.width;
spriteRect.y += spriteRect.height * sprite.rect.y / sprite.texture.height;
spriteRect.width *= sprite.rect.width / sprite.texture.width;
spriteRect.height *= sprite.rect.height / sprite.texture.height;
var pivot = sprite.pivot;
pivot.x /= rect.width;
pivot.y /= rect.height;
data.SetName(index, sprite.name);
data.SetRect(index, spriteRect);
data.SetPivot(index, pivot);
data.SetBorder(index, sprite.border);
index++;
}
EditorUtility.DisplayProgressBar(ProgressTitle, "Saving", 0.9f);
data.Apply();
EditorUtility.SetDirty(importer);
importer.SaveAndReimport();
Selection.activeObject = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
}
finally
{
EditorUtility.ClearProgressBar();
}
}
/************************************************************************************************************************/
private HashSet<Texture2D> GatherTextures()
{
var textures = new HashSet<Texture2D>();
for (int i = 0; i < _AssetsToPack.Count; i++)
{
var obj = _AssetsToPack[i];
var path = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(path))
continue;
if (obj is Texture2D texture)
textures.Add(texture);
else if (obj is Sprite sprite)
textures.Add(sprite.texture);
else if (obj is DefaultAsset)
ForEachTextureInFolder(path, tex => textures.Add(tex));
}
return textures;
}
/************************************************************************************************************************/
private HashSet<Sprite> GatherTightSprites()
{
var sprites = new HashSet<Sprite>();
for (int i = 0; i < _AssetsToPack.Count; i++)
{
var obj = _AssetsToPack[i];
var path = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(path))
continue;
if (obj is Texture2D texture)
GatherTightSprites(sprites, texture);
else if (obj is Sprite sprite)
sprites.Add(CreateTightSprite(sprite));
else if (obj is DefaultAsset)
ForEachTextureInFolder(path, tex => GatherTightSprites(sprites, tex));
}
return sprites;
}
/************************************************************************************************************************/
private static void GatherTightSprites(ICollection<Sprite> sprites, Texture2D texture)
{
var path = AssetDatabase.GetAssetPath(texture);
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
var foundSprite = false;
for (int i = 0; i < assets.Length; i++)
{
if (assets[i] is Sprite sprite)
{
sprite = CreateTightSprite(sprite);
sprites.Add(sprite);
foundSprite = true;
}
}
if (!foundSprite)
{
var sprite = Sprite.Create(texture,
new(0, 0, texture.width, texture.height),
new(0.5f, 0.5f));
sprite.name = texture.name;
sprites.Add(sprite);
}
}
/************************************************************************************************************************/
private static Sprite CreateTightSprite(Sprite sprite)
{
var rect = sprite.rect;
var width = Mathf.CeilToInt(rect.width);
var height = Mathf.CeilToInt(rect.height);
if (width == sprite.texture.width &&
height == sprite.texture.height)
return sprite;
var pixels = sprite.texture.GetPixels(
Mathf.FloorToInt(rect.x),
Mathf.FloorToInt(rect.y),
width,
height);
var texture = new Texture2D(width, height, sprite.texture.format, false, true);
#pragma warning disable UNT0017 // SetPixels invocation is slow.
texture.SetPixels(pixels);
#pragma warning restore UNT0017 // SetPixels invocation is slow.
texture.Apply();
rect.x = 0;
rect.y = 0;
var pivot = sprite.pivot;
pivot.x /= rect.width;
pivot.y /= rect.height;
var newSprite = Sprite.Create(texture, rect, pivot, sprite.pixelsPerUnit);
newSprite.name = sprite.name;
return newSprite;
}
/************************************************************************************************************************/
private static bool MakeTexturesReadable(HashSet<Texture2D> textures)
{
var hasAsked = false;
foreach (var texture in textures)
{
var importer = GetTextureImporter(texture);
if (importer == null)
continue;
if (importer.isReadable &&
importer.textureCompression == TextureImporterCompression.Uncompressed)
continue;
if (!hasAsked)
{
if (!EditorUtility.DisplayDialog("Make Textures Readable and Uncompressed?",
"This tool requires the source textures to be marked as readable and uncompressed in their import settings.",
"Make Textures Readable and Uncompressed", "Cancel"))
return false;
hasAsked = true;
}
importer.isReadable = true;
importer.textureCompression = TextureImporterCompression.Uncompressed;
importer.SaveAndReimport();
}
return true;
}
/************************************************************************************************************************/
private static void ForEachTextureInFolder(string path, Action<Texture2D> action)
{
var guids = AssetDatabase.FindAssets($"t:{nameof(Texture2D)}", new string[] { path });
for (int i = 0; i < guids.Length; i++)
{
path = AssetDatabase.GUIDToAssetPath(guids[i]);
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
if (texture != null)
action(texture);
}
}
/************************************************************************************************************************/
private static void CopyCompressionSettings(TextureImporter copyTo, IEnumerable<Texture2D> copyFrom)
{
var first = true;
foreach (var texture in copyFrom)
{
var copyFromImporter = GetTextureImporter(texture);
if (copyFromImporter == null)
continue;
if (first)
{
first = false;
copyTo.textureCompression = copyFromImporter.textureCompression;
copyTo.crunchedCompression = copyFromImporter.crunchedCompression;
copyTo.compressionQuality = copyFromImporter.compressionQuality;
copyTo.filterMode = copyFromImporter.filterMode;
}
else
{
if (IsHigherQuality(copyFromImporter.textureCompression, copyTo.textureCompression))
copyTo.textureCompression = copyFromImporter.textureCompression;
if (copyFromImporter.crunchedCompression)
copyTo.crunchedCompression = true;
if (copyTo.compressionQuality < copyFromImporter.compressionQuality)
copyTo.compressionQuality = copyFromImporter.compressionQuality;
if (copyTo.filterMode > copyFromImporter.filterMode)
copyTo.filterMode = copyFromImporter.filterMode;
}
}
}
/************************************************************************************************************************/
private static bool IsHigherQuality(TextureImporterCompression higher, TextureImporterCompression lower)
{
return higher switch
{
TextureImporterCompression.Uncompressed
=> lower != TextureImporterCompression.Uncompressed,
TextureImporterCompression.Compressed
=> lower == TextureImporterCompression.CompressedLQ,
TextureImporterCompression.CompressedHQ
=> lower == TextureImporterCompression.Compressed
|| lower == TextureImporterCompression.CompressedLQ,
TextureImporterCompression.CompressedLQ
=> false,
_
=> throw AnimancerUtilities.CreateUnsupportedArgumentException(higher),
};
}
/************************************************************************************************************************/
private static string GetCommonDirectory<T>(IList<T> objects) where T : Object
{
if (objects == null)
return null;
var count = objects.Count;
for (int i = count - 1; i >= 0; i--)
{
if (objects[i] == null)
{
objects.RemoveAt(i);
count--;
}
}
if (count == 0)
return null;
var path = AssetDatabase.GetAssetPath(objects[0]);
path = Path.GetDirectoryName(path);
for (int i = 1; i < count; i++)
{
var otherPath = AssetDatabase.GetAssetPath(objects[i]);
otherPath = Path.GetDirectoryName(otherPath);
while (string.Compare(path, 0, otherPath, 0, path.Length) != 0)
{
path = Path.GetDirectoryName(path);
}
}
return path;
}
/************************************************************************************************************************/
private static TextureImporter GetTextureImporter(Object asset)
{
var path = AssetDatabase.GetAssetPath(asset);
if (string.IsNullOrEmpty(path))
return null;
return GetTextureImporter(path);
}
private static TextureImporter GetTextureImporter(string path)
=> AssetImporter.GetAtPath(path) as TextureImporter;
/************************************************************************************************************************/
#endif
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 70badd2bf7a12d741aea958ec21943a6
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/PackTexturesTool.cs
uploadId: 795566

View File

@@ -0,0 +1,358 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR && UNITY_IMGUI
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// A <see cref="AnimancerToolsWindow.Tool"/> for changing which bones an <see cref="AnimationClip"/>s controls.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/remap-animation-bindings">
/// Remap Animation Bindings</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RemapAnimationBindingsTool
///
[Serializable]
public class RemapAnimationBindingsTool : AnimationModifierTool
{
/************************************************************************************************************************/
[SerializeField] private List<string> _NewBindingPaths;
[NonSerialized] private List<List<EditorCurveBinding>> _BindingGroups;
[NonSerialized] private List<string> _OldBindingPaths;
[NonSerialized] private bool _OldBindingPathsAreDirty;
[NonSerialized] private ReorderableList _OldBindingPathsDisplay;
[NonSerialized] private ReorderableList _NewBindingPathsDisplay;
/************************************************************************************************************************/
/// <inheritdoc/>
public override int DisplayOrder => 5;
/// <inheritdoc/>
public override string Name => "Remap Animation Bindings";
/// <inheritdoc/>
public override string HelpURL => Strings.DocsURLs.RemapAnimationBindings;
/// <inheritdoc/>
public override string Instructions
{
get
{
if (Animation == null)
return "Select the animation you want to remap.";
if (_OldBindingPaths.Count == 0)
{
if (Animation.humanMotion)
return "The selected animation only has Humanoid bindings which cannot be remapped.";
return "The selected animation does not have any bindings.";
}
return "Enter the new paths to change the bindings into then click Save As.";
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnEnable(int index)
{
base.OnEnable(index);
_BindingGroups = new();
_OldBindingPaths = new();
_NewBindingPaths ??= new();
if (Animation == null)
_NewBindingPaths.Clear();
_OldBindingPathsDisplay = AnimancerToolsWindow.CreateReorderableStringList(_OldBindingPaths, "Old Binding Paths");
_NewBindingPathsDisplay = AnimancerToolsWindow.CreateReorderableStringList(_NewBindingPaths, "New Binding Paths", (area, i) =>
{
var color = GUI.color;
var path = _NewBindingPaths[i];
if (path != _OldBindingPaths[i])
GUI.color = new(0.15f, 0.7f, 0.15f, 1);
path = EditorGUI.TextField(area, path);
GUI.color = color;
return path;
});
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void OnAnimationChanged()
{
base.OnAnimationChanged();
_OldBindingPathsAreDirty = true;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
base.DoBodyGUI();
GatherBindings();
GUILayout.BeginHorizontal();
{
GUILayout.BeginVertical();
GUI.enabled = false;
_OldBindingPathsDisplay.DoLayoutList();
GUI.enabled = true;
GUILayout.EndVertical();
GUILayout.BeginVertical();
_NewBindingPathsDisplay.DoLayoutList();
GUILayout.EndVertical();
}
GUILayout.EndHorizontal();
GUI.enabled = Animation != null;
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
if (GUILayout.Button("Reset"))
{
AnimancerGUI.Deselect();
AnimancerToolsWindow.RecordUndo();
_NewBindingPaths.Clear();
_OldBindingPathsAreDirty = true;
}
if (GUILayout.Button("Copy All"))
{
AnimancerGUI.Deselect();
CopyAll();
}
if (GUILayout.Button("Paste All"))
{
AnimancerGUI.Deselect();
PasteAll();
}
if (GUILayout.Button("Save As"))
{
if (SaveAs())
{
_OldBindingPathsAreDirty = true;
}
}
}
GUILayout.EndHorizontal();
}
/************************************************************************************************************************/
/// <summary>Gathers the bindings from the <see cref="AnimationModifierTool.Animation"/>.</summary>
private void GatherBindings()
{
if (!_OldBindingPathsAreDirty)
return;
_OldBindingPathsAreDirty = false;
_BindingGroups.Clear();
_OldBindingPaths.Clear();
if (Animation == null)
{
_NewBindingPaths.Clear();
return;
}
var isHumanoid = Animation.humanMotion;
AnimationBindings.OnAnimationChanged(Animation);
var bindings = AnimationBindings.GetBindings(Animation);
Array.Sort(bindings, (a, b) =>
{
var result = EditorUtility.NaturalCompare(a.path, b.path);
if (result != 0)
return result;
return EditorUtility.NaturalCompare(a.propertyName, b.propertyName);
});
string previousPath = null;
List<EditorCurveBinding> previousGroup = null;
for (int i = 0; i < bindings.Length; i++)
{
var binding = bindings[i];
if (isHumanoid &&
string.IsNullOrEmpty(binding.path) &&
IsHumanoidBinding(binding.propertyName))
continue;
var path = binding.path;
if (path == previousPath)
{
previousGroup.Add(binding);
continue;
}
previousPath = path;
previousGroup = new() { binding };
_BindingGroups.Add(previousGroup);
_OldBindingPaths.Add(path);
if (_NewBindingPaths.Count < _OldBindingPaths.Count)
_NewBindingPaths.Add(path);
}
if (_NewBindingPaths.Count > _OldBindingPaths.Count)
_NewBindingPaths.RemoveRange(_OldBindingPaths.Count, _NewBindingPaths.Count - _OldBindingPaths.Count);
}
/************************************************************************************************************************/
private static HashSet<string> _HumanoidBindingNames;
/// <summary>Is the `propertyName` one of the bindings used by Humanoid animations?</summary>
private static bool IsHumanoidBinding(string propertyName)
{
_HumanoidBindingNames ??= new()
{
"RootT.x", "RootT.y", "RootT.z",
"RootQ.x", "RootQ.y", "RootQ.z", "RootQ.w",
"LeftFootT.x", "LeftFootT.y", "LeftFootT.z",
"LeftFootQ.x", "LeftFootQ.y", "LeftFootQ.z", "LeftFootQ.w",
"RightFootT.x", "RightFootT.y", "RightFootT.z",
"RightFootQ.x", "RightFootQ.y", "RightFootQ.z", "RightFootQ.w",
"LeftHandT.x", "LeftHandT.y", "LeftHandT.z",
"LeftHandQ.x", "LeftHandQ.y", "LeftHandQ.z", "LeftHandQ.w",
"RightHandT.x", "RightHandT.y", "RightHandT.z",
"RightHandQ.x", "RightHandQ.y", "RightHandQ.z", "RightHandQ.w",
"Spine Front-Back", "Spine Left-Right", "Spine Twist Left-Right",
"Chest Front-Back", "Chest Left-Right", "Chest Twist Left-Right",
"UpperChest Front-Back", "UpperChest Left-Right", "UpperChest Twist Left-Right",
"Neck Nod Down-Up", "Neck Tilt Left-Right", "Neck Turn Left-Right",
"Head Nod Down-Up", "Head Tilt Left-Right", "Head Turn Left-Right",
"Left Eye Down-Up", "Left Eye In-Out",
"Right Eye Down-Up", "Right Eye In-Out",
"Jaw Close", "Jaw Left-Right",
"Left Upper Leg Front-Back", "Left Upper Leg In-Out", "Left Upper Leg Twist In-Out",
"Left Lower Leg Stretch", "Left Lower Leg Twist In-Out",
"Left Foot Up-Down", "Left Foot Twist In-Out",
"Left Toes Up-Down",
"Right Upper Leg Front-Back", "Right Upper Leg In-Out", "Right Upper Leg Twist In-Out",
"Right Lower Leg Stretch", "Right Lower Leg Twist In-Out",
"Right Foot Up-Down", "Right Foot Twist In-Out",
"Right Toes Up-Down",
"Left Shoulder Down-Up", "Left Shoulder Front-Back",
"Left Arm Down-Up", "Left Arm Front-Back", "Left Arm Twist In-Out",
"Left Forearm Stretch", "Left Forearm Twist In-Out",
"Left Hand Down-Up", "Left Hand In-Out",
"Right Shoulder Down-Up", "Right Shoulder Front-Back",
"Right Arm Down-Up", "Right Arm Front-Back", "Right Arm Twist In-Out",
"Right Forearm Stretch", "Right Forearm Twist In-Out",
"Right Hand Down-Up", "Right Hand In-Out",
"LeftHand.Thumb.Spread", "LeftHand.Thumb.1 Stretched", "LeftHand.Thumb.2 Stretched", "LeftHand.Thumb.3 Stretched",
"LeftHand.Index.Spread", "LeftHand.Index.1 Stretched", "LeftHand.Index.2 Stretched", "LeftHand.Index.3 Stretched",
"LeftHand.Middle.Spread", "LeftHand.Middle.1 Stretched", "LeftHand.Middle.2 Stretched", "LeftHand.Middle.3 Stretched",
"LeftHand.Ring.Spread", "LeftHand.Ring.1 Stretched", "LeftHand.Ring.2 Stretched", "LeftHand.Ring.3 Stretched",
"LeftHand.Little.Spread", "LeftHand.Little.1 Stretched", "LeftHand.Little.2 Stretched", "LeftHand.Little.3 Stretched",
"RightHand.Thumb.Spread", "RightHand.Thumb.1 Stretched", "RightHand.Thumb.2 Stretched", "RightHand.Thumb.3 Stretched",
"RightHand.Index.Spread", "RightHand.Index.1 Stretched", "RightHand.Index.2 Stretched", "RightHand.Index.3 Stretched",
"RightHand.Middle.Spread", "RightHand.Middle.1 Stretched", "RightHand.Middle.2 Stretched", "RightHand.Middle.3 Stretched",
"RightHand.Ring.Spread", "RightHand.Ring.1 Stretched", "RightHand.Ring.2 Stretched", "RightHand.Ring.3 Stretched",
"RightHand.Little.Spread", "RightHand.Little.1 Stretched", "RightHand.Little.2 Stretched", "RightHand.Little.3 Stretched",
};
return _HumanoidBindingNames.Contains(propertyName);
}
/************************************************************************************************************************/
/// <summary>Copies all of the <see cref="_NewBindingPaths"/> to the system clipboard.</summary>
private void CopyAll()
{
var text = StringBuilderPool.Instance.Acquire();
for (int i = 0; i < _NewBindingPaths.Count; i++)
{
text.AppendLine(_NewBindingPaths[i]);
}
EditorGUIUtility.systemCopyBuffer = text.ReleaseToString();
}
/// <summary>Pastes the string from the system clipboard into the <see cref="_NewBindingPaths"/>.</summary>
private void PasteAll()
{
using var reader = new StringReader(EditorGUIUtility.systemCopyBuffer);
for (int i = 0; i < _NewBindingPaths.Count; i++)
{
var line = reader.ReadLine();
if (line == null)
return;
_NewBindingPaths[i] = line;
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void Modify(AnimationClip animation)
{
for (int iGroup = 0; iGroup < _BindingGroups.Count; iGroup++)
{
var oldPath = _OldBindingPaths[iGroup];
var newPath = _NewBindingPaths[iGroup];
if (oldPath == newPath)
continue;
var group = _BindingGroups[iGroup];
for (int iBinding = 0; iBinding < group.Count; iBinding++)
{
var binding = group[iBinding];
if (binding.isPPtrCurve)
{
var curve = AnimationUtility.GetObjectReferenceCurve(animation, binding);
AnimationUtility.SetObjectReferenceCurve(animation, binding, null);
binding.path = newPath;
AnimationUtility.SetObjectReferenceCurve(animation, binding, curve);
}
else
{
var curve = AnimationUtility.GetEditorCurve(animation, binding);
AnimationUtility.SetEditorCurve(animation, binding, null);
binding.path = newPath;
AnimationUtility.SetEditorCurve(animation, binding, curve);
}
}
}
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: d75e4cf8a82981c4f94e52c1de8b0a78
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/RemapAnimationBindingsTool.cs
uploadId: 795566

View File

@@ -0,0 +1,198 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// An <see cref="AnimationModifierTool"/> for changing which
/// <see cref="Sprite"/>s an <see cref="AnimationClip"/> uses.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/remap-sprite-animation">
/// Remap Sprite Animation</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RemapSpriteAnimationTool
///
[Serializable]
public class RemapSpriteAnimationTool : AnimationModifierTool
{
/************************************************************************************************************************/
[SerializeField] private List<Sprite> _NewSprites;
[NonSerialized] private List<Sprite> _OldSprites;
[NonSerialized] private bool _OldSpritesAreDirty;
[NonSerialized] private ReorderableList _OldSpriteDisplay;
[NonSerialized] private ReorderableList _NewSpriteDisplay;
[NonSerialized] private EditorCurveBinding _SpriteBinding;
[NonSerialized] private ObjectReferenceKeyframe[] _SpriteKeyframes;
/************************************************************************************************************************/
/// <inheritdoc/>
public override int DisplayOrder => 4;
/// <inheritdoc/>
public override string Name => "Remap Sprite Animation";
/// <inheritdoc/>
public override string HelpURL => Strings.DocsURLs.RemapSpriteAnimation;
/// <inheritdoc/>
public override string Instructions
{
get
{
if (Animation == null)
return "Select the animation you want to remap.";
if (_OldSprites.Count == 0)
return "The selected animation does not use Sprites.";
return "Assign the New Sprites that you want to replace the Old Sprites with then click Save As." +
" You can Drag and Drop multiple Sprites onto the New Sprites list at the same time.";
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnEnable(int index)
{
base.OnEnable(index);
_NewSprites ??= new();
if (Animation == null)
_NewSprites.Clear();
_OldSprites = new();
_OldSpriteDisplay = AnimancerToolsWindow.CreateReorderableObjectList(_OldSprites, "Old Sprites");
_NewSpriteDisplay = AnimancerToolsWindow.CreateReorderableObjectList(_NewSprites, "New Sprites");
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void OnAnimationChanged()
{
base.OnAnimationChanged();
_OldSpritesAreDirty = true;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
base.DoBodyGUI();
GatherOldSprites();
GUILayout.BeginHorizontal();
{
GUILayout.BeginVertical();
GUI.enabled = false;
_OldSpriteDisplay.DoLayoutList();
GUI.enabled = true;
GUILayout.EndVertical();
GUILayout.BeginVertical();
_NewSpriteDisplay.DoLayoutList();
GUILayout.EndVertical();
HandleDragAndDropIntoList(GUILayoutUtility.GetLastRect(), _NewSprites, overwrite: true);
}
GUILayout.EndHorizontal();
GUI.enabled = Animation != null;
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
if (GUILayout.Button("Reset"))
{
AnimancerGUI.Deselect();
AnimancerToolsWindow.RecordUndo();
_NewSprites.Clear();
_OldSpritesAreDirty = true;
}
if (GUILayout.Button("Save As"))
{
if (SaveAs())
{
_OldSpritesAreDirty = true;
}
}
}
GUILayout.EndHorizontal();
}
/************************************************************************************************************************/
/// <summary>Gathers the <see cref="_OldSprites"/> from the <see cref="AnimationModifierTool.Animation"/>.</summary>
private void GatherOldSprites()
{
if (!_OldSpritesAreDirty)
return;
_OldSpritesAreDirty = false;
_OldSprites.Clear();
_NewSprites.Clear();
if (Animation == null)
return;
var bindings = AnimationUtility.GetObjectReferenceCurveBindings(Animation);
for (int iBinding = 0; iBinding < bindings.Length; iBinding++)
{
var binding = bindings[iBinding];
if (binding.type == typeof(SpriteRenderer) && binding.propertyName == "m_Sprite")
{
_SpriteBinding = binding;
_SpriteKeyframes = AnimationUtility.GetObjectReferenceCurve(Animation, binding);
for (int iKeyframe = 0; iKeyframe < _SpriteKeyframes.Length; iKeyframe++)
{
var reference = _SpriteKeyframes[iKeyframe].value as Sprite;
if (reference != null)
_OldSprites.Add(reference);
}
_NewSprites.AddRange(_OldSprites);
return;
}
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void Modify(AnimationClip animation)
{
for (int i = 0; i < _SpriteKeyframes.Length; i++)
{
_SpriteKeyframes[i].value = _NewSprites[i];
}
AnimationUtility.SetObjectReferenceCurve(animation, _SpriteBinding, _SpriteKeyframes);
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 5d3732333127095459feb68e4b95a191
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/RemapSpriteAnimationTool.cs
uploadId: 795566

View File

@@ -0,0 +1,359 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only] A <see cref="SpriteModifierTool"/> for bulk-renaming <see cref="Sprite"/>s.</summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/rename-sprites">
/// Rename Sprites</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RenameSpritesTool
///
[Serializable]
public class RenameSpritesTool : SpriteModifierTool
{
/************************************************************************************************************************/
[NonSerialized] private List<string> _GeneratedNames;
[NonSerialized] private bool _NamesAreDirty;
[NonSerialized] private ReorderableList _SpritesDisplay;
[SerializeField] private List<string> _ManualNames;
[SerializeField] private int _FirstIndex = 1;
[SerializeField] private int _MinimumDigits = 1;
/************************************************************************************************************************/
/// <inheritdoc/>
public override int DisplayOrder => 2;
/// <inheritdoc/>
public override string Name => "Rename Sprites";
/// <inheritdoc/>
public override string HelpURL => Strings.DocsURLs.RenameSprites;
/// <inheritdoc/>
public override string Instructions
{
get
{
if (Sprites.Count == 0)
return "Select the Sprites you want to rename.";
return "Enter the new name(s) you want to give the Sprites then click Apply." +
"\n\nEach Sprite below the name you enter will be given the same name" +
" until the next name which will restart the counter.";
}
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnEnable(int index)
{
base.OnEnable(index);
_ManualNames ??= new();
_GeneratedNames ??= new();
_SpritesDisplay = AnimancerToolsWindow.CreateReorderableObjectList(Sprites, "Sprites to Rename");
_SpritesDisplay.onChangedCallback += list => DirtyNames();
_SpritesDisplay.drawElementCallback = DrawItem;
_SpritesDisplay.elementHeight = AnimancerGUI.LineHeight * 3 + AnimancerGUI.StandardSpacing * 2;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnSelectionChanged()
{
base.OnSelectionChanged();
DirtyNames();
}
/************************************************************************************************************************/
/// <summary>Refreshes the <see cref="_GeneratedNames"/>.</summary>
private void UpdateNames()
{
if (!_NamesAreDirty)
return;
_NamesAreDirty = false;
var sprites = Sprites;
AnimancerEditorUtilities.SetCount(_ManualNames, sprites.Count);
AnimancerEditorUtilities.SetCount(_GeneratedNames, sprites.Count);
string name = null;
string digitFormat = null;
int index = 0;
for (int i = 0; i < sprites.Count; i++)
{
var newName = _ManualNames[i];
if (!string.IsNullOrWhiteSpace(newName))
{
name = newName;
index = 0;
var nextNameIndex = IndexOfNextManualName(i);
var digits = Mathf.FloorToInt(Mathf.Log10(nextNameIndex - i)) + 1;
if (digits < _MinimumDigits)
digits = _MinimumDigits;
var formatCharacters = new char[digits];
for (int iDigit = 0; iDigit < digits; iDigit++)
formatCharacters[iDigit] = '0';
digitFormat = new string(formatCharacters);
}
_GeneratedNames[i] = string.IsNullOrWhiteSpace(name)
? sprites[i].name
: name + (index + _FirstIndex).ToString(digitFormat);
index++;
}
}
/************************************************************************************************************************/
private int IndexOfNextManualName(int startIndex)
{
for (int i = startIndex + 1; i < _ManualNames.Count; i++)
if (!string.IsNullOrWhiteSpace(_ManualNames[i]))
return i;
return _ManualNames.Count;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
base.DoBodyGUI();
#if ! UNITY_2D_SPRITE
EditorGUILayout.HelpBox(
"Without the 2D Sprite Package," +
" any references to the renamed sprites will be lost (including animations).",
MessageType.Warning);
#endif
AnimancerToolsWindow.BeginChangeCheck();
var firstIndex = EditorGUILayout.IntField("First Index", _FirstIndex);
if (AnimancerToolsWindow.EndChangeCheck(ref _FirstIndex, Mathf.Max(firstIndex, 0)))
DirtyNames();
AnimancerToolsWindow.BeginChangeCheck();
var digits = EditorGUILayout.IntField("Minimum Digits", _MinimumDigits);
if (AnimancerToolsWindow.EndChangeCheck(ref _MinimumDigits, Mathf.Max(digits, 1)))
DirtyNames();
UpdateNames();
_SpritesDisplay.DoLayoutList();
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
GUI.enabled = HasAnyNames();
if (GUILayout.Button("Clear"))
{
AnimancerGUI.Deselect();
AnimancerToolsWindow.RecordUndo();
_ManualNames.Clear();
DirtyNames();
}
GUI.enabled = HasAnyDifferentNames();
if (GUILayout.Button("Apply"))
{
AnimancerGUI.Deselect();
AskAndApply();
}
}
GUILayout.EndHorizontal();
}
/************************************************************************************************************************/
private void DrawItem(Rect area, int index, bool isActive, bool isFocused)
{
var sprites = Sprites;
var sprite = sprites[index];
var thumbnailWidth = Math.Min(area.height, area.width * 0.5f);
var thumbnailArea = AnimancerGUI.StealFromLeft(ref area, thumbnailWidth, AnimancerGUI.StandardSpacing);
AnimancerGUI.DrawSprite(thumbnailArea, sprite);
area.y += (AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing) * 0.5f;
area.height = AnimancerGUI.LineHeight;
sprites[index] = DrawSpriteField(area, sprite);
AnimancerGUI.NextVerticalArea(ref area);
DrawName(area, index);
}
/************************************************************************************************************************/
private Sprite DrawSpriteField(Rect area, Sprite sprite)
=> AnimancerGUI.DoObjectFieldGUI(area, "", sprite, false);
/************************************************************************************************************************/
private static GUIStyle _TextFieldStyle;
private void DrawName(Rect area, int index)
{
area.y += 1;
area.height = AnimancerGUI.LineHeight;
var manualName = _ManualNames[index];
var generatedName = _GeneratedNames[index];
if (Event.current.type == EventType.Repaint)
{
_TextFieldStyle ??= new(EditorStyles.textField);
_TextFieldStyle.fontStyle = string.IsNullOrWhiteSpace(manualName)
? FontStyle.Italic
: FontStyle.Bold;
GUI.TextField(area, generatedName, _TextFieldStyle);
}
else
{
EditorGUI.BeginChangeCheck();
_ManualNames[index] = GUI.TextField(area, manualName);
if (EditorGUI.EndChangeCheck())
DirtyNames();
}
}
/************************************************************************************************************************/
private bool HasAnyNames()
{
var sprites = Sprites;
for (int i = 0; i < sprites.Count; i++)
if (!string.IsNullOrWhiteSpace(_ManualNames[i]))
return true;
return false;
}
private bool HasAnyDifferentNames()
{
var sprites = Sprites;
for (int i = 0; i < sprites.Count; i++)
if (sprites[i].name != _GeneratedNames[i])
return true;
return false;
}
/************************************************************************************************************************/
private void DirtyNames()
=> _NamesAreDirty = true;
/************************************************************************************************************************/
/// <inheritdoc/>
protected override string AreYouSure =>
"Are you sure you want to rename these Sprites?"
#if UNITY_2D_SPRITE
;
#else
+ "\n\nAny references to the renamed Sprites will be lost (including animations that use them)."
+ " This can be avoided by importing Unity's 2D Sprite Package before using this tool.";
#endif
/************************************************************************************************************************/
private static Dictionary<Sprite, string> _SpriteToName;
/// <inheritdoc/>
protected override void BeforeApply()
{
if (_SpriteToName == null)
_SpriteToName = new();
else
_SpriteToName.Clear();
var sprites = Sprites;
for (int i = 0; i < sprites.Count; i++)
{
_SpriteToName.Add(sprites[i], _GeneratedNames[i]);
}
// Renaming selected Sprites will lose the selection without triggering OnSelectionChanged.
EditorApplication.delayCall += OnSelectionChanged;
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void Modify(SpriteDataEditor data, int index, Sprite sprite)
{
data.SetName(index, _SpriteToName[sprite]);
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void Modify(TextureImporter importer, List<Sprite> sprites)
{
if (sprites.Count == 1 && importer.spriteImportMode != SpriteImportMode.Multiple)
{
var sprite = sprites[0];
var fileName = Path.GetFileNameWithoutExtension(importer.assetPath);
if (fileName == sprite.name)
{
AssetDatabase.RenameAsset(importer.assetPath, _SpriteToName[sprite]);
sprites.Clear();
}
}
base.Modify(importer, sprites);
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void AfterApply()
{
base.AfterApply();
AnimancerToolsWindow.RecordUndo();
_ManualNames.Clear();
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: dc63a439782ec8841905e29c181dbd28
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/RenameSpritesTool.cs
uploadId: 795566

View File

@@ -0,0 +1,310 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
#if UNITY_2D_SPRITE
using UnityEditor.U2D.Sprites;
#else
#pragma warning disable CS0618 // Type or member is obsolete.
#endif
namespace Animancer.Editor.Tools
{
/// <summary>A wrapper around the '2D Sprite' package features for editing Sprite data.</summary>
public class SpriteDataEditor
{
/************************************************************************************************************************/
#if UNITY_2D_SPRITE
/************************************************************************************************************************/
private static SpriteDataProviderFactories _Factories;
private static SpriteDataProviderFactories Factories
{
get
{
if (_Factories == null)
{
_Factories = new();
_Factories.Init();
}
return _Factories;
}
}
/************************************************************************************************************************/
/// <summary>The data provider for the target.</summary>
public readonly ISpriteEditorDataProvider DataProvider;
private SpriteRect[] _SpriteRects;
/************************************************************************************************************************/
/// <summary>The number of sprites in the target data.</summary>
/// <remarks>Setting this value clears all sprites.</remarks>
public int SpriteCount
{
get => _SpriteRects.Length;
set
{
// Unity might have given a UnityEditor.U2D.Sprites.SpriteDataExt[].
// We need to ensure it's a base SpriteRect[] so we can new() them.
if (_SpriteRects == null ||
_SpriteRects.Length != value ||
_SpriteRects.GetType() != typeof(SpriteRect[]))
_SpriteRects = new SpriteRect[value];
for (int i = 0; i < _SpriteRects.Length; i++)
_SpriteRects[i] = new();
}
}
/// <summary>Returns the name of the sprite at the specified `index`.</summary>
public string GetName(int index) => _SpriteRects[index].name;
/// <summary>Sets the name of the sprite at the specified `index`.</summary>
public void SetName(int index, string name) => _SpriteRects[index].name = name;
/// <summary>Returns the rect of the sprite at the specified `index`.</summary>
public Rect GetRect(int index) => _SpriteRects[index].rect;
/// <summary>Sets the rect of the sprite at the specified `index`.</summary>
public void SetRect(int index, Rect rect) => _SpriteRects[index].rect = rect;
/// <summary>Returns the pivot of the sprite at the specified `index`.</summary>
public Vector2 GetPivot(int index) => _SpriteRects[index].pivot;
/// <summary>Sets the pivot of the sprite at the specified `index`.</summary>
public void SetPivot(int index, Vector2 pivot)
{
_SpriteRects[index].pivot = pivot;
_SpriteRects[index].alignment = GetSpriteAlignment(pivot);
}
/// <summary>Returns the alignment of the sprite at the specified `index`.</summary>
public SpriteAlignment GetAlignment(int index) => _SpriteRects[index].alignment;
/// <summary>Sets the alignment of the sprite at the specified `index`.</summary>
public void SetAlignment(int index, SpriteAlignment alignment) => _SpriteRects[index].alignment = alignment;
/// <summary>Returns the border of the sprite at the specified `index`.</summary>
public Vector4 GetBorder(int index) => _SpriteRects[index].border;
/// <summary>Sets the border of the sprite at the specified `index`.</summary>
public void SetBorder(int index, Vector4 border) => _SpriteRects[index].border = border;
/// <summary>References the sprite at the specified `index`.</summary>
public ref SpriteRect RefSprite(int index) => ref _SpriteRects[index];
/************************************************************************************************************************/
#else
/************************************************************************************************************************/
private SpriteMetaData[] _SpriteSheet;
/************************************************************************************************************************/
/// <summary>The number of sprites in the target data.</summary>
public int SpriteCount
{
get => _SpriteSheet.Length;
set => System.Array.Resize(ref _SpriteSheet, value);
}
/// <summary>Returns the name of the sprite at the specified `index`.</summary>
public string GetName(int index) => _SpriteSheet[index].name;
/// <summary>Sets the name of the sprite at the specified `index`.</summary>
public void SetName(int index, string name) => _SpriteSheet[index].name = name;
/// <summary>Returns the rect of the sprite at the specified `index`.</summary>
public Rect GetRect(int index) => _SpriteSheet[index].rect;
/// <summary>Sets the rect of the sprite at the specified `index`.</summary>
public void SetRect(int index, Rect rect) => _SpriteSheet[index].rect = rect;
/// <summary>Returns the pivot of the sprite at the specified `index`.</summary>
public Vector2 GetPivot(int index) => _SpriteSheet[index].pivot;
/// <summary>Sets the pivot of the sprite at the specified `index`.</summary>
public void SetPivot(int index, Vector2 pivot)
{
_SpriteSheet[index].pivot = pivot;
_SpriteSheet[index].alignment = (int)GetSpriteAlignment(pivot);
}
/// <summary>Returns the alignment of the sprite at the specified `index`.</summary>
public SpriteAlignment GetAlignment(int index) => (SpriteAlignment)_SpriteSheet[index].alignment;
/// <summary>Sets the alignment of the sprite at the specified `index`.</summary>
public void SetAlignment(int index, SpriteAlignment alignment) => _SpriteSheet[index].alignment = (int)alignment;
/// <summary>Returns the border of the sprite at the specified `index`.</summary>
public Vector4 GetBorder(int index) => _SpriteSheet[index].border;
/// <summary>Sets the border of the sprite at the specified `index`.</summary>
public void SetBorder(int index, Vector4 border) => _SpriteSheet[index].border = border;
/************************************************************************************************************************/
#endif
/************************************************************************************************************************/
/// <summary>Returns the appropriate alignment for the given `pivot`.</summary>
public static SpriteAlignment GetSpriteAlignment(Vector2 pivot)
{
switch (pivot.x)
{
case 0:
switch (pivot.y)
{
case 0: return SpriteAlignment.BottomLeft;
case 0.5f: return SpriteAlignment.LeftCenter;
case 1: return SpriteAlignment.TopLeft;
}
break;
case 0.5f:
switch (pivot.y)
{
case 0: return SpriteAlignment.BottomCenter;
case 0.5f: return SpriteAlignment.Center;
case 1: return SpriteAlignment.TopCenter;
}
break;
case 1:
switch (pivot.y)
{
case 0: return SpriteAlignment.BottomRight;
case 0.5f: return SpriteAlignment.RightCenter;
case 1: return SpriteAlignment.TopRight;
}
break;
}
return SpriteAlignment.Custom;
}
/************************************************************************************************************************/
private readonly TextureImporter Importer;
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="SpriteDataEditor"/>.</summary>
public SpriteDataEditor(TextureImporter importer)
{
Importer = importer;
#if UNITY_2D_SPRITE
DataProvider = Factories.GetSpriteEditorDataProviderFromObject(importer);
DataProvider.InitSpriteEditorDataProvider();
_SpriteRects = DataProvider.GetSpriteRects();
#else
_SpriteSheet = importer.spritesheet;
#endif
}
/************************************************************************************************************************/
/// <summary>Tries to find the index of the data matching the `sprite`.</summary>
/// <remarks>
/// Returns -1 if there is no data matching the <see cref="UnityEngine.Object.name"/>.
/// <para></para>
/// Returns -2 if there is more than one data matching the <see cref="UnityEngine.Object.name"/> but no
/// <see cref="Sprite.rect"/> match.
/// </remarks>
public int IndexOf(Sprite sprite)
{
var nameMatchIndex = -1;
var count = SpriteCount;
for (int i = 0; i < count; i++)
{
if (GetName(i) == sprite.name)
{
if (GetRect(i) == sprite.rect)
return i;
if (nameMatchIndex == -1)// First name match.
nameMatchIndex = i;
else
nameMatchIndex = -2;// Already found 2 name matches.
}
}
if (nameMatchIndex == -1)
{
Debug.LogError($"No {nameof(SpriteMetaData)} for '{sprite.name}' was found.", sprite);
}
else if (nameMatchIndex == -2)
{
Debug.LogError($"More than one {nameof(SpriteMetaData)} for '{sprite.name}' was found" +
$" but none of them matched the {nameof(Sprite)}.{nameof(Sprite.rect)}." +
$" If the texture's Max Size is smaller than its actual size, increase the Max Size before performing this" +
$" operation so that the {nameof(Rect)}s can be used to identify the correct data.", sprite);
}
return nameMatchIndex;
}
/************************************************************************************************************************/
/// <summary>Logs an error and returns false if the data at the specified `index` is out of the texture bounds.</summary>
public bool ValidateBounds(int index, Sprite sprite)
{
var rect = GetRect(index);
if (rect.xMin >= 0 &&
rect.yMin >= 0 &&
rect.xMax <= sprite.texture.width &&
rect.yMax <= sprite.texture.height)
return true;
var path = AssetDatabase.GetAssetPath(sprite);
// The Max Texture Size import setting may cause the loaded texture to be smaller than the actual image.
// Sprite dimensions are defined against the actual image though, so we need to check those bounds.
var importer = (TextureImporter)AssetImporter.GetAtPath(path);
importer.GetSourceTextureWidthAndHeight(out var width, out var height);
if (rect.xMin >= 0 &&
rect.yMin >= 0 &&
rect.xMax <= width &&
rect.yMax <= height)
return true;
Debug.LogError(
$"This modification would put '{sprite.name}' at {rect}" +
$" which is outside of the texture ({width}x{height})" +
$" so '{path}' was not modified.",
sprite);
return false;
}
/************************************************************************************************************************/
/// <summary>Applies any modifications to the target asset.</summary>
public void Apply()
{
#if UNITY_2D_SPRITE
DataProvider.SetSpriteRects(_SpriteRects);
DataProvider.Apply();
#else
Importer.spritesheet = _SpriteSheet;
EditorUtility.SetDirty(Importer);
#endif
Importer.SaveAndReimport();
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 8abf35ea1a5a4664b88e197e78f09632
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/SpriteDataEditor.cs
uploadId: 795566

View File

@@ -0,0 +1,223 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor.Tools
{
/// <summary>[Editor-Only] [Pro-Only]
/// A base <see cref="AnimancerToolsWindow.Tool"/> for modifying <see cref="Sprite"/>s.
/// </summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
/// Animancer Tools</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/SpriteModifierTool
///
[Serializable]
public abstract class SpriteModifierTool : AnimancerToolsWindow.Tool
{
/************************************************************************************************************************/
private static readonly List<Sprite> SelectedSprites = new();
private static bool _HasGatheredSprites;
/// <summary>The currently selected <see cref="Sprite"/>s.</summary>
public static List<Sprite> Sprites
{
get
{
if (!_HasGatheredSprites)
{
_HasGatheredSprites = true;
GatherSelectedSprites(SelectedSprites);
}
return SelectedSprites;
}
}
/// <inheritdoc/>
public override void OnSelectionChanged()
{
_HasGatheredSprites = false;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoBodyGUI()
{
#if !UNITY_2D_SPRITE
EditorGUILayout.HelpBox(
"This tool works best with Unity's '2D Sprite' package." +
" You should import it via the Package Manager before using this tool.",
MessageType.Warning);
if (AnimancerGUI.TryUseClickEventInLastRect())
EditorApplication.ExecuteMenuItem("Window/Package Manager");
#endif
}
/************************************************************************************************************************/
/// <summary>
/// Adds all <see cref="Sprite"/>s in the <see cref="Selection.objects"/> or their sub-assets to the
/// list of `sprites`.
/// </summary>
public static void GatherSelectedSprites(List<Sprite> sprites)
{
sprites.Clear();
var selection = Selection.objects;
for (int i = 0; i < selection.Length; i++)
{
var selected = selection[i];
if (selected is Sprite sprite)
{
sprites.Add(sprite);
}
else if (selected is Texture2D texture)
{
sprites.AddRange(LoadAllSpritesInTexture(texture));
}
}
sprites.Sort(NaturalCompare);
}
/************************************************************************************************************************/
/// <summary>Returns all the <see cref="Sprite"/> sub-assets of the `texture`.</summary>
public static Sprite[] LoadAllSpritesInTexture(Texture2D texture)
=> LoadAllSpritesAtPath(AssetDatabase.GetAssetPath(texture));
/// <summary>Returns all the <see cref="Sprite"/> assets at the `path`.</summary>
public static Sprite[] LoadAllSpritesAtPath(string path)
{
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
var sprites = new List<Sprite>();
for (int j = 0; j < assets.Length; j++)
{
if (assets[j] is Sprite sprite)
sprites.Add(sprite);
}
return sprites.ToArray();
}
/************************************************************************************************************************/
/// <summary>Calls <see cref="EditorUtility.NaturalCompare"/> on the <see cref="Object.name"/>s.</summary>
public static int NaturalCompare(Object a, Object b)
=> EditorUtility.NaturalCompare(a.name, b.name);
/************************************************************************************************************************/
/// <summary>The message to confirm that the user is certain they want to apply the changes.</summary>
protected virtual string AreYouSure
=> "Are you sure you want to modify these Sprites?";
/// <summary>Called immediately after the user confirms they want to apply changes.</summary>
protected virtual void BeforeApply() { }
/// <summary>Called after all changes are applied.</summary>
protected virtual void AfterApply() { }
/// <summary>Applies the desired modifications to the `data` before it is saved.</summary>
protected virtual void Modify(SpriteDataEditor data, int index, Sprite sprite) { }
/// <summary>Applies the desired modifications to the `data` before it is saved.</summary>
protected virtual void Modify(TextureImporter importer, List<Sprite> sprites)
{
var dataEditor = new SpriteDataEditor(importer);
var hasError = false;
for (int i = 0; i < sprites.Count; i++)
{
var sprite = sprites[i];
var index = dataEditor.IndexOf(sprite);
if (index < 0)
continue;
Modify(dataEditor, index, sprite);
sprites.RemoveAt(i--);
if (!dataEditor.ValidateBounds(index, sprite))
hasError = true;
}
if (!hasError)
dataEditor.Apply();
}
/************************************************************************************************************************/
/// <summary>
/// Asks the user if they want to modify the target <see cref="Sprite"/>s and calls <see cref="Modify"/>
/// on each of them before saving any changes.
/// </summary>
protected void AskAndApply()
{
if (!EditorUtility.DisplayDialog("Are You Sure?",
AreYouSure + "\n\nThis operation cannot be undone.",
"Modify", "Cancel"))
return;
BeforeApply();
var pathToSprites = new Dictionary<string, List<Sprite>>();
var sprites = Sprites;
for (int i = 0; i < sprites.Count; i++)
{
var sprite = sprites[i];
var path = AssetDatabase.GetAssetPath(sprite);
if (!pathToSprites.TryGetValue(path, out var spritesAtPath))
pathToSprites.Add(path, spritesAtPath = new());
spritesAtPath.Add(sprite);
}
foreach (var asset in pathToSprites)
{
var importer = (TextureImporter)AssetImporter.GetAtPath(asset.Key);
Modify(importer, asset.Value);
if (asset.Value.Count > 0)
{
var message = StringBuilderPool.Instance.Acquire()
.Append("Modification failed: unable to find data in '")
.Append(asset.Key)
.Append("' for ")
.Append(asset.Value.Count)
.Append(" Sprites:");
for (int i = 0; i < sprites.Count; i++)
{
message.AppendLine()
.Append(" - ")
.Append(sprites[i].name);
}
Debug.LogError(message.ReleaseToString(), AssetDatabase.LoadAssetAtPath<Object>(asset.Key));
}
}
AfterApply();
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: f495ecfca64b9c04aa19924036014776
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/SpriteModifierTool.cs
uploadId: 795566

View File

@@ -0,0 +1,252 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.AnimatedValues;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor.Tools
{
partial class AnimancerToolsWindow
{
/// <summary>[Editor-Only] [Pro-Only] Base class for tools in the <see cref="AnimancerToolsWindow"/>.</summary>
/// <remarks>
/// <strong>Documentation:</strong>
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
/// Animancer Tools</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/Tool
///
[Serializable]
public abstract class Tool : IComparable<Tool>
{
/************************************************************************************************************************/
private AnimBool _FullAnimator;
private AnimBool _BodyAnimator;
private int _Index;
/************************************************************************************************************************/
/// <summary>Is this tool currently visible?</summary>
public bool IsVisible => Instance._CurrentTool == _Index || Instance._CurrentTool < 0;
/************************************************************************************************************************/
/// <summary>Is the body of this tool currently visible?</summary>
public bool IsExpanded
{
get { return Instance._CurrentTool == _Index; }
set
{
if (value)
Instance._CurrentTool = _Index;
else if (IsExpanded)
Instance._CurrentTool = -1;
}
}
/************************************************************************************************************************/
/// <summary>Lower numbers display first.</summary>
public abstract int DisplayOrder { get; }
/// <summary>Compares the <see cref="DisplayOrder"/> to put lower numbers first.</summary>
public int CompareTo(Tool other)
=> DisplayOrder.CompareTo(other.DisplayOrder);
/************************************************************************************************************************/
/// <summary>The display name of this tool.</summary>
public abstract string Name { get; }
/// <summary>The usage instructions to display at the top of this tool.</summary>
public abstract string Instructions { get; }
/// <summary>The URL for the help button in the header to open.</summary>
public virtual string HelpURL => Strings.DocsURLs.AnimancerTools;
/// <summary>Called whenever the <see cref="Selection"/> changes.</summary>
public virtual void OnSelectionChanged() { }
/************************************************************************************************************************/
/// <summary>Called by <see cref="AnimancerToolsWindow.OnEnable"/>.</summary>
public virtual void OnEnable(int index)
{
_Index = index;
_FullAnimator = new(IsVisible);
_BodyAnimator = new(IsExpanded);
}
/// <summary>Called by <see cref="AnimancerToolsWindow.OnDisable"/>.</summary>
public virtual void OnDisable() { }
/************************************************************************************************************************/
/// <summary>Draws the GUI for this tool.</summary>
public virtual void DoGUI()
{
var enabled = GUI.enabled;
_FullAnimator.target = IsVisible;
if (EditorGUILayout.BeginFadeGroup(_FullAnimator.faded))
{
GUILayout.BeginVertical(EditorStyles.helpBox);
DoHeaderGUI();
_BodyAnimator.target = IsExpanded;
if (EditorGUILayout.BeginFadeGroup(_BodyAnimator.faded))
{
var instructions = Instructions;
if (!string.IsNullOrEmpty(instructions))
EditorGUILayout.HelpBox(instructions, MessageType.Info);
DoBodyGUI();
}
EditorGUILayout.EndFadeGroup();
GUILayout.EndVertical();
}
EditorGUILayout.EndFadeGroup();
if (_FullAnimator.isAnimating || _BodyAnimator.isAnimating)
Repaint();
GUI.enabled = enabled;
}
/************************************************************************************************************************/
/// <summary>
/// Draws the Header GUI for this tool which is displayed regardless of whether it is expanded or not.
/// </summary>
public virtual void DoHeaderGUI()
{
var area = AnimancerGUI.LayoutSingleLineRect(AnimancerGUI.SpacingMode.BeforeAndAfter);
var click = GUI.Button(area, Name, EditorStyles.boldLabel);
area.xMin = area.xMax - area.height;
GUI.DrawTexture(area, HelpIcon);
if (click)
{
if (area.Contains(Event.current.mousePosition))
{
Application.OpenURL(HelpURL);
return;
}
else
{
IsExpanded = !IsExpanded;
}
}
}
/************************************************************************************************************************/
/// <summary>Draws the Body GUI for this tool which is only displayed while it is expanded.</summary>
public abstract void DoBodyGUI();
/************************************************************************************************************************/
/// <summary>Asks the user where they want to save a modified asset, calls `modify` on it, and saves it.</summary>
public static bool SaveModifiedAsset<T>(string saveTitle, string saveMessage,
T obj, Action<T> modify) where T : Object
{
var originalPath = AssetDatabase.GetAssetPath(obj);
var extension = Path.GetExtension(originalPath);
if (extension[0] == '.')
extension = extension[1..];
var directory = Path.GetDirectoryName(originalPath);
var newName = Path.GetFileNameWithoutExtension(AssetDatabase.GenerateUniqueAssetPath(originalPath));
var savePath = EditorUtility.SaveFilePanelInProject(saveTitle, newName, extension, saveMessage, directory);
if (string.IsNullOrEmpty(savePath))
return false;
if (originalPath != savePath)
{
obj = Instantiate(obj);
AssetDatabase.CreateAsset(obj, savePath);
}
modify(obj);
AssetDatabase.SaveAssets();
return true;
}
/************************************************************************************************************************/
private static Texture _HelpIcon;
/// <summary>The help icon image used in the tool header.</summary>
public static Texture HelpIcon
{
get
{
if (_HelpIcon == null)
_HelpIcon = AnimancerIcons.Load("_Help");
return _HelpIcon;
}
}
/************************************************************************************************************************/
/// <summary>Adds any objects dropped in the `area` to the `list`.</summary>
protected void HandleDragAndDropIntoList<T>(
Rect area,
IList<T> list,
bool overwrite)
where T : Object
{
var dropIndex = 0;
// No easy way to avoid this closure.
AnimancerGUI.Handle<T>((obj, isDrop) =>
{
if (!isDrop)
return true;
if (overwrite)
{
RecordUndo();
if (dropIndex < list.Count)
{
list[dropIndex++] = obj;
}
else
{
list.Add(obj);
}
}
else
{
list.Add(obj);
}
return true;
}, area);
}
/************************************************************************************************************************/
}
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 0981a35e6e25d944dacf3f3665c5ab78
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/Tool.cs
uploadId: 795566

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: f196e38e5592d9c4cb6a5e51ba201a13
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerDataMigrator.cs
uploadId: 795566

View File

@@ -0,0 +1,372 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] Various utilities used throughout Animancer.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerEditorUtilities
public static partial class AnimancerEditorUtilities
{
/************************************************************************************************************************/
#region Misc
/************************************************************************************************************************/
/// <summary>[Animancer Extension] [Editor-Only] Is the <see cref="Vector2.x"/> or <see cref="Vector2.y"/> NaN?</summary>
public static bool IsNaN(this Vector2 vector)
=> float.IsNaN(vector.x)
|| float.IsNaN(vector.y);
/// <summary>[Animancer Extension] [Editor-Only] Is the <see cref="Vector3.x"/>, <see cref="Vector3.y"/>, or <see cref="Vector3.z"/> NaN?</summary>
public static bool IsNaN(this Vector3 vector)
=> float.IsNaN(vector.x)
|| float.IsNaN(vector.y)
|| float.IsNaN(vector.z);
/************************************************************************************************************************/
/// <summary>Returns the value of `t` linearly interpolated along the X axis of the `rect`.</summary>
public static float LerpUnclampedX(this Rect rect, float t)
=> rect.x + rect.width * t;
/// <summary>Returns the value of `t` inverse linearly interpolated along the X axis of the `rect`.</summary>
public static float InverseLerpUnclampedX(this Rect rect, float t)
=> (t - rect.x) / rect.width;
/************************************************************************************************************************/
/// <summary>Finds an asset of the specified type anywhere in the project.</summary>
public static T FindAssetOfType<T>()
where T : Object
{
var filter = typeof(Component).IsAssignableFrom(typeof(T))
? $"t:{nameof(GameObject)}"
: $"t:{typeof(T).Name}";
var guids = AssetDatabase.FindAssets(filter);
if (guids.Length == 0)
return null;
for (int i = 0; i < guids.Length; i++)
{
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
var asset = AssetDatabase.LoadAssetAtPath<T>(path);
if (asset != null)
return asset;
}
return null;
}
/************************************************************************************************************************/
/// <summary>Finds or creates an instance of <typeparamref name="T"/>.</summary>
public static T FindOrCreate<T>(ref T scriptableObject, HideFlags hideFlags = default)
where T : ScriptableObject
{
if (scriptableObject != null)
return scriptableObject;
var instances = Resources.FindObjectsOfTypeAll<T>();
if (instances.Length > 0)
{
scriptableObject = instances[0];
}
else
{
scriptableObject = ScriptableObject.CreateInstance<T>();
scriptableObject.hideFlags = hideFlags;
}
return scriptableObject;
}
/************************************************************************************************************************/
/// <summary>The most recent <see cref="PlayModeStateChange"/>.</summary>
public static PlayModeStateChange PlayModeState { get; private set; }
/// <summary>Is the Unity Editor is currently changing between Play Mode and Edit Mode?</summary>
public static bool IsChangingPlayMode =>
PlayModeState == PlayModeStateChange.ExitingEditMode ||
PlayModeState == PlayModeStateChange.ExitingPlayMode;
[InitializeOnLoadMethod]
private static void WatchForPlayModeChanges()
{
PlayModeState = EditorApplication.isPlayingOrWillChangePlaymode
? EditorApplication.isPlaying
? PlayModeStateChange.EnteredPlayMode
: PlayModeStateChange.ExitingEditMode
: PlayModeStateChange.EnteredEditMode;
EditorApplication.playModeStateChanged += change => PlayModeState = change;
}
/************************************************************************************************************************/
/// <summary>Deletes the specified `subAsset`.</summary>
public static void DeleteSubAsset(Object subAsset)
{
AssetDatabase.RemoveObjectFromAsset(subAsset);
AssetDatabase.SaveAssets();
Object.DestroyImmediate(subAsset, true);
}
/************************************************************************************************************************/
/// <summary>Calculates the overall bounds of all renderers under the `transform`.</summary>
public static Bounds CalculateBounds(Transform transform)
{
using var _ = ListPool<Renderer>.Instance.Acquire(out var renderers);
transform.GetComponentsInChildren(renderers);
if (renderers.Count == 0)
return default;
var bounds = renderers[0].bounds;
for (int i = 1; i < renderers.Count; i++)
bounds.Encapsulate(renderers[i].bounds);
return bounds;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Collections
/************************************************************************************************************************/
/// <summary>Adds default items or removes items to make the <see cref="List{T}.Count"/> equal to the `count`.</summary>
public static void SetCount<T>(List<T> list, int count)
{
if (list.Count < count)
{
while (list.Count < count)
list.Add(default);
}
else
{
list.RemoveRange(count, list.Count - count);
}
}
/************************************************************************************************************************/
/// <summary>
/// Removes any items from the `list` that are <c>null</c> and items that appear multiple times.
/// Returns true if the `list` was modified.
/// </summary>
public static bool RemoveMissingAndDuplicates(ref List<GameObject> list)
{
if (list == null)
{
list = new();
return false;
}
var modified = false;
using (SetPool<Object>.Instance.Acquire(out var previousItems))
{
for (int i = list.Count - 1; i >= 0; i--)
{
var item = list[i];
if (item == null || previousItems.Contains(item))
{
list.RemoveAt(i);
modified = true;
}
else
{
previousItems.Add(item);
}
}
}
return modified;
}
/************************************************************************************************************************/
/// <summary>
/// Removes any <c>null</c> items and ensures that it contains
/// an instance of each type derived from <typeparamref name="T"/>.
/// </summary>
public static void InstantiateDerivedTypes<T>(ref List<T> list)
where T : IComparable<T>
{
if (list == null)
{
list = new();
}
else
{
for (int i = list.Count - 1; i >= 0; i--)
if (list[i] == null)
list.RemoveAt(i);
}
var types = TypeSelectionMenu.GetDerivedTypes(typeof(T));
for (int i = 0; i < types.Count; i++)
{
var toolType = types[i];
if (IndexOfType(list, toolType) >= 0)
continue;
var instance = (T)Activator.CreateInstance(toolType);
list.Add(instance);
}
list.Sort();
}
/************************************************************************************************************************/
/// <summary>Finds the index of the first item with the specified `type`.</summary>
public static int IndexOfType<T>(IList<T> list, Type type)
{
for (int i = 0; i < list.Count; i++)
if (list[i].GetType() == type)
return i;
return -1;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Context Menus
/************************************************************************************************************************/
/// <summary>
/// Adds a menu function which passes the result of <see cref="CalculateEditorFadeDuration"/> into `startFade`.
/// </summary>
public static void AddFadeFunction(
GenericMenu menu,
string label,
bool isEnabled,
AnimancerNode node,
Action<float> startFade)
{
// Fade functions need to be delayed twice since the context menu itself causes the next frame delta
// time to be unreasonably high (which would skip the start of the fade).
menu.AddFunction(label, isEnabled,
() => EditorApplication.delayCall +=
() => EditorApplication.delayCall +=
() =>
{
startFade(node.CalculateEditorFadeDuration());
});
}
/// <summary>[Animancer Extension] [Editor-Only]
/// Returns the duration of the `node`s current fade (if any), otherwise returns the `defaultDuration`.
/// </summary>
public static float CalculateEditorFadeDuration(this AnimancerNode node, float defaultDuration = 1)
=> node.FadeSpeed > 0
? 1 / node.FadeSpeed
: defaultDuration;
/************************************************************************************************************************/
/// <summary>
/// Adds a menu function to open a web page.
/// If the `linkSuffix` starts with a '/' then it will be
/// relative to the <see cref="Strings.DocsURLs.Documentation"/>.
/// </summary>
public static void AddDocumentationLink(GenericMenu menu, string label, string linkSuffix)
{
if (linkSuffix[0] == '/')
linkSuffix = Strings.DocsURLs.Documentation + linkSuffix;
menu.AddItem(new(label), false, () =>
{
EditorUtility.OpenWithDefaultApp(linkSuffix);
});
}
/************************************************************************************************************************/
/// <summary>Is the <see cref="MenuCommand.context"/> editable?</summary>
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Looping", validate = true)]
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Legacy", validate = true)]
private static bool ValidateEditable(MenuCommand command)
{
return (command.context.hideFlags & HideFlags.NotEditable) != HideFlags.NotEditable;
}
/************************************************************************************************************************/
/// <summary>Toggles the <see cref="Motion.isLooping"/> flag between true and false.</summary>
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Looping")]
private static void ToggleLooping(MenuCommand command)
{
var clip = (AnimationClip)command.context;
SetLooping(clip, !clip.isLooping);
}
/// <summary>Sets the <see cref="Motion.isLooping"/> flag.</summary>
public static void SetLooping(AnimationClip clip, bool looping)
{
var settings = AnimationUtility.GetAnimationClipSettings(clip);
settings.loopTime = looping;
AnimationUtility.SetAnimationClipSettings(clip, settings);
Debug.Log($"Set {clip.name} to be {(looping ? "Looping" : "Not Looping")}." +
" Note that you may need to restart Unity for this change to take effect.", clip);
// None of these let us avoid the need to restart Unity.
//EditorUtility.SetDirty(clip);
//AssetDatabase.SaveAssets();
//var path = AssetDatabase.GetAssetPath(clip);
//AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
}
/************************************************************************************************************************/
/// <summary>Swaps the <see cref="AnimationClip.legacy"/> flag between true and false.</summary>
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Legacy")]
private static void ToggleLegacy(MenuCommand command)
{
var clip = (AnimationClip)command.context;
clip.legacy = !clip.legacy;
}
/************************************************************************************************************************/
/// <summary>Calls <see cref="Animator.Rebind"/>.</summary>
[MenuItem("CONTEXT/" + nameof(Animator) + "/Restore Bind Pose", priority = 110)]
private static void RestoreBindPose(MenuCommand command)
{
var animator = (Animator)command.context;
Undo.RegisterFullObjectHierarchyUndo(animator.gameObject, "Restore bind pose");
const string TypeName = "UnityEditor.AvatarSetupTool, UnityEditor";
var type = Type.GetType(TypeName)
?? throw new TypeLoadException($"Unable to find the type '{TypeName}'");
const string MethodName = "SampleBindPose";
var method = type.GetMethod(MethodName, AnimancerReflection.StaticBindings)
?? throw new MissingMethodException($"Unable to find the method '{MethodName}'");
method.Invoke(null, new object[] { animator.gameObject });
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 27eecf5aa14ee5a44b06436c4b4ab75b
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerEditorUtilities.cs
uploadId: 795566

View File

@@ -0,0 +1,137 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A welcome screen for <see cref="Animancer"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerReadMe
///
// [CreateAssetMenu]
[AnimancerHelpUrl(typeof(AnimancerReadMe))]
public class AnimancerReadMe : ReadMe
{
/************************************************************************************************************************/
/// <summary>The release ID of the current version.</summary>
/// <example><list type="bullet">
/// <item>[ 1] = v1.0.0: 2018-05-02.</item>
/// <item>[ 2] = v1.1.0: 2018-05-29.</item>
/// <item>[ 3] = v1.2.0: 2018-08-14.</item>
/// <item>[ 4] = v1.3.0: 2018-09-12.</item>
/// <item>[ 5] = v2.0.0: 2018-10-08.</item>
/// <item>[ 6] = v3.0.0: 2019-05-27.</item>
/// <item>[ 7] = v3.1.0: 2019-08-12.</item>
/// <item>[ 8] = v4.0.0: 2020-01-28.</item>
/// <item>[ 9] = v4.1.0: 2020-02-21.</item>
/// <item>[10] = v4.2.0: 2020-03-02.</item>
/// <item>[11] = v4.3.0: 2020-03-13.</item>
/// <item>[12] = v4.4.0: 2020-03-27.</item>
/// <item>[13] = v5.0.0: 2020-07-17.</item>
/// <item>[14] = v5.1.0: 2020-07-27.</item>
/// <item>[15] = v5.2.0: 2020-09-16.</item>
/// <item>[16] = v5.3.0: 2020-10-06.</item>
/// <item>[17] = v6.0.0: 2020-12-04.</item>
/// <item>[18] = v6.1.0: 2021-04-13.</item>
/// <item>[19] = v7.0.0: 2021-07-29.</item>
/// <item>[20] = v7.1.0: 2021-08-13.</item>
/// <item>[21] = v7.2.0: 2021-10-17.</item>
/// <item>[22] = v7.3.0: 2022-07-03.</item>
/// <item>[23] = v7.4.0: 2023-01-26.</item>
/// <item>[24] = v7.4.1: 2023-01-28.</item>
/// <item>[25] = v7.4.2: 2023-01-31.</item>
/// <item>[26] = v7.4.3: 2023-04-16.</item>
/// <item>[27] = v8.0.0: 2024-08-17.</item>
/// <item>[28] = v8.0.1: 2024-09-08.</item>
/// <item>[29] = v8.0.2: 2024-11-02.</item>
/// <item>[30] = v8.1.0: 2025-02-26.</item>
/// <item>[31] = v8.1.1: 2025-05-26.</item>
/// <item>[32] = v8.2.0: 2025-09-17.</item>
/// <item>[33] = v8.2.1: 2025-09-18.</item>
/// <item>[34] = v8.2.2: 2025-09-27.</item>
/// </list></example>
public override int ReleaseNumber => 34;
/// <inheritdoc/>
public override string VersionName => Strings.DocsURLs.VersionName;
/// <inheritdoc/>
public override string PrefKey => nameof(Animancer);
/// <inheritdoc/>
public override string BaseProductName => Strings.ProductName;
/// <inheritdoc/>
public override string ProductName => Strings.ProductName + " Pro";
/// <inheritdoc/>
public override string DocumentationURL => Strings.DocsURLs.Documentation;
/// <inheritdoc/>
public override string ChangeLogURL => Strings.DocsURLs.ChangeLogURL;
/// <inheritdoc/>
public override string SamplesURL => Strings.DocsURLs.Samples;
/// <inheritdoc/>
public override string UpdateURL => Strings.DocsURLs.LatestVersion;
/************************************************************************************************************************/
public AnimancerReadMe() : base(
new("Issues",
"for questions, suggestions, and bug reports",
Strings.DocsURLs.Issues),
new("Discussions",
"for general discussions, feedback, and news",
Strings.DocsURLs.Discussions),
new("Email",
"for anything private",
GetEmailURL(Strings.DocsURLs.DeveloperEmail, Strings.ProductName),
Strings.DocsURLs.DeveloperEmail))
{
ExtraSamples = new LinkSection[]
{
new("Platformer Game Kit", null, "https://kybernetik.com.au/platformer"),
};
}
/************************************************************************************************************************/
/// <summary>[Editor-Only] A custom Inspector for <see cref="AnimancerReadMe"/>.</summary>
[CustomEditor(typeof(AnimancerReadMe), editorForChildClasses: true)]
public new class Editor : ReadMe.Editor
{
/************************************************************************************************************************/
/// <summary>A callback to execute data migration.</summary>
public static event Action<string> MigrateOldAssetData;
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void DoNewVersionDetails()
{
base.DoNewVersionDetails();
if (MigrateOldAssetData == null)
return;
var text = $"Migrate old asset data to {Target.BaseProductName} {Target.VersionName}";
if (GUILayout.Button(text))
MigrateOldAssetData(text);
}
/************************************************************************************************************************/
}
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: e1be19fc3858e524fa17a5518e8c4d39
labels:
- Documentation
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d4e06a71fc03595429cac47cd385c4c1, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerReadMe.cs
uploadId: 795566

View File

@@ -0,0 +1,323 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] Persistent settings used by Animancer.</summary>
/// <remarks>
/// This asset automatically creates itself when first accessed.
/// <para></para>
/// The default location is <em>Packages/com.kybernetik.animancer/Code/Editor</em>, but you can freely move it
/// (and the whole Animancer folder) anywhere in your project.
/// <para></para>
/// These settings can also be accessed via the Settings in the <see cref="Tools.AnimancerToolsWindow"/>
/// (<c>Window/Animation/Animancer Tools</c>).
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerSettings
///
[AnimancerHelpUrl(typeof(AnimancerSettings))]
public class AnimancerSettings : ScriptableObject
{
/************************************************************************************************************************/
private static AnimancerSettings _Instance;
/// <summary>
/// Loads an existing <see cref="AnimancerSettings"/> if there is one anywhere in your project.
/// Otherwise, creates a new one and saves it in the Assets folder.
/// </summary>
public static AnimancerSettings Instance
{
get
{
if (_Instance != null)
return _Instance;
_Instance = AnimancerEditorUtilities.FindAssetOfType<AnimancerSettings>();
if (_Instance != null)
return _Instance;
_Instance = CreateInstance<AnimancerSettings>();
_Instance.name = "Animancer Settings";
_Instance.hideFlags = HideFlags.DontSaveInBuild;
var path = $"Assets/{_Instance.name}.asset";
path = AssetDatabase.GenerateUniqueAssetPath(path);
AssetDatabase.CreateAsset(_Instance, path);
return _Instance;
}
}
/************************************************************************************************************************/
/// <summary>Finds an existing instance of this asset anywhere in the project.</summary>
[InitializeOnLoadMethod]
private static void FindExistingInstance()
{
if (_Instance == null)
_Instance = AnimancerEditorUtilities.FindAssetOfType<AnimancerSettings>();
}
/************************************************************************************************************************/
private SerializedObject _SerializedObject;
/// <summary>The <see cref="SerializedProperty"/> representing the <see cref="Instance"/>.</summary>
public static SerializedObject SerializedObject
=> Instance._SerializedObject ?? (Instance._SerializedObject = new(Instance));
/************************************************************************************************************************/
private readonly List<Dictionary<string, SerializedProperty>>
SerializedProperties = new();
private static SerializedProperty GetSerializedProperty(int index, string propertyPath)
{
while (index >= Instance.SerializedProperties.Count)
Instance.SerializedProperties.Add(null);
var properties = Instance.SerializedProperties[index];
properties ??= Instance.SerializedProperties[index] = new();
if (!properties.TryGetValue(propertyPath, out var property))
{
property = SerializedObject.FindProperty(propertyPath);
properties.Add(propertyPath, property);
}
return property;
}
/// <summary>Returns a <see cref="SerializedProperty"/> relative to the data at the specified `index`.</summary>
public static SerializedProperty GetSerializedProperty(
int index,
ref string basePropertyPath,
string propertyPath)
{
if (string.IsNullOrEmpty(basePropertyPath))
basePropertyPath =
$"{nameof(_Data)}{Serialization.ArrayDataPrefix}{index}{Serialization.ArrayDataSuffix}";
if (string.IsNullOrEmpty(propertyPath))
propertyPath = basePropertyPath;
else
propertyPath = $"{basePropertyPath}.{propertyPath}";
return GetSerializedProperty(index, propertyPath);
}
/************************************************************************************************************************/
[SerializeReference]
private List<AnimancerSettingsGroup> _Data;
/// <summary>Returns a stored item of the specified type or creates a new one if necessary.</summary>
public static T GetOrCreateData<T>()
where T : AnimancerSettingsGroup, new()
{
ref var data = ref Instance._Data;
data ??= new();
var index = AnimancerEditorUtilities.IndexOfType(Instance._Data, typeof(T));
if (index >= 0)
return (T)data[index];
var newT = new T();
newT.SetDataIndex(data.Count);
data.Add(newT);
SetDirty();
return newT;
}
/************************************************************************************************************************/
/// <summary>Calls <see cref="EditorUtility.SetDirty"/> on the <see cref="Instance"/>.</summary>
public static new void SetDirty()
=> EditorUtility.SetDirty(_Instance);
/************************************************************************************************************************/
/// <summary>
/// Ensures that there is an instance of each class derived from <see cref="AnimancerSettingsGroup"/>.
/// </summary>
protected virtual void OnEnable()
{
AnimancerEditorUtilities.InstantiateDerivedTypes(ref _Data);
for (int i = 0; i < _Data.Count; i++)
_Data[i].SetDataIndex(i);
}
/************************************************************************************************************************/
/// <summary>A custom Inspector for <see cref="AnimancerSettings"/>.</summary>
[CustomEditor(typeof(AnimancerSettings), true), CanEditMultipleObjects]
public class Editor : UnityEditor.Editor
{
/************************************************************************************************************************/
[NonSerialized]
private SerializedProperty _Data;
/************************************************************************************************************************/
/// <summary>Called when this object is first loaded.</summary>
protected virtual void OnEnable()
{
_Data = serializedObject.FindProperty(nameof(AnimancerSettings._Data));
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnInspectorGUI()
{
DoInfoGUI();
DoOptionalWarningsGUI();
serializedObject.Update();
var count = _Data.arraySize;
for (int i = 0; i < count; i++)
DoDataGUI(_Data.GetArrayElementAtIndex(i), i);
serializedObject.ApplyModifiedProperties();
}
/************************************************************************************************************************/
/// <summary>
/// If <c>true</c>, the next <see cref="OnInspectorGUI"/> will skip drawing the info panel.
/// </summary>
public static bool HideNextInfo { get; set; }
private void DoInfoGUI()
{
if (HideNextInfo)
{
HideNextInfo = false;
}
else
{
EditorGUILayout.HelpBox(
"Feel free to move this asset anywhere in your project." +
"\n\nIt should generally not be in the Animancer folder" +
" so that if you ever update Animancer you can delete that folder" +
" without losing these settings." +
"\n\nIf this asset is deleted, it will be automatically recreated" +
" with default values when something needs it.",
MessageType.Info);
}
}
/************************************************************************************************************************/
private void DoDataGUI(SerializedProperty property, int index)
{
if (property.managedReferenceValue is AnimancerSettingsGroup value)
{
DoHeading(value.DisplayName);
var first = true;
var depth = property.depth;
while (property.NextVisible(first) && property.depth > depth)
{
first = false;
EditorGUILayout.PropertyField(property, true);
}
}
else
{
EditorGUILayout.BeginHorizontal();
DoHeading("Missing Type");
if (GUILayout.Button("X", AnimancerGUI.MiniButtonStyle))
{
var count = _Data.arraySize;
_Data.DeleteArrayElementAtIndex(index);
if (count == _Data.arraySize)
_Data.DeleteArrayElementAtIndex(index);
serializedObject.ApplyModifiedProperties();
GUIUtility.ExitGUI();
}
EditorGUILayout.EndHorizontal();
}
}
/************************************************************************************************************************/
private void DoOptionalWarningsGUI()
{
DoHeading("Optional Warnings");
EditorGUILayout.BeginHorizontal();
using (var label = PooledGUIContent.Acquire("Disabled Warnings"))
{
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.EnumFlagsField(label, Validate.PermanentlyDisabledWarnings);
if (EditorGUI.EndChangeCheck())
Validate.PermanentlyDisabledWarnings = (OptionalWarning)value;
}
if (GUILayout.Button("Help", EditorStyles.miniButton, AnimancerGUI.DontExpandWidth))
Application.OpenURL(Strings.DocsURLs.OptionalWarning);
EditorGUILayout.EndHorizontal();
}
/************************************************************************************************************************/
private static GUIStyle _HeadingStyle;
/// <summary>Draws a heading label.</summary>
public static void DoHeading(string text)
=> GUILayout.Label(text, _HeadingStyle ??= new(EditorStyles.largeLabel)
{
fontSize = 18,
});
/************************************************************************************************************************/
/// <summary>Creates the Project Settings page.</summary>
[SettingsProvider]
public static SettingsProvider CreateSettingsProvider()
{
UnityEditor.Editor editor = null;
return new("Project/" + Strings.ProductName, SettingsScope.Project)
{
keywords = new HashSet<string>() { Strings.ProductName },
guiHandler = searchContext =>
{
if (editor == null)
editor = CreateEditor(Instance);
HideNextInfo = true;
editor.OnInspectorGUI();
},
};
}
/************************************************************************************************************************/
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 18bd840b706853d4a934ec3199f63a41
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerSettings.cs
uploadId: 795566

View File

@@ -0,0 +1,85 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A static reference to a persistent setting stored in <see cref="AnimancerSettings"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerSettingsGroup_1
public static class AnimancerSettingsGroup<T>
where T : AnimancerSettingsGroup, new()
{
/************************************************************************************************************************/
/// <summary>Gets or creates a <typeparamref name="T"/> in the <see cref="AnimancerSettings"/> asset.</summary>
public static T Instance
=> AnimancerSettings.GetOrCreateData<T>();
/************************************************************************************************************************/
}
/// <summary>Base class for groups of fields that can be serialized inside <see cref="AnimancerSettings"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerSettingsGroup
[Serializable, InternalSerializableType]
public abstract class AnimancerSettingsGroup : IComparable<AnimancerSettingsGroup>
{
/************************************************************************************************************************/
private int _DataIndex = -1;
private string _BasePropertyPath;
/************************************************************************************************************************/
/// <summary>The user-firendly name to display in the Inspector.</summary>
public abstract string DisplayName { get; }
/// <summary>The index to display this data at in the Inspector.</summary>
public abstract int Index { get; }
/************************************************************************************************************************/
/// <summary>Sets the index used to find <see cref="SerializedProperty"/> instances for this group.</summary>
internal void SetDataIndex(int index)
{
if (_DataIndex == index)
return;
_DataIndex = index;
_BasePropertyPath = null;
}
/************************************************************************************************************************/
/// <summary>Returns a <see cref="SerializedProperty"/> relative to the base of this group.</summary>
protected SerializedProperty GetSerializedProperty(string propertyPath)
=> AnimancerSettings.GetSerializedProperty(_DataIndex, ref _BasePropertyPath, propertyPath);
/************************************************************************************************************************/
/// <summary>
/// Draws a <see cref="EditorGUILayout.PropertyField(SerializedProperty, GUILayoutOption[])"/> for a
/// property in this group.
/// </summary>
protected SerializedProperty DoPropertyField(string propertyPath)
{
var property = GetSerializedProperty(propertyPath);
EditorGUILayout.PropertyField(property, true);
return property;
}
/************************************************************************************************************************/
/// <summary>Compares the <see cref="Index"/>.</summary>
public int CompareTo(AnimancerSettingsGroup other)
=> Index.CompareTo(other.Index);
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: dc77ccece8dfec64b9da74266f0ec4fa
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerSettingsGroup.cs
uploadId: 795566

View File

@@ -0,0 +1,63 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
[assembly: AssemblyTitle("Kybernetik.Animancer.Editor")]
[assembly: AssemblyProduct("Animancer Pro")]
[assembly: AssemblyDescription("An animation system for Unity which is based on the Playables API.")]
[assembly: AssemblyCompany("Kybernetik")]
[assembly: AssemblyCopyright("Copyright © Kybernetik 2018-2025")]
[assembly: AssemblyVersion("8.2.2.34")]
#if UNITY_EDITOR
[assembly: SuppressMessage("Style", "IDE0039:Use local function",
Justification = "Locals create a new delegate with each use which is less efficient and can break code.")]
[assembly: SuppressMessage("Style", "IDE0044:Make field readonly",
Justification = "Using the [SerializeField] attribute on a private field means Unity will set it from serialized data.")]
[assembly: SuppressMessage("Code Quality", "IDE0051:Remove unused private members",
Justification = "Unity messages can be private, but the IDE will not know that Unity can still call them.")]
[assembly: SuppressMessage("Code Quality", "IDE0052:Remove unread private members",
Justification = "Unity messages can be private and don't need to be called manually.")]
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter",
Justification = "Unity messages sometimes need specific signatures, even if you don't use all the parameters.")]
[assembly: SuppressMessage("Style", "IDE0062:Make local function 'static'",
Justification = "Not supported by Unity")]
[assembly: SuppressMessage("Style", "IDE0063:Use simple 'using' statement",
Justification = "Not always good for implying intent.")]
[assembly: SuppressMessage("Code Quality", "IDE0067:Dispose objects before losing scope",
Justification = "Not always relevant.")]
[assembly: SuppressMessage("Code Quality", "IDE0068:Use recommended dispose pattern",
Justification = "Not always relevant.")]
[assembly: SuppressMessage("Code Quality", "IDE0069:Disposable fields should be disposed",
Justification = "Not always relevant.")]
[assembly: SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression",
Justification = "Don't give code style advice in publically released code.")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles",
Justification = "Don't give code style advice in publically released code.")]
[assembly: SuppressMessage("Correctness", "UNT0005:Suspicious Time.deltaTime usage",
Justification = "Time.deltaTime is not suspicious in FixedUpdate, it has the same value as Time.fixedDeltaTime")]
[assembly: SuppressMessage("Type Safety", "UNT0014:Invalid type for call to GetComponent",
Justification = "Doesn't account for generic constraints.")]
[assembly: SuppressMessage("Correctness", "UNT0029:Pattern matching with null on Unity objects",
Justification = "Use a regular equality check if handling destroyed objects is necessary")]
[assembly: SuppressMessage("Code Quality", "CS0649:Field is never assigned to, and will always have its default value",
Justification = "Using the [SerializeField] attribute on a private field means Unity will set it from serialized data.")]
[assembly: SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
Justification = "Having a field doesn't mean you are responsible for creating and destroying it.")]
[assembly: SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Not all events need to care about the sender.")]
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
Justification = "No need to pollute the member list of implementing types.")]
[assembly: SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly",
Justification = "No need to pollute the member list of implementing types.")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2235:MarkAllNonSerializableFields",
Justification = "UnityEngine.Object is serializable by Unity.")]
#endif

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 44a1a6ee5f922404ab7d9747df2d31a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/AssemblyInfo.cs
uploadId: 795566

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a5900fc4c275cf748a92ed28bbef13c1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 9e5936589d9791a4288596c4b383b592
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/AnimationBindings.cs
uploadId: 795566

View File

@@ -0,0 +1,54 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System.Runtime.CompilerServices;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] Caches <see cref="AnimationClip.events"/> to reduce garbage allocations.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimationEventCache
///
public static class AnimationEventCache
{
/************************************************************************************************************************/
private static readonly ConditionalWeakTable<AnimationClip, AnimationEvent[]>
ClipToEvents = new();
/************************************************************************************************************************/
/// <summary>
/// Returns the <see cref="AnimationClip.events"/> and caches the result to avoid allocating more memory with
/// each subsequent call.
/// </summary>
public static AnimationEvent[] GetCachedEvents(this AnimationClip clip)
{
if (!ClipToEvents.TryGetValue(clip, out var events))
{
events = clip.events;
ClipToEvents.Add(clip, events);
}
return events;
}
/************************************************************************************************************************/
/// <summary>Clears the cache.</summary>
public static void Clear()
=> ClipToEvents.Clear();
/************************************************************************************************************************/
/// <summary>Removes the `clip` from the cache so its events will be retrieved again next time.</summary>
public static void Remove(AnimationClip clip)
=> ClipToEvents.Remove(clip);
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: bb828e75ad28a6441b43f561f568f98d
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/AnimationEventCache.cs
uploadId: 795566

View File

@@ -0,0 +1,140 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A cache to optimize repeated attribute access.</summary>
/// <remarks>
/// If <typeparamref name="TAttribute"/> implements <see cref="IInitializable{T}"/> for <see cref="MemberInfo"/>,
/// its <see cref="IInitializable{T}.Initialize(T)"/> method will be called automatically.
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AttributeCache_1
public static class AttributeCache<TAttribute>
where TAttribute : class
{
/************************************************************************************************************************/
private static readonly Dictionary<MemberInfo, TAttribute>
MemberToAttribute = new();
/************************************************************************************************************************/
/// <summary>
/// Returns the <typeparamref name="TAttribute"/> attribute on the specified `member` (if there is one).
/// </summary>
public static TAttribute GetAttribute(MemberInfo member)
{
if (!MemberToAttribute.TryGetValue(member, out var attribute))
{
try
{
attribute = member.GetAttribute<TAttribute>();
if (attribute is IInitializable<MemberInfo> initializable)
initializable.Initialize(member);
}
catch (Exception exception)
{
Debug.LogException(exception);
attribute = null;
}
MemberToAttribute.Add(member, attribute);
}
return attribute;
}
/************************************************************************************************************************/
/// <summary>
/// Returns the <typeparamref name="TAttribute"/> attribute (if any)
/// on the specified `type` or its <see cref="Type.BaseType"/> (recursively).
/// </summary>
public static TAttribute GetAttribute(Type type)
{
if (type == null)
return null;
var attribute = GetAttribute((MemberInfo)type);
if (attribute != null)
return attribute;
return MemberToAttribute[type] = GetAttribute(type.BaseType);
}
/************************************************************************************************************************/
/// <summary>
/// Returns the <typeparamref name="TAttribute"/> attribute on the specified `field` or its
/// <see cref="FieldInfo.FieldType"/> or <see cref="MemberInfo.DeclaringType"/>.
/// </summary>
public static TAttribute FindAttribute(FieldInfo field)
{
var attribute = GetAttribute(field);
if (attribute != null)
return attribute;
attribute = GetAttribute(field.FieldType);
if (attribute != null)
return MemberToAttribute[field] = attribute;
attribute = GetAttribute(field.DeclaringType);
if (attribute != null)
return MemberToAttribute[field] = attribute;
return attribute;
}
/************************************************************************************************************************/
/// <summary>[Editor-Only]
/// Returns the <typeparamref name="TAttribute"/> attribute on the underlying field
/// of the `property` or its <see cref="FieldInfo.FieldType"/> or
/// <see cref="MemberInfo.DeclaringType"/> or any of the parent properties
/// or the type of the <see cref="SerializedObject.targetObject"/>.
/// </summary>
public static TAttribute FindAttribute(SerializedProperty property)
{
var accessor = property.GetAccessor();
while (accessor != null)
{
var field = accessor.GetField(property);
var attribute = GetAttribute(field);
if (attribute != null)
return attribute;
var value = accessor.GetValue(property);
if (value != null)
{
attribute = GetAttribute(value.GetType());
if (attribute != null)
return attribute;
}
accessor = accessor.Parent;
}
// If none of the fields of types they are declared in have names, try the actual type of the target.
{
var attribute = GetAttribute(property.serializedObject.targetObject.GetType());
if (attribute != null)
return attribute;
}
return null;
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: ea23cad192fdb024b83eb42c328bc444
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/AttributeCache.cs
uploadId: 795566

View File

@@ -0,0 +1,257 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
// Inspector Gadgets // https://kybernetik.com.au/animancer // Copyright 2017-2024 Kybernetik //
#if UNITY_EDITOR && UNITY_IMGUI
using Animancer.Editor;
using System;
using System.Collections.Generic;
using System.Globalization;
using UnityEditor;
namespace Animancer.Units.Editor
//namespace InspectorGadgets.Editor
{
/// <summary>[Editor-Only]
/// A system for formatting floats as strings that fit into a limited area and storing the results so they can be
/// reused to minimise the need for garbage collection, particularly for string construction.
/// </summary>
///
/// <remarks>
/// This system only affects the display value. Once you select a field, it shows its actual value.
/// <para></para>
/// <strong>Example:</strong>
/// With <c>"x"</c> as the suffix:
/// <list type="bullet">
/// <item><c>1.111111</c> could instead show <c>1.111~x</c>.</item>
/// <item><c>0.00001234567</c> would normally show <c>1.234567e-05</c>, but with this it instead shows <c>0~x</c>
/// because very small values generally aren't useful.</item>
/// <item><c>99999999</c> shows <c>1e+08x</c> because very large values are already approximations and trying to
/// format them correctly would be very difficult.</item>
/// </list>
/// </remarks>
///
/// https://kybernetik.com.au/animancer/api/Animancer.Units.Editor/CompactUnitConversionCache
/// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/CompactUnitConversionCache
///
public class CompactUnitConversionCache
{
/************************************************************************************************************************/
/// <summary>Should the fields show approximations if the value is too long for the GUI?</summary>
public static bool ShowApproximations
=> AnimancerSettingsGroup<AnimationTimeAttributeSettings>.Instance.showApproximations;
// => PropertyDrawers.TransformPropertyDrawer.ShowApproximations;
/************************************************************************************************************************/
/// <summary>The suffix added to the end of each value.</summary>
public readonly string Suffix;
/// <summary>The <see cref="Suffix"/> with a <c>~</c> before it to indicate an approximation.</summary>
public readonly string ApproximateSuffix;
/// <summary>The value <c>0</c> with the <see cref="Suffix"/>.</summary>
public readonly string ConvertedZero;
/// <summary>The value <c>0</c> with the <see cref="ApproximateSuffix"/>.</summary>
public readonly string ConvertedSmallPositive;
/// <summary>The value <c>-0</c> with the <see cref="ApproximateSuffix"/>.</summary>
public readonly string ConvertedSmallNegative;
/// <summary>The pixel width of the <see cref="Suffix"/> when drawn by <see cref="EditorStyles.numberField"/>.</summary>
public float _SuffixWidth;
/// <summary>The caches for each character count.</summary>
/// <remarks><c>this[x]</c> is a cache that outputs strings with <c>x</c> characters.</remarks>
private readonly List<ConversionCache<float, string>>
Caches = new();
/************************************************************************************************************************/
/// <summary>Strings mapped to the width they would require for a <see cref="EditorStyles.numberField"/>.</summary>
private static ConversionCache<string, float> _WidthCache;
/// <summary>Padding around the text in a <see cref="EditorStyles.numberField"/>.</summary>
public static float _FieldPadding;
/// <summary>The pixel width of the <c>~</c> character when drawn by <see cref="EditorStyles.numberField"/>.</summary>
public static float _ApproximateSymbolWidth;
/// <summary>The character(s) used to separate decimal values in the current OS language.</summary>
public static string _DecimalSeparator;
/// <summary>Values smaller than this become <c>0~</c> or <c>-0~</c>.</summary>
public const float
SmallExponentialThreshold = 0.0001f;
/// <summary>Values larger than this can't be approximated.</summary>
public const float
LargeExponentialThreshold = 9999999f;
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="CompactUnitConversionCache"/>.</summary>
public CompactUnitConversionCache(string suffix)
{
Suffix = suffix;
ApproximateSuffix = "~" + Suffix;
ConvertedZero = "0" + Suffix;
ConvertedSmallPositive = "0" + ApproximateSuffix;
ConvertedSmallNegative = "-0" + ApproximateSuffix;
}
/************************************************************************************************************************/
/// <summary>
/// Returns a cached string representing the `value` trimmed to fit within the `width` (if necessary) and with
/// the <see cref="Suffix"/> added on the end.
/// </summary>
public string Convert(float value, float width)
{
if (value == 0)
return ConvertedZero;
if (!ShowApproximations)
return GetCache(0).Convert(value);
if (value < SmallExponentialThreshold &&
value > -SmallExponentialThreshold)
return value > 0 ? ConvertedSmallPositive : ConvertedSmallNegative;
var index = CalculateCacheIndex(value, width);
return GetCache(index).Convert(value);
}
/************************************************************************************************************************/
/// <summary>Calculate the index of the cache to use for the given parameters.</summary>
private int CalculateCacheIndex(float value, float width)
{
//if (value > LargeExponentialThreshold ||
// value < -LargeExponentialThreshold)
// return 0;
var valueString = value.ToStringCached();
// It the approximated string wouldn't be shorter than the original, don't approximate.
if (valueString.Length < 2 + ApproximateSuffix.Length)
return 0;
if (_SuffixWidth == 0)
{
if (_WidthCache == null)
{
_WidthCache = ConversionCache.CreateWidthCache(EditorStyles.numberField);
_FieldPadding = EditorStyles.numberField.padding.horizontal;
_ApproximateSymbolWidth = _WidthCache.Convert("~") - _FieldPadding;
}
if (!string.IsNullOrWhiteSpace(Suffix))
_SuffixWidth = _WidthCache.Convert(Suffix);
}
// If the field is wide enough to fit the full value, don't approximate.
width -= _FieldPadding + _ApproximateSymbolWidth * 0.75f;
var valueWidth = _WidthCache.Convert(valueString) + _SuffixWidth;
if (valueWidth <= width)
return 0;
// If the number of allowed characters would include the full value, don't approximate.
var suffixedLength = valueString.Length + Suffix.Length;
var allowedCharacters = (int)(suffixedLength * width / valueWidth);
if (allowedCharacters + 2 >= suffixedLength)
return 0;
return allowedCharacters;
}
/************************************************************************************************************************/
/// <summary>Creates and returns a cache for the specified `characterCount`.</summary>
private ConversionCache<float, string> GetCache(int characterCount)
{
while (Caches.Count <= characterCount)
Caches.Add(null);
var cache = Caches[characterCount];
if (cache == null)
{
if (characterCount == 0)
{
cache = new((value) =>
{
return value.ToStringCached() + Suffix;
});
}
else
{
cache = new((value) =>
{
var valueString = value.ToStringCached();
if (value > LargeExponentialThreshold ||
value < -LargeExponentialThreshold)
goto IsExponential;
_DecimalSeparator ??= CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
var decimalIndex = valueString.IndexOf(_DecimalSeparator);
if (decimalIndex < 0 || decimalIndex > characterCount)
goto IsExponential;
// Not exponential.
return valueString[..characterCount] + ApproximateSuffix;
IsExponential:
var digits = Math.Max(0, characterCount - ApproximateSuffix.Length - 1);
var format = GetExponentialFormat(digits);
valueString = value.ToString(format);
TrimExponential(ref valueString);
return valueString + Suffix;
});
}
Caches[characterCount] = cache;
}
return cache;
}
/************************************************************************************************************************/
private static List<string> _ExponentialFormats;
/// <summary>Returns a format string to include the specified number of `digits` in an exponential number.</summary>
public static string GetExponentialFormat(int digits)
{
_ExponentialFormats ??= new();
while (_ExponentialFormats.Count <= digits)
_ExponentialFormats.Add("g" + _ExponentialFormats.Count);
return _ExponentialFormats[digits];
}
/************************************************************************************************************************/
private static void TrimExponential(ref string valueString)
{
var length = valueString.Length;
if (length <= 4 ||
valueString[length - 4] != 'e' ||
valueString[length - 2] != '0')
return;
valueString =
valueString[..(length - 2)] +
valueString[length - 1];
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: b282ce649e44335499d21e0472c670e1
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/CompactUnitConversionCache.cs
uploadId: 795566

View File

@@ -0,0 +1,137 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
// Inspector Gadgets // https://kybernetik.com.au/inspector-gadgets // Copyright 2017-2024 Kybernetik //
#if UNITY_EDITOR
//#define LOG_CONVERSION_CACHE
using System;
using System.Collections.Generic;
using UnityEngine;
// Shared File Last Modified: 2023-09-02
namespace Animancer.Editor
//namespace InspectorGadgets.Editor
{
/// <summary>[Editor-Only]
/// A simple system for converting objects and storing the results so they can be reused to minimise the need for
/// garbage collection, particularly for string construction.
/// </summary>
/// <remarks>This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime.</remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache_2
/// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache_2
///
public class ConversionCache<TKey, TValue>
{
/************************************************************************************************************************/
private class CachedValue
{
public int lastFrameAccessed;
public TValue value;
}
/************************************************************************************************************************/
private readonly Dictionary<TKey, CachedValue>
Cache = new();
private readonly List<TKey>
Keys = new();
private readonly Func<TKey, TValue>
Converter;
private int _LastCleanupFrame;
/************************************************************************************************************************/
/// <summary>
/// Creates a new <see cref="ConversionCache{TKey, TValue}"/> which uses the specified delegate to convert values.
/// </summary>
public ConversionCache(Func<TKey, TValue> converter)
=> Converter = converter;
/************************************************************************************************************************/
/// <summary>
/// If a value has already been cached for the specified `key`, return it. Otherwise create a new one using
/// the delegate provided in the constructor and cache it.
/// <para></para>
/// If the `key` is <c>null</c>, this method returns the default <typeparamref name="TValue"/>.
/// </summary>
/// <remarks>This method also periodically removes values that have not been used recently.</remarks>
public TValue Convert(TKey key)
{
if (key == null)
return default;
CachedValue cached;
// The next time a value is retrieved after at least 100 frames, clear out any old ones.
var frame = Time.frameCount;
if (_LastCleanupFrame + 100 < frame)
{
for (int i = Keys.Count - 1; i >= 0; i--)
{
var checkKey = Keys[i];
if (!Cache.TryGetValue(checkKey, out cached) ||
cached.lastFrameAccessed <= _LastCleanupFrame)
{
Cache.Remove(checkKey);
Keys.RemoveAt(i);
}
}
_LastCleanupFrame = frame;
}
if (!Cache.TryGetValue(key, out cached))
{
Cache.Add(key, cached = new() { value = Converter(key) });
Keys.Add(key);
}
cached.lastFrameAccessed = frame;
return cached.value;
}
/************************************************************************************************************************/
}
/// <summary>[Editor-Only] Utilities for <see cref="ConversionCache{TKey, TValue}"/>.</summary>
/// <remarks>This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime.</remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache
/// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache
///
public static class ConversionCache
{
/************************************************************************************************************************/
/// <summary>
/// Creates a <see cref="ConversionCache{TKey, TValue}"/> for calculating the GUI width occupied by text using
/// the specified `style`.
/// </summary>
public static ConversionCache<string, float> CreateWidthCache(GUIStyle style)
=> new(style.CalculateWidth);
/************************************************************************************************************************/
// The "g" format gives a lower case 'e' for exponentials instead of upper case 'E'.
private static readonly ConversionCache<float, string>
FloatToString = new((value) => $"{value:g}");
/// <summary>[Animancer Extension]
/// Calls <see cref="float.ToString(string)"/> using <c>"g"</c> as the format and caches the result.
/// </summary>
public static string ToStringCached(this float value)
=> FloatToString.Convert(value);
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 1b0e970556a97204991d267c5cdb3ca9
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/ConversionCache.cs
uploadId: 795566

View File

@@ -0,0 +1,92 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System.Reflection;
using UnityEditor;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] Utilities for using <see cref="DefaultValueAttribute"/>s.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/DefaultValues
public static class DefaultValues
{
/************************************************************************************************************************/
/// <summary>[Editor-Only]
/// If the field represented by the `property` has a <see cref="DefaultValueAttribute"/>,
/// this method sets the `value` to its <see cref="DefaultValueAttribute.Primary"/> value.
/// If it was already at the value, it sets it to the <see cref="DefaultValueAttribute.Secondary"/>
/// value instead. And if the field has no attribute, it uses the default for the type.
/// </summary>
public static void SetToDefault<T>(ref T value, SerializedProperty property)
{
var accessor = property.GetAccessor();
var field = accessor.GetField(property);
if (field == null)
accessor.SetValue(property, null);
else
SetToDefault(ref value, field);
}
/************************************************************************************************************************/
/// <summary>[Editor-Only]
/// If the field represented by the `property` has a <see cref="DefaultValueAttribute"/>,
/// this method sets the `value` to its <see cref="DefaultValueAttribute.Primary"/> value.
/// If it was already at the value, it sets it to the <see cref="DefaultValueAttribute.Secondary"/>
/// value instead. And if the field has no attribute, it uses the default for the type.
/// </summary>
public static void SetToDefault<T>(ref T value, FieldInfo field)
{
var defaults = field.GetAttribute<DefaultValueAttribute>();
if (defaults != null)
defaults.SetToDefault(ref value);
else
value = default;
}
/************************************************************************************************************************/
/// <summary>[Editor-Only]
/// Sets the `value` equal to the <see cref="DefaultValueAttribute.Primary"/> value.
/// If it was already at the value, it sets it equal to the <see cref="DefaultValueAttribute.Secondary"/>
/// value instead.
/// </summary>
public static void SetToDefault<T>(this DefaultValueAttribute attribute, ref T value)
{
var primary = attribute.Primary;
if (!Equals(value, primary))
{
value = (T)primary;
return;
}
var secondary = attribute.Secondary;
if (secondary != null || !typeof(T).IsValueType)
{
value = (T)secondary;
return;
}
}
/************************************************************************************************************************/
/// <summary>[Editor-Only]
/// Sets the `value` equal to the `primary` value.
/// If it was already at the value, it sets it equal to the `secondary` value instead.
/// </summary>
public static void SetToDefault<T>(ref T value, T primary, T secondary)
{
if (!Equals(value, primary))
value = primary;
else
value = secondary;
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 83cc1f85e7da4a545a63f04ffb42d8ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/DefaultValues.cs
uploadId: 795566

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ef1e58ab0b0cf224d9837d422a8647f1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: aa67bea4f1d70534987fb1358fd71903
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/AnimancerGUI.cs
uploadId: 795566

View File

@@ -0,0 +1,118 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] Icon textures used throughout Animancer.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerIcons
public static class AnimancerIcons
{
/************************************************************************************************************************/
/// <summary>A standard icon for information.</summary>
public static readonly Texture Info = Load("console.infoicon");
/// <summary>A standard icon for warnings.</summary>
public static readonly Texture Warning = Load("console.warnicon");
/// <summary>A standard icon for errors.</summary>
public static readonly Texture Error = Load("console.erroricon");
/************************************************************************************************************************/
private static Texture _ScriptableObject;
/// <summary>The icon for <see cref="UnityEngine.ScriptableObject"/>.</summary>
public static Texture ScriptableObject
{
get
{
if (_ScriptableObject == null)
{
_ScriptableObject = Load("d_ScriptableObject Icon");
if (_ScriptableObject == null)
_ScriptableObject = AssetPreview.GetMiniTypeThumbnail(typeof(StringAsset));
}
return _ScriptableObject;
}
}
/************************************************************************************************************************/
/// <summary>Loads an icon texture.</summary>
public static Texture Load(string name, FilterMode filterMode = FilterMode.Bilinear)
{
var icon = EditorGUIUtility.Load(name) as Texture;
if (icon != null)
icon.filterMode = filterMode;
return icon;
}
/// <summary>Loads an icon `texture` if it was <c>null</c>.</summary>
public static Texture Load(ref Texture texture, string name, FilterMode filterMode = FilterMode.Bilinear)
=> texture != null
? texture
: texture = Load(name, filterMode);
/************************************************************************************************************************/
private static GUIContent
_PlayIcon,
_PauseIcon,
_StepBackwardIcon,
_StepForwardIcon,
_AddIcon,
_ClearIcon,
_CopyIcon;
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a play button.</summary>
public static GUIContent PlayIcon
=> IconContent(ref _PlayIcon, "PlayButton");
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a pause button.</summary>
public static GUIContent PauseIcon
=> IconContent(ref _PauseIcon, "PauseButton");
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a step backward button.</summary>
public static GUIContent StepBackwardIcon
=> IconContent(ref _StepBackwardIcon, "Animation.PrevKey");
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a step forward button.</summary>
public static GUIContent StepForwardIcon
=> IconContent(ref _StepForwardIcon, "Animation.NextKey");
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for an add button.</summary>
public static GUIContent AddIcon(string tooltip = "Add")
=> IconContent(ref _AddIcon, "Toolbar Plus", tooltip);
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a clear button.</summary>
public static GUIContent ClearIcon(string tooltip = "Clear")
=> IconContent(ref _ClearIcon, "Grid.EraserTool", tooltip);
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a copy button.</summary>
public static GUIContent CopyIcon(string tooltip = "Copy to clipboard")
=> IconContent(ref _CopyIcon, "UnityEditor.ConsoleWindow", tooltip);
/************************************************************************************************************************/
/// <summary>Calls <see cref="EditorGUIUtility.IconContent(string)"/> if the `content` was null.</summary>
public static GUIContent IconContent(ref GUIContent content, string name, string tooltip = "")
{
content ??= EditorGUIUtility.IconContent(name);
content.tooltip = tooltip;
return content;
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 6481076e108e435459e58460a5d52a74
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/AnimancerIcons.cs
uploadId: 795566

View File

@@ -0,0 +1,88 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] <see cref="GUIStyle"/>s for a group of connected buttons.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ButtonGroupStyles
public struct ButtonGroupStyles
{
/************************************************************************************************************************/
/// <summary>The style for the button on the far left.</summary>
public GUIStyle left;
/// <summary>The style for any buttons in the middle.</summary>
public GUIStyle middle;
/// <summary>The style for the button on the far right.</summary>
public GUIStyle right;
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="ButtonGroupStyles"/>.</summary>
public ButtonGroupStyles(
GUIStyle left,
GUIStyle middle,
GUIStyle right)
{
this.left = left;
this.middle = middle;
this.right = right;
}
/************************************************************************************************************************/
/// <summary>Copies any <c>null</c> values from another group.</summary>
public void CopyMissingStyles(ButtonGroupStyles copyFrom)
{
left ??= copyFrom.left;
middle ??= copyFrom.middle;
right ??= copyFrom.right;
}
/************************************************************************************************************************/
/// <summary>The default styles for a mini button.</summary>
public static ButtonGroupStyles MiniButton => new(
EditorStyles.miniButtonLeft,
EditorStyles.miniButtonMid,
EditorStyles.miniButtonRight);
/************************************************************************************************************************/
private static ButtonGroupStyles _Button;
/// <summary>The default styles for a button.</summary>
public static ButtonGroupStyles Button
{
get
{
_Button.left ??= MiniToRegularButtonStyle(EditorStyles.miniButtonLeft);
_Button.middle ??= MiniToRegularButtonStyle(EditorStyles.miniButtonMid);
_Button.right ??= MiniToRegularButtonStyle(EditorStyles.miniButtonRight);
return _Button;
}
}
/************************************************************************************************************************/
/// <summary>Creates a copy of the `style` with the size of a regular button.</summary>
public static GUIStyle MiniToRegularButtonStyle(GUIStyle style)
=> new(style)
{
fixedHeight = 0,
padding = GUI.skin.button.padding,
stretchWidth = false,
};
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: cbe793685698bc24bb5b1087e4c93cd2
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/ButtonGroupStyles.cs
uploadId: 795566

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4cf051da751d8b14ab331c9a4d43511d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A custom GUI for an <see cref="AnimancerEvent.Dispatcher"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerEventDispatcherDrawer
[CustomGUI(typeof(AnimancerEvent.Dispatcher))]
public class AnimancerEventDispatcherDrawer : CustomGUI<AnimancerEvent.Dispatcher>
{
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
{
var state = Value.State;
var events = state?.SharedEvents;
if (events == null)
{
EditorGUILayout.LabelField("Event Dispatcher", "Null");
return;
}
var targetPath = state != null
? state.GetPath()
: "Null";
var eventSequenceDrawer = EventSequenceDrawer.Get(events);
var area = AnimancerGUI.LayoutRect(eventSequenceDrawer.CalculateHeight(events));
using (var label = PooledGUIContent.Acquire("Event Dispatcher"))
using (var summary = PooledGUIContent.Acquire(targetPath))
eventSequenceDrawer.DoGUI(ref area, events, label, summary);
if (eventSequenceDrawer.IsExpanded && state != null)
{
EditorGUI.indentLevel++;
var enabled = GUI.enabled;
GUI.enabled = false;
EditorGUILayout.Toggle("Has Owned Events", state.HasOwnedEvents);
GUI.enabled = enabled;
EditorGUI.indentLevel--;
}
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 342d6bdafab430c48ac47d5f67eced91
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/AnimancerEventDispatcherDrawer.cs
uploadId: 795566

View File

@@ -0,0 +1,73 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="float"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/FloatGUI
///
[CustomGUI(typeof(float))]
public class FloatGUI : CustomGUI<float>
{
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
=> Value = EditorGUILayout.FloatField(Label, Value);
/************************************************************************************************************************/
}
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="int"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/IntGUI
///
[CustomGUI(typeof(int))]
public class IntGUI : CustomGUI<int>
{
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
=> Value = EditorGUILayout.IntField(Label, Value);
/************************************************************************************************************************/
}
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="string"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/StringGUI
///
[CustomGUI(typeof(string))]
public class StringGUI : CustomGUI<string>
{
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
=> Value = EditorGUILayout.TextField(Label, Value);
/************************************************************************************************************************/
}
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="Object"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ObjectGUI_1
///
[CustomGUI(typeof(Object))]
public class ObjectGUI<T> : CustomGUI<T>
where T : Object
{
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
=> Value = AnimancerGUI.DoObjectFieldGUI(Label, Value, true);
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 8e4d11fbf9dad8149b1966ed610c9916
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/BasicCustomGUI.cs
uploadId: 795566

View File

@@ -0,0 +1,92 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] Draws a custom GUI for an object.</summary>
/// <remarks>
/// Every non-abstract type implementing this interface must have at least one <see cref="CustomGUIAttribute"/>.
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ICustomGUI
///
public interface ICustomGUI
{
/************************************************************************************************************************/
/// <summary>The optional label to draw in front of the field.</summary>
GUIContent Label { get; set; }
/// <summary>The target object for which this GUI will be drawn.</summary>
object Value { get; set; }
/// <summary>Draws the GUI for the <see cref="Value"/>.</summary>
void DoGUI();
/************************************************************************************************************************/
}
/// <summary>[Editor-Only] Draws a custom GUI for an object.</summary>
/// <remarks>
/// Every non-abstract type inheriting from this class must have at least one <see cref="CustomGUIAttribute"/>.
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUI_1
///
public abstract class CustomGUI<T> : ICustomGUI
{
/************************************************************************************************************************/
/// <summary>The object for which this GUI will be drawn.</summary>
public T Value { get; protected set; }
/// <inheritdoc/>
object ICustomGUI.Value
{
get => Value;
set => Value = (T)value;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public GUIContent Label { get; set; } = GUIContent.none;
/************************************************************************************************************************/
/// <inheritdoc/>
public abstract void DoGUI();
/************************************************************************************************************************/
}
/// <summary>[Editor-Only] Extension methods for <see cref="ICustomGUI"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIExtensions
///
public static class CustomGUIExtensions
{
/************************************************************************************************************************/
/// <summary>Sets the <see cref="ICustomGUI.Label"/>.</summary>
public static void SetLabel(
this ICustomGUI customGUI,
string text,
string tooltip = null,
Texture image = null)
{
var label = customGUI.Label;
if (label == null || label == GUIContent.none)
customGUI.Label = label = new(text);
label.text = text;
label.tooltip = tooltip;
label.image = image;
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: c5792b81ce4ba30448a3367876e92058
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/CustomGUI.cs
uploadId: 795566

View File

@@ -0,0 +1,35 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
namespace Animancer.Editor
{
/// <summary>[Editor-Only]
/// Attribute for classes which implement <see cref="CustomGUI{T}"/> to specify the type of objects they apply to.
/// </summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIAttribute
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class CustomGUIAttribute : Attribute
{
/************************************************************************************************************************/
/// <summary>The type of object which the attributed <see cref="CustomGUI{T}"/> class applies to.</summary>
public readonly Type TargetType;
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="CustomGUIAttribute"/>.</summary>
public CustomGUIAttribute(Type targetType)
{
TargetType = targetType;
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: ea9f8ff21895492479bdc457361a570c
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/CustomGUIAttribute.cs
uploadId: 795566

View File

@@ -0,0 +1,152 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
//#define LOG_CUSTOM_GUI_FACTORY
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] Draws a custom GUI for an object.</summary>
/// <remarks>
/// Every non-abstract type implementing this interface must have at least one <see cref="CustomGUIAttribute"/>.
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIFactory
///
public static class CustomGUIFactory
{
/************************************************************************************************************************/
private static readonly Dictionary<Type, Type>
TargetTypeToGUIType = new();
static CustomGUIFactory()
{
foreach (var guiType in TypeCache.GetTypesWithAttribute(typeof(CustomGUIAttribute)))
{
if (guiType.IsAbstract ||
guiType.IsInterface)
continue;
if (!typeof(ICustomGUI).IsAssignableFrom(guiType))
{
Debug.LogWarning(
$"{guiType.FullName} has a {nameof(CustomGUIAttribute)}" +
$" but doesn't implement {nameof(ICustomGUI)}.");
continue;
}
var attribute = guiType.GetCustomAttribute<CustomGUIAttribute>();
if (attribute.TargetType != null)
{
TargetTypeToGUIType.Add(attribute.TargetType, guiType);
}
}
}
/************************************************************************************************************************/
private static readonly ConditionalWeakTable<object, ICustomGUI>
TargetToGUI = new();
/// <summary>Returns an existing <see cref="ICustomGUI"/> for the `targetType` or creates one if necessary.</summary>
/// <remarks>Returns null if the `targetType` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
public static ICustomGUI GetOrCreateForType(Type targetType)
{
if (targetType == null)
return null;
if (TargetToGUI.TryGetValue(targetType, out var gui))
return gui;
gui = Create(targetType);
TargetToGUI.Add(targetType, gui);
return gui;
}
/// <summary>Returns an existing <see cref="ICustomGUI"/> for the `value` or creates one if necessary.</summary>
/// <remarks>Returns null if the `value` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
public static ICustomGUI GetOrCreateForObject(object value)
{
if (value == null)
return null;
if (TargetToGUI.TryGetValue(value, out var gui))
return gui;
gui = Create(value.GetType());
if (gui != null)
gui.Value = value;
TargetToGUI.Add(value, gui);
return gui;
}
/************************************************************************************************************************/
/// <summary>Creates an <see cref="ICustomGUI"/> for the `targetType`.</summary>
/// <remarks>Returns null if the `value` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
public static ICustomGUI Create(Type targetType)
{
if (!TryGetGUIType(targetType, out var guiType))
return null;
try
{
if (guiType.IsGenericTypeDefinition)
guiType = guiType.MakeGenericType(targetType);
var gui = (ICustomGUI)Activator.CreateInstance(guiType);
return gui;
}
catch (Exception exception)
{
Debug.LogException(exception);
return null;
}
}
/************************************************************************************************************************/
/// <summary>Tries to determine the valid <see cref="ICustomGUI"/> type for drawing the `target`.</summary>
public static bool TryGetGUIType(Type target, out Type gui)
{
// Try the target and its base types.
var type = target;
while (type != null && type != typeof(object))
{
if (TargetTypeToGUIType.TryGetValue(type, out gui))
return true;
type = type.BaseType;
}
// Try any interfaces.
var interfaces = target.GetInterfaces();
for (int i = 0; i < interfaces.Length; i++)
if (TargetTypeToGUIType.TryGetValue(interfaces[i], out gui))
return true;
// Try base object.
return TargetTypeToGUIType.TryGetValue(typeof(object), out gui);
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: fb3162b4afff1f34eaff91a1a123ffde
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/CustomGUIFactory.cs
uploadId: 795566

View File

@@ -0,0 +1,148 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] An <see cref="ICustomGUI"/> for <see cref="MulticastDelegate"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/DelegateGUI
[CustomGUI(typeof(MulticastDelegate))]
public class DelegateGUI : CustomGUI<MulticastDelegate>
{
/************************************************************************************************************************/
private static readonly HashSet<MulticastDelegate>
ExpandedItems = new();
/************************************************************************************************************************/
/// <summary>Calculates the number of vertical pixels required to draw the specified <see cref="MulticastDelegate"/>.</summary>
public static float CalculateHeight(MulticastDelegate del)
=> AnimancerGUI.CalculateHeight(CalculateLineCount(del));
/// <summary>Calculates the number of lines required to draw the specified <see cref="MulticastDelegate"/>.</summary>
public static int CalculateLineCount(MulticastDelegate del)
=> del == null || !ExpandedItems.Contains(del)
? 1
: 1 + CalculateLineCount(AnimancerReflection.GetInvocationList(del));
/// <summary>Calculates the number of lines required to draw the specified `invocationList`.</summary>
public static int CalculateLineCount(Delegate[] invocationList)
=> invocationList == null
? 3
: invocationList.Length * 3;
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
{
var area = AnimancerGUI.LayoutRect(CalculateHeight(Value));
DoGUI(ref area, Label, Value);
}
/// <summary>Draws the GUI for the given delegate.</summary>
public static void DoGUI(
ref Rect area,
GUIContent label,
MulticastDelegate del,
GUIContent valueLabel = null)
{
area.height = AnimancerGUI.LineHeight;
var delegates = AnimancerReflection.GetInvocationList(del);
var isExpanded = del != null && AnimancerGUI.DoHashedFoldoutGUI(area, ExpandedItems, del);
if (valueLabel != null)
{
EditorGUI.LabelField(area, label, valueLabel);
}
else
{
var count = delegates == null ? 0 : delegates.Length;
using (var countLabel = PooledGUIContent.Acquire(count.ToStringCached()))
EditorGUI.LabelField(area, label, countLabel);
}
AnimancerGUI.NextVerticalArea(ref area);
if (!isExpanded)
return;
EditorGUI.indentLevel++;
if (delegates == null)
{
DoSingleGUI(ref area, del);
}
else
{
for (int i = 0; i < delegates.Length; i++)
DoSingleGUI(ref area, delegates[i]);
}
EditorGUI.indentLevel--;
}
/************************************************************************************************************************/
private const int TargetFieldCacheCapacity = 128;
private static readonly Dictionary<object, FastObjectField>
TargetFieldCache = new(TargetFieldCacheCapacity);
/// <summary>Draws the target and name of the specified <see cref="Delegate"/>.</summary>
public static void DoSingleGUI(ref Rect area, Delegate del)
{
area.height = AnimancerGUI.LineHeight;
if (del == null)
{
EditorGUI.LabelField(area, "Delegate", "Null");
AnimancerGUI.NextVerticalArea(ref area);
return;
}
var method = del.Method;
EditorGUI.LabelField(area, "Method", method.ToString());
AnimancerGUI.NextVerticalArea(ref area);
EditorGUI.LabelField(area, "Declaring Type", method.DeclaringType.GetNameCS());
AnimancerGUI.NextVerticalArea(ref area);
var target = del.Target;
FastObjectField field;
if (target is not null)
TargetFieldCache.TryGetValue(target, out field);
else
field = FastObjectField.Null;
field.Draw(area, "Target", target);
if (target is not null)
{
if (TargetFieldCache.Count == TargetFieldCacheCapacity)
TargetFieldCache.Clear();
TargetFieldCache[target] = field;
}
AnimancerGUI.NextVerticalArea(ref area);
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: a080cd63060e68249ad553cae3671b62
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/DelegateGUI.cs
uploadId: 795566

View File

@@ -0,0 +1,139 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// <summary>[Editor-Only] A custom GUI for <see cref="FadeGroup"/>.</summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/FadeGroupDrawer
[CustomGUI(typeof(FadeGroup))]
public class FadeGroupDrawer : CustomGUI<FadeGroup>
{
/************************************************************************************************************************/
private bool _IsExpanded;
private AnimationCurve _DisplayCurve;
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
{
_IsExpanded = EditorGUILayout.Foldout(_IsExpanded, "", true);
var area = GUILayoutUtility.GetLastRect();
InitializeDisplayCurve(ref _DisplayCurve);
_DisplayCurve = EditorGUI.CurveField(area, TargetName, _DisplayCurve);
if (_IsExpanded)
DoDetailsGUI();
}
/************************************************************************************************************************/
/// <summary>The display name of the target.</summary>
protected virtual string TargetName
{
get
{
var name = Value.GetType().GetNameCS(false);
if (!Value.IsValid)
name += " (Cancelled)";
return name;
}
}
/************************************************************************************************************************/
private static readonly Keyframe[] DisplayCurveKeyframes = new Keyframe[16];
/// <summary>Initializes the `curve` to represent the target's fade values over normalized time.</summary>
protected virtual void InitializeDisplayCurve(ref AnimationCurve curve)
{
curve ??= new();
try
{
var increment = 1f / (DisplayCurveKeyframes.Length - 1);
for (int i = 0; i < DisplayCurveKeyframes.Length; i++)
{
var progress = increment * i;
var weight = Value.Easing != null
? Value.Easing(progress)
: progress;
DisplayCurveKeyframes[i] = new(progress, weight);
}
}
catch (Exception exception)
{
Debug.LogException(exception);
Array.Clear(DisplayCurveKeyframes, 0, DisplayCurveKeyframes.Length);
}
curve.keys = DisplayCurveKeyframes;
}
/************************************************************************************************************************/
/// <summary>Draws the GUI for the target's fields.</summary>
protected virtual void DoDetailsGUI()
{
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
Value.NormalizedTime = EditorGUILayout.Slider("Normalized Time", Value.NormalizedTime, 0, 1);
if (EditorGUI.EndChangeCheck())
{
Value.NormalizedTime = Mathf.Clamp(Value.NormalizedTime, 0, 0.99f);
Value.ApplyWeights();
}
EditorGUI.BeginChangeCheck();
var fadeDuration = EditorGUILayout.FloatField("Fade Duration", Value.FadeDuration);
if (EditorGUI.EndChangeCheck())
Value.FadeDuration = fadeDuration;
EditorGUILayout.LabelField(
Value.TargetWeight > 0 ? "Fade In" : "Fade Out",
"To " + Value.TargetWeight);
EditorGUI.indentLevel++;
DoNodeWeightGUI(Value.FadeIn);
EditorGUI.indentLevel--;
var fadeOutCount = Value.FadeOut.Count;
if (fadeOutCount > 0)
{
EditorGUILayout.LabelField("Fade Out", fadeOutCount.ToStringCached());
EditorGUI.indentLevel++;
for (int i = 0; i < fadeOutCount; i++)
DoNodeWeightGUI(Value.FadeOut[i]);
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
/************************************************************************************************************************/
/// <summary>Draws the GUI for the given `nodeWeight`.</summary>
private void DoNodeWeightGUI(NodeWeight nodeWeight)
{
EditorGUILayout.LabelField(nodeWeight.Node?.GetPath());
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: dbc29ff53b377cc498adb3c6cad4ec5a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/FadeGroupDrawer.cs
uploadId: 795566

View File

@@ -0,0 +1,43 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR
using System;
using UnityEditor;
namespace Animancer.Editor
{
/// <summary>[Editor-Only]
/// A default <see cref="ICustomGUI"/> which simply draws the <see cref="object.ToString"/>.
/// </summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/LabelGUI
[CustomGUI(typeof(object))]
public class LabelGUI : CustomGUI<object>
{
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
{
string text;
try
{
text = Value != null
? Value.ToString()
: "Null";
}
catch (Exception exception)
{
text = exception.ToString();
}
using (var value = PooledGUIContent.Acquire(text))
EditorGUILayout.LabelField(Label, value);
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 998ce5fd65a930b428fda6b0a0d9fd2d
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 293522
packageName: Animancer Pro v8
packageVersion: 8.2.2
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/LabelGUI.cs
uploadId: 795566

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1649432a594f0b149b2190e2160b59e6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,535 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
#if UNITY_EDITOR && UNITY_IMGUI
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using static Animancer.Editor.AnimancerGUI;
using Object = UnityEngine.Object;
namespace Animancer.Editor
{
/// <summary>[Editor-Only]
/// A custom Inspector for an <see cref="AnimancerLayer"/> which sorts and exposes some of its internal values.
/// </summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerLayerDrawer
///
[CustomGUI(typeof(AnimancerLayer))]
public class AnimancerLayerDrawer : AnimancerNodeDrawer<AnimancerLayer>
{
/************************************************************************************************************************/
/// <summary>The states in the target layer which have non-zero <see cref="AnimancerNode.Weight"/>.</summary>
public readonly List<AnimancerState> ActiveStates = new();
/// <summary>The states in the target layer which have zero <see cref="AnimancerNode.Weight"/>.</summary>
public readonly List<AnimancerState> InactiveStates = new();
/************************************************************************************************************************/
#region Gathering
/************************************************************************************************************************/
/// <summary>Initializes an editor in the list for each layer in the `graph`.</summary>
/// <remarks>
/// The `count` indicates the number of elements actually being used.
/// Spare elements are kept in the list in case they need to be used again later.
/// </remarks>
internal static void GatherLayerEditors(
AnimancerGraph graph,
List<AnimancerLayerDrawer> editors,
out int count)
{
count = graph.Layers.Count;
for (int i = 0; i < count; i++)
{
AnimancerLayerDrawer editor;
if (editors.Count <= i)
{
editor = new();
editors.Add(editor);
}
else
{
editor = editors[i];
}
editor.GatherStates(graph.Layers[i]);
}
}
/************************************************************************************************************************/
/// <summary>
/// Sets the target `layer` and sorts its states and their keys into the active/inactive lists.
/// </summary>
private void GatherStates(AnimancerLayer layer)
{
Value = layer;
ActiveStates.Clear();
InactiveStates.Clear();
foreach (var state in layer)
{
if (state.IsActive ||
(!AnimancerGraphDrawer.SeparateActiveFromInactiveStates && AnimancerGraphDrawer.ShowInactiveStates))
{
ActiveStates.Add(state);
continue;
}
if (AnimancerGraphDrawer.ShowInactiveStates)
InactiveStates.Add(state);
}
SortAndGatherKeys(ActiveStates);
SortAndGatherKeys(InactiveStates);
}
/************************************************************************************************************************/
/// <summary>
/// Sorts any entries that use another state as their key to come right after that state.
/// See <see cref="AnimancerLayer.Play(AnimancerState, float, FadeMode)"/>.
/// </summary>
private static void SortAndGatherKeys(List<AnimancerState> states)
{
var count = states.Count;
if (count == 0)
return;
AnimancerGraphDrawer.ApplySortStatesByName(states);
// Sort any states that use another state as their key to be right after the key.
for (int i = 0; i < count; i++)
{
var state = states[i];
var key = state.Key;
if (key is not AnimancerState keyState)
continue;
var keyStateIndex = states.IndexOf(keyState);
if (keyStateIndex < 0 || keyStateIndex + 1 == i)
continue;
states.RemoveAt(i);
if (keyStateIndex < i)
keyStateIndex++;
states.Insert(keyStateIndex, state);
i--;
}
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
/// <summary>Draws the layer's name and weight.</summary>
protected override void DoLabelGUI(Rect area)
{
var label = Value.IsAdditive ? "Additive" : "Override";
if (Value._Mask != null)
label = $"{label} ({Value._Mask.GetCachedName()})";
area.xMin += FoldoutIndent;
DoWeightLabel(ref area, Value.Weight, Value.EffectiveWeight);
EditorGUIUtility.labelWidth -= FoldoutIndent;
EditorGUI.LabelField(area, Value.ToString(), label);
EditorGUIUtility.labelWidth += FoldoutIndent;
}
/************************************************************************************************************************/
/// <summary>The number of pixels of indentation required to fit the foldout arrow.</summary>
const float FoldoutIndent = 12;
/// <inheritdoc/>
protected override void DoFoldoutGUI(Rect area)
{
var hierarchyMode = EditorGUIUtility.hierarchyMode;
EditorGUIUtility.hierarchyMode = true;
area.xMin += FoldoutIndent;
IsExpanded = EditorGUI.Foldout(area, IsExpanded, GUIContent.none, true);
EditorGUIUtility.hierarchyMode = hierarchyMode;
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void DoDetailsGUI()
{
EditorGUI.indentLevel++;
base.DoDetailsGUI();
if (IsExpanded)
{
GUILayout.BeginHorizontal();
GUILayout.Space(FoldoutIndent);
GUILayout.BeginVertical();
DoLayerDetailsGUI();
DoNodeDetailsGUI();
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
EditorGUI.indentLevel--;
DoStatesGUI();
}
/************************************************************************************************************************/
/// <summary>
/// Draws controls for <see cref="AnimancerLayer.IsAdditive"/> and <see cref="AnimancerLayer._Mask"/>.
/// </summary>
private void DoLayerDetailsGUI()
{
var area = LayoutSingleLineRect(SpacingMode.Before);
area = EditorGUI.IndentedRect(area);
area.xMin += ExtraLeftPadding;
var labelWidth = EditorGUIUtility.labelWidth;
var indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var additiveLabel = "Is Additive";
var additiveWidth = GUI.skin.toggle.CalculateWidth(additiveLabel) + StandardSpacing * 2;
var additiveArea = StealFromLeft(ref area, additiveWidth, StandardSpacing);
var maskArea = area;
// Additive.
EditorGUIUtility.labelWidth = CalculateLabelWidth(additiveLabel);
EditorGUI.BeginChangeCheck();
var isAdditive = EditorGUI.Toggle(additiveArea, additiveLabel, Value.IsAdditive);
if (EditorGUI.EndChangeCheck())
Value.IsAdditive = isAdditive;
// Mask.
using (var label = PooledGUIContent.Acquire("Mask"))
{
EditorGUIUtility.labelWidth = CalculateLabelWidth(label.text);
EditorGUI.BeginChangeCheck();
var mask = DoObjectFieldGUI(maskArea, label, Value.Mask, false);
if (EditorGUI.EndChangeCheck())
Value.Mask = mask;
}
EditorGUI.indentLevel = indentLevel;
EditorGUIUtility.labelWidth = labelWidth;
}
/************************************************************************************************************************/
private void DoStatesGUI()
{
if (!AnimancerGraphDrawer.ShowInactiveStates)
{
DoStatesGUI("Active States", ActiveStates);
}
else if (AnimancerGraphDrawer.SeparateActiveFromInactiveStates)
{
DoStatesGUI("Active States", ActiveStates);
DoStatesGUI("Inactive States", InactiveStates);
}
else
{
DoStatesGUI("States", ActiveStates);
}
if (Value.Weight != 0 &&
!Value.IsAdditive &&
!Mathf.Approximately(Value.GetTotalChildWeight(), 1))
{
var message =
"The total Weight of all states in this layer does not equal 1" +
" which will likely give undesirable results.";
if (AreAllStatesFadingOut())
message +=
" If you no longer want anything playing on a layer," +
" you should fade out that layer instead of fading out its states.";
message += " Click here for more information.";
EditorGUILayout.HelpBox(message, MessageType.Warning);
if (TryUseClickEventInLastRect())
EditorUtility.OpenWithDefaultApp(Strings.DocsURLs.Layers);
}
}
/************************************************************************************************************************/
/// <summary>Are all the target's states fading out to 0?</summary>
private bool AreAllStatesFadingOut()
{
var count = Value.ActiveStates.Count;
if (count == 0)
return false;
for (int i = 0; i < count; i++)
{
var state = Value.ActiveStates[i];
if (state.TargetWeight != 0)
return false;
}
return true;
}
/************************************************************************************************************************/
/// <summary>Draws all `states` in the given list.</summary>
public void DoStatesGUI(string label, List<AnimancerState> states)
{
var area = LayoutSingleLineRect();
const string Label = "Weight";
var width = CalculateLabelWidth(Label);
GUI.Label(StealFromRight(ref area, width), Label);
EditorGUI.LabelField(area, label, states.Count.ToStringCached());
EditorGUI.indentLevel++;
for (int i = 0; i < states.Count; i++)
{
DoStateGUI(states[i]);
}
EditorGUI.indentLevel--;
}
/************************************************************************************************************************/
/// <summary>Cached Inspectors that have already been created for states.</summary>
private readonly Dictionary<AnimancerState, ICustomGUI>
StateInspectors = new();
/// <summary>Draws the Inspector for the given `state`.</summary>
private void DoStateGUI(AnimancerState state)
{
if (!StateInspectors.TryGetValue(state, out var inspector))
{
inspector = CustomGUIFactory.GetOrCreateForObject(state);
StateInspectors.Add(state, inspector);
}
inspector?.DoGUI();
DoChildStatesGUI(state);
}
/************************************************************************************************************************/
/// <summary>Draws all child states of the `state`.</summary>
private void DoChildStatesGUI(AnimancerState state)
{
if (!state._IsInspectorExpanded)
return;
EditorGUI.indentLevel++;
foreach (var child in state)
if (child != null)
DoStateGUI(child);
EditorGUI.indentLevel--;
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void DoHeaderGUI()
{
if (!AnimancerGraphDrawer.ShowSingleLayerHeader &&
Value.Graph.Layers.Count == 1 &&
Value.Weight == 1 &&
Value.TargetWeight == 1 &&
Value.Speed == 1 &&
!Value.IsAdditive &&
Value._Mask == null &&
Value.Graph.Component != null &&
Value.Graph.Component.Animator != null &&
Value.Graph.Component.Animator.runtimeAnimatorController == null)
return;
base.DoHeaderGUI();
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void DoGUI()
{
if (!Value.IsValid())
return;
base.DoGUI();
var area = GUILayoutUtility.GetLastRect();
HandleDragAndDropToPlay(area, Value);
}
/************************************************************************************************************************/
/// <summary>
/// If <see cref="AnimationClip"/>s or <see cref="IAnimationClipSource"/>s are dropped inside the `dropArea`,
/// this method creates a new state in the `target` for each animation.
/// </summary>
public static void HandleDragAndDropToPlay(Rect area, object layerOrGraph)
{
if (layerOrGraph == null)
return;
_DragAndDropPlayTarget = layerOrGraph;
_DragAndDropPlayHandler ??= HandleDragAndDropToPlay;
_DragAndDropPlayHandler.Handle(area);
_DragAndDropPlayTarget = null;
}
private static DragAndDropHandler<Object> _DragAndDropPlayHandler;
private static object _DragAndDropPlayTarget;
private static AnimancerLayer DragAndDropPlayTargetLayer
=> _DragAndDropPlayTarget as AnimancerLayer
?? (_DragAndDropPlayTarget is AnimancerGraph graph ? graph.Layers[0] : null);
/// <summary>Handles drag and drop events to play animations and transitions.</summary>
public static bool HandleDragAndDropToPlay(Object obj, bool isDrop)
{
if (_DragAndDropPlayTarget == null)
return false;
if (obj is AnimationClip clip)
{
if (clip.legacy)
return false;
if (isDrop)
DragAndDropPlayTargetLayer.Play(clip);
return true;
}
if (obj is ITransition transition)
{
if (isDrop)
DragAndDropPlayTargetLayer.Play(transition);
return true;
}
var transitionAsset = TryCreateTransitionAttribute.TryCreateTransitionAsset(obj);
if (transitionAsset != null)
{
if (isDrop)
DragAndDropPlayTargetLayer.Play(transitionAsset);
if (!EditorUtility.IsPersistent(transitionAsset))
Object.DestroyImmediate(transitionAsset);
return true;
}
using (ListPool<AnimationClip>.Instance.Acquire(out var clips))
{
clips.GatherFromSource(obj);
var anyValid = false;
for (int i = 0; i < clips.Count; i++)
{
clip = clips[i];
if (clip.legacy)
continue;
if (!isDrop)
return true;
anyValid = true;
DragAndDropPlayTargetLayer.Play(clip);
}
if (anyValid)
return true;
}
return false;
}
/************************************************************************************************************************/
#region Context Menu
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void PopulateContextMenu(GenericMenu menu)
{
menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.CurrentState)}: {Value.CurrentState}"));
menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.CommandCount)}: {Value.CommandCount}"));
menu.AddFunction("Stop",
HasAnyStates((state) => state.IsPlaying || state.Weight != 0),
() => Value.Stop());
AnimancerEditorUtilities.AddFadeFunction(menu, "Fade In",
Value.Index > 0 && Value.Weight != 1, Value,
(duration) => Value.StartFade(1, duration));
AnimancerEditorUtilities.AddFadeFunction(menu, "Fade Out",
Value.Index > 0 && Value.Weight != 0, Value,
(duration) => Value.StartFade(0, duration));
AnimancerNodeBase.AddContextMenuIK(menu, Value);
menu.AddSeparator("");
menu.AddFunction("Destroy States",
ActiveStates.Count > 0 || InactiveStates.Count > 0,
() => Value.DestroyStates());
AnimancerGraphDrawer.AddRootFunctions(menu, Value.Graph);
menu.AddSeparator("");
AnimancerGraphDrawer.AddDisplayOptions(menu);
AnimancerEditorUtilities.AddDocumentationLink(menu, "Layer Documentation", Strings.DocsURLs.Layers);
menu.ShowAsContext();
}
/************************************************************************************************************************/
private bool HasAnyStates(Func<AnimancerState, bool> condition)
{
foreach (var state in Value)
{
if (condition(state))
return true;
}
return false;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More