mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-24 10:41:24 +08:00
[unity] Providing error dialog and verbose error log message in case of version mismatch. Also providing error description when viewing skeletonData in inspector. Closes #1463.
This commit is contained in:
parent
45362e60bf
commit
38d0204e72
@ -40,7 +40,7 @@ using System.Collections.Generic;
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
using Spine;
|
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
|
||||||
|
|
||||||
namespace Spine.Unity.Editor {
|
namespace Spine.Unity.Editor {
|
||||||
using Event = UnityEngine.Event;
|
using Event = UnityEngine.Event;
|
||||||
@ -69,6 +69,7 @@ namespace Spine.Unity.Editor {
|
|||||||
SkeletonData targetSkeletonData;
|
SkeletonData targetSkeletonData;
|
||||||
|
|
||||||
readonly List<string> warnings = new List<string>();
|
readonly List<string> warnings = new List<string>();
|
||||||
|
CompatibilityProblemInfo compatibilityProblemInfo = null;
|
||||||
readonly SkeletonInspectorPreview preview = new SkeletonInspectorPreview();
|
readonly SkeletonInspectorPreview preview = new SkeletonInspectorPreview();
|
||||||
|
|
||||||
GUIStyle activePlayButtonStyle, idlePlayButtonStyle;
|
GUIStyle activePlayButtonStyle, idlePlayButtonStyle;
|
||||||
@ -140,9 +141,9 @@ namespace Spine.Unity.Editor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetSkeletonData = warnings.Count == 0 ? targetSkeletonDataAsset.GetSkeletonData(false) : null;
|
targetSkeletonData = NoProblems() ? targetSkeletonDataAsset.GetSkeletonData(false) : null;
|
||||||
|
|
||||||
if (targetSkeletonData != null && warnings.Count <= 0) {
|
if (targetSkeletonData != null && NoProblems()) {
|
||||||
preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName);
|
preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,12 +175,15 @@ namespace Spine.Unity.Editor {
|
|||||||
// Header
|
// Header
|
||||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(target.name + " (SkeletonDataAsset)", Icons.spine), EditorStyles.whiteLargeLabel);
|
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(target.name + " (SkeletonDataAsset)", Icons.spine), EditorStyles.whiteLargeLabel);
|
||||||
if (targetSkeletonData != null) EditorGUILayout.LabelField("(Drag and Drop to instantiate.)", EditorStyles.miniLabel);
|
if (targetSkeletonData != null) EditorGUILayout.LabelField("(Drag and Drop to instantiate.)", EditorStyles.miniLabel);
|
||||||
|
|
||||||
// Main Serialized Fields
|
// Main Serialized Fields
|
||||||
using (var changeCheck = new EditorGUI.ChangeCheckScope()) {
|
using (var changeCheck = new EditorGUI.ChangeCheckScope()) {
|
||||||
using (new SpineInspectorUtility.BoxScope())
|
using (new SpineInspectorUtility.BoxScope())
|
||||||
DrawSkeletonDataFields();
|
DrawSkeletonDataFields();
|
||||||
|
|
||||||
|
if (compatibilityProblemInfo != null)
|
||||||
|
return;
|
||||||
|
|
||||||
using (new SpineInspectorUtility.BoxScope()) {
|
using (new SpineInspectorUtility.BoxScope()) {
|
||||||
DrawAtlasAssetsFields();
|
DrawAtlasAssetsFields();
|
||||||
HandleAtlasAssetsNulls();
|
HandleAtlasAssetsNulls();
|
||||||
@ -197,11 +201,11 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unity Quirk: Some code depends on valid preview. If preview is initialized elsewhere, this can cause contents to change between Layout and Repaint events, causing GUILayout control count errors.
|
// Unity Quirk: Some code depends on valid preview. If preview is initialized elsewhere, this can cause contents to change between Layout and Repaint events, causing GUILayout control count errors.
|
||||||
if (warnings.Count <= 0)
|
if (NoProblems())
|
||||||
preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName);
|
preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName);
|
||||||
|
|
||||||
if (targetSkeletonData != null) {
|
if (targetSkeletonData != null) {
|
||||||
GUILayout.Space(20f);
|
GUILayout.Space(20f);
|
||||||
|
|
||||||
@ -317,6 +321,12 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine));
|
EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine));
|
||||||
|
|
||||||
|
if (compatibilityProblemInfo != null) {
|
||||||
|
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(compatibilityProblemInfo.DescriptionString(), Icons.warning), GUILayout.Height(52));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EditorGUILayout.DelayedFloatField(scale); //EditorGUILayout.PropertyField(scale);
|
EditorGUILayout.DelayedFloatField(scale); //EditorGUILayout.PropertyField(scale);
|
||||||
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
||||||
EditorGUILayout.PropertyField(skeletonDataModifiers, true);
|
EditorGUILayout.PropertyField(skeletonDataModifiers, true);
|
||||||
@ -581,12 +591,14 @@ namespace Spine.Unity.Editor {
|
|||||||
|
|
||||||
void PopulateWarnings () {
|
void PopulateWarnings () {
|
||||||
warnings.Clear();
|
warnings.Clear();
|
||||||
|
compatibilityProblemInfo = null;
|
||||||
|
|
||||||
if (skeletonJSON.objectReferenceValue == null) {
|
if (skeletonJSON.objectReferenceValue == null) {
|
||||||
warnings.Add("Missing Skeleton JSON");
|
warnings.Add("Missing Skeleton JSON");
|
||||||
} else {
|
} else {
|
||||||
var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue;
|
var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue;
|
||||||
if (!AssetUtility.IsSpineData(fieldValue)) {
|
|
||||||
|
if (!AssetUtility.IsSpineData(fieldValue, out compatibilityProblemInfo)) {
|
||||||
warnings.Add("Skeleton data file is not a valid Spine JSON or binary file.");
|
warnings.Add("Skeleton data file is not a valid Spine JSON or binary file.");
|
||||||
} else {
|
} else {
|
||||||
#if SPINE_TK2D
|
#if SPINE_TK2D
|
||||||
@ -657,6 +669,10 @@ namespace Spine.Unity.Editor {
|
|||||||
EditorPrefs.SetString(LastSkinKey, skinName);
|
EditorPrefs.SetString(LastSkinKey, skinName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NoProblems() {
|
||||||
|
return warnings.Count == 0 && compatibilityProblemInfo == null;
|
||||||
|
}
|
||||||
|
|
||||||
#region Preview Handlers
|
#region Preview Handlers
|
||||||
void HandleOnDestroyPreview () {
|
void HandleOnDestroyPreview () {
|
||||||
EditorApplication.update -= preview.HandleEditorUpdate;
|
EditorApplication.update -= preview.HandleEditorUpdate;
|
||||||
@ -677,7 +693,7 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public void OnInteractivePreviewGUI (Rect r, GUIStyle background) {
|
override public void OnInteractivePreviewGUI (Rect r, GUIStyle background) {
|
||||||
if (warnings.Count <= 0) {
|
if (NoProblems()) {
|
||||||
preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName);
|
preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName);
|
||||||
preview.HandleInteractivePreviewGUI(r, background);
|
preview.HandleInteractivePreviewGUI(r, background);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,18 +46,17 @@ using System.IO;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Globalization;
|
|
||||||
|
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
|
||||||
|
|
||||||
namespace Spine.Unity.Editor {
|
namespace Spine.Unity.Editor {
|
||||||
|
using PathAndProblemInfo = System.Collections.Generic.KeyValuePair<string, CompatibilityProblemInfo>;
|
||||||
|
|
||||||
public static class AssetUtility {
|
public static class AssetUtility {
|
||||||
|
|
||||||
public const string SkeletonDataSuffix = "_SkeletonData";
|
public const string SkeletonDataSuffix = "_SkeletonData";
|
||||||
public const string AtlasSuffix = "_Atlas";
|
public const string AtlasSuffix = "_Atlas";
|
||||||
|
|
||||||
static readonly int[][] compatibleBinaryVersions = { new[] { 3, 8, 0 }, new[] { 3, 9, 0 } };
|
|
||||||
static readonly int[][] compatibleJsonVersions = { new[] { 3, 8, 0 }, new[] { 3, 9, 0 } };
|
|
||||||
//static bool isFixVersionRequired = false;
|
|
||||||
|
|
||||||
/// HACK: This list keeps the asset reference temporarily during importing.
|
/// HACK: This list keeps the asset reference temporarily during importing.
|
||||||
///
|
///
|
||||||
/// In cases of very large projects/sufficient RAM pressure, when AssetDatabase.SaveAssets is called,
|
/// In cases of very large projects/sufficient RAM pressure, when AssetDatabase.SaveAssets is called,
|
||||||
@ -125,7 +124,11 @@ namespace Spine.Unity.Editor {
|
|||||||
if (root == null || !root.ContainsKey("skins"))
|
if (root == null || !root.ContainsKey("skins"))
|
||||||
return requiredPaths;
|
return requiredPaths;
|
||||||
|
|
||||||
foreach (Dictionary<string, object> skinMap in (List<object>)root["skins"]) {
|
var skinsList = root["skins"] as List<object>;
|
||||||
|
if (skinsList == null)
|
||||||
|
return requiredPaths;
|
||||||
|
|
||||||
|
foreach (Dictionary<string, object> skinMap in skinsList) {
|
||||||
if (!skinMap.ContainsKey("attachments"))
|
if (!skinMap.ContainsKey("attachments"))
|
||||||
continue;
|
continue;
|
||||||
foreach (var slot in (Dictionary<string, object>)skinMap["attachments"]) {
|
foreach (var slot in (Dictionary<string, object>)skinMap["attachments"]) {
|
||||||
@ -243,7 +246,8 @@ namespace Spine.Unity.Editor {
|
|||||||
public static void ImportSpineContent (string[] imported, bool reimport = false) {
|
public static void ImportSpineContent (string[] imported, bool reimport = false) {
|
||||||
var atlasPaths = new List<string>();
|
var atlasPaths = new List<string>();
|
||||||
var imagePaths = new List<string>();
|
var imagePaths = new List<string>();
|
||||||
var skeletonPaths = new List<string>();
|
var skeletonPaths = new List<PathAndProblemInfo>();
|
||||||
|
CompatibilityProblemInfo compatibilityProblemInfo = null;
|
||||||
|
|
||||||
foreach (string str in imported) {
|
foreach (string str in imported) {
|
||||||
string extension = Path.GetExtension(str).ToLower();
|
string extension = Path.GetExtension(str).ToLower();
|
||||||
@ -263,13 +267,13 @@ namespace Spine.Unity.Editor {
|
|||||||
break;
|
break;
|
||||||
case ".json":
|
case ".json":
|
||||||
var jsonAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(str);
|
var jsonAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(str);
|
||||||
if (jsonAsset != null && IsSpineData(jsonAsset))
|
if (jsonAsset != null && IsSpineData(jsonAsset, out compatibilityProblemInfo))
|
||||||
skeletonPaths.Add(str);
|
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo));
|
||||||
break;
|
break;
|
||||||
case ".bytes":
|
case ".bytes":
|
||||||
if (str.ToLower().EndsWith(".skel.bytes", System.StringComparison.Ordinal)) {
|
if (str.ToLower().EndsWith(".skel.bytes", System.StringComparison.Ordinal)) {
|
||||||
if (IsSpineData(AssetDatabase.LoadAssetAtPath<TextAsset>(str)))
|
if (IsSpineData(AssetDatabase.LoadAssetAtPath<TextAsset>(str), out compatibilityProblemInfo))
|
||||||
skeletonPaths.Add(str);
|
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -287,24 +291,32 @@ namespace Spine.Unity.Editor {
|
|||||||
|
|
||||||
// Import skeletons and match them with atlases.
|
// Import skeletons and match them with atlases.
|
||||||
bool abortSkeletonImport = false;
|
bool abortSkeletonImport = false;
|
||||||
foreach (string skeletonPath in skeletonPaths) {
|
foreach (var skeletonPathEntry in skeletonPaths) {
|
||||||
|
string skeletonPath = skeletonPathEntry.Key;
|
||||||
|
var compatibilityProblems = skeletonPathEntry.Value;
|
||||||
if (skeletonPath.StartsWith("Packages"))
|
if (skeletonPath.StartsWith("Packages"))
|
||||||
continue;
|
continue;
|
||||||
if (!reimport && CheckForValidSkeletonData(skeletonPath)) {
|
if (!reimport && CheckForValidSkeletonData(skeletonPath)) {
|
||||||
ReloadSkeletonData(skeletonPath);
|
ReloadSkeletonData(skeletonPath, compatibilityProblems);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loadedAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(skeletonPath);
|
||||||
|
if (compatibilityProblems != null) {
|
||||||
|
IngestIncompatibleSpineProject(loadedAsset, compatibilityProblems);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string dir = Path.GetDirectoryName(skeletonPath);
|
string dir = Path.GetDirectoryName(skeletonPath);
|
||||||
|
|
||||||
#if SPINE_TK2D
|
#if SPINE_TK2D
|
||||||
IngestSpineProject(AssetDatabase.LoadAssetAtPath<TextAsset>(skeletonPath), null);
|
IngestSpineProject(loadedAsset, compatibilityProblems, null);
|
||||||
#else
|
#else
|
||||||
var localAtlases = FindAtlasesAtPath(dir);
|
var localAtlases = FindAtlasesAtPath(dir);
|
||||||
var requiredPaths = GetRequiredAtlasRegions(skeletonPath);
|
var requiredPaths = GetRequiredAtlasRegions(skeletonPath);
|
||||||
var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases);
|
var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases);
|
||||||
if (atlasMatch != null || requiredPaths.Count == 0) {
|
if (atlasMatch != null || requiredPaths.Count == 0) {
|
||||||
IngestSpineProject(AssetDatabase.LoadAssetAtPath<TextAsset>(skeletonPath), atlasMatch);
|
IngestSpineProject(loadedAsset, atlasMatch);
|
||||||
} else {
|
} else {
|
||||||
SkeletonImportDialog(skeletonPath, localAtlases, requiredPaths, ref abortSkeletonImport);
|
SkeletonImportDialog(skeletonPath, localAtlases, requiredPaths, ref abortSkeletonImport);
|
||||||
}
|
}
|
||||||
@ -338,7 +350,7 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReloadSkeletonData (string skeletonJSONPath) {
|
static void ReloadSkeletonData (string skeletonJSONPath, CompatibilityProblemInfo compatibilityProblemInfo) {
|
||||||
string dir = Path.GetDirectoryName(skeletonJSONPath);
|
string dir = Path.GetDirectoryName(skeletonJSONPath);
|
||||||
TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(skeletonJSONPath);
|
TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(skeletonJSONPath);
|
||||||
DirectoryInfo dirInfo = new DirectoryInfo(dir);
|
DirectoryInfo dirInfo = new DirectoryInfo(dir);
|
||||||
@ -353,6 +365,11 @@ namespace Spine.Unity.Editor {
|
|||||||
if (Selection.activeObject == skeletonDataAsset)
|
if (Selection.activeObject == skeletonDataAsset)
|
||||||
Selection.activeObject = null;
|
Selection.activeObject = null;
|
||||||
|
|
||||||
|
if (compatibilityProblemInfo != null) {
|
||||||
|
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), textAsset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Debug.LogFormat("Changes to '{0}' detected. Clearing SkeletonDataAsset: {1}", skeletonJSONPath, localPath);
|
Debug.LogFormat("Changes to '{0}' detected. Clearing SkeletonDataAsset: {1}", skeletonJSONPath, localPath);
|
||||||
skeletonDataAsset.Clear();
|
skeletonDataAsset.Clear();
|
||||||
|
|
||||||
@ -545,10 +562,34 @@ namespace Spine.Unity.Editor {
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Import SkeletonData (json or binary)
|
#region Import SkeletonData (json or binary)
|
||||||
internal static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAssetBase[] atlasAssets) {
|
internal static string GetSkeletonDataAssetFilePath(TextAsset spineJson) {
|
||||||
string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
|
string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
|
||||||
string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
|
string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
|
||||||
string filePath = assetPath + "/" + primaryName + SkeletonDataSuffix + ".asset";
|
return assetPath + "/" + primaryName + SkeletonDataSuffix + ".asset";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SkeletonDataAsset IngestIncompatibleSpineProject(TextAsset spineJson,
|
||||||
|
CompatibilityProblemInfo compatibilityProblemInfo) {
|
||||||
|
|
||||||
|
string filePath = GetSkeletonDataAssetFilePath(spineJson);
|
||||||
|
|
||||||
|
if (spineJson == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
SkeletonDataAsset skeletonDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset));
|
||||||
|
if (skeletonDataAsset == null) {
|
||||||
|
skeletonDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
|
||||||
|
skeletonDataAsset.skeletonJSON = spineJson;
|
||||||
|
AssetDatabase.CreateAsset(skeletonDataAsset, filePath);
|
||||||
|
}
|
||||||
|
EditorUtility.SetDirty(skeletonDataAsset);
|
||||||
|
|
||||||
|
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), spineJson);
|
||||||
|
return skeletonDataAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAssetBase[] atlasAssets) {
|
||||||
|
string filePath = GetSkeletonDataAssetFilePath(spineJson);
|
||||||
|
|
||||||
#if SPINE_TK2D
|
#if SPINE_TK2D
|
||||||
if (spineJson != null) {
|
if (spineJson != null) {
|
||||||
@ -622,76 +663,10 @@ namespace Spine.Unity.Editor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsSpineData (TextAsset asset) {
|
public static bool IsSpineData (TextAsset asset, out CompatibilityProblemInfo compatibilityProblemInfo) {
|
||||||
if (asset == null)
|
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(asset);
|
||||||
return false;
|
compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
|
||||||
|
return fileVersion != null;
|
||||||
bool isSpineData = false;
|
|
||||||
string rawVersion = null;
|
|
||||||
|
|
||||||
int[][] compatibleVersions;
|
|
||||||
if (asset.name.Contains(".skel")) {
|
|
||||||
try {
|
|
||||||
using (var memStream = new MemoryStream(asset.bytes)) {
|
|
||||||
rawVersion = SkeletonBinary.GetVersionString(memStream);
|
|
||||||
}
|
|
||||||
isSpineData = !(string.IsNullOrEmpty(rawVersion));
|
|
||||||
compatibleVersions = compatibleBinaryVersions;
|
|
||||||
} catch (System.Exception e) {
|
|
||||||
Debug.LogErrorFormat("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
object obj = Json.Deserialize(new StringReader(asset.text));
|
|
||||||
if (obj == null) {
|
|
||||||
Debug.LogErrorFormat("'{0}' is not valid JSON.", asset.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var root = obj as Dictionary<string, object>;
|
|
||||||
if (root == null) {
|
|
||||||
Debug.LogError("Parser returned an incorrect type.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isSpineData = root.ContainsKey("skeleton");
|
|
||||||
if (isSpineData) {
|
|
||||||
var skeletonInfo = (Dictionary<string, object>)root["skeleton"];
|
|
||||||
object jv;
|
|
||||||
skeletonInfo.TryGetValue("spine", out jv);
|
|
||||||
rawVersion = jv as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
compatibleVersions = compatibleJsonVersions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version warning
|
|
||||||
if (isSpineData) {
|
|
||||||
string primaryRuntimeVersionDebugString = compatibleVersions[0][0] + "." + compatibleVersions[0][1];
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(rawVersion)) {
|
|
||||||
Debug.LogWarningFormat("Skeleton '{0}' has no version information. It may be incompatible with your runtime version: spine-unity v{1}", asset.name, primaryRuntimeVersionDebugString);
|
|
||||||
} else {
|
|
||||||
string[] versionSplit = rawVersion.Split('.');
|
|
||||||
bool match = false;
|
|
||||||
foreach (var version in compatibleVersions) {
|
|
||||||
bool primaryMatch = version[0] == int.Parse(versionSplit[0], CultureInfo.InvariantCulture);
|
|
||||||
bool secondaryMatch = version[1] == int.Parse(versionSplit[1], CultureInfo.InvariantCulture);
|
|
||||||
|
|
||||||
// if (isFixVersionRequired) secondaryMatch &= version[2] <= int.Parse(jsonVersionSplit[2], CultureInfo.InvariantCulture);
|
|
||||||
|
|
||||||
if (primaryMatch && secondaryMatch) {
|
|
||||||
match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match)
|
|
||||||
Debug.LogWarningFormat("Skeleton '{0}' (exported with Spine {1}) may be incompatible with your runtime version: spine-csharp v{2}", asset.name, rawVersion, primaryRuntimeVersionDebugString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isSpineData;
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
using Spine;
|
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
|
||||||
|
|
||||||
namespace Spine.Unity {
|
namespace Spine.Unity {
|
||||||
|
|
||||||
@ -114,26 +114,26 @@ namespace Spine.Unity {
|
|||||||
Clear();
|
Clear();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disabled to support attachmentless/skinless SkeletonData.
|
// Disabled to support attachmentless/skinless SkeletonData.
|
||||||
// if (atlasAssets == null) {
|
// if (atlasAssets == null) {
|
||||||
// atlasAssets = new AtlasAsset[0];
|
// atlasAssets = new AtlasAsset[0];
|
||||||
// if (!quiet)
|
// if (!quiet)
|
||||||
// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
|
// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
|
||||||
// Clear();
|
// Clear();
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
// #if !SPINE_TK2D
|
// #if !SPINE_TK2D
|
||||||
// if (atlasAssets.Length == 0) {
|
// if (atlasAssets.Length == 0) {
|
||||||
// Clear();
|
// Clear();
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
// #else
|
// #else
|
||||||
// if (atlasAssets.Length == 0 && spriteCollection == null) {
|
// if (atlasAssets.Length == 0 && spriteCollection == null) {
|
||||||
// Clear();
|
// Clear();
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
if (skeletonData != null)
|
if (skeletonData != null)
|
||||||
return skeletonData;
|
return skeletonData;
|
||||||
@ -163,6 +163,17 @@ namespace Spine.Unity {
|
|||||||
bool isBinary = skeletonJSON.name.ToLower().Contains(".skel");
|
bool isBinary = skeletonJSON.name.ToLower().Contains(".skel");
|
||||||
SkeletonData loadedSkeletonData;
|
SkeletonData loadedSkeletonData;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (skeletonJSON) {
|
||||||
|
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON);
|
||||||
|
CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
|
||||||
|
if (compatibilityProblemInfo != null) {
|
||||||
|
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isBinary)
|
if (isBinary)
|
||||||
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
|
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
|
||||||
|
|||||||
@ -0,0 +1,160 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated May 1, 2019. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2019, Esoteric Software LLC
|
||||||
|
*
|
||||||
|
* Integration of the Spine Runtimes into software or otherwise creating
|
||||||
|
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||||
|
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||||
|
* http://esotericsoftware.com/spine-editor-license
|
||||||
|
*
|
||||||
|
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||||
|
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||||
|
* "Products"), provided that each user of the Products must obtain their own
|
||||||
|
* Spine Editor license and redistribution of the Products in any form must
|
||||||
|
* include this license and copyright notice.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||||
|
* NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
|
||||||
|
* INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Spine.Unity {
|
||||||
|
|
||||||
|
public static class SkeletonDataCompatibility {
|
||||||
|
|
||||||
|
static readonly int[][] compatibleBinaryVersions = { new[] { 3, 9, 0 }, new[] { 3, 8, 0 } };
|
||||||
|
static readonly int[][] compatibleJsonVersions = { new[] { 3, 9, 0 }, new[] { 3, 8, 0 } };
|
||||||
|
|
||||||
|
static bool wasVersionDialogShown = false;
|
||||||
|
|
||||||
|
public enum SourceType {
|
||||||
|
Json,
|
||||||
|
Binary
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public class VersionInfo {
|
||||||
|
public string rawVersion = null;
|
||||||
|
public int[] version = null;
|
||||||
|
public SourceType sourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public class CompatibilityProblemInfo {
|
||||||
|
public VersionInfo actualVersion;
|
||||||
|
public int[][] compatibleVersions;
|
||||||
|
|
||||||
|
public string DescriptionString () {
|
||||||
|
string compatibleVersionString = "";
|
||||||
|
string optionalOr = null;
|
||||||
|
foreach (int[] version in compatibleVersions) {
|
||||||
|
compatibleVersionString += string.Format("{0}{1}.{2}", optionalOr, version[0], version[1]);
|
||||||
|
optionalOr = " or ";
|
||||||
|
}
|
||||||
|
return string.Format("Skeleton data could not be loaded. Data version: {0}. Required version: {1}.\nPlease re-export skeleton data with Spine {1} or change runtime to version {2}.{3}.",
|
||||||
|
actualVersion.rawVersion, compatibleVersionString, actualVersion.version[0], actualVersion.version[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
public static VersionInfo GetVersionInfo (TextAsset asset) {
|
||||||
|
if (asset == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
VersionInfo fileVersion = new VersionInfo();
|
||||||
|
fileVersion.sourceType = asset.name.Contains(".skel") ? SourceType.Binary : SourceType.Json;
|
||||||
|
|
||||||
|
if (fileVersion.sourceType == SourceType.Binary) {
|
||||||
|
try {
|
||||||
|
using (var memStream = new MemoryStream(asset.bytes)) {
|
||||||
|
fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e) {
|
||||||
|
Debug.LogErrorFormat("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
object obj = Json.Deserialize(new StringReader(asset.text));
|
||||||
|
if (obj == null) {
|
||||||
|
Debug.LogErrorFormat("'{0}' is not valid JSON.", asset.name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = obj as Dictionary<string, object>;
|
||||||
|
if (root == null) {
|
||||||
|
Debug.LogErrorFormat("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.ContainsKey("skeleton")) {
|
||||||
|
var skeletonInfo = (Dictionary<string, object>)root["skeleton"];
|
||||||
|
object jv;
|
||||||
|
skeletonInfo.TryGetValue("spine", out jv);
|
||||||
|
fileVersion.rawVersion = jv as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string primaryRuntimeVersionDebugString = compatibleBinaryVersions[0][0] + "." + compatibleBinaryVersions[0][1];
|
||||||
|
if (string.IsNullOrEmpty(fileVersion.rawVersion)) {
|
||||||
|
Debug.LogWarningFormat("Skeleton '{0}' has no version information. It is incompatible with your runtime version: spine-unity v{1}", asset.name, primaryRuntimeVersionDebugString);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionSplit = fileVersion.rawVersion.Split('.');
|
||||||
|
try {
|
||||||
|
fileVersion.version = new[]{ int.Parse(versionSplit[0], CultureInfo.InvariantCulture),
|
||||||
|
int.Parse(versionSplit[1], CultureInfo.InvariantCulture) };
|
||||||
|
}
|
||||||
|
catch (System.Exception e) {
|
||||||
|
Debug.LogErrorFormat("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return fileVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) {
|
||||||
|
CompatibilityProblemInfo info = new CompatibilityProblemInfo();
|
||||||
|
info.actualVersion = fileVersion;
|
||||||
|
info.compatibleVersions = (fileVersion.sourceType == SourceType.Binary) ? compatibleBinaryVersions
|
||||||
|
: compatibleJsonVersions;
|
||||||
|
|
||||||
|
if (fileVersion == null)
|
||||||
|
return info;
|
||||||
|
|
||||||
|
foreach (var compatibleVersion in info.compatibleVersions) {
|
||||||
|
bool majorMatch = fileVersion.version[0] == compatibleVersion[0];
|
||||||
|
bool minorMatch = fileVersion.version[1] == compatibleVersion[1];
|
||||||
|
if (majorMatch && minorMatch) {
|
||||||
|
return null; // is compatible, thus no problem info returned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DisplayCompatibilityProblem (string descriptionString, TextAsset spineJson) {
|
||||||
|
if (!wasVersionDialogShown) {
|
||||||
|
wasVersionDialogShown = true;
|
||||||
|
UnityEditor.EditorUtility.DisplayDialog("Version mismatch!", descriptionString, "OK");
|
||||||
|
}
|
||||||
|
Debug.LogError(string.Format("Error importing skeleton '{0}': {1}",
|
||||||
|
spineJson.name, descriptionString), spineJson);
|
||||||
|
}
|
||||||
|
#endif // UNITY_EDITOR
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4224df6e20549f0449154531ae080201
|
||||||
|
timeCreated: 1567002861
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Loading…
x
Reference in New Issue
Block a user