From 2a792c0247123337eeea6b632e19adb9f4780a86 Mon Sep 17 00:00:00 2001 From: pharan Date: Tue, 17 Apr 2018 08:00:19 +0800 Subject: [PATCH] [unity] Update SkeletonInspectorPreview. --- .../Editor/SkeletonDataAssetInspector.cs | 201 ++++++++++++------ 1 file changed, 140 insertions(+), 61 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index 8216151ad..34892081b 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs @@ -31,9 +31,12 @@ #define SPINE_SKELETON_ANIMATOR using System; +using System.Reflection; using System.Collections.Generic; using UnityEditor; using UnityEngine; + + using Spine; namespace Spine.Unity.Editor { @@ -60,7 +63,6 @@ namespace Spine.Unity.Editor { SkeletonDataAsset targetSkeletonDataAsset; SkeletonData targetSkeletonData; - string targetSkeletonDataAssetGUIDString; readonly List warnings = new List(); readonly SkeletonInspectorPreview preview = new SkeletonInspectorPreview(); @@ -68,17 +70,21 @@ namespace Spine.Unity.Editor { GUIStyle activePlayButtonStyle, idlePlayButtonStyle; readonly GUIContent DefaultMixLabel = new GUIContent("Default Mix Duration", "Sets 'SkeletonDataAsset.defaultMix' in the asset and 'AnimationState.data.defaultMix' at runtime load time."); - string LastSkinKey { get { return targetSkeletonDataAssetGUIDString + "_lastSkin"; } } + string TargetAssetGUID { get { return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(targetSkeletonDataAsset)); } } + string LastSkinKey { get { return TargetAssetGUID + "_lastSkin"; } } string LastSkinName { get { return EditorPrefs.GetString(LastSkinKey, ""); } } void OnEnable () { InitializeEditor(); } + void OnDestroy () { + HandleOnDestroyPreview(); + } + void InitializeEditor () { SpineEditorUtilities.ConfirmInitialization(); targetSkeletonDataAsset = (SkeletonDataAsset)target; - targetSkeletonDataAssetGUIDString = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(targetSkeletonDataAsset)); bool newAtlasAssets = atlasAssets == null; if (newAtlasAssets) atlasAssets = serializedObject.FindProperty("atlasAssets"); @@ -101,8 +107,8 @@ namespace Spine.Unity.Editor { if (newAtlasAssets) atlasAssets.isExpanded = true; #endif - EditorApplication.update -= EditorUpdate; - EditorApplication.update += EditorUpdate; + EditorApplication.update -= preview.HandleEditorUpdate; + EditorApplication.update += preview.HandleEditorUpdate; preview.OnSkinChanged -= HandlePreviewSkinChanged; preview.OnSkinChanged += HandlePreviewSkinChanged; @@ -115,23 +121,11 @@ namespace Spine.Unity.Editor { targetSkeletonData = warnings.Count == 0 ? targetSkeletonDataAsset.GetSkeletonData(false) : null; if (targetSkeletonData != null && warnings.Count <= 0) { - preview.Initialize(targetSkeletonDataAsset, this.LastSkinName); + preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); } } - void EditorUpdate () { - preview.AdjustCamera(); - - if (preview.IsPlayingAnimation) { - preview.RefreshOnNextUpdate(); - Repaint(); - } else if (preview.requiresRefresh) { - Repaint(); - } // else // needed if using smooth menus - - } - void Clear () { preview.Clear(); targetSkeletonDataAsset.Clear(); @@ -180,7 +174,7 @@ 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. if (warnings.Count <= 0) - preview.Initialize(targetSkeletonDataAsset, this.LastSkinName); + preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); if (targetSkeletonData != null) { GUILayout.Space(20f); @@ -193,6 +187,13 @@ namespace Spine.Unity.Editor { EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel); DrawAnimationList(); + if (targetSkeletonData.Animations.Count > 0) { + const string AnimationReferenceButtonText = "Create Animation Reference Assets"; + const string AnimationReferenceTooltipText = "AnimationReferenceAsset acts as Unity asset for a reference to a Spine.Animation. This can be used in inspectors.\n\nIt serializes a reference to a SkeletonDataAsset and an animationName.\n\nAt runtime, a reference to its Spine.Animation is loaded and cached into the object to be used as needed. This skips the need to find and cache animation references in individual MonoBehaviours."; + if (GUILayout.Button(SpineInspectorUtility.TempContent(AnimationReferenceButtonText, Icons.animationRoot, AnimationReferenceTooltipText), GUILayout.Width(250), GUILayout.Height(26))) { + CreateAnimationReferenceAssets(); + } + } EditorGUILayout.Space(); DrawSlotList(); EditorGUILayout.Space(); @@ -217,6 +218,28 @@ namespace Spine.Unity.Editor { serializedObject.ApplyModifiedProperties(); } + void CreateAnimationReferenceAssets () { + const string AssetFolderName = "ReferenceAssets"; + string parentFolder = AssetDatabase.GetAssetPath(targetSkeletonDataAsset); + string dataPath = System.IO.Path.GetDirectoryName(parentFolder) + "/" + AssetFolderName; + if (!AssetDatabase.IsValidFolder(dataPath)) { + AssetDatabase.CreateFolder(parentFolder, AssetFolderName); + } + + FieldInfo nameField = typeof(AnimationReferenceAsset).GetField("animationName", BindingFlags.NonPublic | BindingFlags.Instance); + FieldInfo skeletonDataAssetField = typeof(AnimationReferenceAsset).GetField("skeletonDataAsset", BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var animation in targetSkeletonData.Animations) { + string assetPath = string.Format("{0}/{1}.asset", dataPath, SpineEditorUtilities.GetPathSafeName(animation.Name)); + AnimationReferenceAsset existingAsset = AssetDatabase.LoadAssetAtPath(assetPath); + if (existingAsset == null) { + AnimationReferenceAsset newAsset = ScriptableObject.CreateInstance(); + skeletonDataAssetField.SetValue(newAsset, targetSkeletonDataAsset); + nameField.SetValue(newAsset, animation.Name); + AssetDatabase.CreateAsset(newAsset, assetPath); + } + } + } + void OnInspectorGUIMulti () { // Skeleton data file field. @@ -359,7 +382,7 @@ namespace Spine.Unity.Editor { return; bool isPreviewWindowOpen = preview.IsValid; - + if (isPreviewWindowOpen) { if (GUILayout.Button(SpineInspectorUtility.TempContent("Setup Pose", Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) { preview.ClearAnimationSetupPose(); @@ -500,7 +523,7 @@ namespace Spine.Unity.Editor { warnings.Add("Missing Skeleton JSON"); } else { var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue; - if (!SpineEditorUtilities.IsSpineData(fieldValue)) { + if (!SpineEditorUtilities.SkeletonDataFileValidator.IsSpineData(fieldValue)) { warnings.Add("Skeleton data file is not a valid JSON or binary file."); } else { #if SPINE_TK2D @@ -571,12 +594,12 @@ namespace Spine.Unity.Editor { EditorPrefs.SetString(LastSkinKey, skinName); } - void OnDestroy () { - EditorApplication.update -= EditorUpdate; + #region Preview Handlers + void HandleOnDestroyPreview () { + EditorApplication.update -= preview.HandleEditorUpdate; preview.OnDestroy(); } - #region Preview Handlers override public bool HasPreviewGUI () { if (serializedObject.isEditingMultipleObjects) return false; @@ -590,37 +613,20 @@ namespace Spine.Unity.Editor { return skeletonJSON.objectReferenceValue != null; } - override public GUIContent GetPreviewTitle () { - return SpineInspectorUtility.TempContent("Preview"); - } - override public void OnInteractivePreviewGUI (Rect r, GUIStyle background) { if (warnings.Count <= 0) { - preview.Initialize(targetSkeletonDataAsset, this.LastSkinName); + preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); preview.HandleInteractivePreviewGUI(r, background); } } - public override void OnPreviewSettings () { - const float SliderWidth = 150; - const float SliderSnap = 0.25f; - const float SliderMin = 0f; - const float SliderMax = 2f; - - if (preview.IsValid) { - float timeScale = GUILayout.HorizontalSlider(preview.TimeScale, SliderMin, SliderMax, GUILayout.MaxWidth(SliderWidth)); - timeScale = Mathf.RoundToInt(timeScale/SliderSnap) * SliderSnap; - preview.TimeScale = timeScale; - } - } - - public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) { - return preview.GetStaticPreview(width, height); - } + override public GUIContent GetPreviewTitle () { return SpineInspectorUtility.TempContent("Preview"); } + public override void OnPreviewSettings () { preview.HandleDrawSettings(); } + public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) { return preview.GetStaticPreview(width, height); } #endregion } - class SkeletonInspectorPreview { + internal class SkeletonInspectorPreview { Color OriginColor = new Color(0.3f, 0.3f, 0.3f, 1); static readonly int SliderHash = "Slider".GetHashCode(); @@ -632,17 +638,14 @@ namespace Spine.Unity.Editor { internal bool requiresRefresh; float animationLastTime; + Action Repaint; public event Action OnSkinChanged; Texture previewTexture; PreviewRenderUtility previewRenderUtility; Camera PreviewUtilityCamera { get { - if (previewRenderUtility == null) { - - return null; - } - + if (previewRenderUtility == null) return null; #if UNITY_2017_1_OR_NEWER return previewRenderUtility.camera; #else @@ -651,6 +654,8 @@ namespace Spine.Unity.Editor { } } + static Vector3 lastCameraPositionGoal; + static float lastCameraOrthoGoal; float cameraOrthoGoal = 1; Vector3 cameraPositionGoal = new Vector3(0, 0, -10); double cameraAdjustEndFrame = 0; @@ -679,14 +684,27 @@ namespace Spine.Unity.Editor { get { return IsValid ? skeletonAnimation.AnimationState.GetCurrent(0) : null; } } - public void Initialize (SkeletonDataAsset skeletonDataAsset, string skinName = "") { - if (skeletonDataAsset == null) return; - if (skeletonDataAsset.GetSkeletonData(false) == null) - return; + public Vector3 PreviewCameraPosition { + get { return PreviewUtilityCamera.transform.position; } + set { PreviewUtilityCamera.transform.position = value; } + } + public void Initialize (Action repaintCallback, SkeletonDataAsset skeletonDataAsset, string skinName = "") { + if (skeletonDataAsset == null) return; + if (skeletonDataAsset.GetSkeletonData(false) == null) { + DestroyPreviewGameObject(); + return; + } + + this.Repaint = repaintCallback; this.skeletonDataAsset = skeletonDataAsset; this.skeletonData = skeletonDataAsset.GetSkeletonData(false); + if (skeletonData == null) { + DestroyPreviewGameObject(); + return; + } + if (previewRenderUtility == null) { previewRenderUtility = new PreviewRenderUtility(true); animationLastTime = Time.realtimeSinceStartup; @@ -697,10 +715,11 @@ namespace Spine.Unity.Editor { { var c = this.PreviewUtilityCamera; c.orthographic = true; - c.orthographicSize = 1; c.cullingMask = PreviewCameraCullingMask; c.nearClipPlane = 0.01f; - c.farClipPlane = 1000f; + c.farClipPlane = 1000f; + c.orthographicSize = lastCameraOrthoGoal; + c.transform.position = lastCameraPositionGoal; } DestroyPreviewGameObject(); @@ -727,6 +746,19 @@ namespace Spine.Unity.Editor { } } + public void HandleDrawSettings () { + const float SliderWidth = 150; + const float SliderSnap = 0.25f; + const float SliderMin = 0f; + const float SliderMax = 2f; + + if (IsValid) { + float timeScale = GUILayout.HorizontalSlider(TimeScale, SliderMin, SliderMax, GUILayout.MaxWidth(SliderWidth)); + timeScale = Mathf.RoundToInt(timeScale / SliderSnap) * SliderSnap; + TimeScale = timeScale; + } + } + public void OnDestroy () { DisposePreviewRenderUtility(); DestroyPreviewGameObject(); @@ -765,8 +797,19 @@ namespace Spine.Unity.Editor { } public void PlayPauseAnimation (string animationName, bool loop) { + if (skeletonData == null) return; + if (skeletonAnimation == null) { - Debug.LogWarning("Animation was stopped but preview doesn't exist. It's possible that the Preview Panel is closed."); + //Debug.LogWarning("Animation was stopped but preview doesn't exist. It's possible that the Preview Panel is closed."); + return; + } + + if (!skeletonAnimation.valid) return; + + if (string.IsNullOrEmpty(animationName)) { + skeletonAnimation.Skeleton.SetToSetupPose(); + skeletonAnimation.AnimationState.ClearTracks(); + return; } var targetAnimation = skeletonData.FindAnimation(animationName); @@ -805,7 +848,7 @@ namespace Spine.Unity.Editor { } } } else { - Debug.LogFormat("Something went wrong. The Spine.Animation named '{0}' was not found.", animationName); + Debug.LogFormat("The Spine.Animation named '{0}' was not found for this Skeleton.", animationName); } } @@ -823,6 +866,7 @@ namespace Spine.Unity.Editor { } DrawSkinToolbar(r); + //DrawSetupPoseButton(r); DrawTimeBar(r); MouseScroll(r); } @@ -848,13 +892,16 @@ namespace Spine.Unity.Editor { if (EditorApplication.timeSinceStartup < cameraAdjustEndFrame) AdjustCameraGoals(); + lastCameraPositionGoal = cameraPositionGoal; + lastCameraOrthoGoal = cameraOrthoGoal; + var c = this.PreviewUtilityCamera; float orthoSet = Mathf.Lerp(c.orthographicSize, cameraOrthoGoal, 0.1f); c.orthographicSize = orthoSet; float dist = Vector3.Distance(c.transform.position, cameraPositionGoal); - if(dist > 0f) { + if (dist > 0f) { Vector3 pos = Vector3.Lerp(c.transform.position, cameraPositionGoal, 0.1f); pos.x = 0; c.transform.position = pos; @@ -877,6 +924,16 @@ namespace Spine.Unity.Editor { return tex; } + public void HandleEditorUpdate () { + AdjustCamera(); + if (IsPlayingAnimation) { + RefreshOnNextUpdate(); + Repaint(); + } else if (requiresRefresh) { + Repaint(); + } + } + public void DoRenderPreview (bool drawHandles) { if (this.PreviewUtilityCamera.activeTexture == null || this.PreviewUtilityCamera.targetTexture == null ) return; @@ -940,6 +997,28 @@ namespace Spine.Unity.Editor { } } + void DrawSetupPoseButton (Rect r) { + if (!this.IsValid) + return; + + var skeleton = this.Skeleton; + + Rect popRect = new Rect(r); + popRect.y += 64; + popRect.x += 4; + popRect.height = 24; + popRect.width = 40; + + //popRect.y += 11; + popRect.width = 150; + //popRect.x += 44; + + if (GUI.Button(popRect, SpineInspectorUtility.TempContent("Reset to SetupPose", Icons.skeleton))) { + ClearAnimationSetupPose(); + RefreshOnNextUpdate(); + } + } + void DrawSkinDropdown () { var menu = new GenericMenu(); foreach (Skin s in skeletonData.Skins) @@ -953,7 +1032,7 @@ namespace Spine.Unity.Editor { skeletonAnimation.initialSkinName = skin.Name; skeletonAnimation.Initialize(true); RefreshOnNextUpdate(); - OnSkinChanged(skin.Name); + if (OnSkinChanged != null) OnSkinChanged(skin.Name); } void DrawTimeBar (Rect r) {