Spine.Unity namespace and some editor tweaks.

This commit is contained in:
pharan 2016-03-19 16:54:43 +08:00
parent 50a0960a7d
commit 9e8fdeef6c
9 changed files with 1226 additions and 1174 deletions

View File

@ -28,126 +28,124 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
//#define BAKE_ALL_BUTTON
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.IO; using System.IO;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using Spine; using Spine;
namespace Spine.Unity {
[CustomEditor(typeof(AtlasAsset))]
public class AtlasAssetInspector : Editor {
private SerializedProperty atlasFile, materials;
private AtlasAsset atlasAsset;
private List<bool> baked;
private List<GameObject> bakedObjects;
[CustomEditor(typeof(AtlasAsset))] void OnEnable () {
public class AtlasAssetInspector : Editor { SpineEditorUtilities.ConfirmInitialization();
private SerializedProperty atlasFile, materials; atlasFile = serializedObject.FindProperty("atlasFile");
private AtlasAsset atlasAsset; materials = serializedObject.FindProperty("materials");
private List<bool> baked; atlasAsset = (AtlasAsset)target;
private List<GameObject> bakedObjects; UpdateBakedList();
}
void OnEnable () { void UpdateBakedList () {
SpineEditorUtilities.ConfirmInitialization(); AtlasAsset asset = (AtlasAsset)target;
atlasFile = serializedObject.FindProperty("atlasFile"); baked = new List<bool>();
materials = serializedObject.FindProperty("materials"); bakedObjects = new List<GameObject>();
atlasAsset = (AtlasAsset)target; if (atlasFile.objectReferenceValue != null) {
UpdateBakedList(); Atlas atlas = asset.GetAtlas();
} FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
void UpdateBakedList () { string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
AtlasAsset asset = (AtlasAsset)target; string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
baked = new List<bool>(); string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
bakedObjects = new List<GameObject>();
if (atlasFile.objectReferenceValue != null) {
Atlas atlas = asset.GetAtlas();
FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
for (int i = 0; i < regions.Count; i++) { for (int i = 0; i < regions.Count; i++) {
AtlasRegion region = regions[i]; AtlasRegion region = regions[i];
string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/"); string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/");
GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject));
baked.Add(prefab != null); baked.Add(prefab != null);
bakedObjects.Add(prefab); bakedObjects.Add(prefab);
}
} }
} }
}
override public void OnInspectorGUI () {
serializedObject.Update();
AtlasAsset asset = (AtlasAsset)target;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(atlasFile);
EditorGUILayout.PropertyField(materials, true);
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
override public void OnInspectorGUI () { if (materials.arraySize == 0) {
serializedObject.Update(); EditorGUILayout.LabelField(new GUIContent("Error: Missing materials", SpineEditorUtilities.Icons.warning));
AtlasAsset asset = (AtlasAsset)target;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(atlasFile);
EditorGUILayout.PropertyField(materials, true);
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
if (materials.arraySize == 0) {
EditorGUILayout.LabelField(new GUIContent("Error: Missing materials", SpineEditorUtilities.Icons.warning));
return;
}
for (int i = 0; i < materials.arraySize; i++) {
SerializedProperty prop = materials.GetArrayElementAtIndex(i);
Material mat = (Material)prop.objectReferenceValue;
if (mat == null) {
EditorGUILayout.LabelField(new GUIContent("Error: Materials cannot be null", SpineEditorUtilities.Icons.warning));
return; return;
} }
}
if (atlasFile.objectReferenceValue != null) { for (int i = 0; i < materials.arraySize; i++) {
Atlas atlas = asset.GetAtlas(); SerializedProperty prop = materials.GetArrayElementAtIndex(i);
FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); Material mat = (Material)prop.objectReferenceValue;
List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas); if (mat == null) {
EditorGUILayout.LabelField(new GUIContent("Region Baking", SpineEditorUtilities.Icons.unityIcon)); EditorGUILayout.LabelField(new GUIContent("Error: Materials cannot be null", SpineEditorUtilities.Icons.warning));
EditorGUI.indentLevel++; return;
AtlasPage lastPage = null;
for (int i = 0; i < regions.Count; i++) {
if (lastPage != regions[i].page) {
if (lastPage != null) {
EditorGUILayout.Separator();
EditorGUILayout.Separator();
}
lastPage = regions[i].page;
Material mat = ((Material)lastPage.rendererObject);
if (mat != null) {
GUILayout.BeginHorizontal();
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250));
EditorGUI.EndDisabledGroup();
}
GUILayout.EndHorizontal();
} else {
EditorGUILayout.LabelField(new GUIContent("Page missing material!", SpineEditorUtilities.Icons.warning));
}
} }
GUILayout.BeginHorizontal(); }
{
//EditorGUILayout.ToggleLeft(baked[i] ? "" : regions[i].name, baked[i]); if (atlasFile.objectReferenceValue != null) {
bool result = baked[i] ? EditorGUILayout.ToggleLeft("", baked[i], GUILayout.Width(24)) : EditorGUILayout.ToggleLeft(" " + regions[i].name, baked[i]); Atlas atlas = asset.GetAtlas();
if(baked[i]){ FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
EditorGUILayout.ObjectField(bakedObjects[i], typeof(GameObject), false, GUILayout.Width(250)); List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
EditorGUILayout.LabelField(new GUIContent("Region Baking", SpineEditorUtilities.Icons.unityIcon));
EditorGUI.indentLevel++;
AtlasPage lastPage = null;
for (int i = 0; i < regions.Count; i++) {
if (lastPage != regions[i].page) {
if (lastPage != null) {
EditorGUILayout.Separator();
EditorGUILayout.Separator();
}
lastPage = regions[i].page;
Material mat = ((Material)lastPage.rendererObject);
if (mat != null) {
GUILayout.BeginHorizontal();
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250));
EditorGUI.EndDisabledGroup();
}
GUILayout.EndHorizontal();
} else {
EditorGUILayout.LabelField(new GUIContent("Page missing material!", SpineEditorUtilities.Icons.warning));
}
} }
if (result && !baked[i]) { GUILayout.BeginHorizontal();
//bake {
baked[i] = true; //EditorGUILayout.ToggleLeft(baked[i] ? "" : regions[i].name, baked[i]);
bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]); bool result = baked[i] ? EditorGUILayout.ToggleLeft("", baked[i], GUILayout.Width(24)) : EditorGUILayout.ToggleLeft(" " + regions[i].name, baked[i]);
EditorGUIUtility.PingObject(bakedObjects[i]); if(baked[i]){
} else if (!result && baked[i]) { EditorGUILayout.ObjectField(bakedObjects[i], typeof(GameObject), false, GUILayout.Width(250));
//unbake }
bool unbakeResult = EditorUtility.DisplayDialog("Delete Baked Region", "Do you want to delete the prefab for " + regions[i].name, "Yes", "Cancel"); if (result && !baked[i]) {
switch (unbakeResult) { //bake
baked[i] = true;
bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]);
EditorGUIUtility.PingObject(bakedObjects[i]);
} else if (!result && baked[i]) {
//unbake
bool unbakeResult = EditorUtility.DisplayDialog("Delete Baked Region", "Do you want to delete the prefab for " + regions[i].name, "Yes", "Cancel");
switch (unbakeResult) {
case true: case true:
//delete //delete
string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
@ -160,18 +158,62 @@ public class AtlasAssetInspector : Editor {
case false: case false:
//do nothing //do nothing
break; break;
}
} }
} }
GUILayout.EndHorizontal();
} }
GUILayout.EndHorizontal(); EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
if (serializedObject.ApplyModifiedProperties() || #if BAKE_ALL_BUTTON
(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed") // Check state
) { bool allBaked = true;
asset.Reset(); bool allUnbaked = true;
for (int i = 0; i < regions.Count; i++) {
allBaked &= baked[i];
allUnbaked &= !baked[i];
}
if (!allBaked && GUILayout.Button("Bake All")) {
for (int i = 0; i < regions.Count; i++) {
if (!baked[i]) {
baked[i] = true;
bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]);
}
}
} else if (!allUnbaked && GUILayout.Button("Unbake All")) {
bool unbakeResult = EditorUtility.DisplayDialog("Delete All Baked Regions", "Are you sure you want to unbake all region prefabs? This cannot be undone.", "Yes", "Cancel");
switch (unbakeResult) {
case true:
//delete
for (int i = 0; i < regions.Count; i++) {
if (baked[i]) {
string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/");
AssetDatabase.DeleteAsset(bakedPrefabPath);
baked[i] = false;
}
}
break;
case false:
//do nothing
break;
}
}
#endif
}
if (serializedObject.ApplyModifiedProperties() ||
(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
) {
asset.Reset();
}
} }
} }
} }

View File

@ -28,57 +28,57 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
using System; using System;
using System.IO; using System.IO;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using Spine;
public class Menus { namespace Spine.Unity {
[MenuItem("Assets/Create/Spine Atlas")] public static class Menus {
static public void CreateAtlas () { [MenuItem("Assets/Create/Spine Atlas")]
CreateAsset<AtlasAsset>("New Atlas"); static public void CreateAtlas () {
} CreateAsset<AtlasAsset>("New Atlas");
[MenuItem("Assets/Create/Spine SkeletonData")]
static public void CreateSkeletonData () {
CreateAsset<SkeletonDataAsset>("New SkeletonData");
}
static private void CreateAsset <T> (String name) where T : ScriptableObject {
var dir = "Assets/";
var selected = Selection.activeObject;
if (selected != null) {
var assetDir = AssetDatabase.GetAssetPath(selected.GetInstanceID());
if (assetDir.Length > 0 && Directory.Exists(assetDir))
dir = assetDir + "/";
} }
ScriptableObject asset = ScriptableObject.CreateInstance<T>();
AssetDatabase.CreateAsset(asset, dir + name + ".asset");
AssetDatabase.SaveAssets();
EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
}
[MenuItem("GameObject/Spine/SkeletonRenderer", false, 10)] [MenuItem("Assets/Create/Spine SkeletonData")]
static public void CreateSkeletonRendererGameObject () { static public void CreateSkeletonData () {
CreateSpineGameObject<SkeletonRenderer>("New SkeletonRenderer"); CreateAsset<SkeletonDataAsset>("New SkeletonData");
} }
[MenuItem("GameObject/Spine/SkeletonAnimation", false, 10)] static private void CreateAsset <T> (String name) where T : ScriptableObject {
static public void CreateSkeletonAnimationGameObject () { var dir = "Assets/";
CreateSpineGameObject<SkeletonAnimation>("New SkeletonAnimation"); var selected = Selection.activeObject;
} if (selected != null) {
var assetDir = AssetDatabase.GetAssetPath(selected.GetInstanceID());
if (assetDir.Length > 0 && Directory.Exists(assetDir))
dir = assetDir + "/";
}
ScriptableObject asset = ScriptableObject.CreateInstance<T>();
AssetDatabase.CreateAsset(asset, dir + name + ".asset");
AssetDatabase.SaveAssets();
EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
}
static public void CreateSpineGameObject<T> (string name) where T : MonoBehaviour { [MenuItem("GameObject/Spine/SkeletonRenderer", false, 10)]
var parentGameObject = Selection.activeObject as GameObject; static public void CreateSkeletonRendererGameObject () {
var parentTransform = parentGameObject == null ? null : parentGameObject.transform; CreateSpineGameObject<SkeletonRenderer>("New SkeletonRenderer");
}
var gameObject = new GameObject("New SkeletonRenderer", typeof(T)); [MenuItem("GameObject/Spine/SkeletonAnimation", false, 10)]
gameObject.transform.SetParent(parentTransform, false); static public void CreateSkeletonAnimationGameObject () {
EditorUtility.FocusProjectWindow(); CreateSpineGameObject<SkeletonAnimation>("New SkeletonAnimation");
Selection.activeObject = gameObject; }
EditorGUIUtility.PingObject(Selection.activeObject);
static public void CreateSpineGameObject<T> (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);
}
} }
} }

View File

@ -28,83 +28,85 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
using System; using System;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using Spine; using Spine;
[CustomEditor(typeof(SkeletonAnimation))] namespace Spine.Unity {
public class SkeletonAnimationInspector : SkeletonRendererInspector {
protected SerializedProperty animationName, loop, timeScale, autoReset; [CustomEditor(typeof(SkeletonAnimation))]
protected bool m_isPrefab; public class SkeletonAnimationInspector : SkeletonRendererInspector {
protected bool wasAnimationNameChanged; protected SerializedProperty animationName, loop, timeScale, autoReset;
protected bool m_isPrefab;
protected bool wasAnimationNameChanged;
protected override void OnEnable () { protected override void OnEnable () {
base.OnEnable(); base.OnEnable();
animationName = serializedObject.FindProperty("_animationName"); animationName = serializedObject.FindProperty("_animationName");
loop = serializedObject.FindProperty("loop"); loop = serializedObject.FindProperty("loop");
timeScale = serializedObject.FindProperty("timeScale"); timeScale = serializedObject.FindProperty("timeScale");
if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
m_isPrefab = true; m_isPrefab = true;
}
protected override void DrawInspectorGUI () {
base.DrawInspectorGUI();
SkeletonAnimation component = (SkeletonAnimation)target;
if (!component.valid)
return;
if (wasAnimationNameChanged) {
if (!Application.isPlaying) {
if (component.state != null) component.state.ClearTrack(0);
component.skeleton.SetToSetupPose();
}
Spine.Animation animationToUse = component.skeleton.Data.FindAnimation(animationName.stringValue);
if (!Application.isPlaying) {
if (animationToUse != null) animationToUse.Apply(component.skeleton, 0f, 0f, false, null);
component.Update();
component.LateUpdate();
SceneView.RepaintAll();
} else {
if (animationToUse != null)
component.state.SetAnimation(0, animationToUse, loop.boolValue);
else
component.state.ClearTrack(0);
}
wasAnimationNameChanged = false;
} }
// Reflect animationName serialized property in the inspector even if SetAnimation API was used. protected override void DrawInspectorGUI () {
if (Application.isPlaying) { base.DrawInspectorGUI();
TrackEntry current = component.state.GetCurrent(0);
if (current != null) { SkeletonAnimation component = (SkeletonAnimation)target;
if (component.AnimationName != animationName.stringValue) { if (!component.valid)
animationName.stringValue = current.Animation.Name; return;
if (wasAnimationNameChanged) {
if (!Application.isPlaying) {
if (component.state != null) component.state.ClearTrack(0);
component.skeleton.SetToSetupPose();
}
Spine.Animation animationToUse = component.skeleton.Data.FindAnimation(animationName.stringValue);
if (!Application.isPlaying) {
if (animationToUse != null) animationToUse.Apply(component.skeleton, 0f, 0f, false, null);
component.Update();
component.LateUpdate();
SceneView.RepaintAll();
} else {
if (animationToUse != null)
component.state.SetAnimation(0, animationToUse, loop.boolValue);
else
component.state.ClearTrack(0);
}
wasAnimationNameChanged = false;
}
// Reflect animationName serialized property in the inspector even if SetAnimation API was used.
if (Application.isPlaying) {
TrackEntry current = component.state.GetCurrent(0);
if (current != null) {
if (component.AnimationName != animationName.stringValue) {
animationName.stringValue = current.Animation.Name;
}
} }
} }
}
EditorGUILayout.Space(); EditorGUILayout.Space();
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(animationName); EditorGUILayout.PropertyField(animationName);
wasAnimationNameChanged |= EditorGUI.EndChangeCheck(); wasAnimationNameChanged |= EditorGUI.EndChangeCheck();
EditorGUILayout.PropertyField(loop);
EditorGUILayout.PropertyField(timeScale);
component.timeScale = Math.Max(component.timeScale, 0);
EditorGUILayout.Space(); EditorGUILayout.PropertyField(loop);
EditorGUILayout.PropertyField(timeScale);
component.timeScale = Math.Max(component.timeScale, 0);
if (!m_isPrefab) { EditorGUILayout.Space();
if (component.GetComponent<SkeletonUtility>() == null) {
if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) { if (!m_isPrefab) {
component.gameObject.AddComponent<SkeletonUtility>(); if (component.GetComponent<SkeletonUtility>() == null) {
if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
component.gameObject.AddComponent<SkeletonUtility>();
}
} }
} }
} }

View File

@ -1,5 +1,3 @@
/***************************************************************************** /*****************************************************************************
* SkeletonAnimatorInspector created by Mitch Thompson * SkeletonAnimatorInspector created by Mitch Thompson
* Full irrevocable rights and permissions granted to Esoteric Software * Full irrevocable rights and permissions granted to Esoteric Software
@ -7,37 +5,38 @@
using System; using System;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using Spine;
[CustomEditor(typeof(SkeletonAnimator))] namespace Spine.Unity {
public class SkeletonAnimatorInspector : SkeletonRendererInspector { [CustomEditor(typeof(SkeletonAnimator))]
protected SerializedProperty layerMixModes; public class SkeletonAnimatorInspector : SkeletonRendererInspector {
protected bool isPrefab; protected SerializedProperty layerMixModes;
protected override void OnEnable () { protected bool isPrefab;
base.OnEnable(); protected override void OnEnable () {
layerMixModes = serializedObject.FindProperty("layerMixModes"); base.OnEnable();
layerMixModes = serializedObject.FindProperty("layerMixModes");
if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
isPrefab = true; isPrefab = true;
} }
protected override void DrawInspectorGUI () { protected override void DrawInspectorGUI () {
base.DrawInspectorGUI(); base.DrawInspectorGUI();
EditorGUILayout.PropertyField(layerMixModes, true); EditorGUILayout.PropertyField(layerMixModes, true);
SkeletonAnimator component = (SkeletonAnimator)target; SkeletonAnimator component = (SkeletonAnimator)target;
if (!component.valid) if (!component.valid)
return; return;
EditorGUILayout.Space(); EditorGUILayout.Space();
if (!isPrefab) { if (!isPrefab) {
if (component.GetComponent<SkeletonUtility>() == null) { if (component.GetComponent<SkeletonUtility>() == null) {
if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) { if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
component.gameObject.AddComponent<SkeletonUtility>(); component.gameObject.AddComponent<SkeletonUtility>();
}
} }
} }
} }
} }
} }

View File

@ -33,118 +33,121 @@ using System;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
[CustomEditor(typeof(SkeletonRenderer))] namespace Spine.Unity {
public class SkeletonRendererInspector : Editor {
protected static bool advancedFoldout; [CustomEditor(typeof(SkeletonRenderer))]
public class SkeletonRendererInspector : Editor {
protected static bool advancedFoldout;
protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, submeshSeparators, front, zSpacing; protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, submeshSeparators, front, zSpacing;
protected SpineInspectorUtility.SerializedSortingProperties sortingProperties; protected SpineInspectorUtility.SerializedSortingProperties sortingProperties;
protected virtual void OnEnable () { protected virtual void OnEnable () {
SpineEditorUtilities.ConfirmInitialization(); SpineEditorUtilities.ConfirmInitialization();
skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset"); skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset");
initialSkinName = serializedObject.FindProperty("initialSkinName"); initialSkinName = serializedObject.FindProperty("initialSkinName");
normals = serializedObject.FindProperty("calculateNormals"); normals = serializedObject.FindProperty("calculateNormals");
tangents = serializedObject.FindProperty("calculateTangents"); tangents = serializedObject.FindProperty("calculateTangents");
meshes = serializedObject.FindProperty("renderMeshes"); meshes = serializedObject.FindProperty("renderMeshes");
immutableTriangles = serializedObject.FindProperty("immutableTriangles"); immutableTriangles = serializedObject.FindProperty("immutableTriangles");
submeshSeparators = serializedObject.FindProperty("submeshSeparators"); submeshSeparators = serializedObject.FindProperty("submeshSeparators");
front = serializedObject.FindProperty("frontFacing"); front = serializedObject.FindProperty("frontFacing");
zSpacing = serializedObject.FindProperty("zSpacing"); zSpacing = serializedObject.FindProperty("zSpacing");
var renderer = ((SkeletonRenderer)target).GetComponent<Renderer>(); var renderer = ((SkeletonRenderer)target).GetComponent<Renderer>();
sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(renderer); sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(renderer);
} }
protected virtual void DrawInspectorGUI () { protected virtual void DrawInspectorGUI () {
SkeletonRenderer component = (SkeletonRenderer)target; SkeletonRenderer component = (SkeletonRenderer)target;
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(skeletonDataAsset); EditorGUILayout.PropertyField(skeletonDataAsset);
float reloadWidth = GUI.skin.label.CalcSize(new GUIContent("Reload")).x + 20; float reloadWidth = GUI.skin.label.CalcSize(new GUIContent("Reload")).x + 20;
if (GUILayout.Button("Reload", GUILayout.Width(reloadWidth))) { if (GUILayout.Button("Reload", GUILayout.Width(reloadWidth))) {
if (component.skeletonDataAsset != null) { if (component.skeletonDataAsset != null) {
foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) { foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) {
if (aa != null) if (aa != null)
aa.Reset(); aa.Reset();
}
component.skeletonDataAsset.Reset();
} }
component.Initialize(true);
component.skeletonDataAsset.Reset();
} }
component.Initialize(true); EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndHorizontal();
if (!component.valid) { if (!component.valid) {
component.Initialize(true); component.Initialize(true);
component.LateUpdate(); component.LateUpdate();
if (!component.valid) if (!component.valid)
return; return;
}
// Initial skin name.
{
String[] skins = new String[component.skeleton.Data.Skins.Count];
int skinIndex = 0;
for (int i = 0; i < skins.Length; i++) {
String skinNameString = component.skeleton.Data.Skins.Items[i].Name;
skins[i] = skinNameString;
if (skinNameString == initialSkinName.stringValue)
skinIndex = i;
} }
skinIndex = EditorGUILayout.Popup("Initial Skin", skinIndex, skins); // Initial skin name.
initialSkinName.stringValue = skins[skinIndex]; {
} String[] skins = new String[component.skeleton.Data.Skins.Count];
int skinIndex = 0;
EditorGUILayout.Space(); for (int i = 0; i < skins.Length; i++) {
String skinNameString = component.skeleton.Data.Skins.Items[i].Name;
// Sorting Layers skins[i] = skinNameString;
{ if (skinNameString == initialSkinName.stringValue)
SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true); skinIndex = i;
}
// More Render Options...
{
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
if(advancedFoldout) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(submeshSeparators, true);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(meshes,
new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
EditorGUILayout.PropertyField(immutableTriangles,
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
EditorGUILayout.Space();
const float MinZSpacing = -0.1f;
const float MaxZSpacing = 0f;
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
if (normals != null) {
EditorGUILayout.PropertyField(normals);
EditorGUILayout.PropertyField(tangents);
} }
if (front != null) { skinIndex = EditorGUILayout.Popup("Initial Skin", skinIndex, skins);
EditorGUILayout.PropertyField(front); initialSkinName.stringValue = skins[skinIndex];
} }
EditorGUILayout.Separator(); EditorGUILayout.Space();
EditorGUI.indentLevel--;
// Sorting Layers
{
SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true);
}
// More Render Options...
{
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
if(advancedFoldout) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(submeshSeparators, true);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(meshes,
new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
EditorGUILayout.PropertyField(immutableTriangles,
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
EditorGUILayout.Space();
const float MinZSpacing = -0.1f;
const float MaxZSpacing = 0f;
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
if (normals != null) {
EditorGUILayout.PropertyField(normals);
EditorGUILayout.PropertyField(tangents);
}
if (front != null) {
EditorGUILayout.PropertyField(front);
}
EditorGUILayout.Separator();
EditorGUI.indentLevel--;
}
} }
} }
}
override public void OnInspectorGUI () { override public void OnInspectorGUI () {
serializedObject.Update(); serializedObject.Update();
DrawInspectorGUI(); DrawInspectorGUI();
if (serializedObject.ApplyModifiedProperties() || if (serializedObject.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") (UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
) { ) {
if (!Application.isPlaying) if (!Application.isPlaying)
((SkeletonRenderer)target).Initialize(true); ((SkeletonRenderer)target).Initialize(true);
}
} }
}
} }
}

View File

@ -34,59 +34,62 @@ using System.Collections;
using UnityEditor; using UnityEditor;
using System.Reflection; using System.Reflection;
public static class SpineInspectorUtility { namespace Spine.Unity {
public static class SpineInspectorUtility {
#region Sorting Layer Field Helpers
static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer");
static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer");
static MethodInfo m_SortingLayerFieldMethod; #region Sorting Layer Field Helpers
static MethodInfo SortingLayerFieldMethod { static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer");
get { static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer");
if (m_SortingLayerFieldMethod == null)
m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new System.Type[] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
return m_SortingLayerFieldMethod; static MethodInfo m_SortingLayerFieldMethod;
} static MethodInfo SortingLayerFieldMethod {
} get {
if (m_SortingLayerFieldMethod == null)
m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new System.Type[] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
public struct SerializedSortingProperties { return m_SortingLayerFieldMethod;
public SerializedObject renderer;
public SerializedProperty sortingLayerID;
public SerializedProperty sortingOrder;
public SerializedSortingProperties (Renderer r) {
renderer = new SerializedObject(r);
sortingLayerID = renderer.FindProperty("m_SortingLayerID");
sortingOrder = renderer.FindProperty("m_SortingOrder");
}
public void ApplyModifiedProperties () {
renderer.ApplyModifiedProperties();
}
}
public static void SortingPropertyFields (SerializedSortingProperties prop, bool applyModifiedProperties) {
if (applyModifiedProperties) {
EditorGUI.BeginChangeCheck();
SortingPropertyFields(prop.sortingLayerID, prop.sortingOrder);
if(EditorGUI.EndChangeCheck()) {
prop.ApplyModifiedProperties();
EditorUtility.SetDirty(prop.renderer.targetObject);
} }
} else {
SortingPropertyFields(prop.sortingLayerID, prop.sortingOrder);
}
}
public static void SortingPropertyFields (SerializedProperty m_SortingLayerID, SerializedProperty m_SortingOrder) {
if (SpineInspectorUtility.SortingLayerFieldMethod != null && m_SortingLayerID != null) {
SpineInspectorUtility.SortingLayerFieldMethod.Invoke(null, new object[] { SortingLayerLabel, m_SortingLayerID, EditorStyles.popup } );
} else {
EditorGUILayout.PropertyField(m_SortingLayerID);
} }
EditorGUILayout.PropertyField(m_SortingOrder, OrderInLayerLabel); public struct SerializedSortingProperties {
public SerializedObject renderer;
public SerializedProperty sortingLayerID;
public SerializedProperty sortingOrder;
public SerializedSortingProperties (Renderer r) {
renderer = new SerializedObject(r);
sortingLayerID = renderer.FindProperty("m_SortingLayerID");
sortingOrder = renderer.FindProperty("m_SortingOrder");
}
public void ApplyModifiedProperties () {
renderer.ApplyModifiedProperties();
}
}
public static void SortingPropertyFields (SerializedSortingProperties prop, bool applyModifiedProperties) {
if (applyModifiedProperties) {
EditorGUI.BeginChangeCheck();
SortingPropertyFields(prop.sortingLayerID, prop.sortingOrder);
if(EditorGUI.EndChangeCheck()) {
prop.ApplyModifiedProperties();
EditorUtility.SetDirty(prop.renderer.targetObject);
}
} else {
SortingPropertyFields(prop.sortingLayerID, prop.sortingOrder);
}
}
public static void SortingPropertyFields (SerializedProperty m_SortingLayerID, SerializedProperty m_SortingOrder) {
if (SpineInspectorUtility.SortingLayerFieldMethod != null && m_SortingLayerID != null) {
SpineInspectorUtility.SortingLayerFieldMethod.Invoke(null, new object[] { SortingLayerLabel, m_SortingLayerID, EditorStyles.popup } );
} else {
EditorGUILayout.PropertyField(m_SortingLayerID);
}
EditorGUILayout.PropertyField(m_SortingOrder, OrderInLayerLabel);
}
#endregion
} }
#endregion
} }

View File

@ -149,7 +149,7 @@ namespace Spine.Unity {
EditorGUIUtility.PingObject(Selection.activeObject); EditorGUIUtility.PingObject(Selection.activeObject);
} }
[MenuItem("Assets/Spine/Instantiate (UnityUI)", false, 0)] [MenuItem("Assets/Spine/Instantiate (UnityUI)", false, 10)]
static void InstantiateSkeletonGraphic () { static void InstantiateSkeletonGraphic () {
Object[] arr = Selection.objects; Object[] arr = Selection.objects;
foreach (Object o in arr) { foreach (Object o in arr) {
@ -161,7 +161,7 @@ namespace Spine.Unity {
} }
} }
[MenuItem("Assets/Spine/Instantiate (UnityUI)", true, 0)] [MenuItem("Assets/Spine/Instantiate (UnityUI)", true, 10)]
static bool ValidateInstantiateSkeletonGraphic () { static bool ValidateInstantiateSkeletonGraphic () {
Object[] arr = Selection.objects; Object[] arr = Selection.objects;

View File

@ -212,7 +212,7 @@ namespace Spine.Unity {
spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas? spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas?
canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton)); canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton));
this.UpdateMaterial(); //this.UpdateMaterial(); // TODO: This allocates memory.
} }
} }
#endregion #endregion