[unity] Update SkeletonInspectorPreview.

This commit is contained in:
pharan 2018-04-17 08:00:19 +08:00
parent 0a23ed3d85
commit 2a792c0247

View File

@ -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<string> warnings = new List<string>();
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<AnimationReferenceAsset>(assetPath);
if (existingAsset == null) {
AnimationReferenceAsset newAsset = ScriptableObject.CreateInstance<AnimationReferenceAsset>();
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<string> 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) {