diff --git a/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimation.cs b/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimation.cs index 886d82e..0d66742 100644 --- a/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimation.cs +++ b/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimation.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; +using LitJson; namespace UnitySpineImporter{ public class SpineAnimation { - public Dictionary bones; - public Dictionary slots; - public List draworder; - + public Dictionary< string, SpineBoneAnimation > bones; + public Dictionary< string, SpineSlotAnimation > slots; + public List< SpineDrawOrderAnimation > draworder; + public List< JsonData > events; } } \ No newline at end of file diff --git a/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimationEvent.cs b/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimationEvent.cs index 369587e..fe29fd6 100644 --- a/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimationEvent.cs +++ b/Assets/UnitySpineImporter/Scripts/Editor/Model/Spine/Data/Animation/SpineAnimationEvent.cs @@ -1,15 +1,9 @@ -using UnityEngine; -using System.Collections; +using LitJson; +namespace UnitySpineImporter { + public class SpineAnimationEvent { + public double time; + public string name; -public class SpineAnimationEvent : MonoBehaviour { - // Use this for initialization - void Start () { - - } - - // Update is called once per frame - void Update () { - } } diff --git a/Assets/UnitySpineImporter/Scripts/Editor/SpineImporterWizard.cs b/Assets/UnitySpineImporter/Scripts/Editor/SpineImporterWizard.cs index 7ee95f4..bad17f8 100644 --- a/Assets/UnitySpineImporter/Scripts/Editor/SpineImporterWizard.cs +++ b/Assets/UnitySpineImporter/Scripts/Editor/SpineImporterWizard.cs @@ -56,15 +56,16 @@ void OnWizardCreate(){ Dictionary slotByName; List skins; AttachmentGOByNameBySlot attachmentGOByNameBySlot; + if (File.Exists(path)){ try{ SpineMultiatlas spineMultiAtlas = SpineMultiatlas.deserializeFromFile(atlasPath ); SpineData spineData = SpineData .deserializeFromFile(path); SpineUtil.updateImporters(spineMultiAtlas, directory, pixelsPerUnit, out spriteByName); - GameObject rootGO = SpineUtil.buildSceleton(name, spineData, pixelsPerUnit, out boneGOByName, out slotByName); + GameObject rootGO = SpineUtil.buildSceleton(name, spineData, pixelsPerUnit, zStep, out boneGOByName, out slotByName); rootGO.name = name; - SpineUtil.addAllAttahcmentsSlots(spineData, spriteByName, slotByName, zStep, pixelsPerUnit, out skins, out attachmentGOByNameBySlot); + SpineUtil.addAllAttahcmentsSlots(spineData, spriteByName, slotByName, pixelsPerUnit, out skins, out attachmentGOByNameBySlot); SkinController sk = SpineUtil.addSkinController(rootGO, spineData, skins, slotByName); if (animationImportType == AnimationImportType.MECANIM){ Animator animator = SpineUtil.addAnimator(rootGO); @@ -74,8 +75,8 @@ void OnWizardCreate(){ ModelImporterAnimationType modelImporterAnimationType = getModelImporterAnimationType(); if (spineData.animations !=null && spineData.animations.Count > 0) - SpineUtil.addAnimation(rootGO, directory, spineData, boneGOByName, attachmentGOByNameBySlot, - pixelsPerUnit, modelImporterAnimationType, updateResources); + SpineUtil.addAnimation(rootGO, directory, spineData, boneGOByName, slotByName, attachmentGOByNameBySlot, skins, + pixelsPerUnit, zStep, modelImporterAnimationType, updateResources ); sk.showDefaulSlots(); SpineUtil.buildPrefab(rootGO, directory, name); GameObject.DestroyImmediate(rootGO); diff --git a/Assets/UnitySpineImporter/Scripts/Editor/Util/SpineUtil.cs b/Assets/UnitySpineImporter/Scripts/Editor/Util/SpineUtil.cs index 121388e..a1eb1fb 100644 --- a/Assets/UnitySpineImporter/Scripts/Editor/Util/SpineUtil.cs +++ b/Assets/UnitySpineImporter/Scripts/Editor/Util/SpineUtil.cs @@ -211,7 +211,7 @@ public static void swap2Float(ref float float1, ref float float2){ float2 = tmp; } - public static GameObject buildSceleton(string name, SpineData data, int pixelsPerUnit, out Dictionary boneGOByName, out Dictionary slotByName){ + public static GameObject buildSceleton( string name, SpineData data, int pixelsPerUnit, float zStep, out Dictionary boneGOByName, out Dictionary slotByName ) { float ratio = 1.0f / (float)pixelsPerUnit; boneGOByName = new Dictionary(); slotByName = new Dictionary(); @@ -240,6 +240,8 @@ public static GameObject buildSceleton(string name, SpineData data, int pixelsPe GameObject go = new GameObject(getSlotGOName(spineSlot.name)); go.transform.parent = boneGOByName[spineSlot.bone].transform; resetLocalTRS(go); + int drawOrder = data.slotOrder[ spineSlot.name ]; + go.transform.localPosition = new Vector3( 0, 0, (- drawOrder ) * zStep ); Slot slot = new Slot(); slot.bone = spineSlot.bone; slot.name = spineSlot.name; @@ -261,7 +263,7 @@ public static void resetLocalTRS(GameObject go){ go.transform.localScale = Vector3.one; } - public static void addAllAttahcmentsSlots(SpineData spineData, SpritesByName spriteByName, Dictionary slotByName, float zStep, int pixelsPerUnit, out List skins, out AttachmentGOByNameBySlot attachmentGOByNameBySlot){ + public static void addAllAttahcmentsSlots(SpineData spineData, SpritesByName spriteByName, Dictionary slotByName, int pixelsPerUnit, out List skins, out AttachmentGOByNameBySlot attachmentGOByNameBySlot){ float ratio = 1.0f / (float) pixelsPerUnit; skins = new List(); attachmentGOByNameBySlot= new AttachmentGOByNameBySlot(); @@ -277,7 +279,7 @@ public static void addAllAttahcmentsSlots(SpineData spineData, SpritesByName spr GameObject slotGO = slotByName[slotName].gameObject; Slot slot = slotByName[slotName]; - + string spritePath = spineData.slotPathByName[ slotName ] + "/"; SkinSlot skinSlot = new SkinSlot(); @@ -294,7 +296,6 @@ public static void addAllAttahcmentsSlots(SpineData spineData, SpritesByName spr // - create skined object or direct GO for default skin Sprite sprite; spriteByName.TryGetValue(spineAttachment.name, out sprite); - int drawOrder = spineData.slotOrder[slotName]; GameObject parentGO; GameObject spriteGO; @@ -302,6 +303,7 @@ public static void addAllAttahcmentsSlots(SpineData spineData, SpritesByName spr if (isDefault){ parentGO = slotGO; spriteGO = new GameObject(fixedName); + spritePath += fixedName; Attachment a = new Attachment(attachmenName, AttachmentType.SINGLE_SPRITE, spriteGO); slot.addAttachment(a); } else { @@ -315,19 +317,21 @@ public static void addAllAttahcmentsSlots(SpineData spineData, SpritesByName spr a = new Attachment(attachmenName, AttachmentType.SKINED_SPRITE, attachmentGO); slot.addAttachment(a); } - + spritePath += fixedName + "/" + skinName; parentGO = a.gameObject; } attachment.gameObject = spriteGO; + attachment.ObPath = spritePath; spriteGO.transform.parent = parentGO.gameObject.transform; // - if (spineAttachment.type.Equals("region")){ SpriteRenderer sr = spriteGO.AddComponent(); sr.sprite = sprite; - spriteGO.transform.localPosition = getAttachmentPosition(spineAttachment, ratio,-( drawOrder * zStep)); + spriteGO.transform.localPosition = getAttachmentPosition(spineAttachment, ratio, 0); spriteGO.transform.localRotation = getAttachmentRotation(spineAttachment, spriteByName.rotatedSprites.Contains(sprite)); spriteGO.transform.localScale = getAttachmentScale(spineAttachment); + attachment.sprite = sr; } else if (spineAttachment.type.Equals("boundingbox")) { PolygonCollider2D collider = spriteGO.AddComponent(); resetLocalTRS(spriteGO); @@ -384,8 +388,11 @@ public static void addAnimation(GameObject rootGO, string rootDirectory, SpineData spineData, Dictionary boneGOByName, + Dictionary slotByName, AttachmentGOByNameBySlot attachmentGOByNameBySlot, + List skinList, int pixelsPerUnit, + float zStep, ModelImporterAnimationType modelImporterAnimationType, bool updateResources) { @@ -411,10 +418,12 @@ public static void addAnimation(GameObject rootGO, if (spineAnimation.bones!=null) addBoneAnimationToClip(animationClip,spineAnimation.bones, spineData, boneGOByName, ratio); if (spineAnimation.slots!=null) - addSlotAnimationToClip(animationClip, spineAnimation.slots, spineData, attachmentGOByNameBySlot); - if (spineAnimation.draworder!=null) - Debug.LogWarning("draworder animation implemented yet"); + addSlotAnimationToClip(animationClip, spineAnimation.slots, spineData, skinList, attachmentGOByNameBySlot); + if ( spineAnimation.events != null ) + AddEvents( animationClip, spineAnimation.events, animationName ); + if (spineAnimation.draworder!=null) + addDrawOrderAnimation( animationClip, spineAnimation.draworder, spineData, zStep, animationName, slotByName ); if (updateCurve){ EditorUtility.SetDirty(animationClip); @@ -434,6 +443,68 @@ public static void addAnimation(GameObject rootGO, } } + static void AddEvents( AnimationClip clip, + List< JsonData > events, + string animName ) + { + List< UnityEngine.AnimationEvent > unityEvents = new List( ); + foreach ( JsonData entry in events ) { + if ( !entry.IsObject ) + Debug.LogError( "JSON data is wrong. Event is not an Object??!!" ); + IDictionary entry_dict = entry as IDictionary; + + UnityEngine.AnimationEvent ev = new UnityEngine.AnimationEvent( ); + + if ( entry_dict.Contains( "name" ) ) + ev.functionName = ( ( string ) entry[ "name" ] ); + else + Debug.LogError( "JSON data is wrong. Missing Name in event data: " + animName ); + + if ( entry_dict.Contains( "time" ) ) + ev.time = getNumberData( entry[ "time" ], animName ); + else + Debug.LogError( "JSON data is wrong. Missing Time in event data: " + animName + " EVENT_NAME: " + ev.functionName ); + + bool ParamAdded = false; + if ( entry_dict.Contains( "int" ) ) { + ev.intParameter = ( int ) entry[ "int" ]; + ParamAdded = true; + } + + if ( entry_dict.Contains( "float" ) ) { + if ( ParamAdded ) + Debug.LogError( "JSON data is wrong. Unity Supports only one event parameter!!!! CLIP NAME: " + animName + " EVENT_NAME: " + entry.ToJson( ) ); + ev.floatParameter = getNumberData( entry[ "float" ], animName ); + ParamAdded = true; + } + + if ( entry_dict.Contains( "string" ) ) { + if ( ParamAdded ) + Debug.LogError( "JSON data is wrong. Unity Supports only one event parameter!!!! CLIP NAME: " + animName + " EVENT_NAME: " + entry.ToJson( ) ); + ev.stringParameter = ( string ) entry[ "string" ]; + } + + ev.messageOptions = SendMessageOptions.RequireReceiver; + + unityEvents.Add( ev ); + } + + AnimationUtility.SetAnimationEvents( clip, unityEvents.ToArray( ) ); + } + + static float getNumberData( JsonData data, string animName ) { + + if ( data.IsDouble ) + return ( float )( ( double )data ); + + if ( data.IsInt ) + return ( float )( ( int )data ); + + Debug.LogError( "JSON data is wrong. Unrecognizable number format!!!! CLIP NAME: " + animName + " JsonData: " + data.ToJson( ) ); + + return 0.0f; + } + static void AddClipToLegacyAnimationComponent(GameObject rootGO, AnimationClip animationClip){ Animation animation = rootGO.GetComponent(); if (animation == null) @@ -456,9 +527,85 @@ public static string getFirstAttachmentName(SpineSlotAnimation spineSlotAnimatio return ""; } + public static void addDrawOrderAnimation( AnimationClip clip, + List orderAnimation, + SpineData spineData, + float zStep, + string animName, + Dictionary slotNameByName ) + { + string[] BaseSlotOrder = new string[ spineData.slotOrder.Count ]; + + Dictionary< string, AnimationCurve > Curvs = new Dictionary( ); + + foreach ( KeyValuePair p in spineData.slotOrder ) { + BaseSlotOrder[ p.Value ] = p.Key; + AnimationCurve Curv = new AnimationCurve(); + Keyframe keyFrame = new Keyframe( 0.0f, ( - p.Value ) * zStep ); + Curv.AddKey( keyFrame ); + Curvs[ p.Key ] = Curv; + } + + foreach ( SpineDrawOrderAnimation orderAnim in orderAnimation ) { + string[] NewSlotOrder = null; + if ( orderAnim.offsets != null ) { + NewSlotOrder = new string[ BaseSlotOrder.Length ]; + string[] BaseOrder_Copy = BaseSlotOrder.Clone( ) as string[]; + + for ( int i = 0; i != orderAnim.offsets.Length; i++ ) { + SpineDrawOrderAnimationSlot slot = orderAnim.offsets[ i ]; + int newIdx = spineData.slotOrder[ slot.slot ] + slot.offset; + NewSlotOrder[ newIdx ] = slot.slot; + int base_idx = Array.IndexOf( BaseOrder_Copy, slot.slot ); + BaseOrder_Copy[ base_idx ] = null; + } + + int pos = 0; + for ( int i = 0; i != NewSlotOrder.Length; i++ ) { + if ( NewSlotOrder[ i ] == null ) { + bool found = false; + for ( ; pos != BaseOrder_Copy.Length; ) { + if ( BaseOrder_Copy[ pos ] != null ) { + found = true; + NewSlotOrder[ i ] = BaseOrder_Copy[ pos ]; + pos++; + break; + } else pos++; + } + + if ( !found ) Debug.LogError( "Can't create new draw order" ); + } + } + } else NewSlotOrder = BaseSlotOrder; + + for ( int j = 0; j != NewSlotOrder.Length; j++ ) { + float t = ( float )orderAnim.time; + float val = ( - j ) * zStep; + AnimationCurve curv = Curvs[ NewSlotOrder[ j ] ]; + float priv_val = curv.Evaluate( t ); + if ( t > 0.0f ) { + Keyframe keyFrameY_help = new Keyframe( t - 0.00001f, priv_val ); + Keyframe keyFrameY = new Keyframe( t, val ); + curv.AddKey( keyFrameY_help ); + curv.AddKey( keyFrameY ); + } else { + Keyframe keyFrameY = new Keyframe( t, val ); + curv.AddKey( keyFrameY ); + } + } + } + + for ( int i = 0; i != BaseSlotOrder.Length; i++ ) { + string slotpath = spineData.slotPathByName[ BaseSlotOrder[ i ] ]; + AnimationCurve curv = Curvs[ BaseSlotOrder[ i ] ]; + AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( slotpath, typeof( Transform ), "m_LocalPosition.z" ), curv ); + } + } + public static void addSlotAnimationToClip(AnimationClip clip, - Dictionary slotsAnimation, - SpineData spineData, + Dictionary slotsAnimation, + SpineData spineData, + List skinList, AttachmentGOByNameBySlot attachmentGOByNameBySlot) { foreach(KeyValuePair kvp in slotsAnimation){ @@ -529,6 +676,57 @@ public static void addSlotAnimationToClip(AnimationClip } if (slotAnimation.color != null && slotAnimation.color.Count >0){ + AnimationCurve Curv_R = new AnimationCurve( ); + AnimationCurve Curv_G = new AnimationCurve( ); + AnimationCurve Curv_B = new AnimationCurve( ); + AnimationCurve Curv_A = new AnimationCurve( ); + Keyframe startKeyFrame = new Keyframe( 0.0f, 1.0f ); + Curv_R.AddKey( startKeyFrame ); + Curv_G.AddKey( startKeyFrame ); + Curv_B.AddKey( startKeyFrame ); + Curv_A.AddKey( startKeyFrame ); + + JsonData[] curveData = new JsonData[ slotAnimation.color.Count ]; + for( int i = 0 ; i != slotAnimation.color.Count ;i++ ) { + SpineSlotColorAnimation color = slotAnimation.color[ i ]; + uint col = Convert.ToUInt32( color.color, 16 ); + uint r = ( col ) >> 24; + uint g = (col & 0xff0000) >> 16; + uint b = (col & 0xff00) >> 8; + uint a = (col & 0xff); + float t = ( (float) (color.time) ); + Keyframe keyFrame_R = new Keyframe( t, r / 255.0f ); + Keyframe keyFrame_G = new Keyframe( t, g / 255.0f ); + Keyframe keyFrame_B = new Keyframe( t, b / 255.0f ); + Keyframe keyFrame_A = new Keyframe( t, a / 255.0f ); + Curv_R.AddKey( keyFrame_R ); + Curv_G.AddKey( keyFrame_G ); + Curv_B.AddKey( keyFrame_B ); + Curv_A.AddKey( keyFrame_A ); + curveData[ i ] = color.curve; + } + + setTangents( Curv_R, curveData ); + setTangents( Curv_G, curveData ); + setTangents( Curv_B, curveData ); + setTangents( Curv_A, curveData ); + + for ( int i = 0; i != skinList.Count; i++ ) { + if ( skinList[ i ].containsSlot( slotName ) ) { + SkinSlot skinSlot = skinList[ i ][ slotName ]; + for ( int j = 0; j != skinSlot.attachments.Length; j++ ) { + SpriteRenderer sprite = skinSlot.attachments[ j ].sprite; + if ( sprite != null ) { + string spritePath = skinSlot.attachments[ j ].ObPath; + AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( spritePath, typeof( SpriteRenderer ), "m_Color.r" ), Curv_R ); + AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( spritePath, typeof( SpriteRenderer ), "m_Color.g" ), Curv_G ); + AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( spritePath, typeof( SpriteRenderer ), "m_Color.b" ), Curv_B ); + AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( spritePath, typeof( SpriteRenderer ), "m_Color.a" ), Curv_A ); + } + } + } + } + Debug.LogWarning("slot color animation is not supported yet"); } } @@ -840,9 +1038,7 @@ public static void addBoneAnimationToClip(AnimationClip clip, Dictionary