From 72bf9b25ff7d748212ed79345dbb4f818b883b48 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 30 Oct 2019 17:27:04 +0100 Subject: [PATCH 1/3] [unity] Fixed path name backslashes in Path.GetDirectoryName(), replaced by slashes. Due to pathname differences, SetDefaultTextureSettings was not called on import on Unity 2019.1 and 2019.2. See #1514. Added asset context to some log statements to quickly jump to corresponding asset. --- .../Editor/Utility/AssetUtility.cs | 35 ++++++++++--------- .../Editor/Utility/SpineEditorUtilities.cs | 4 +-- .../Editor/Windows/SkeletonBaker.cs | 4 +-- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs index 939a93ed2..a4437df04 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs @@ -143,7 +143,7 @@ namespace Spine.Unity.Editor { attachmentType = (AttachmentType)System.Enum.Parse(typeof(AttachmentType), typeString, true); } catch (System.ArgumentException e) { // For more info, visit: http://esotericsoftware.com/forum/Spine-editor-and-runtime-version-management-6534 - Debug.LogWarning(string.Format("Unidentified Attachment type: \"{0}\". Skeleton may have been exported from an incompatible Spine version.", typeString)); + Debug.LogWarning(string.Format("Unidentified Attachment type: \"{0}\". Skeleton may have been exported from an incompatible Spine version.", typeString), spineJson); throw e; } @@ -309,7 +309,7 @@ namespace Spine.Unity.Editor { continue; } - string dir = Path.GetDirectoryName(skeletonPath); + string dir = Path.GetDirectoryName(skeletonPath).Replace('\\', '/'); #if SPINE_TK2D IngestSpineProject(loadedAsset, null); @@ -353,7 +353,7 @@ namespace Spine.Unity.Editor { } static void ReloadSkeletonData (string skeletonJSONPath, CompatibilityProblemInfo compatibilityProblemInfo) { - string dir = Path.GetDirectoryName(skeletonJSONPath); + string dir = Path.GetDirectoryName(skeletonJSONPath).Replace('\\', '/'); TextAsset textAsset = AssetDatabase.LoadAssetAtPath(skeletonJSONPath); DirectoryInfo dirInfo = new DirectoryInfo(dir); FileInfo[] files = dirInfo.GetFiles("*.asset"); @@ -437,7 +437,7 @@ namespace Spine.Unity.Editor { } string primaryName = Path.GetFileNameWithoutExtension(atlasText.name).Replace(".atlas", ""); - string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(atlasText)); + string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(atlasText)).Replace('\\', '/'); string atlasPath = assetPath + "/" + primaryName + AtlasSuffix + ".asset"; @@ -515,9 +515,9 @@ namespace Spine.Unity.Editor { AssetDatabase.SaveAssets(); if (pageFiles.Count != atlasAsset.materials.Length) - Debug.LogWarning(string.Format("{0} :: Not all atlas pages were imported. If you rename your image files, please make sure you also edit the filenames specified in the atlas file.", atlasAsset.name)); + Debug.LogWarning(string.Format("{0} :: Not all atlas pages were imported. If you rename your image files, please make sure you also edit the filenames specified in the atlas file.", atlasAsset.name), atlasAsset); else - Debug.Log(string.Format("{0} :: Imported with {1} material", atlasAsset.name, atlasAsset.materials.Length)); + Debug.Log(string.Format("{0} :: Imported with {1} material", atlasAsset.name, atlasAsset.materials.Length), atlasAsset); // Iterate regions and bake marked. Atlas atlas = atlasAsset.GetAtlas(); @@ -525,7 +525,7 @@ namespace Spine.Unity.Editor { FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); var regions = (List)field.GetValue(atlas); string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); - string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath).Replace('\\', '/'); string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); bool hasBakedRegions = false; @@ -552,7 +552,7 @@ namespace Spine.Unity.Editor { static bool SetDefaultTextureSettings (string texturePath, SpineAtlasAsset atlasAsset) { TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath); if (texImporter == null) { - Debug.LogWarning(string.Format("{0}: Texture asset \"{1}\" not found. Skipping. Please check your atlas file for renamed files.", atlasAsset.name, texturePath)); + Debug.LogWarning(string.Format("{0}: Texture asset \"{1}\" not found. Skipping. Please check your atlas file for renamed files.", atlasAsset.name, texturePath), atlasAsset); return false; } @@ -573,7 +573,7 @@ namespace Spine.Unity.Editor { #region Import SkeletonData (json or binary) internal static string GetSkeletonDataAssetFilePath(TextAsset spineJson) { string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); - string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); + string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)).Replace('\\', '/'); return assetPath + "/" + primaryName + SkeletonDataSuffix + ".asset"; } @@ -656,7 +656,7 @@ namespace Spine.Unity.Editor { #region Spine Skeleton Data File Validation public static bool CheckForValidSkeletonData (string skeletonJSONPath) { - string dir = Path.GetDirectoryName(skeletonJSONPath); + string dir = Path.GetDirectoryName(skeletonJSONPath).Replace('\\', '/'); TextAsset textAsset = AssetDatabase.LoadAssetAtPath(skeletonJSONPath); DirectoryInfo dirInfo = new DirectoryInfo(dir); FileInfo[] files = dirInfo.GetFiles("*.asset"); @@ -694,7 +694,7 @@ namespace Spine.Unity.Editor { switch (result) { case -1: //Debug.Log("Select Atlas"); - AtlasAssetBase selectedAtlas = BrowseAtlasDialog(Path.GetDirectoryName(skeletonPath)); + AtlasAssetBase selectedAtlas = BrowseAtlasDialog(Path.GetDirectoryName(skeletonPath).Replace('\\', '/')); if (selectedAtlas != null) { localAtlases.Clear(); localAtlases.Add(selectedAtlas); @@ -706,7 +706,8 @@ namespace Spine.Unity.Editor { } break; case 0: // Resolve AtlasAssets... - var atlasList = MultiAtlasDialog(requiredPaths, Path.GetDirectoryName(skeletonPath), Path.GetFileNameWithoutExtension(skeletonPath)); + var atlasList = MultiAtlasDialog(requiredPaths, Path.GetDirectoryName(skeletonPath).Replace('\\', '/'), + Path.GetFileNameWithoutExtension(skeletonPath)); if (atlasList != null) AssetUtility.IngestSpineProject(AssetDatabase.LoadAssetAtPath(skeletonPath), atlasList.ToArray()); @@ -905,7 +906,7 @@ namespace Spine.Unity.Editor { } if (data == null) { - Debug.LogWarning("InstantiateSkeletonAnimation tried to instantiate a skeleton from an invalid SkeletonDataAsset."); + Debug.LogWarning("InstantiateSkeletonAnimation tried to instantiate a skeleton from an invalid SkeletonDataAsset.", skeletonDataAsset); return null; } @@ -920,7 +921,7 @@ namespace Spine.Unity.Editor { newSkeletonAnimation.Initialize(false); } catch (System.Exception e) { if (destroyInvalid) { - Debug.LogWarning("Editor-instantiated SkeletonAnimation threw an Exception. Destroying GameObject to prevent orphaned GameObject."); + Debug.LogWarning("Editor-instantiated SkeletonAnimation threw an Exception. Destroying GameObject to prevent orphaned GameObject.", skeletonDataAsset); GameObject.DestroyImmediate(go); } throw e; @@ -982,7 +983,7 @@ namespace Spine.Unity.Editor { } if (data == null) { - Debug.LogWarning("InstantiateSkeletonMecanim tried to instantiate a skeleton from an invalid SkeletonDataAsset."); + Debug.LogWarning("InstantiateSkeletonMecanim tried to instantiate a skeleton from an invalid SkeletonDataAsset.", skeletonDataAsset); return null; } @@ -991,7 +992,7 @@ namespace Spine.Unity.Editor { if (skeletonDataAsset.controller == null) { SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset); - Debug.Log(string.Format("Mecanim controller was automatically generated and assigned for {0}", skeletonDataAsset.name)); + Debug.Log(string.Format("Mecanim controller was automatically generated and assigned for {0}", skeletonDataAsset.name), skeletonDataAsset); } go.GetComponent().runtimeAnimatorController = skeletonDataAsset.controller; @@ -1005,7 +1006,7 @@ namespace Spine.Unity.Editor { newSkeletonMecanim.Initialize(false); } catch (System.Exception e) { if (destroyInvalid) { - Debug.LogWarning("Editor-instantiated SkeletonAnimation threw an Exception. Destroying GameObject to prevent orphaned GameObject."); + Debug.LogWarning("Editor-instantiated SkeletonAnimation threw an Exception. Destroying GameObject to prevent orphaned GameObject.", skeletonDataAsset); GameObject.DestroyImmediate(go); } throw e; diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineEditorUtilities.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineEditorUtilities.cs index de571cb48..b6a40dd0e 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineEditorUtilities.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineEditorUtilities.cs @@ -106,12 +106,12 @@ namespace Spine.Unity.Editor { string[] assets = AssetDatabase.FindAssets("t:script SpineEditorUtilities"); string assetPath = AssetDatabase.GUIDToAssetPath(assets[0]); - editorPath = Path.GetDirectoryName(assetPath).Replace("\\", "/"); + editorPath = Path.GetDirectoryName(assetPath).Replace('\\', '/'); assets = AssetDatabase.FindAssets("t:texture icon-subMeshRenderer"); if (assets.Length > 0) { assetPath = AssetDatabase.GUIDToAssetPath(assets[0]); - editorGUIPath = Path.GetDirectoryName(assetPath).Replace("\\", "/"); + editorGUIPath = Path.GetDirectoryName(assetPath).Replace('\\', '/'); } else { editorGUIPath = editorPath.Replace("/Utility", "/GUI"); diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs index 094e75e4b..4fae837ba 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs @@ -186,7 +186,7 @@ namespace Spine.Unity.Editor { } if (outputPath == "") { - outputPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(skeletonDataAsset)) + "/Baked"; + outputPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(skeletonDataAsset)).Replace('\\', '/') + "/Baked"; System.IO.Directory.CreateDirectory(outputPath); } @@ -1424,7 +1424,7 @@ namespace Spine.Unity.Editor { atlasAsset.GetAtlas(); // Initializes atlasAsset. string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); - string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath).Replace('\\', '/'); string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); string bakedPrefabPath = Path.Combine(bakedDirPath, AssetUtility.GetPathSafeName(region.name) + ".prefab").Replace("\\", "/"); From 739b4884d6e7289e4bd7bf53cf6435c2681c36b0 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 31 Oct 2019 12:18:22 +0100 Subject: [PATCH 2/3] [unity] Fixed duplicated SkeletonUtilityBones not following properly. Closes #1536. --- .../SkeletonUtility/SkeletonUtility.cs | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs index 859888e34..a39214317 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs @@ -128,12 +128,30 @@ namespace Spine.Unity { public delegate void SkeletonUtilityDelegate (); public event SkeletonUtilityDelegate OnReset; public Transform boneRoot; + /// + /// If true, and are followed + /// by 180 degree rotation. If false, negative Transform scale is used. + /// Note that using negative scale is consistent with previous behaviour (hence the default), + /// however causes serious problems with rigidbodies and physics. Therefore, it is recommended to + /// enable this parameter where possible. When creating hinge chains for a chain of skeleton bones + /// via , it is mandatory to have flipBy180DegreeRotation enabled. + /// + public bool flipBy180DegreeRotation = false; void Update () { var skeleton = skeletonRenderer.skeleton; if (skeleton != null && boneRoot != null) { - boneRoot.localScale = new Vector3(skeleton.ScaleX, skeleton.ScaleY, 1f); - } + + if (flipBy180DegreeRotation) { + boneRoot.localScale = new Vector3(Mathf.Abs(skeleton.ScaleX), Mathf.Abs(skeleton.ScaleY), 1f); + boneRoot.eulerAngles = new Vector3(skeleton.ScaleY > 0 ? 0 : 180, + skeleton.ScaleX > 0 ? 0 : 180, + 0); + } + else { + boneRoot.localScale = new Vector3(skeleton.ScaleX, skeleton.ScaleY, 1f); + } + } } [HideInInspector] public SkeletonRenderer skeletonRenderer; @@ -233,7 +251,10 @@ namespace Spine.Unity { var boneComponents = this.boneComponents; for (int i = 0, n = boneComponents.Count; i < n; i++) { var b = boneComponents[i]; - if (b.bone == null) continue; + if (b.bone == null) { + b.DoUpdate(SkeletonUtilityBone.UpdatePhase.Local); + if (b.bone == null) continue; + } hasOverrideBones |= (b.mode == SkeletonUtilityBone.Mode.Override); hasConstraints |= constraintTargets.Contains(b.bone); } From bcc7cb2da028ea31fa437f0c271895e540ed4cf2 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 31 Oct 2019 18:02:53 +0100 Subject: [PATCH 3/3] [unity] Added changelog section added in last commit to 3.9 section as well (physics hinge chain support). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19b4354e5..d38064af2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,8 @@ You can leave this parameter disabled when everything is drawn correctly to save the additional performance cost. * **Additional Timeline features.** SpineAnimationStateClip now provides a `Speed Multiplier`, a start time offset parameter `Clip In`, support for blending successive animations by overlapping tracks. An additional `Use Blend Duration` parameter *(defaults to true)* allows for automatic synchronisation of MixDuration with the current overlap blend duration. An additional Spine preferences parameter `Use Blend Duration` has been added which can be disabled to default to the previous behaviour before this update. * Additional `SpriteMask and RectMask2D` example scene added for demonstration of mask setup and interaction. + * `Real physics hinge chains` for both 2D and 3D physics. The [SkeletonUtilityBone](http://esotericsoftware.com/spine-unity#SkeletonUtilityBone) Inspector provides an interface to create 2D and 3D hinge chains. Previously created chains have only been respecting gravity, but not momentum of the skeleton or parent bones. The new physics rig created when pressing `Create 3D Hinge Chain` and `Create 2D Hinge Chain` creates a more complex setup that also works when flipping the skeleton. Note that the chain root node is no longer parented to bones of the skeleton. This is a requirement in Unity to have momentum applied properly - do not reparent the chain root to bones of your skeleton, or you will loose any momentum applied by the skeleton's movement. + * **Changes of default values**