From fe02e78681bca243c58ad501e86b71a4ee768465 Mon Sep 17 00:00:00 2001 From: pharan Date: Tue, 16 Feb 2016 13:27:20 +0800 Subject: [PATCH] SkeletonGraphic Inspector and Edtor, small fixes. --- .../Assets/spine-unity/Editor/Menus.cs | 19 +- .../Editor/SkeletonAnimationInspector.cs | 2 +- .../Editor/SkeletonRendererInspector.cs | 6 +- .../Simple/ArraysSimpleMeshGenerator.cs | 9 +- .../Simple/ISimpleMeshGenerator.cs | 1 + .../VertexHelperSpineMeshGenerator.cs | 4 + .../Modules/SkeletonGraphic/Editor.meta | 9 + .../Editor/SkeletonGraphicInspector.cs | 204 ++++++++++++++++++ .../Editor/SkeletonGraphicInspector.cs.meta | 12 ++ .../SkeletonGraphic/SkeletonGraphic.cs | 39 ++-- .../SkeletonGraphic/SkeletonGraphic.cs.meta | 6 +- 11 files changed, 280 insertions(+), 31 deletions(-) create mode 100644 spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor.meta create mode 100644 spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs create mode 100644 spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs.meta diff --git a/spine-unity/Assets/spine-unity/Editor/Menus.cs b/spine-unity/Assets/spine-unity/Editor/Menus.cs index 761db5084..5e4def1d5 100644 --- a/spine-unity/Assets/spine-unity/Editor/Menus.cs +++ b/spine-unity/Assets/spine-unity/Editor/Menus.cs @@ -61,17 +61,24 @@ public class Menus { Selection.activeObject = asset; } - [MenuItem("GameObject/Create Other/Spine SkeletonRenderer")] + [MenuItem("GameObject/Spine/SkeletonRenderer", false, 10)] static public void CreateSkeletonRendererGameObject () { - GameObject gameObject = new GameObject("New SkeletonRenderer", typeof(SkeletonRenderer)); - EditorUtility.FocusProjectWindow(); - Selection.activeObject = gameObject; + CreateSpineGameObject("New SkeletonRenderer"); } - [MenuItem("GameObject/Create Other/Spine SkeletonAnimation")] + [MenuItem("GameObject/Spine/SkeletonAnimation", false, 10)] static public void CreateSkeletonAnimationGameObject () { - GameObject gameObject = new GameObject("New SkeletonAnimation", typeof(SkeletonAnimation)); + CreateSpineGameObject("New SkeletonAnimation"); + } + + static public void CreateSpineGameObject (string name) where T : MonoBehaviour { + var parentGameObject = Selection.activeObject as GameObject; + var parentTransform = parentGameObject == null ? null : parentGameObject.transform; + + var gameObject = new GameObject("New SkeletonRenderer", typeof(T)); + gameObject.transform.SetParent(parentTransform, false); EditorUtility.FocusProjectWindow(); Selection.activeObject = gameObject; + EditorGUIUtility.PingObject(Selection.activeObject); } } diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs index 9cef3e48d..b2723b504 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -59,7 +59,7 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector { if (wasAnimationNameChanged) { if (!Application.isPlaying) { - component.state.ClearTrack(0); + if (component.state != null) component.state.ClearTrack(0); component.skeleton.SetToSetupPose(); } diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs index 3bcc39e08..3fd26bbd7 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs @@ -138,7 +138,11 @@ public class SkeletonRendererInspector : Editor { EditorGUILayout.PropertyField(immutableTriangles, new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility")); EditorGUILayout.Space(); - EditorGUILayout.PropertyField(zSpacing); + + const float MinZSpacing = 0f; + const float MaxZSpacing = 0.1f; + EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing); + EditorGUILayout.PropertyField(normals); EditorGUILayout.PropertyField(tangents); EditorGUILayout.PropertyField(front); diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ArraysSimpleMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ArraysSimpleMeshGenerator.cs index e2531ddc1..7343f1dff 100644 --- a/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ArraysSimpleMeshGenerator.cs +++ b/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ArraysSimpleMeshGenerator.cs @@ -35,7 +35,7 @@ using System.Collections; namespace Spine.Unity { public class ArraysSimpleMeshGenerator : ISimpleMeshGenerator { #region Settings - protected float scale; + protected float scale = 1f; public float Scale { get { return scale; } set { scale = value; } @@ -53,6 +53,9 @@ namespace Spine.Unity { private int[] triangles; #endregion + private Mesh lastGeneratedMesh; + public Mesh LastGeneratedMesh { get { return lastGeneratedMesh; } } + public Mesh GenerateMesh (Skeleton skeleton) { int totalVertexCount = 0; // size of vertex arrays int totalTriangleCount = 0; // size of index array @@ -309,10 +312,12 @@ namespace Spine.Unity { mesh.uv = uvs; Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin) * scale; - mesh.bounds = new Bounds(meshBoundsMin + meshBoundsExtents * 0.5f, meshBoundsExtents); + Vector3 meshCenter = (meshBoundsMin * scale) + meshBoundsExtents * 0.5f; + mesh.bounds = new Bounds(meshCenter, meshBoundsExtents); mesh.SetTriangles(triangles, 0); + lastGeneratedMesh = mesh; return mesh; } diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ISimpleMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ISimpleMeshGenerator.cs index efab03eec..32ed3db51 100644 --- a/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ISimpleMeshGenerator.cs +++ b/spine-unity/Assets/spine-unity/Mesh Generation/Simple/ISimpleMeshGenerator.cs @@ -6,5 +6,6 @@ public interface ISimpleMeshGenerator { float Scale { set; } UnityEngine.Mesh GenerateMesh (Spine.Skeleton skeleton); + UnityEngine.Mesh LastGeneratedMesh { get; } } } diff --git a/spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples/VertexHelperSpineMeshGenerator.cs b/spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples/VertexHelperSpineMeshGenerator.cs index d24754dd6..a9e66cbed 100644 --- a/spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples/VertexHelperSpineMeshGenerator.cs +++ b/spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples/VertexHelperSpineMeshGenerator.cs @@ -45,6 +45,9 @@ namespace Spine.Unity { public int CurrentVertexCount { get { return this.positions.Count; } } + private Mesh lastGeneratedMesh; + public Mesh LastGeneratedMesh { get { return lastGeneratedMesh; } } + public Mesh GenerateMesh (Skeleton skeleton) { skeletonColor.r = skeleton.r; skeletonColor.g = skeleton.g; @@ -59,6 +62,7 @@ namespace Spine.Unity { Mesh currentMesh = doubleBufferedMesh.GetNextMesh(); FillMesh(currentMesh); + lastGeneratedMesh = currentMesh; return currentMesh; } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor.meta b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor.meta new file mode 100644 index 000000000..12c3774fd --- /dev/null +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 78bba472871bc624f8930d51dea6dd3d +folderAsset: yes +timeCreated: 1455570938 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs new file mode 100644 index 000000000..465266144 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs @@ -0,0 +1,204 @@ +using UnityEngine; +using System.Collections; + +using UnityEditor; +using Spine; + +[CustomEditor(typeof(SkeletonGraphic))] +public class SkeletonGraphicInspector : Editor { + SerializedProperty material_, color_; + SerializedProperty skeletonDataAsset_, initialSkinName_; + SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_; + SerializedProperty raycastTarget_; + + SkeletonGraphic thisSkeletonGraphic; + + void OnEnable () { + var so = this.serializedObject; + thisSkeletonGraphic = target as SkeletonGraphic; + + // MaskableGraphic + material_ = so.FindProperty("m_Material"); + color_ = so.FindProperty("m_Color"); + raycastTarget_ = so.FindProperty("m_RaycastTarget"); + + // SkeletonRenderer + skeletonDataAsset_ = so.FindProperty("skeletonDataAsset"); + initialSkinName_ = so.FindProperty("initialSkinName"); + + // SkeletonAnimation + startingAnimation_ = so.FindProperty("startingAnimation"); + startingLoop_ = so.FindProperty("startingLoop"); + timeScale_ = so.FindProperty("timeScale"); + freeze_ = so.FindProperty("freeze"); + } + + + public override void OnInspectorGUI () { + + var s = thisSkeletonGraphic; + s.skeletonDataAsset = SkeletonGraphicInspector.ObjectField(skeletonDataAsset_); + s.material = SkeletonGraphicInspector.ObjectField(material_); + + EditorGUI.BeginChangeCheck(); + thisSkeletonGraphic.color = EditorGUILayout.ColorField(color_.displayName, color_.colorValue); + if (EditorGUI.EndChangeCheck()) + SkeletonGraphicInspector.ForceUpdateHack(thisSkeletonGraphic.transform); + + if (thisSkeletonGraphic.skeletonDataAsset == null) { + EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info); + serializedObject.ApplyModifiedProperties(); + serializedObject.Update(); + return; + } + + EditorGUILayout.Space(); + EditorGUILayout.PropertyField(initialSkinName_); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(startingAnimation_); + s.startingLoop = SkeletonGraphicInspector.BoolField(startingLoop_); + s.timeScale = EditorGUILayout.FloatField(timeScale_.displayName, timeScale_.floatValue); + EditorGUILayout.Space(); + s.freeze = SkeletonGraphicInspector.BoolField(freeze_); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("UI", EditorStyles.boldLabel); + s.raycastTarget = SkeletonGraphicInspector.BoolField(raycastTarget_); + + } + + #region HAX - Thanks, Unity + // colors weren't updating in realtime in the custom inspector. + // Why the hell do I have to do this?? + /// Use this when scene repaint and proper explicit update methods don't work. + public static void ForceUpdateHack (Transform t) { + var origValue = t.localScale; + t.localScale = new Vector3(11f, 22f, 33f); + t.localScale = origValue; + } + + // Hack for Unity 5.3 problem with PropertyField + public static T ObjectField (SerializedProperty property) where T : UnityEngine.Object { + return (T)EditorGUILayout.ObjectField(property.displayName, property.objectReferenceValue, typeof(T), false); + } + + public static bool BoolField (SerializedProperty property) { + return EditorGUILayout.Toggle(property.displayName, property.boolValue); + } + #endregion + + #region Menus + // Add a menu item called "Double Mass" to a Rigidbody's context menu. + [MenuItem ("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")] + static void MatchRectTransformWithBounds (MenuCommand command) { + var skeletonGraphic = (SkeletonGraphic)command.context; + var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh; + + var bounds = mesh.bounds; + var size = bounds.size; + var center = bounds.center; + var p = new Vector2( + 0.5f - (center.x / size.x), + 0.5f - (center.y / size.y) + ); + + skeletonGraphic.rectTransform.sizeDelta = size; + skeletonGraphic.rectTransform.pivot = p; + } + + public static Material DefaultSkeletonGraphicMaterial { + get { + var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material"); if (guids.Length <= 0) return null; + var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]); if (string.IsNullOrEmpty(firstAssetPath)) return null; + var firstMaterial = AssetDatabase.LoadAssetAtPath(firstAssetPath); + return firstMaterial; + } + } + + [MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 10)] + static public void SkeletonGraphicCreateMenuItem () { + var parentGameObject = Selection.activeObject as GameObject; + var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent(); + + if (parentTransform == null) { + Debug.LogWarning("Your new SkeletonGraphic will not be visible until it is placed under a Canvas"); + } + + var gameObject = NewSkeletonGraphicGameObject("New SkeletonGraphic"); + gameObject.transform.SetParent(parentTransform, false); + EditorUtility.FocusProjectWindow(); + Selection.activeObject = gameObject; + EditorGUIUtility.PingObject(Selection.activeObject); + } + + [MenuItem("Assets/Spine/Instantiate (UnityUI)", false, 0)] + static void InstantiateSkeletonGraphic () { + Object[] arr = Selection.objects; + foreach (Object o in arr) { + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + string skinName = EditorPrefs.GetString(guid + "_lastSkin", ""); + + InstantiateSkeletonGraphic((SkeletonDataAsset)o, skinName); + SceneView.RepaintAll(); + } + } + + [MenuItem("Assets/Spine/Instantiate (UnityUI)", true, 0)] + static bool ValidateInstantiateSkeletonGraphic () { + Object[] arr = Selection.objects; + + if (arr.Length == 0) + return false; + + foreach (var selected in arr) { + if (selected.GetType() != typeof(SkeletonDataAsset)) + return false; + } + + return true; + } + + public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, string skinName) { + return InstantiateSkeletonGraphic(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); + } + + public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { + string spineGameObjectName = string.Format("SkeletonGraphic ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); + var go = NewSkeletonGraphicGameObject(spineGameObjectName); + var graphic = go.GetComponent(); + graphic.skeletonDataAsset = skeletonDataAsset; + + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + + if (data == null) { + for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { + string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); + skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + } + + data = skeletonDataAsset.GetSkeletonData(true); + } + + if (skin == null) + skin = data.DefaultSkin; + + if (skin == null) + skin = data.Skins.Items[0]; + + graphic.Initialize(false); + graphic.Skeleton.SetSkin(skin); + graphic.initialSkinName = skin.Name; + graphic.Skeleton.UpdateWorldTransform(); + graphic.UpdateMesh(); + + return graphic; + } + + static GameObject NewSkeletonGraphicGameObject (string gameObjectName) { + var go = new GameObject(gameObjectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic)); + var graphic = go.GetComponent(); + graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial; + return go; + } + #endregion +} \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs.meta b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs.meta new file mode 100644 index 000000000..5fced2e97 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0d81cc76b52fcdf499b2db252a317726 +timeCreated: 1455570945 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs index a7a9ca9bd..4d9b41cea 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs @@ -34,20 +34,16 @@ using System.Collections; using UnityEngine.UI; using Spine; -public delegate void SkeletonGraphicDelegate (SkeletonGraphic skeletonGraphic); - [ExecuteInEditMode, RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent] [AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")] public class SkeletonGraphic : MaskableGraphic { #region Inspector - [Header("Skeleton Renderer")] public SkeletonDataAsset skeletonDataAsset; [SpineSkin(dataField:"skeletonDataAsset")] public string initialSkinName = "default"; - [Header("Skeleton Animation")] [SpineAnimation(dataField:"skeletonDataAsset")] public string startingAnimation; public bool startingLoop; @@ -74,11 +70,13 @@ public class SkeletonGraphic : MaskableGraphic { } else { if (freeze) return; skeleton.SetToSetupPose(); - skeleton.PoseWithAnimation(startingAnimation, 0f, false); + if (!string.IsNullOrEmpty(startingAnimation)) { + skeleton.PoseWithAnimation(startingAnimation, 0f, false); + } } } else { if (skeletonDataAsset != null) - Initialize(false); + Initialize(true); } } @@ -93,16 +91,11 @@ public class SkeletonGraphic : MaskableGraphic { #endregion #region Internals - protected Spine.Unity.ISimpleMeshGenerator spineMeshGenerator; // This is any object that can give you a mesh when you give it a skeleton to render. - protected Skeleton skeleton; - protected Spine.AnimationState state; - // This is used by the UI system to determine what to put in the MaterialPropertyBlock. public override Texture mainTexture { get { - if (skeletonDataAsset == null) return null; // Fail loudly when incorrectly set up. - return skeletonDataAsset.atlasAssets[0].materials[0].mainTexture; + return skeletonDataAsset == null ? null : skeletonDataAsset.atlasAssets[0].materials[0].mainTexture; } } @@ -153,14 +146,22 @@ public class SkeletonGraphic : MaskableGraphic { #endregion #region API + protected Skeleton skeleton; public Skeleton Skeleton { get { return skeleton; } } - public Spine.AnimationState AnimationState { get { return state; } } public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } } public bool IsValid { get { return skeleton != null; } } - public event SkeletonGraphicDelegate UpdateLocal; - public event SkeletonGraphicDelegate UpdateWorld; - public event SkeletonGraphicDelegate UpdateComplete; + protected Spine.AnimationState state; + public Spine.AnimationState AnimationState { get { return state; } } + + // This is any object that can give you a mesh when you give it a skeleton to render. + protected Spine.Unity.ISimpleMeshGenerator spineMeshGenerator; + public Spine.Unity.ISimpleMeshGenerator SpineMeshGenerator { get { return this.spineMeshGenerator; } } + + public delegate void UpdateDelegate (SkeletonGraphic skeletonGraphic); + public event UpdateDelegate UpdateLocal; + public event UpdateDelegate UpdateWorld; + public event UpdateDelegate UpdateComplete; public void Clear () { skeleton = null; @@ -194,12 +195,12 @@ public class SkeletonGraphic : MaskableGraphic { state.SetAnimation(0, startingAnimation, startingLoop); } - public void UpdateMesh () { - //Debug.Log("update mesh"); if (this.IsValid) { skeleton.SetColor(this.color); - spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas? + if (canvas != null) + spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas? + canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton)); this.UpdateMaterial(); } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs.meta b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs.meta index d5c0fc204..e44ab782a 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs.meta +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs.meta @@ -1,10 +1,12 @@ fileFormatVersion: 2 guid: d85b887af7e6c3f45a2e2d2920d641bc -timeCreated: 1455074790 +timeCreated: 1455576193 licenseType: Free MonoImporter: serializedVersion: 2 - defaultReferences: [] + defaultReferences: + - m_Material: {fileID: 2100000, guid: b66cf7a186d13054989b33a5c90044e4, type: 2} + - skeletonDataAsset: {instanceID: 0} executionOrder: 0 icon: {instanceID: 0} userData: