e
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01cbef8f24d105f4bafa9668d669e040
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -1,139 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Spine;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
|
||||
[CustomEditor(typeof(SkeletonAnimation))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SkeletonAnimationInspector : SkeletonRendererInspector {
|
||||
protected SerializedProperty animationName, loop, timeScale, autoReset;
|
||||
protected bool wasAnimationParameterChanged = false;
|
||||
protected bool requireRepaint;
|
||||
readonly GUIContent LoopLabel = new GUIContent("Loop", "Whether or not .AnimationName should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.");
|
||||
readonly GUIContent TimeScaleLabel = new GUIContent("Time Scale", "The rate at which animations progress over time. 1 means normal speed. 0.5 means 50% speed.");
|
||||
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
animationName = serializedObject.FindProperty("_animationName");
|
||||
loop = serializedObject.FindProperty("loop");
|
||||
timeScale = serializedObject.FindProperty("timeScale");
|
||||
}
|
||||
|
||||
protected override void DrawInspectorGUI (bool multi) {
|
||||
base.DrawInspectorGUI(multi);
|
||||
if (!TargetIsValid) return;
|
||||
bool sameData = SpineInspectorUtility.TargetsUseSameData(serializedObject);
|
||||
|
||||
foreach (var o in targets)
|
||||
TrySetAnimation(o as SkeletonAnimation);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
if (!sameData) {
|
||||
EditorGUILayout.DelayedTextField(animationName);
|
||||
} else {
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(animationName);
|
||||
wasAnimationParameterChanged |= EditorGUI.EndChangeCheck(); // Value used in the next update.
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(loop, LoopLabel);
|
||||
wasAnimationParameterChanged |= EditorGUI.EndChangeCheck(); // Value used in the next update.
|
||||
EditorGUILayout.PropertyField(timeScale, TimeScaleLabel);
|
||||
foreach (var o in targets) {
|
||||
var component = o as SkeletonAnimation;
|
||||
component.timeScale = Mathf.Max(component.timeScale, 0);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
SkeletonRootMotionParameter();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (!isInspectingPrefab) {
|
||||
if (requireRepaint) {
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
requireRepaint = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void TrySetAnimation (SkeletonAnimation skeletonAnimation) {
|
||||
if (skeletonAnimation == null) return;
|
||||
if (!skeletonAnimation.valid || skeletonAnimation.AnimationState == null)
|
||||
return;
|
||||
|
||||
TrackEntry current = skeletonAnimation.AnimationState.GetCurrent(0);
|
||||
if (!isInspectingPrefab) {
|
||||
string activeAnimation = (current != null) ? current.Animation.Name : "";
|
||||
bool activeLoop = (current != null) ? current.Loop : false;
|
||||
bool animationParameterChanged = this.wasAnimationParameterChanged &&
|
||||
((activeAnimation != animationName.stringValue) || (activeLoop != loop.boolValue));
|
||||
if (animationParameterChanged) {
|
||||
this.wasAnimationParameterChanged = false;
|
||||
var skeleton = skeletonAnimation.Skeleton;
|
||||
var state = skeletonAnimation.AnimationState;
|
||||
|
||||
if (!Application.isPlaying) {
|
||||
if (state != null) state.ClearTrack(0);
|
||||
skeleton.SetToSetupPose();
|
||||
}
|
||||
|
||||
Spine.Animation animationToUse = skeleton.Data.FindAnimation(animationName.stringValue);
|
||||
|
||||
if (!Application.isPlaying) {
|
||||
if (animationToUse != null) {
|
||||
skeletonAnimation.AnimationState.SetAnimation(0, animationToUse, loop.boolValue);
|
||||
}
|
||||
skeletonAnimation.Update(0);
|
||||
skeletonAnimation.LateUpdate();
|
||||
requireRepaint = true;
|
||||
} else {
|
||||
if (animationToUse != null)
|
||||
state.SetAnimation(0, animationToUse, loop.boolValue);
|
||||
else
|
||||
state.ClearTrack(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Reflect animationName serialized property in the inspector even if SetAnimation API was used.
|
||||
if (Application.isPlaying) {
|
||||
if (current != null && current.Animation != null) {
|
||||
if (skeletonAnimation.AnimationName != animationName.stringValue)
|
||||
animationName.stringValue = current.Animation.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39fbfef61034ca045b5aa80088e1e8a4
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,159 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Spine.Unity.Examples;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
|
||||
// This script is not intended for use with code. See spine-unity documentation page for additional information.
|
||||
[CustomEditor(typeof(SkeletonGraphicCustomMaterials))]
|
||||
public class SkeletonGraphicCustomMaterialsInspector : UnityEditor.Editor {
|
||||
List<SkeletonGraphicCustomMaterials.AtlasMaterialOverride> componentCustomMaterialOverrides, _customMaterialOverridesPrev;
|
||||
List<SkeletonGraphicCustomMaterials.AtlasTextureOverride> componentCustomTextureOverrides, _customTextureOverridesPrev;
|
||||
SkeletonGraphicCustomMaterials component;
|
||||
|
||||
const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
MethodInfo RemoveCustomMaterialOverrides, RemoveCustomTextureOverrides, SetCustomMaterialOverrides, SetCustomTextureOverrides;
|
||||
|
||||
#region SkeletonGraphic context menu
|
||||
[MenuItem("CONTEXT/SkeletonGraphic/Add Basic Serialized Custom Materials")]
|
||||
static void AddSkeletonGraphicCustomMaterials (MenuCommand menuCommand) {
|
||||
var skeletonGraphic = (SkeletonGraphic)menuCommand.context;
|
||||
var newComponent = skeletonGraphic.gameObject.AddComponent<SkeletonGraphicCustomMaterials>();
|
||||
Undo.RegisterCreatedObjectUndo(newComponent, "Add Basic Serialized Custom Materials");
|
||||
}
|
||||
|
||||
[MenuItem("CONTEXT/SkeletonGraphic/Add Basic Serialized Custom Materials", true)]
|
||||
static bool AddSkeletonGraphicCustomMaterials_Validate (MenuCommand menuCommand) {
|
||||
var skeletonGraphic = (SkeletonGraphic)menuCommand.context;
|
||||
return (skeletonGraphic.GetComponent<SkeletonGraphicCustomMaterials>() == null);
|
||||
}
|
||||
#endregion
|
||||
|
||||
void OnEnable () {
|
||||
Type cm = typeof(SkeletonGraphicCustomMaterials);
|
||||
RemoveCustomMaterialOverrides = cm.GetMethod("RemoveCustomMaterialOverrides", PrivateInstance);
|
||||
RemoveCustomTextureOverrides = cm.GetMethod("RemoveCustomTextureOverrides", PrivateInstance);
|
||||
SetCustomMaterialOverrides = cm.GetMethod("SetCustomMaterialOverrides", PrivateInstance);
|
||||
SetCustomTextureOverrides = cm.GetMethod("SetCustomTextureOverrides", PrivateInstance);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
component = (SkeletonGraphicCustomMaterials)target;
|
||||
var skeletonGraphic = component.skeletonGraphic;
|
||||
|
||||
// Draw the default inspector
|
||||
DrawDefaultInspector();
|
||||
|
||||
if (serializedObject.isEditingMultipleObjects)
|
||||
return;
|
||||
|
||||
if (componentCustomMaterialOverrides == null) {
|
||||
Type cm = typeof(SkeletonGraphicCustomMaterials);
|
||||
componentCustomMaterialOverrides = cm.GetField("customMaterialOverrides", PrivateInstance).GetValue(component) as List<SkeletonGraphicCustomMaterials.AtlasMaterialOverride>;
|
||||
componentCustomTextureOverrides = cm.GetField("customTextureOverrides", PrivateInstance).GetValue(component) as List<SkeletonGraphicCustomMaterials.AtlasTextureOverride>;
|
||||
if (componentCustomMaterialOverrides == null) {
|
||||
Debug.Log("Reflection failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill with current values at start
|
||||
if (_customMaterialOverridesPrev == null || _customTextureOverridesPrev == null) {
|
||||
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
|
||||
_customTextureOverridesPrev = CopyList(componentCustomTextureOverrides);
|
||||
}
|
||||
|
||||
// Compare new values with saved. If change is detected:
|
||||
// store new values, restore old values, remove overrides, restore new values, restore overrides.
|
||||
|
||||
// 1. Store new values
|
||||
var customMaterialOverridesNew = CopyList(componentCustomMaterialOverrides);
|
||||
var customTextureOverridesNew = CopyList(componentCustomTextureOverrides);
|
||||
|
||||
// Detect changes
|
||||
if (!_customMaterialOverridesPrev.SequenceEqual(customMaterialOverridesNew) ||
|
||||
!_customTextureOverridesPrev.SequenceEqual(customTextureOverridesNew)) {
|
||||
// 2. Restore old values
|
||||
componentCustomMaterialOverrides.Clear();
|
||||
componentCustomTextureOverrides.Clear();
|
||||
componentCustomMaterialOverrides.AddRange(_customMaterialOverridesPrev);
|
||||
componentCustomTextureOverrides.AddRange(_customTextureOverridesPrev);
|
||||
|
||||
// 3. Remove overrides
|
||||
RemoveCustomMaterials();
|
||||
|
||||
// 4. Restore new values
|
||||
componentCustomMaterialOverrides.Clear();
|
||||
componentCustomTextureOverrides.Clear();
|
||||
componentCustomMaterialOverrides.AddRange(customMaterialOverridesNew);
|
||||
componentCustomTextureOverrides.AddRange(customTextureOverridesNew);
|
||||
|
||||
// 5. Restore overrides
|
||||
SetCustomMaterials();
|
||||
|
||||
if (skeletonGraphic != null)
|
||||
skeletonGraphic.LateUpdate();
|
||||
}
|
||||
|
||||
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
|
||||
_customTextureOverridesPrev = CopyList(componentCustomTextureOverrides);
|
||||
|
||||
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Clear and Reapply Changes", tooltip: "Removes all non-serialized overrides in the SkeletonGraphic and reapplies the overrides on this component."))) {
|
||||
if (skeletonGraphic != null) {
|
||||
skeletonGraphic.CustomMaterialOverride.Clear();
|
||||
skeletonGraphic.CustomTextureOverride.Clear();
|
||||
RemoveCustomMaterials();
|
||||
SetCustomMaterials();
|
||||
skeletonGraphic.LateUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveCustomMaterials () {
|
||||
RemoveCustomMaterialOverrides.Invoke(component, null);
|
||||
RemoveCustomTextureOverrides.Invoke(component, null);
|
||||
}
|
||||
|
||||
void SetCustomMaterials () {
|
||||
SetCustomMaterialOverrides.Invoke(component, null);
|
||||
SetCustomTextureOverrides.Invoke(component, null);
|
||||
}
|
||||
|
||||
static List<T> CopyList<T> (List<T> list) {
|
||||
return list.GetRange(0, list.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 349bf125947e3aa4bb78690fec69ea17
|
||||
timeCreated: 1588789940
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,436 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
using Icons = SpineEditorUtilities.Icons;
|
||||
|
||||
[CustomEditor(typeof(SkeletonGraphic))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SkeletonGraphicInspector : UnityEditor.Editor {
|
||||
|
||||
const string SeparatorSlotNamesFieldName = "separatorSlotNames";
|
||||
const string ReloadButtonString = "Reload";
|
||||
protected GUIContent SkeletonDataAssetLabel;
|
||||
static GUILayoutOption reloadButtonWidth;
|
||||
static GUILayoutOption ReloadButtonWidth { get { return reloadButtonWidth = reloadButtonWidth ?? GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonString)).x + 20); } }
|
||||
static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButton; } }
|
||||
|
||||
SerializedProperty material, color;
|
||||
SerializedProperty skeletonDataAsset, initialSkinName;
|
||||
SerializedProperty startingAnimation, startingLoop, timeScale, freeze, updateWhenInvisible, unscaledTime, tintBlack;
|
||||
SerializedProperty initialFlipX, initialFlipY;
|
||||
SerializedProperty meshGeneratorSettings;
|
||||
SerializedProperty allowMultipleCanvasRenderers, separatorSlotNames, enableSeparatorSlots, updateSeparatorPartLocation;
|
||||
SerializedProperty raycastTarget;
|
||||
|
||||
SkeletonGraphic thisSkeletonGraphic;
|
||||
protected bool isInspectingPrefab;
|
||||
protected bool slotsReapplyRequired = false;
|
||||
protected bool forceReloadQueued = false;
|
||||
|
||||
protected bool TargetIsValid {
|
||||
get {
|
||||
if (serializedObject.isEditingMultipleObjects) {
|
||||
foreach (var o in targets) {
|
||||
var component = (SkeletonGraphic)o;
|
||||
if (!component.IsValid)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
var component = (SkeletonGraphic)target;
|
||||
return component.IsValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
isInspectingPrefab = false;
|
||||
#else
|
||||
isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
|
||||
#endif
|
||||
SpineEditorUtilities.ConfirmInitialization();
|
||||
|
||||
// Labels
|
||||
SkeletonDataAssetLabel = new GUIContent("SkeletonData Asset", Icons.spine);
|
||||
|
||||
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");
|
||||
|
||||
initialFlipX = so.FindProperty("initialFlipX");
|
||||
initialFlipY = so.FindProperty("initialFlipY");
|
||||
|
||||
// SkeletonAnimation
|
||||
startingAnimation = so.FindProperty("startingAnimation");
|
||||
startingLoop = so.FindProperty("startingLoop");
|
||||
timeScale = so.FindProperty("timeScale");
|
||||
unscaledTime = so.FindProperty("unscaledTime");
|
||||
freeze = so.FindProperty("freeze");
|
||||
updateWhenInvisible = so.FindProperty("updateWhenInvisible");
|
||||
|
||||
meshGeneratorSettings = so.FindProperty("meshGenerator").FindPropertyRelative("settings");
|
||||
meshGeneratorSettings.isExpanded = SkeletonRendererInspector.advancedFoldout;
|
||||
|
||||
allowMultipleCanvasRenderers = so.FindProperty("allowMultipleCanvasRenderers");
|
||||
updateSeparatorPartLocation = so.FindProperty("updateSeparatorPartLocation");
|
||||
enableSeparatorSlots = so.FindProperty("enableSeparatorSlots");
|
||||
|
||||
separatorSlotNames = so.FindProperty("separatorSlotNames");
|
||||
separatorSlotNames.isExpanded = true;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
|
||||
if (UnityEngine.Event.current.type == EventType.Layout) {
|
||||
if (forceReloadQueued) {
|
||||
forceReloadQueued = false;
|
||||
foreach (var c in targets) {
|
||||
SpineEditorUtilities.ReloadSkeletonDataAssetAndComponent(c as SkeletonGraphic);
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (var c in targets) {
|
||||
var component = c as SkeletonGraphic;
|
||||
if (!component.IsValid) {
|
||||
SpineEditorUtilities.ReinitializeComponent(component);
|
||||
if (!component.IsValid) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool wasChanged = false;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
|
||||
SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel);
|
||||
if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth))
|
||||
forceReloadQueued = true;
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(material);
|
||||
EditorGUILayout.PropertyField(color);
|
||||
|
||||
if (thisSkeletonGraphic.skeletonDataAsset == null) {
|
||||
EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
serializedObject.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
string errorMessage = null;
|
||||
if (SpineEditorUtilities.Preferences.componentMaterialWarning &&
|
||||
MaterialChecks.IsMaterialSetupProblematic(thisSkeletonGraphic, ref errorMessage)) {
|
||||
EditorGUILayout.HelpBox(errorMessage, MessageType.Error, true);
|
||||
}
|
||||
|
||||
bool isSingleRendererOnly = (!allowMultipleCanvasRenderers.hasMultipleDifferentValues && allowMultipleCanvasRenderers.boolValue == false);
|
||||
bool isSeparationEnabledButNotMultipleRenderers =
|
||||
isSingleRendererOnly && (!enableSeparatorSlots.hasMultipleDifferentValues && enableSeparatorSlots.boolValue == true);
|
||||
bool meshRendersIncorrectlyWithSingleRenderer =
|
||||
isSingleRendererOnly && SkeletonHasMultipleSubmeshes();
|
||||
|
||||
if (isSeparationEnabledButNotMultipleRenderers || meshRendersIncorrectlyWithSingleRenderer)
|
||||
meshGeneratorSettings.isExpanded = true;
|
||||
|
||||
using (new SpineInspectorUtility.BoxScope()) {
|
||||
|
||||
EditorGUILayout.PropertyField(meshGeneratorSettings, SpineInspectorUtility.TempContent("Advanced..."), includeChildren: true);
|
||||
SkeletonRendererInspector.advancedFoldout = meshGeneratorSettings.isExpanded;
|
||||
|
||||
if (meshGeneratorSettings.isExpanded) {
|
||||
EditorGUILayout.Space();
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(allowMultipleCanvasRenderers, SpineInspectorUtility.TempContent("Multiple CanvasRenderers"));
|
||||
|
||||
if (GUILayout.Button(new GUIContent("Trim Renderers", "Remove currently unused CanvasRenderer GameObjects. These will be regenerated whenever needed."),
|
||||
EditorStyles.miniButton, GUILayout.Width(100f))) {
|
||||
|
||||
foreach (var skeletonGraphic in targets) {
|
||||
((SkeletonGraphic)skeletonGraphic).TrimRenderers();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.PropertyField(updateWhenInvisible);
|
||||
|
||||
// warning box
|
||||
if (isSeparationEnabledButNotMultipleRenderers) {
|
||||
using (new SpineInspectorUtility.BoxScope()) {
|
||||
meshGeneratorSettings.isExpanded = true;
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("'Multiple Canvas Renderers' must be enabled\nwhen 'Enable Separation' is enabled.", Icons.warning), GUILayout.Height(42), GUILayout.Width(340));
|
||||
}
|
||||
}
|
||||
else if (meshRendersIncorrectlyWithSingleRenderer) {
|
||||
using (new SpineInspectorUtility.BoxScope()) {
|
||||
meshGeneratorSettings.isExpanded = true;
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("This mesh uses multiple atlas pages. You\n" +
|
||||
"need to enable 'Multiple Canvas Renderers'\n" +
|
||||
"for correct rendering. Consider packing\n" +
|
||||
"attachments to a single atlas page if possible.", Icons.warning), GUILayout.Height(60), GUILayout.Width(340));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
SeparatorsField(separatorSlotNames, enableSeparatorSlots, updateSeparatorPartLocation);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(initialSkinName);
|
||||
{
|
||||
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent("Initial Flip"));
|
||||
rect.x += EditorGUIUtility.labelWidth;
|
||||
rect.width = 30f;
|
||||
SpineInspectorUtility.ToggleLeft(rect, initialFlipX, SpineInspectorUtility.TempContent("X", tooltip: "initialFlipX"));
|
||||
rect.x += 35f;
|
||||
SpineInspectorUtility.ToggleLeft(rect, initialFlipY, SpineInspectorUtility.TempContent("Y", tooltip: "initialFlipY"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(startingAnimation);
|
||||
EditorGUILayout.PropertyField(startingLoop);
|
||||
EditorGUILayout.PropertyField(timeScale);
|
||||
EditorGUILayout.PropertyField(unscaledTime, SpineInspectorUtility.TempContent(unscaledTime.displayName, tooltip: "If checked, this will use Time.unscaledDeltaTime to make this update independent of game Time.timeScale. Instance SkeletonGraphic.timeScale will still be applied."));
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(freeze);
|
||||
EditorGUILayout.Space();
|
||||
SkeletonRendererInspector.SkeletonRootMotionParameter(targets);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(raycastTarget);
|
||||
|
||||
EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5));
|
||||
EditorGUILayout.PrefixLabel("Match RectTransform with Mesh");
|
||||
if (GUILayout.Button("Match", EditorStyles.miniButton, GUILayout.Width(65f))) {
|
||||
foreach (var skeletonGraphic in targets) {
|
||||
MatchRectTransformWithBounds((SkeletonGraphic)skeletonGraphic);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (TargetIsValid && !isInspectingPrefab) {
|
||||
EditorGUILayout.Space();
|
||||
if (SpineInspectorUtility.CenteredButton(new GUIContent("Add Skeleton Utility", Icons.skeletonUtility), 21, true, 200f))
|
||||
foreach (var t in targets) {
|
||||
var component = t as Component;
|
||||
if (component.GetComponent<SkeletonUtility>() == null) {
|
||||
component.gameObject.AddComponent<SkeletonUtility>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wasChanged |= EditorGUI.EndChangeCheck();
|
||||
|
||||
if (wasChanged) {
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
slotsReapplyRequired = true;
|
||||
}
|
||||
|
||||
if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) {
|
||||
foreach (var target in targets) {
|
||||
var skeletonGraphic = (SkeletonGraphic)target;
|
||||
skeletonGraphic.ReapplySeparatorSlotNames();
|
||||
skeletonGraphic.LateUpdate();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
slotsReapplyRequired = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool SkeletonHasMultipleSubmeshes () {
|
||||
foreach (var target in targets) {
|
||||
var skeletonGraphic = (SkeletonGraphic)target;
|
||||
if (skeletonGraphic.HasMultipleSubmeshInstructions())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetSeparatorSlotNames (SkeletonRenderer skeletonRenderer, string[] newSlotNames) {
|
||||
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
|
||||
field.SetValue(skeletonRenderer, newSlotNames);
|
||||
}
|
||||
|
||||
public static string[] GetSeparatorSlotNames (SkeletonRenderer skeletonRenderer) {
|
||||
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
|
||||
return field.GetValue(skeletonRenderer) as string[];
|
||||
}
|
||||
|
||||
public static void SeparatorsField (SerializedProperty separatorSlotNames, SerializedProperty enableSeparatorSlots,
|
||||
SerializedProperty updateSeparatorPartLocation) {
|
||||
|
||||
bool multi = separatorSlotNames.serializedObject.isEditingMultipleObjects;
|
||||
bool hasTerminalSlot = false;
|
||||
if (!multi) {
|
||||
var sr = separatorSlotNames.serializedObject.targetObject as ISkeletonComponent;
|
||||
var skeleton = sr.Skeleton;
|
||||
int lastSlot = skeleton.Slots.Count - 1;
|
||||
if (skeleton != null) {
|
||||
for (int i = 0, n = separatorSlotNames.arraySize; i < n; i++) {
|
||||
int index = skeleton.FindSlotIndex(separatorSlotNames.GetArrayElementAtIndex(i).stringValue);
|
||||
if (index == 0 || index == lastSlot) {
|
||||
hasTerminalSlot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string terminalSlotWarning = hasTerminalSlot ? " (!)" : "";
|
||||
|
||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
|
||||
const string SeparatorsDescription = "Stored names of slots where the Skeleton's render will be split into different batches. This is used by separate components that split the render into different MeshRenderers or GameObjects.";
|
||||
if (separatorSlotNames.isExpanded) {
|
||||
EditorGUILayout.PropertyField(separatorSlotNames, SpineInspectorUtility.TempContent(separatorSlotNames.displayName + terminalSlotWarning, Icons.slotRoot, SeparatorsDescription), true);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("+", GUILayout.MaxWidth(28f), GUILayout.MaxHeight(15f))) {
|
||||
separatorSlotNames.arraySize++;
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format("{0} [{1}]", terminalSlotWarning, separatorSlotNames.arraySize), SeparatorsDescription), true);
|
||||
|
||||
EditorGUILayout.PropertyField(enableSeparatorSlots, SpineInspectorUtility.TempContent("Enable Separation", tooltip: "Whether to enable separation at the above separator slots."));
|
||||
EditorGUILayout.PropertyField(updateSeparatorPartLocation, SpineInspectorUtility.TempContent("Update Part Location", tooltip:"Update separator part GameObject location to match the position of the SkeletonGraphic. This can be helpful when re-parenting parts to a different GameObject."));
|
||||
}
|
||||
}
|
||||
|
||||
#region Menus
|
||||
[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
|
||||
static void MatchRectTransformWithBounds (MenuCommand command) {
|
||||
var skeletonGraphic = (SkeletonGraphic)command.context;
|
||||
MatchRectTransformWithBounds(skeletonGraphic);
|
||||
}
|
||||
|
||||
static void MatchRectTransformWithBounds (SkeletonGraphic skeletonGraphic) {
|
||||
if (!skeletonGraphic.MatchRectTransformWithBounds())
|
||||
Debug.Log("Mesh was not previously generated.");
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 15)]
|
||||
static public void SkeletonGraphicCreateMenuItem () {
|
||||
var parentGameObject = Selection.activeObject as GameObject;
|
||||
var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent<RectTransform>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// SpineEditorUtilities.InstantiateDelegate. Used by drag and drop.
|
||||
public static Component SpawnSkeletonGraphicFromDrop (SkeletonDataAsset data) {
|
||||
return InstantiateSkeletonGraphic(data);
|
||||
}
|
||||
|
||||
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<SkeletonGraphic>();
|
||||
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] = (AtlasAssetBase)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAssetBase));
|
||||
}
|
||||
|
||||
data = skeletonDataAsset.GetSkeletonData(true);
|
||||
}
|
||||
|
||||
skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0];
|
||||
graphic.MeshGenerator.settings.zSpacing = SpineEditorUtilities.Preferences.defaultZSpacing;
|
||||
|
||||
graphic.startingLoop = SpineEditorUtilities.Preferences.defaultInstantiateLoop;
|
||||
graphic.Initialize(false);
|
||||
if (skin != null) graphic.Skeleton.SetSkin(skin);
|
||||
graphic.initialSkinName = skin.Name;
|
||||
graphic.Skeleton.UpdateWorldTransform();
|
||||
graphic.UpdateMesh();
|
||||
return graphic;
|
||||
}
|
||||
|
||||
static GameObject NewSkeletonGraphicGameObject (string gameObjectName) {
|
||||
var go = EditorInstantiation.NewGameObject(gameObjectName, true, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic));
|
||||
var graphic = go.GetComponent<SkeletonGraphic>();
|
||||
graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial;
|
||||
return go;
|
||||
}
|
||||
|
||||
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<Material>(firstAssetPath);
|
||||
return firstMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d81cc76b52fcdf499b2db252a317726
|
||||
timeCreated: 1455570945
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,153 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
[CustomEditor(typeof(SkeletonMecanim))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SkeletonMecanimInspector : SkeletonRendererInspector {
|
||||
public static bool mecanimSettingsFoldout;
|
||||
|
||||
protected SerializedProperty autoReset;
|
||||
protected SerializedProperty useCustomMixMode;
|
||||
protected SerializedProperty layerMixModes;
|
||||
protected SerializedProperty layerBlendModes;
|
||||
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
SerializedProperty mecanimTranslator = serializedObject.FindProperty("translator");
|
||||
autoReset = mecanimTranslator.FindPropertyRelative("autoReset");
|
||||
useCustomMixMode = mecanimTranslator.FindPropertyRelative("useCustomMixMode");
|
||||
layerMixModes = mecanimTranslator.FindPropertyRelative("layerMixModes");
|
||||
layerBlendModes = mecanimTranslator.FindPropertyRelative("layerBlendModes");
|
||||
}
|
||||
|
||||
protected override void DrawInspectorGUI (bool multi) {
|
||||
|
||||
AddRootMotionComponentIfEnabled();
|
||||
|
||||
base.DrawInspectorGUI(multi);
|
||||
|
||||
using (new SpineInspectorUtility.BoxScope()) {
|
||||
mecanimSettingsFoldout = EditorGUILayout.Foldout(mecanimSettingsFoldout, "Mecanim Translator");
|
||||
if (mecanimSettingsFoldout) {
|
||||
EditorGUILayout.PropertyField(autoReset, new GUIContent("Auto Reset",
|
||||
"When set to true, the skeleton state is mixed out to setup-" +
|
||||
"pose when an animation finishes, according to the " +
|
||||
"animation's keyed items."));
|
||||
|
||||
EditorGUILayout.PropertyField(useCustomMixMode, new GUIContent("Custom MixMode",
|
||||
"When disabled, the recommended MixMode is used according to the layer blend mode. Enable to specify a custom MixMode for each Mecanim layer."));
|
||||
|
||||
if (useCustomMixMode.hasMultipleDifferentValues || useCustomMixMode.boolValue == true) {
|
||||
DrawLayerSettings();
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddRootMotionComponentIfEnabled () {
|
||||
foreach (var t in targets) {
|
||||
var component = t as Component;
|
||||
var animator = component.GetComponent<Animator>();
|
||||
if (animator != null && animator.applyRootMotion) {
|
||||
if (component.GetComponent<SkeletonMecanimRootMotion>() == null) {
|
||||
component.gameObject.AddComponent<SkeletonMecanimRootMotion>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawLayerSettings () {
|
||||
string[] layerNames = GetLayerNames();
|
||||
float widthLayerColumn = 140;
|
||||
float widthMixColumn = 84;
|
||||
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
|
||||
rect.width = widthLayerColumn;
|
||||
EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mecanim Layer"), EditorStyles.boldLabel);
|
||||
|
||||
var savedIndent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
rect.position += new Vector2(rect.width, 0);
|
||||
rect.width = widthMixColumn;
|
||||
EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mix Mode"), EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.indentLevel = savedIndent;
|
||||
}
|
||||
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
int layerCount = layerMixModes.arraySize;
|
||||
for (int i = 0; i < layerCount; ++i) {
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
string layerName = i < layerNames.Length ? layerNames[i] : ("Layer " + i);
|
||||
|
||||
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
|
||||
rect.width = widthLayerColumn;
|
||||
EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent(layerName));
|
||||
|
||||
var savedIndent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
var mixMode = layerMixModes.GetArrayElementAtIndex(i);
|
||||
rect.position += new Vector2(rect.width, 0);
|
||||
rect.width = widthMixColumn;
|
||||
EditorGUI.PropertyField(rect, mixMode, GUIContent.none);
|
||||
|
||||
EditorGUI.indentLevel = savedIndent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected string[] GetLayerNames () {
|
||||
int maxLayerCount = 0;
|
||||
int maxIndex = 0;
|
||||
for (int i = 0; i < targets.Length; ++i) {
|
||||
var skeletonMecanim = ((SkeletonMecanim)targets[i]);
|
||||
int count = skeletonMecanim.Translator.MecanimLayerCount;
|
||||
if (count > maxLayerCount) {
|
||||
maxLayerCount = count;
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
if (maxLayerCount == 0)
|
||||
return new string[0];
|
||||
var skeletonMecanimMaxLayers = ((SkeletonMecanim)targets[maxIndex]);
|
||||
return skeletonMecanimMaxLayers.Translator.MecanimLayerNames;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a9ca5213a3a4614c9a9f2e60909bc33
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,81 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
[CustomEditor(typeof(SkeletonMecanimRootMotion))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SkeletonMecanimRootMotionInspector : SkeletonRootMotionBaseInspector {
|
||||
protected SerializedProperty mecanimLayerFlags;
|
||||
|
||||
protected GUIContent mecanimLayersLabel;
|
||||
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
mecanimLayerFlags = serializedObject.FindProperty("mecanimLayerFlags");
|
||||
|
||||
mecanimLayersLabel = new UnityEngine.GUIContent("Mecanim Layers", "Mecanim layers to apply root motion at. Defaults to the first Mecanim layer.");
|
||||
}
|
||||
|
||||
override public void OnInspectorGUI () {
|
||||
|
||||
base.MainPropertyFields();
|
||||
MecanimLayerMaskPropertyField();
|
||||
|
||||
base.OptionalPropertyFields();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
protected string[] GetLayerNames () {
|
||||
int maxLayerCount = 0;
|
||||
int maxIndex = 0;
|
||||
for (int i = 0; i < targets.Length; ++i) {
|
||||
var skeletonMecanim = ((SkeletonMecanimRootMotion)targets[i]).SkeletonMecanim;
|
||||
int count = skeletonMecanim.Translator.MecanimLayerCount;
|
||||
if (count > maxLayerCount) {
|
||||
maxLayerCount = count;
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
if (maxLayerCount == 0)
|
||||
return new string[0];
|
||||
var skeletonMecanimMaxLayers = ((SkeletonMecanimRootMotion)targets[maxIndex]).SkeletonMecanim;
|
||||
return skeletonMecanimMaxLayers.Translator.MecanimLayerNames;
|
||||
}
|
||||
|
||||
protected void MecanimLayerMaskPropertyField () {
|
||||
string[] layerNames = GetLayerNames();
|
||||
if (layerNames.Length > 0)
|
||||
mecanimLayerFlags.intValue = EditorGUILayout.MaskField(
|
||||
mecanimLayersLabel, mecanimLayerFlags.intValue, layerNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4613924c50d66cf458f0db803776dd2f
|
||||
timeCreated: 1593175106
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,165 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#define SPINE_OPTIONAL_MATERIALOVERRIDE
|
||||
|
||||
// Contributed by: Lost Polygon
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Spine.Unity.Examples;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
|
||||
// This script is not intended for use with code. See the readme.txt file in SkeletonRendererCustomMaterials folder to learn more.
|
||||
[CustomEditor(typeof(SkeletonRendererCustomMaterials))]
|
||||
public class SkeletonRendererCustomMaterialsInspector : UnityEditor.Editor {
|
||||
List<SkeletonRendererCustomMaterials.AtlasMaterialOverride> componentCustomMaterialOverrides, _customMaterialOverridesPrev;
|
||||
List<SkeletonRendererCustomMaterials.SlotMaterialOverride> componentCustomSlotMaterials, _customSlotMaterialsPrev;
|
||||
SkeletonRendererCustomMaterials component;
|
||||
|
||||
const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
MethodInfo RemoveCustomMaterialOverrides, RemoveCustomSlotMaterials, SetCustomMaterialOverrides, SetCustomSlotMaterials;
|
||||
|
||||
#region SkeletonRenderer context menu
|
||||
[MenuItem("CONTEXT/SkeletonRenderer/Add Basic Serialized Custom Materials")]
|
||||
static void AddSkeletonRendererCustomMaterials (MenuCommand menuCommand) {
|
||||
var skeletonRenderer = (SkeletonRenderer)menuCommand.context;
|
||||
var newComponent = skeletonRenderer.gameObject.AddComponent<SkeletonRendererCustomMaterials>();
|
||||
Undo.RegisterCreatedObjectUndo(newComponent, "Add Basic Serialized Custom Materials");
|
||||
}
|
||||
|
||||
[MenuItem("CONTEXT/SkeletonRenderer/Add Basic Serialized Custom Materials", true)]
|
||||
static bool AddSkeletonRendererCustomMaterials_Validate (MenuCommand menuCommand) {
|
||||
var skeletonRenderer = (SkeletonRenderer)menuCommand.context;
|
||||
return (skeletonRenderer.GetComponent<SkeletonRendererCustomMaterials>() == null);
|
||||
}
|
||||
#endregion
|
||||
|
||||
void OnEnable () {
|
||||
Type cm = typeof(SkeletonRendererCustomMaterials);
|
||||
RemoveCustomMaterialOverrides = cm.GetMethod("RemoveCustomMaterialOverrides", PrivateInstance);
|
||||
RemoveCustomSlotMaterials = cm.GetMethod("RemoveCustomSlotMaterials", PrivateInstance);
|
||||
SetCustomMaterialOverrides = cm.GetMethod("SetCustomMaterialOverrides", PrivateInstance);
|
||||
SetCustomSlotMaterials = cm.GetMethod("SetCustomSlotMaterials", PrivateInstance);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
component = (SkeletonRendererCustomMaterials)target;
|
||||
var skeletonRenderer = component.skeletonRenderer;
|
||||
|
||||
// Draw the default inspector
|
||||
DrawDefaultInspector();
|
||||
|
||||
if (serializedObject.isEditingMultipleObjects)
|
||||
return;
|
||||
|
||||
if (componentCustomMaterialOverrides == null) {
|
||||
Type cm = typeof(SkeletonRendererCustomMaterials);
|
||||
componentCustomMaterialOverrides = cm.GetField("customMaterialOverrides", PrivateInstance).GetValue(component) as List<SkeletonRendererCustomMaterials.AtlasMaterialOverride>;
|
||||
componentCustomSlotMaterials = cm.GetField("customSlotMaterials", PrivateInstance).GetValue(component) as List<SkeletonRendererCustomMaterials.SlotMaterialOverride>;
|
||||
if (componentCustomMaterialOverrides == null) {
|
||||
Debug.Log("Reflection failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill with current values at start
|
||||
if (_customMaterialOverridesPrev == null || _customSlotMaterialsPrev == null) {
|
||||
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
|
||||
_customSlotMaterialsPrev = CopyList(componentCustomSlotMaterials);
|
||||
}
|
||||
|
||||
// Compare new values with saved. If change is detected:
|
||||
// store new values, restore old values, remove overrides, restore new values, restore overrides.
|
||||
|
||||
// 1. Store new values
|
||||
var customMaterialOverridesNew = CopyList(componentCustomMaterialOverrides);
|
||||
var customSlotMaterialsNew = CopyList(componentCustomSlotMaterials);
|
||||
|
||||
// Detect changes
|
||||
if (!_customMaterialOverridesPrev.SequenceEqual(customMaterialOverridesNew) ||
|
||||
!_customSlotMaterialsPrev.SequenceEqual(customSlotMaterialsNew)) {
|
||||
// 2. Restore old values
|
||||
componentCustomMaterialOverrides.Clear();
|
||||
componentCustomSlotMaterials.Clear();
|
||||
componentCustomMaterialOverrides.AddRange(_customMaterialOverridesPrev);
|
||||
componentCustomSlotMaterials.AddRange(_customSlotMaterialsPrev);
|
||||
|
||||
// 3. Remove overrides
|
||||
RemoveCustomMaterials();
|
||||
|
||||
// 4. Restore new values
|
||||
componentCustomMaterialOverrides.Clear();
|
||||
componentCustomSlotMaterials.Clear();
|
||||
componentCustomMaterialOverrides.AddRange(customMaterialOverridesNew);
|
||||
componentCustomSlotMaterials.AddRange(customSlotMaterialsNew);
|
||||
|
||||
// 5. Restore overrides
|
||||
SetCustomMaterials();
|
||||
|
||||
if (skeletonRenderer != null)
|
||||
skeletonRenderer.LateUpdate();
|
||||
}
|
||||
|
||||
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
|
||||
_customSlotMaterialsPrev = CopyList(componentCustomSlotMaterials);
|
||||
|
||||
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Clear and Reapply Changes", tooltip: "Removes all non-serialized overrides in the SkeletonRenderer and reapplies the overrides on this component."))) {
|
||||
if (skeletonRenderer != null) {
|
||||
#if SPINE_OPTIONAL_MATERIALOVERRIDE
|
||||
skeletonRenderer.CustomMaterialOverride.Clear();
|
||||
#endif
|
||||
skeletonRenderer.CustomSlotMaterials.Clear();
|
||||
RemoveCustomMaterials();
|
||||
SetCustomMaterials();
|
||||
skeletonRenderer.LateUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveCustomMaterials () {
|
||||
RemoveCustomMaterialOverrides.Invoke(component, null);
|
||||
RemoveCustomSlotMaterials.Invoke(component, null);
|
||||
}
|
||||
|
||||
void SetCustomMaterials () {
|
||||
SetCustomMaterialOverrides.Invoke(component, null);
|
||||
SetCustomSlotMaterials.Invoke(component, null);
|
||||
}
|
||||
|
||||
static List<T> CopyList<T> (List<T> list) {
|
||||
return list.GetRange(0, list.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e70f7f2a241d6d34aafd6a4a52a368d0
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,593 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#else
|
||||
#define NO_PREFAB_MESH
|
||||
#endif
|
||||
|
||||
#if UNITY_2018_1_OR_NEWER
|
||||
#define PER_MATERIAL_PROPERTY_BLOCKS
|
||||
#endif
|
||||
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
#define BUILT_IN_SPRITE_MASK_COMPONENT
|
||||
#endif
|
||||
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
using Event = UnityEngine.Event;
|
||||
using Icons = SpineEditorUtilities.Icons;
|
||||
|
||||
[CustomEditor(typeof(SkeletonRenderer))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SkeletonRendererInspector : UnityEditor.Editor {
|
||||
public static bool advancedFoldout;
|
||||
|
||||
const string SeparatorSlotNamesFieldName = "separatorSlotNames";
|
||||
|
||||
protected SerializedProperty skeletonDataAsset, initialSkinName;
|
||||
protected SerializedProperty initialFlipX, initialFlipY;
|
||||
protected SerializedProperty updateWhenInvisible, singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles, fixDrawOrder;
|
||||
protected SerializedProperty normals, tangents, zSpacing, pmaVertexColors, tintBlack; // MeshGenerator settings
|
||||
protected SerializedProperty maskInteraction;
|
||||
protected SerializedProperty maskMaterialsNone, maskMaterialsInside, maskMaterialsOutside;
|
||||
protected SpineInspectorUtility.SerializedSortingProperties sortingProperties;
|
||||
|
||||
protected bool isInspectingPrefab;
|
||||
protected bool forceReloadQueued = false;
|
||||
protected bool setMaskNoneMaterialsQueued = false;
|
||||
protected bool setInsideMaskMaterialsQueued = false;
|
||||
protected bool setOutsideMaskMaterialsQueued = false;
|
||||
protected bool deleteInsideMaskMaterialsQueued = false;
|
||||
protected bool deleteOutsideMaskMaterialsQueued = false;
|
||||
|
||||
protected GUIContent SkeletonDataAssetLabel, SkeletonUtilityButtonContent;
|
||||
protected GUIContent PMAVertexColorsLabel, ClearStateOnDisableLabel, ZSpacingLabel, ImmubleTrianglesLabel, TintBlackLabel, UpdateWhenInvisibleLabel, SingleSubmeshLabel, FixDrawOrderLabel;
|
||||
protected GUIContent NormalsLabel, TangentsLabel, MaskInteractionLabel;
|
||||
protected GUIContent MaskMaterialsHeadingLabel, MaskMaterialsNoneLabel, MaskMaterialsInsideLabel, MaskMaterialsOutsideLabel;
|
||||
protected GUIContent SetMaterialButtonLabel, ClearMaterialButtonLabel, DeleteMaterialButtonLabel;
|
||||
|
||||
const string ReloadButtonString = "Reload";
|
||||
static GUILayoutOption reloadButtonWidth;
|
||||
static GUILayoutOption ReloadButtonWidth { get { return reloadButtonWidth = reloadButtonWidth ?? GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonString)).x + 20); } }
|
||||
static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButton; } }
|
||||
|
||||
protected bool TargetIsValid {
|
||||
get {
|
||||
if (serializedObject.isEditingMultipleObjects) {
|
||||
foreach (var o in targets) {
|
||||
var component = (SkeletonRenderer)o;
|
||||
if (!component.valid)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
var component = (SkeletonRenderer)target;
|
||||
return component.valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnEnable () {
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
isInspectingPrefab = false;
|
||||
#else
|
||||
isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
|
||||
#endif
|
||||
SpineEditorUtilities.ConfirmInitialization();
|
||||
|
||||
// Labels
|
||||
SkeletonDataAssetLabel = new GUIContent("SkeletonData Asset", Icons.spine);
|
||||
SkeletonUtilityButtonContent = new GUIContent("Add Skeleton Utility", Icons.skeletonUtility);
|
||||
ImmubleTrianglesLabel = new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility");
|
||||
PMAVertexColorsLabel = new GUIContent("PMA Vertex Colors", "Use this if you are using the default Spine/Skeleton shader or any premultiply-alpha shader.");
|
||||
ClearStateOnDisableLabel = new GUIContent("Clear State On Disable", "Use this if you are pooling or enabling/disabling your Spine GameObject.");
|
||||
ZSpacingLabel = new GUIContent("Z Spacing", "A value other than 0 adds a space between each rendered attachment to prevent Z Fighting when using shaders that read or write to the depth buffer. Large values may cause unwanted parallax and spaces depending on camera setup.");
|
||||
NormalsLabel = new GUIContent("Add Normals", "Use this if your shader requires vertex normals. A more efficient solution for 2D setups is to modify the shader to assume a single normal value for the whole mesh.");
|
||||
TangentsLabel = new GUIContent("Solve Tangents", "Calculates the tangents per frame. Use this if you are using lit shaders (usually with normal maps) that require vertex tangents.");
|
||||
TintBlackLabel = new GUIContent("Tint Black (!)", "Adds black tint vertex data to the mesh as UV2 and UV3. Black tinting requires that the shader interpret UV2 and UV3 as black tint colors for this effect to work. You may also use the default [Spine/Skeleton Tint Black] shader.\n\nIf you only need to tint the whole skeleton and not individual parts, the [Spine/Skeleton Tint] shader is recommended for better efficiency and changing/animating the _Black material property via MaterialPropertyBlock.");
|
||||
SingleSubmeshLabel = new GUIContent("Use Single Submesh", "Simplifies submesh generation by assuming you are only using one Material and need only one submesh. This is will disable multiple materials, render separation, and custom slot materials.");
|
||||
UpdateWhenInvisibleLabel = new GUIContent("Update When Invisible", "Update mode used when the MeshRenderer becomes invisible. Update mode is automatically reset to UpdateMode.FullUpdate when the mesh becomes visible again.");
|
||||
FixDrawOrderLabel = new GUIContent("Fix Draw Order", "Applies only when 3+ submeshes are used (2+ materials with alternating order, e.g. \"A B A\"). If true, GPU instancing will be disabled at all materials and MaterialPropertyBlocks are assigned at each material to prevent aggressive batching of submeshes by e.g. the LWRP renderer, leading to incorrect draw order (e.g. \"A1 B A2\" changed to \"A1A2 B\"). You can disable this parameter when everything is drawn correctly to save the additional performance cost. Note: the GPU instancing setting will remain disabled at affected material assets after exiting play mode, you have to enable it manually if you accidentally enabled this parameter.");
|
||||
MaskInteractionLabel = new GUIContent("Mask Interaction", "SkeletonRenderer's interaction with a Sprite Mask.");
|
||||
MaskMaterialsHeadingLabel = new GUIContent("Mask Interaction Materials", "Materials used for different interaction with sprite masks.");
|
||||
MaskMaterialsNoneLabel = new GUIContent("Normal Materials", "Normal materials used when Mask Interaction is set to None.");
|
||||
MaskMaterialsInsideLabel = new GUIContent("Inside Mask", "Materials used when Mask Interaction is set to Inside Mask.");
|
||||
MaskMaterialsOutsideLabel = new GUIContent("Outside Mask", "Materials used when Mask Interaction is set to Outside Mask.");
|
||||
SetMaterialButtonLabel = new GUIContent("Set", "Prepares material references for switching to the corresponding Mask Interaction mode at runtime. Creates the required materials if they do not exist.");
|
||||
ClearMaterialButtonLabel = new GUIContent("Clear", "Clears unused material references. Note: when switching to the corresponding Mask Interaction mode at runtime, a new material is generated on the fly.");
|
||||
DeleteMaterialButtonLabel = new GUIContent("Delete", "Clears unused material references and deletes the corresponding assets. Note: when switching to the corresponding Mask Interaction mode at runtime, a new material is generated on the fly.");
|
||||
|
||||
var so = this.serializedObject;
|
||||
skeletonDataAsset = so.FindProperty("skeletonDataAsset");
|
||||
initialSkinName = so.FindProperty("initialSkinName");
|
||||
initialFlipX = so.FindProperty("initialFlipX");
|
||||
initialFlipY = so.FindProperty("initialFlipY");
|
||||
normals = so.FindProperty("addNormals");
|
||||
tangents = so.FindProperty("calculateTangents");
|
||||
immutableTriangles = so.FindProperty("immutableTriangles");
|
||||
pmaVertexColors = so.FindProperty("pmaVertexColors");
|
||||
clearStateOnDisable = so.FindProperty("clearStateOnDisable");
|
||||
tintBlack = so.FindProperty("tintBlack");
|
||||
updateWhenInvisible = so.FindProperty("updateWhenInvisible");
|
||||
singleSubmesh = so.FindProperty("singleSubmesh");
|
||||
fixDrawOrder = so.FindProperty("fixDrawOrder");
|
||||
maskInteraction = so.FindProperty("maskInteraction");
|
||||
maskMaterialsNone = so.FindProperty("maskMaterials.materialsMaskDisabled");
|
||||
maskMaterialsInside = so.FindProperty("maskMaterials.materialsInsideMask");
|
||||
maskMaterialsOutside = so.FindProperty("maskMaterials.materialsOutsideMask");
|
||||
|
||||
separatorSlotNames = so.FindProperty("separatorSlotNames");
|
||||
separatorSlotNames.isExpanded = true;
|
||||
|
||||
zSpacing = so.FindProperty("zSpacing");
|
||||
|
||||
SerializedObject renderersSerializedObject = SpineInspectorUtility.GetRenderersSerializedObject(serializedObject); // Allows proper multi-edit behavior.
|
||||
sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(renderersSerializedObject);
|
||||
}
|
||||
|
||||
public void OnSceneGUI () {
|
||||
var skeletonRenderer = (SkeletonRenderer)target;
|
||||
var skeleton = skeletonRenderer.Skeleton;
|
||||
var transform = skeletonRenderer.transform;
|
||||
if (skeleton == null) return;
|
||||
|
||||
SpineHandles.DrawBones(transform, skeleton);
|
||||
}
|
||||
|
||||
override public void OnInspectorGUI () {
|
||||
bool multi = serializedObject.isEditingMultipleObjects;
|
||||
DrawInspectorGUI(multi);
|
||||
HandleSkinChange();
|
||||
if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current) ||
|
||||
AreAnyMaskMaterialsMissing()) {
|
||||
if (!Application.isPlaying) {
|
||||
foreach (var o in targets)
|
||||
SpineEditorUtilities.ReinitializeComponent((SkeletonRenderer)o);
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void DrawInspectorGUI (bool multi) {
|
||||
// Initialize.
|
||||
if (Event.current.type == EventType.Layout) {
|
||||
if (forceReloadQueued) {
|
||||
forceReloadQueued = false;
|
||||
foreach (var c in targets) {
|
||||
SpineEditorUtilities.ReloadSkeletonDataAssetAndComponent(c as SkeletonRenderer);
|
||||
}
|
||||
} else {
|
||||
foreach (var c in targets) {
|
||||
var component = c as SkeletonRenderer;
|
||||
if (!component.valid) {
|
||||
SpineEditorUtilities.ReinitializeComponent(component);
|
||||
if (!component.valid) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILT_IN_SPRITE_MASK_COMPONENT
|
||||
if (setMaskNoneMaterialsQueued) {
|
||||
setMaskNoneMaterialsQueued = false;
|
||||
foreach (var c in targets)
|
||||
EditorSetMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.None);
|
||||
}
|
||||
if (setInsideMaskMaterialsQueued) {
|
||||
setInsideMaskMaterialsQueued = false;
|
||||
foreach (var c in targets)
|
||||
EditorSetMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleInsideMask);
|
||||
}
|
||||
if (setOutsideMaskMaterialsQueued) {
|
||||
setOutsideMaskMaterialsQueued = false;
|
||||
foreach (var c in targets)
|
||||
EditorSetMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleOutsideMask);
|
||||
}
|
||||
|
||||
if (deleteInsideMaskMaterialsQueued) {
|
||||
deleteInsideMaskMaterialsQueued = false;
|
||||
foreach (var c in targets)
|
||||
EditorDeleteMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleInsideMask);
|
||||
}
|
||||
if (deleteOutsideMaskMaterialsQueued) {
|
||||
deleteOutsideMaskMaterialsQueued = false;
|
||||
foreach (var c in targets)
|
||||
EditorDeleteMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleOutsideMask);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NO_PREFAB_MESH
|
||||
if (isInspectingPrefab) {
|
||||
foreach (var c in targets) {
|
||||
var component = (SkeletonRenderer)c;
|
||||
MeshFilter meshFilter = component.GetComponent<MeshFilter>();
|
||||
if (meshFilter != null && meshFilter.sharedMesh != null)
|
||||
meshFilter.sharedMesh = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool valid = TargetIsValid;
|
||||
|
||||
// Fields.
|
||||
if (multi) {
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
|
||||
SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel);
|
||||
if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth))
|
||||
forceReloadQueued = true;
|
||||
}
|
||||
|
||||
if (valid) EditorGUILayout.PropertyField(initialSkinName, SpineInspectorUtility.TempContent("Initial Skin"));
|
||||
|
||||
} else {
|
||||
var component = (SkeletonRenderer)target;
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
|
||||
SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel);
|
||||
if (component.valid) {
|
||||
if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth))
|
||||
forceReloadQueued = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (component.skeletonDataAsset == null) {
|
||||
EditorGUILayout.HelpBox("Skeleton Data Asset required", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.skeletonDataAsset)) {
|
||||
EditorGUILayout.HelpBox("Skeleton Data Asset error. Please check Skeleton Data Asset.", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (valid)
|
||||
EditorGUILayout.PropertyField(initialSkinName, SpineInspectorUtility.TempContent("Initial Skin"));
|
||||
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Sorting Layers
|
||||
SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true);
|
||||
|
||||
if (maskInteraction != null) EditorGUILayout.PropertyField(maskInteraction, MaskInteractionLabel);
|
||||
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
string errorMessage = null;
|
||||
if (SpineEditorUtilities.Preferences.componentMaterialWarning &&
|
||||
MaterialChecks.IsMaterialSetupProblematic((SkeletonRenderer)this.target, ref errorMessage)) {
|
||||
EditorGUILayout.HelpBox(errorMessage, MessageType.Error, true);
|
||||
}
|
||||
|
||||
// More Render Options...
|
||||
using (new SpineInspectorUtility.BoxScope()) {
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5));
|
||||
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
|
||||
if (advancedFoldout) {
|
||||
EditorGUILayout.Space();
|
||||
if (GUILayout.Button("Debug", EditorStyles.miniButton, GUILayout.Width(65f)))
|
||||
SkeletonDebugWindow.Init();
|
||||
} else {
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (advancedFoldout) {
|
||||
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
SpineInspectorUtility.ToggleLeftLayout(initialFlipX);
|
||||
SpineInspectorUtility.ToggleLeftLayout(initialFlipY);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Renderer Settings", EditorStyles.boldLabel);
|
||||
using (new SpineInspectorUtility.LabelWidthScope()) {
|
||||
// Optimization options
|
||||
if (updateWhenInvisible != null) EditorGUILayout.PropertyField(updateWhenInvisible, UpdateWhenInvisibleLabel);
|
||||
|
||||
if (singleSubmesh != null) EditorGUILayout.PropertyField(singleSubmesh, SingleSubmeshLabel);
|
||||
#if PER_MATERIAL_PROPERTY_BLOCKS
|
||||
if (fixDrawOrder != null) EditorGUILayout.PropertyField(fixDrawOrder, FixDrawOrderLabel);
|
||||
#endif
|
||||
if (immutableTriangles != null) EditorGUILayout.PropertyField(immutableTriangles, ImmubleTrianglesLabel);
|
||||
EditorGUILayout.PropertyField(clearStateOnDisable, ClearStateOnDisableLabel);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
SeparatorsField(separatorSlotNames);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Render options
|
||||
const float MinZSpacing = -0.1f;
|
||||
const float MaxZSpacing = 0f;
|
||||
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing, ZSpacingLabel);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
using (new SpineInspectorUtility.LabelWidthScope()) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Vertex Data", SpineInspectorUtility.UnityIcon<MeshFilter>()), EditorStyles.boldLabel);
|
||||
if (pmaVertexColors != null) EditorGUILayout.PropertyField(pmaVertexColors, PMAVertexColorsLabel);
|
||||
EditorGUILayout.PropertyField(tintBlack, TintBlackLabel);
|
||||
|
||||
// Optional fields. May be disabled in SkeletonRenderer.
|
||||
if (normals != null) EditorGUILayout.PropertyField(normals, NormalsLabel);
|
||||
if (tangents != null) EditorGUILayout.PropertyField(tangents, TangentsLabel);
|
||||
}
|
||||
|
||||
#if BUILT_IN_SPRITE_MASK_COMPONENT
|
||||
EditorGUILayout.Space();
|
||||
if (maskMaterialsNone.arraySize > 0 || maskMaterialsInside.arraySize > 0 || maskMaterialsOutside.arraySize > 0) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Mask Interaction Materials", SpineInspectorUtility.UnityIcon<SpriteMask>()), EditorStyles.boldLabel);
|
||||
bool differentMaskModesSelected = maskInteraction.hasMultipleDifferentValues;
|
||||
int activeMaskInteractionValue = differentMaskModesSelected ? -1 : maskInteraction.intValue;
|
||||
|
||||
bool ignoredParam = true;
|
||||
MaskMaterialsEditingField(ref setMaskNoneMaterialsQueued, ref ignoredParam, maskMaterialsNone, MaskMaterialsNoneLabel,
|
||||
differentMaskModesSelected, allowDelete : false, isActiveMaterial : activeMaskInteractionValue == (int)SpriteMaskInteraction.None);
|
||||
MaskMaterialsEditingField(ref setInsideMaskMaterialsQueued, ref deleteInsideMaskMaterialsQueued, maskMaterialsInside, MaskMaterialsInsideLabel,
|
||||
differentMaskModesSelected, allowDelete: true, isActiveMaterial: activeMaskInteractionValue == (int)SpriteMaskInteraction.VisibleInsideMask);
|
||||
MaskMaterialsEditingField(ref setOutsideMaskMaterialsQueued, ref deleteOutsideMaskMaterialsQueued, maskMaterialsOutside, MaskMaterialsOutsideLabel,
|
||||
differentMaskModesSelected, allowDelete : true, isActiveMaterial: activeMaskInteractionValue == (int)SpriteMaskInteraction.VisibleOutsideMask);
|
||||
}
|
||||
#endif
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (valid && !isInspectingPrefab) {
|
||||
if (multi) {
|
||||
// Support multi-edit SkeletonUtility button.
|
||||
// EditorGUILayout.Space();
|
||||
// bool addSkeletonUtility = GUILayout.Button(buttonContent, GUILayout.Height(30));
|
||||
// foreach (var t in targets) {
|
||||
// var component = t as Component;
|
||||
// if (addSkeletonUtility && component.GetComponent<SkeletonUtility>() == null)
|
||||
// component.gameObject.AddComponent<SkeletonUtility>();
|
||||
// }
|
||||
} else {
|
||||
var component = (Component)target;
|
||||
if (component.GetComponent<SkeletonUtility>() == null) {
|
||||
if (SpineInspectorUtility.CenteredButton(SkeletonUtilityButtonContent, 21, true, 200f))
|
||||
component.gameObject.AddComponent<SkeletonUtility>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected void SkeletonRootMotionParameter() {
|
||||
SkeletonRootMotionParameter(targets);
|
||||
}
|
||||
|
||||
public static void SkeletonRootMotionParameter(Object[] targets) {
|
||||
int rootMotionComponentCount = 0;
|
||||
foreach (var t in targets) {
|
||||
var component = t as Component;
|
||||
if (component.GetComponent<SkeletonRootMotion>() != null) {
|
||||
++rootMotionComponentCount;
|
||||
}
|
||||
}
|
||||
bool allHaveRootMotion = rootMotionComponentCount == targets.Length;
|
||||
bool anyHaveRootMotion = rootMotionComponentCount > 0;
|
||||
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
EditorGUILayout.PrefixLabel("Root Motion");
|
||||
|
||||
if (!allHaveRootMotion) {
|
||||
if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Component", Icons.constraintTransform), GUILayout.MaxWidth(130), GUILayout.Height(18))) {
|
||||
foreach (var t in targets) {
|
||||
var component = t as Component;
|
||||
if (component.GetComponent<SkeletonRootMotion>() == null) {
|
||||
component.gameObject.AddComponent<SkeletonRootMotion>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (anyHaveRootMotion) {
|
||||
if (GUILayout.Button(SpineInspectorUtility.TempContent("Remove Component", Icons.constraintTransform), GUILayout.MaxWidth(140), GUILayout.Height(18))) {
|
||||
foreach (var t in targets) {
|
||||
var component = t as Component;
|
||||
var rootMotionComponent = component.GetComponent<SkeletonRootMotion>();
|
||||
if (rootMotionComponent != null) {
|
||||
DestroyImmediate(rootMotionComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetSeparatorSlotNames (SkeletonRenderer skeletonRenderer, string[] newSlotNames) {
|
||||
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
|
||||
field.SetValue(skeletonRenderer, newSlotNames);
|
||||
}
|
||||
|
||||
public static string[] GetSeparatorSlotNames (SkeletonRenderer skeletonRenderer) {
|
||||
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
|
||||
return field.GetValue(skeletonRenderer) as string[];
|
||||
}
|
||||
|
||||
public static void SeparatorsField (SerializedProperty separatorSlotNames) {
|
||||
bool multi = separatorSlotNames.serializedObject.isEditingMultipleObjects;
|
||||
bool hasTerminalSlot = false;
|
||||
if (!multi) {
|
||||
var sr = separatorSlotNames.serializedObject.targetObject as ISkeletonComponent;
|
||||
var skeleton = sr.Skeleton;
|
||||
int lastSlot = skeleton.Slots.Count - 1;
|
||||
if (skeleton != null) {
|
||||
for (int i = 0, n = separatorSlotNames.arraySize; i < n; i++) {
|
||||
int index = skeleton.FindSlotIndex(separatorSlotNames.GetArrayElementAtIndex(i).stringValue);
|
||||
if (index == 0 || index == lastSlot) {
|
||||
hasTerminalSlot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string terminalSlotWarning = hasTerminalSlot ? " (!)" : "";
|
||||
|
||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
|
||||
const string SeparatorsDescription = "Stored names of slots where the Skeleton's render will be split into different batches. This is used by separate components that split the render into different MeshRenderers or GameObjects.";
|
||||
if (separatorSlotNames.isExpanded) {
|
||||
EditorGUILayout.PropertyField(separatorSlotNames, SpineInspectorUtility.TempContent(separatorSlotNames.displayName + terminalSlotWarning, Icons.slotRoot, SeparatorsDescription), true);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("+", GUILayout.MaxWidth(28f), GUILayout.MaxHeight(15f))) {
|
||||
separatorSlotNames.arraySize++;
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
} else
|
||||
EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format("{0} [{1}]", terminalSlotWarning, separatorSlotNames.arraySize), SeparatorsDescription), true);
|
||||
}
|
||||
}
|
||||
|
||||
public void MaskMaterialsEditingField(ref bool wasSetRequested, ref bool wasDeleteRequested,
|
||||
SerializedProperty maskMaterials, GUIContent label,
|
||||
bool differentMaskModesSelected, bool allowDelete, bool isActiveMaterial) {
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
|
||||
EditorGUILayout.LabelField(label, isActiveMaterial ? EditorStyles.boldLabel : EditorStyles.label, GUILayout.MinWidth(80f), GUILayout.MaxWidth(140));
|
||||
EditorGUILayout.LabelField(maskMaterials.hasMultipleDifferentValues ? "-" : maskMaterials.arraySize.ToString(), EditorStyles.miniLabel, GUILayout.Width(42f));
|
||||
|
||||
bool enableSetButton = differentMaskModesSelected || maskMaterials.arraySize == 0;
|
||||
bool enableClearButtons = differentMaskModesSelected || (maskMaterials.arraySize != 0 && !isActiveMaterial);
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!enableSetButton);
|
||||
if (GUILayout.Button(SetMaterialButtonLabel, EditorStyles.miniButtonLeft, GUILayout.Width(46f))) {
|
||||
wasSetRequested = true;
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!enableClearButtons);
|
||||
{
|
||||
if (GUILayout.Button(ClearMaterialButtonLabel, allowDelete ? EditorStyles.miniButtonMid : EditorStyles.miniButtonRight, GUILayout.Width(46f))) {
|
||||
maskMaterials.ClearArray();
|
||||
}
|
||||
else if (allowDelete && GUILayout.Button(DeleteMaterialButtonLabel, EditorStyles.miniButtonRight, GUILayout.Width(46f))) {
|
||||
wasDeleteRequested = true;
|
||||
}
|
||||
if (!allowDelete)
|
||||
GUILayout.Space(46f);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleSkinChange() {
|
||||
if (!Application.isPlaying && Event.current.type == EventType.Layout && !initialSkinName.hasMultipleDifferentValues) {
|
||||
bool mismatchDetected = false;
|
||||
string newSkinName = initialSkinName.stringValue;
|
||||
foreach (var o in targets) {
|
||||
mismatchDetected |= UpdateIfSkinMismatch((SkeletonRenderer)o, newSkinName);
|
||||
}
|
||||
|
||||
if (mismatchDetected) {
|
||||
mismatchDetected = false;
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool UpdateIfSkinMismatch (SkeletonRenderer skeletonRenderer, string componentSkinName) {
|
||||
if (!skeletonRenderer.valid || skeletonRenderer.EditorSkipSkinSync) return false;
|
||||
|
||||
var skin = skeletonRenderer.Skeleton.Skin;
|
||||
string skeletonSkinName = skin != null ? skin.Name : null;
|
||||
bool defaultCase = skin == null && string.IsNullOrEmpty(componentSkinName);
|
||||
bool fieldMatchesSkin = defaultCase || string.Equals(componentSkinName, skeletonSkinName, System.StringComparison.Ordinal);
|
||||
|
||||
if (!fieldMatchesSkin) {
|
||||
Skin skinToSet = string.IsNullOrEmpty(componentSkinName) ? null : skeletonRenderer.Skeleton.Data.FindSkin(componentSkinName);
|
||||
skeletonRenderer.Skeleton.SetSkin(skinToSet);
|
||||
skeletonRenderer.Skeleton.SetSlotsToSetupPose();
|
||||
|
||||
// Note: the UpdateIfSkinMismatch concept shall be replaced with e.g. an OnValidate based
|
||||
// solution or in a separate commit. The current solution does not repaint the Game view because
|
||||
// it is first applying values and in the next editor pass is calling this skin-changing method.
|
||||
if (skeletonRenderer is SkeletonAnimation)
|
||||
((SkeletonAnimation) skeletonRenderer).Update(0f);
|
||||
else if (skeletonRenderer is SkeletonMecanim)
|
||||
((SkeletonMecanim) skeletonRenderer).Update();
|
||||
|
||||
skeletonRenderer.LateUpdate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AreAnyMaskMaterialsMissing() {
|
||||
#if BUILT_IN_SPRITE_MASK_COMPONENT
|
||||
foreach (var o in targets) {
|
||||
var component = (SkeletonRenderer)o;
|
||||
if (!component.valid)
|
||||
continue;
|
||||
if (SpineMaskUtilities.AreMaskMaterialsMissing(component))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#if BUILT_IN_SPRITE_MASK_COMPONENT
|
||||
static void EditorSetMaskMaterials(SkeletonRenderer component, SpriteMaskInteraction maskType)
|
||||
{
|
||||
if (component == null) return;
|
||||
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
|
||||
SpineMaskUtilities.EditorInitMaskMaterials(component, component.maskMaterials, maskType);
|
||||
}
|
||||
|
||||
static void EditorDeleteMaskMaterials(SkeletonRenderer component, SpriteMaskInteraction maskType) {
|
||||
if (component == null) return;
|
||||
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
|
||||
SpineMaskUtilities.EditorDeleteMaskMaterials(component.maskMaterials, maskType);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0fc5db9788bce4418ad3252d43faa8a
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,113 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
[CustomEditor(typeof(SkeletonRootMotionBase))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SkeletonRootMotionBaseInspector : UnityEditor.Editor {
|
||||
protected SerializedProperty rootMotionBoneName;
|
||||
protected SerializedProperty transformPositionX;
|
||||
protected SerializedProperty transformPositionY;
|
||||
protected SerializedProperty rootMotionScaleX;
|
||||
protected SerializedProperty rootMotionScaleY;
|
||||
protected SerializedProperty rootMotionTranslateXPerY;
|
||||
protected SerializedProperty rootMotionTranslateYPerX;
|
||||
protected SerializedProperty rigidBody2D;
|
||||
protected SerializedProperty rigidBody;
|
||||
|
||||
protected GUIContent rootMotionBoneNameLabel;
|
||||
protected GUIContent transformPositionXLabel;
|
||||
protected GUIContent transformPositionYLabel;
|
||||
protected GUIContent rootMotionScaleXLabel;
|
||||
protected GUIContent rootMotionScaleYLabel;
|
||||
protected GUIContent rootMotionTranslateXPerYLabel;
|
||||
protected GUIContent rootMotionTranslateYPerXLabel;
|
||||
protected GUIContent rigidBody2DLabel;
|
||||
protected GUIContent rigidBodyLabel;
|
||||
|
||||
protected virtual void OnEnable () {
|
||||
|
||||
rootMotionBoneName = serializedObject.FindProperty("rootMotionBoneName");
|
||||
transformPositionX = serializedObject.FindProperty("transformPositionX");
|
||||
transformPositionY = serializedObject.FindProperty("transformPositionY");
|
||||
rootMotionScaleX = serializedObject.FindProperty("rootMotionScaleX");
|
||||
rootMotionScaleY = serializedObject.FindProperty("rootMotionScaleY");
|
||||
rootMotionTranslateXPerY = serializedObject.FindProperty("rootMotionTranslateXPerY");
|
||||
rootMotionTranslateYPerX = serializedObject.FindProperty("rootMotionTranslateYPerX");
|
||||
rigidBody2D = serializedObject.FindProperty("rigidBody2D");
|
||||
rigidBody = serializedObject.FindProperty("rigidBody");
|
||||
|
||||
rootMotionBoneNameLabel = new UnityEngine.GUIContent("Root Motion Bone", "The bone to take the motion from.");
|
||||
transformPositionXLabel = new UnityEngine.GUIContent("X", "Root transform position (X)");
|
||||
transformPositionYLabel = new UnityEngine.GUIContent("Y", "Use the Y-movement of the bone.");
|
||||
rootMotionScaleXLabel = new UnityEngine.GUIContent("Root Motion Scale (X)", "Scale applied to the horizontal root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance.");
|
||||
rootMotionScaleYLabel = new UnityEngine.GUIContent("Root Motion Scale (Y)", "Scale applied to the vertical root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance.");
|
||||
rootMotionTranslateXPerYLabel = new UnityEngine.GUIContent("Root Motion Translate (X)", "Added X translation per root motion Y delta. Can be used for delta compensation when scaling is not enough, to e.g. offset a horizontal jump to a vertically different goal.");
|
||||
rootMotionTranslateYPerXLabel = new UnityEngine.GUIContent("Root Motion Translate (Y)", "Added Y translation per root motion X delta. Can be used for delta compensation when scaling is not enough, to e.g. offset a horizontal jump to a vertically different goal.");
|
||||
rigidBody2DLabel = new UnityEngine.GUIContent("Rigidbody2D",
|
||||
"Optional Rigidbody2D: Assign a Rigidbody2D here if you want " +
|
||||
" to apply the root motion to the rigidbody instead of the Transform." +
|
||||
"\n\n" +
|
||||
"Note that animation and physics updates are not always in sync." +
|
||||
"Some jitter may result at certain framerates.");
|
||||
rigidBodyLabel = new UnityEngine.GUIContent("Rigidbody",
|
||||
"Optional Rigidbody: Assign a Rigidbody here if you want " +
|
||||
" to apply the root motion to the rigidbody instead of the Transform." +
|
||||
"\n\n" +
|
||||
"Note that animation and physics updates are not always in sync." +
|
||||
"Some jitter may result at certain framerates.");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
MainPropertyFields();
|
||||
OptionalPropertyFields();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
protected virtual void MainPropertyFields () {
|
||||
EditorGUILayout.PropertyField(rootMotionBoneName, rootMotionBoneNameLabel);
|
||||
EditorGUILayout.PropertyField(transformPositionX, transformPositionXLabel);
|
||||
EditorGUILayout.PropertyField(transformPositionY, transformPositionYLabel);
|
||||
|
||||
EditorGUILayout.PropertyField(rootMotionScaleX, rootMotionScaleXLabel);
|
||||
EditorGUILayout.PropertyField(rootMotionScaleY, rootMotionScaleYLabel);
|
||||
|
||||
EditorGUILayout.PropertyField(rootMotionTranslateXPerY, rootMotionTranslateXPerYLabel);
|
||||
EditorGUILayout.PropertyField(rootMotionTranslateYPerX, rootMotionTranslateYPerXLabel);
|
||||
}
|
||||
|
||||
protected virtual void OptionalPropertyFields () {
|
||||
EditorGUILayout.PropertyField(rigidBody2D, rigidBody2DLabel);
|
||||
EditorGUILayout.PropertyField(rigidBody, rigidBodyLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2cba83baf6afdf44a996e40017c6325
|
||||
timeCreated: 1593175106
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,79 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
[CustomEditor(typeof(SkeletonRootMotion))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SkeletonRootMotionInspector : SkeletonRootMotionBaseInspector {
|
||||
protected SerializedProperty animationTrackFlags;
|
||||
protected GUIContent animationTrackFlagsLabel;
|
||||
|
||||
string[] TrackNames;
|
||||
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
|
||||
animationTrackFlags = serializedObject.FindProperty("animationTrackFlags");
|
||||
animationTrackFlagsLabel = new UnityEngine.GUIContent("Animation Tracks",
|
||||
"Animation tracks to apply root motion at. Defaults to the first" +
|
||||
" animation track (index 0).");
|
||||
}
|
||||
|
||||
override public void OnInspectorGUI () {
|
||||
|
||||
base.MainPropertyFields();
|
||||
AnimationTracksPropertyField();
|
||||
|
||||
base.OptionalPropertyFields();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
protected void AnimationTracksPropertyField () {
|
||||
|
||||
if (TrackNames == null) {
|
||||
InitTrackNames();
|
||||
|
||||
}
|
||||
|
||||
animationTrackFlags.intValue = EditorGUILayout.MaskField(
|
||||
animationTrackFlagsLabel, animationTrackFlags.intValue, TrackNames);
|
||||
}
|
||||
|
||||
protected void InitTrackNames () {
|
||||
int numEntries = 32;
|
||||
TrackNames = new string[numEntries];
|
||||
for (int i = 0; i < numEntries; ++i) {
|
||||
TrackNames[i] = string.Format("Track {0}", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4836100aed984c4a9af11d39c63cb6b
|
||||
timeCreated: 1593183609
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,541 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
// Contributed by: Mitch Thompson
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
#define HINGE_JOINT_NEW_BEHAVIOUR
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
using Icons = SpineEditorUtilities.Icons;
|
||||
|
||||
[CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects]
|
||||
public class SkeletonUtilityBoneInspector : UnityEditor.Editor {
|
||||
SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, hierarchy, parentReference;
|
||||
GUIContent hierarchyLabel;
|
||||
|
||||
//multi selected flags
|
||||
bool containsFollows, containsOverrides, multiObject;
|
||||
|
||||
//single selected helpers
|
||||
SkeletonUtilityBone utilityBone;
|
||||
SkeletonUtility skeletonUtility;
|
||||
bool canCreateHingeChain = false;
|
||||
|
||||
Dictionary<Slot, List<BoundingBoxAttachment>> boundingBoxTable = new Dictionary<Slot, List<BoundingBoxAttachment>>();
|
||||
|
||||
void OnEnable () {
|
||||
mode = this.serializedObject.FindProperty("mode");
|
||||
boneName = this.serializedObject.FindProperty("boneName");
|
||||
zPosition = this.serializedObject.FindProperty("zPosition");
|
||||
position = this.serializedObject.FindProperty("position");
|
||||
rotation = this.serializedObject.FindProperty("rotation");
|
||||
scale = this.serializedObject.FindProperty("scale");
|
||||
overrideAlpha = this.serializedObject.FindProperty("overrideAlpha");
|
||||
hierarchy = this.serializedObject.FindProperty("hierarchy");
|
||||
hierarchyLabel = new GUIContent("Skeleton Utility Parent");
|
||||
parentReference = this.serializedObject.FindProperty("parentReference");
|
||||
|
||||
utilityBone = (SkeletonUtilityBone)target;
|
||||
skeletonUtility = utilityBone.hierarchy;
|
||||
EvaluateFlags();
|
||||
|
||||
if (!utilityBone.valid && skeletonUtility != null) {
|
||||
if (skeletonUtility.skeletonRenderer != null)
|
||||
skeletonUtility.skeletonRenderer.Initialize(false);
|
||||
if (skeletonUtility.skeletonGraphic != null)
|
||||
skeletonUtility.skeletonGraphic.Initialize(false);
|
||||
}
|
||||
|
||||
canCreateHingeChain = CanCreateHingeChain();
|
||||
boundingBoxTable.Clear();
|
||||
|
||||
if (multiObject) return;
|
||||
if (utilityBone.bone == null) return;
|
||||
|
||||
var skeleton = utilityBone.bone.Skeleton;
|
||||
int slotCount = skeleton.Slots.Count;
|
||||
Skin skin = skeleton.Skin;
|
||||
if (skeleton.Skin == null)
|
||||
skin = skeleton.Data.DefaultSkin;
|
||||
|
||||
for(int i = 0; i < slotCount; i++){
|
||||
Slot slot = skeletonUtility.Skeleton.Slots.Items[i];
|
||||
if (slot.Bone == utilityBone.bone) {
|
||||
var slotAttachments = new List<Skin.SkinEntry>();
|
||||
int slotIndex = skeleton.FindSlotIndex(slot.Data.Name);
|
||||
skin.GetAttachments(slotIndex, slotAttachments);
|
||||
|
||||
var boundingBoxes = new List<BoundingBoxAttachment>();
|
||||
foreach (var att in slotAttachments) {
|
||||
var boundingBoxAttachment = att.Attachment as BoundingBoxAttachment;
|
||||
if (boundingBoxAttachment != null)
|
||||
boundingBoxes.Add(boundingBoxAttachment);
|
||||
}
|
||||
|
||||
if (boundingBoxes.Count > 0)
|
||||
boundingBoxTable.Add(slot, boundingBoxes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EvaluateFlags () {
|
||||
if (Selection.objects.Length == 1) {
|
||||
containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow;
|
||||
containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override;
|
||||
} else {
|
||||
int boneCount = 0;
|
||||
foreach (Object o in Selection.objects) {
|
||||
var go = o as GameObject;
|
||||
if (go != null) {
|
||||
SkeletonUtilityBone sub = go.GetComponent<SkeletonUtilityBone>();
|
||||
if (sub != null) {
|
||||
boneCount++;
|
||||
containsFollows |= (sub.mode == SkeletonUtilityBone.Mode.Follow);
|
||||
containsOverrides |= (sub.mode == SkeletonUtilityBone.Mode.Override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
multiObject |= (boneCount > 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(mode);
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
containsOverrides = mode.enumValueIndex == 1;
|
||||
containsFollows = mode.enumValueIndex == 0;
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(multiObject)) {
|
||||
string str = boneName.stringValue;
|
||||
if (str == "")
|
||||
str = "<None>";
|
||||
if (multiObject)
|
||||
str = "<Multiple>";
|
||||
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
EditorGUILayout.PrefixLabel("Bone");
|
||||
if (GUILayout.Button(str, EditorStyles.popup)) {
|
||||
BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).hierarchy.Skeleton.Bones, "<None>", TargetBoneSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(zPosition);
|
||||
EditorGUILayout.PropertyField(position);
|
||||
EditorGUILayout.PropertyField(rotation);
|
||||
EditorGUILayout.PropertyField(scale);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(containsFollows)) {
|
||||
EditorGUILayout.PropertyField(overrideAlpha);
|
||||
EditorGUILayout.PropertyField(parentReference);
|
||||
EditorGUILayout.PropertyField(hierarchy, hierarchyLabel);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
EditorGUILayout.Space();
|
||||
using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0)) {
|
||||
if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Child Bone", Icons.bone), GUILayout.MinWidth(120), GUILayout.Height(24)))
|
||||
BoneSelectorContextMenu("", utilityBone.bone.Children, "<Recursively>", SpawnChildBoneSelected);
|
||||
}
|
||||
using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides)) {
|
||||
if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Override", Icons.poseBones), GUILayout.MinWidth(120), GUILayout.Height(24)))
|
||||
SpawnOverride();
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
EditorGUILayout.Space();
|
||||
using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || !canCreateHingeChain)) {
|
||||
if (GUILayout.Button(SpineInspectorUtility.TempContent("Create 3D Hinge Chain", Icons.hingeChain), GUILayout.MinWidth(120), GUILayout.Height(24)))
|
||||
CreateHingeChain();
|
||||
if (GUILayout.Button(SpineInspectorUtility.TempContent("Create 2D Hinge Chain", Icons.hingeChain), GUILayout.MinWidth(120), GUILayout.Height(24)))
|
||||
CreateHingeChain2D();
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(multiObject || boundingBoxTable.Count == 0)) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bounding Boxes", Icons.boundingBox), EditorStyles.boldLabel);
|
||||
|
||||
foreach (var entry in boundingBoxTable){
|
||||
Slot slot = entry.Key;
|
||||
var boundingBoxes = entry.Value;
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.LabelField(slot.Data.Name);
|
||||
EditorGUI.indentLevel++;
|
||||
{
|
||||
foreach (var box in boundingBoxes) {
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
GUILayout.Space(30);
|
||||
string buttonLabel = box.IsWeighted() ? box.Name + " (!)" : box.Name;
|
||||
if (GUILayout.Button(buttonLabel, GUILayout.Width(200))) {
|
||||
utilityBone.bone.Skeleton.UpdateWorldTransform();
|
||||
var bbTransform = utilityBone.transform.Find("[BoundingBox]" + box.Name); // Use FindChild in older versions of Unity.
|
||||
if (bbTransform != null) {
|
||||
var originalCollider = bbTransform.GetComponent<PolygonCollider2D>();
|
||||
if (originalCollider != null)
|
||||
SkeletonUtility.SetColliderPointsLocal(originalCollider, slot, box);
|
||||
else
|
||||
SkeletonUtility.AddBoundingBoxAsComponent(box, slot, bbTransform.gameObject);
|
||||
} else {
|
||||
var newPolygonCollider = SkeletonUtility.AddBoundingBoxGameObject(null, box, slot, utilityBone.transform);
|
||||
bbTransform = newPolygonCollider.transform;
|
||||
}
|
||||
EditorGUIUtility.PingObject(bbTransform);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
BoneFollowerInspector.RecommendRigidbodyButton(utilityBone);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
static void BoneSelectorContextMenu (string current, ExposedList<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback) {
|
||||
var menu = new GenericMenu();
|
||||
|
||||
if (topValue != "")
|
||||
menu.AddItem(new GUIContent(topValue), current == topValue, callback, null);
|
||||
|
||||
for (int i = 0; i < bones.Count; i++)
|
||||
menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]);
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void TargetBoneSelected (object obj) {
|
||||
if (obj == null) {
|
||||
boneName.stringValue = "";
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
} else {
|
||||
var bone = (Bone)obj;
|
||||
boneName.stringValue = bone.Data.Name;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
utilityBone.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnChildBoneSelected (object obj) {
|
||||
if (obj == null) {
|
||||
// Add recursively
|
||||
foreach (var bone in utilityBone.bone.Children) {
|
||||
GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (SkeletonUtilityBone utilBone in newUtilityBones)
|
||||
SkeletonUtilityInspector.AttachIcon(utilBone);
|
||||
}
|
||||
} else {
|
||||
var bone = (Bone)obj;
|
||||
GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
||||
Selection.activeGameObject = go;
|
||||
EditorGUIUtility.PingObject(go);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnOverride () {
|
||||
GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||
go.name = go.name + " [Override]";
|
||||
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
||||
Selection.activeGameObject = go;
|
||||
EditorGUIUtility.PingObject(go);
|
||||
}
|
||||
|
||||
bool CanCreateHingeChain () {
|
||||
if (utilityBone == null)
|
||||
return false;
|
||||
if (utilityBone.GetComponent<Rigidbody>() != null || utilityBone.GetComponent<Rigidbody2D>() != null)
|
||||
return false;
|
||||
if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0)
|
||||
return false;
|
||||
|
||||
var rigidbodies = utilityBone.GetComponentsInChildren<Rigidbody>();
|
||||
var rigidbodies2D = utilityBone.GetComponentsInChildren<Rigidbody2D>();
|
||||
return rigidbodies.Length <= 0 && rigidbodies2D.Length <= 0;
|
||||
}
|
||||
|
||||
void CreateHingeChain2D () {
|
||||
var kinematicParentUtilityBone = utilityBone.transform.parent.GetComponent<SkeletonUtilityBone>();
|
||||
if (kinematicParentUtilityBone == null) {
|
||||
UnityEditor.EditorUtility.DisplayDialog("No parent SkeletonUtilityBone found!", "Please select the first physically moving chain node, having a parent GameObject with a SkeletonUtilityBone component attached.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
float mass = 10;
|
||||
const float rotationLimit = 20.0f;
|
||||
|
||||
SetSkeletonUtilityToFlipByRotation();
|
||||
|
||||
kinematicParentUtilityBone.mode = SkeletonUtilityBone.Mode.Follow;
|
||||
kinematicParentUtilityBone.position = kinematicParentUtilityBone.rotation = kinematicParentUtilityBone.scale = kinematicParentUtilityBone.zPosition = true;
|
||||
|
||||
GameObject commonParentObject = new GameObject(skeletonUtility.name + " HingeChain Parent " + utilityBone.name);
|
||||
var commonParentActivateOnFlip = commonParentObject.AddComponent<ActivateBasedOnFlipDirection>();
|
||||
commonParentActivateOnFlip.skeletonRenderer = skeletonUtility.skeletonRenderer;
|
||||
commonParentActivateOnFlip.skeletonGraphic = skeletonUtility.skeletonGraphic;
|
||||
|
||||
// HingeChain Parent
|
||||
// Needs to be on top hierarchy level (not attached to the moving skeleton at least) for physics to apply proper momentum.
|
||||
GameObject normalChainParentObject = new GameObject("HingeChain");
|
||||
normalChainParentObject.transform.SetParent(commonParentObject.transform);
|
||||
commonParentActivateOnFlip.activeOnNormalX = normalChainParentObject;
|
||||
|
||||
//var followRotationComponent = normalChainParentObject.AddComponent<FollowSkeletonUtilityRootRotation>();
|
||||
//followRotationComponent.reference = skeletonUtility.boneRoot;
|
||||
|
||||
// Follower Kinematic Rigidbody
|
||||
GameObject followerKinematicObject = new GameObject(kinematicParentUtilityBone.name + " Follower");
|
||||
followerKinematicObject.transform.parent = normalChainParentObject.transform;
|
||||
var followerRigidbody = followerKinematicObject.AddComponent<Rigidbody2D>();
|
||||
followerRigidbody.mass = mass;
|
||||
followerRigidbody.isKinematic = true;
|
||||
followerKinematicObject.AddComponent<FollowLocationRigidbody2D>().reference = kinematicParentUtilityBone.transform;
|
||||
followerKinematicObject.transform.position = kinematicParentUtilityBone.transform.position;
|
||||
followerKinematicObject.transform.rotation = kinematicParentUtilityBone.transform.rotation;
|
||||
|
||||
// Child Bones
|
||||
var utilityBones = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
var childBoneParentReference = followerKinematicObject.transform;
|
||||
for (int i = 0; i < utilityBones.Length; ++i) {
|
||||
var childBone = utilityBones[i];
|
||||
mass *= 0.75f;
|
||||
childBone.parentReference = (i == 0) ? kinematicParentUtilityBone.transform : childBoneParentReference;
|
||||
childBone.transform.SetParent(normalChainParentObject.transform, true); // we need a flat hierarchy of all Joint objects in Unity.
|
||||
AttachRigidbodyAndCollider2D(childBone);
|
||||
childBone.mode = SkeletonUtilityBone.Mode.Override;
|
||||
childBone.scale = childBone.position = childBone.zPosition = false;
|
||||
|
||||
HingeJoint2D joint = childBone.gameObject.AddComponent<HingeJoint2D>();
|
||||
joint.connectedBody = childBoneParentReference.GetComponent<Rigidbody2D>();
|
||||
joint.useLimits = true;
|
||||
ApplyJoint2DAngleLimits(joint, rotationLimit, childBoneParentReference, childBone.transform);
|
||||
|
||||
childBone.GetComponent<Rigidbody2D>().mass = mass;
|
||||
childBoneParentReference = childBone.transform;
|
||||
}
|
||||
|
||||
Duplicate2DHierarchyForFlippedChains(normalChainParentObject, commonParentActivateOnFlip, skeletonUtility.transform, rotationLimit);
|
||||
UnityEditor.Selection.activeGameObject = commonParentObject;
|
||||
}
|
||||
|
||||
void ApplyJoint2DAngleLimits (HingeJoint2D joint, float rotationLimit, Transform parentBone, Transform bone) {
|
||||
#if HINGE_JOINT_NEW_BEHAVIOUR
|
||||
float referenceAngle = (parentBone.eulerAngles.z - bone.eulerAngles.z + 360f) % 360f;
|
||||
float minAngle = referenceAngle - rotationLimit;
|
||||
float maxAngle = referenceAngle + rotationLimit;
|
||||
if (maxAngle > 270f) {
|
||||
minAngle -= 360f;
|
||||
maxAngle -= 360f;
|
||||
}
|
||||
if (minAngle < -90f) {
|
||||
minAngle += 360f;
|
||||
maxAngle += 360f;
|
||||
}
|
||||
#else
|
||||
float minAngle = - rotationLimit;
|
||||
float maxAngle = rotationLimit;
|
||||
#endif
|
||||
joint.limits = new JointAngleLimits2D {
|
||||
min = minAngle,
|
||||
max = maxAngle
|
||||
};
|
||||
}
|
||||
|
||||
void Duplicate2DHierarchyForFlippedChains (GameObject normalChainParentObject, ActivateBasedOnFlipDirection commonParentActivateOnFlip,
|
||||
Transform skeletonUtilityRoot, float rotationLimit) {
|
||||
|
||||
GameObject mirroredChain = GameObject.Instantiate(normalChainParentObject, normalChainParentObject.transform.position,
|
||||
normalChainParentObject.transform.rotation, commonParentActivateOnFlip.transform);
|
||||
mirroredChain.name = normalChainParentObject.name + " FlippedX";
|
||||
|
||||
commonParentActivateOnFlip.activeOnFlippedX = mirroredChain;
|
||||
|
||||
var followerKinematicObject = mirroredChain.GetComponentInChildren<FollowLocationRigidbody2D>();
|
||||
followerKinematicObject.followFlippedX = true;
|
||||
FlipBone2DHorizontal(followerKinematicObject.transform, skeletonUtilityRoot);
|
||||
|
||||
var childBoneJoints = mirroredChain.GetComponentsInChildren<HingeJoint2D>();
|
||||
Transform prevRotatedChild = null;
|
||||
Transform parentTransformForAngles = followerKinematicObject.transform;
|
||||
for (int i = 0; i < childBoneJoints.Length; ++i) {
|
||||
var joint = childBoneJoints[i];
|
||||
FlipBone2DHorizontal(joint.transform, skeletonUtilityRoot);
|
||||
ApplyJoint2DAngleLimits(joint, rotationLimit, parentTransformForAngles, joint.transform);
|
||||
|
||||
GameObject rotatedChild = GameObject.Instantiate(joint.gameObject, joint.transform, true);
|
||||
rotatedChild.name = joint.name + " rotated";
|
||||
var rotationEulerAngles = rotatedChild.transform.localEulerAngles;
|
||||
rotationEulerAngles.x = 180;
|
||||
rotatedChild.transform.localEulerAngles = rotationEulerAngles;
|
||||
DestroyImmediate(rotatedChild.GetComponent<HingeJoint2D>());
|
||||
DestroyImmediate(rotatedChild.GetComponent<BoxCollider2D>());
|
||||
DestroyImmediate(rotatedChild.GetComponent<Rigidbody2D>());
|
||||
|
||||
DestroyImmediate(joint.gameObject.GetComponent<SkeletonUtilityBone>());
|
||||
|
||||
if (i > 0) {
|
||||
var utilityBone = rotatedChild.GetComponent<SkeletonUtilityBone>();
|
||||
utilityBone.parentReference = prevRotatedChild;
|
||||
}
|
||||
prevRotatedChild = rotatedChild.transform;
|
||||
parentTransformForAngles = joint.transform;
|
||||
}
|
||||
|
||||
mirroredChain.SetActive(false);
|
||||
}
|
||||
|
||||
void FlipBone2DHorizontal(Transform bone, Transform mirrorPosition) {
|
||||
Vector3 position = bone.position;
|
||||
position.x = 2 * mirrorPosition.position.x - position.x; // = mirrorPosition + (mirrorPosition - bone.position)
|
||||
bone.position = position;
|
||||
|
||||
Vector3 boneZ = bone.forward;
|
||||
Vector3 boneX = bone.right;
|
||||
boneX.x *= -1;
|
||||
|
||||
bone.rotation = Quaternion.LookRotation(boneZ, Vector3.Cross(boneZ, boneX));
|
||||
}
|
||||
|
||||
void CreateHingeChain () {
|
||||
var kinematicParentUtilityBone = utilityBone.transform.parent.GetComponent<SkeletonUtilityBone>();
|
||||
if (kinematicParentUtilityBone == null) {
|
||||
UnityEditor.EditorUtility.DisplayDialog("No parent SkeletonUtilityBone found!", "Please select the first physically moving chain node, having a parent GameObject with a SkeletonUtilityBone component attached.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
SetSkeletonUtilityToFlipByRotation();
|
||||
|
||||
kinematicParentUtilityBone.mode = SkeletonUtilityBone.Mode.Follow;
|
||||
kinematicParentUtilityBone.position = kinematicParentUtilityBone.rotation = kinematicParentUtilityBone.scale = kinematicParentUtilityBone.zPosition = true;
|
||||
|
||||
// HingeChain Parent
|
||||
// Needs to be on top hierarchy level (not attached to the moving skeleton at least) for physics to apply proper momentum.
|
||||
GameObject chainParentObject = new GameObject(skeletonUtility.name + " HingeChain Parent " + utilityBone.name);
|
||||
var followRotationComponent = chainParentObject.AddComponent<FollowSkeletonUtilityRootRotation>();
|
||||
followRotationComponent.reference = skeletonUtility.boneRoot;
|
||||
|
||||
// Follower Kinematic Rigidbody
|
||||
GameObject followerKinematicObject = new GameObject(kinematicParentUtilityBone.name + " Follower");
|
||||
followerKinematicObject.transform.parent = chainParentObject.transform;
|
||||
var followerRigidbody = followerKinematicObject.AddComponent<Rigidbody>();
|
||||
followerRigidbody.mass = 10;
|
||||
followerRigidbody.isKinematic = true;
|
||||
followerKinematicObject.AddComponent<FollowLocationRigidbody>().reference = kinematicParentUtilityBone.transform;
|
||||
followerKinematicObject.transform.position = kinematicParentUtilityBone.transform.position;
|
||||
followerKinematicObject.transform.rotation = kinematicParentUtilityBone.transform.rotation;
|
||||
|
||||
// Child Bones
|
||||
var utilityBones = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
var childBoneParentReference = followerKinematicObject.transform;
|
||||
foreach (var childBone in utilityBones) {
|
||||
childBone.parentReference = childBoneParentReference;
|
||||
childBone.transform.SetParent(chainParentObject.transform, true); // we need a flat hierarchy of all Joint objects in Unity.
|
||||
AttachRigidbodyAndCollider(childBone);
|
||||
childBone.mode = SkeletonUtilityBone.Mode.Override;
|
||||
|
||||
HingeJoint joint = childBone.gameObject.AddComponent<HingeJoint>();
|
||||
joint.axis = Vector3.forward;
|
||||
joint.connectedBody = childBoneParentReference.GetComponent<Rigidbody>();
|
||||
joint.useLimits = true;
|
||||
joint.limits = new JointLimits {
|
||||
min = -20,
|
||||
max = 20
|
||||
};
|
||||
childBone.GetComponent<Rigidbody>().mass = childBoneParentReference.transform.GetComponent<Rigidbody>().mass * 0.75f;
|
||||
|
||||
childBoneParentReference = childBone.transform;
|
||||
}
|
||||
UnityEditor.Selection.activeGameObject = chainParentObject;
|
||||
}
|
||||
|
||||
void SetSkeletonUtilityToFlipByRotation () {
|
||||
if (!skeletonUtility.flipBy180DegreeRotation) {
|
||||
skeletonUtility.flipBy180DegreeRotation = true;
|
||||
Debug.Log("Set SkeletonUtility " + skeletonUtility.name + " to flip by rotation instead of negative scale (required).", skeletonUtility);
|
||||
}
|
||||
}
|
||||
|
||||
static void AttachRigidbodyAndCollider (SkeletonUtilityBone utilBone, bool enableCollider = false) {
|
||||
if (utilBone.GetComponent<Collider>() == null) {
|
||||
if (utilBone.bone.Data.Length == 0) {
|
||||
SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
|
||||
sphere.radius = 0.1f;
|
||||
sphere.enabled = enableCollider;
|
||||
} else {
|
||||
float length = utilBone.bone.Data.Length;
|
||||
BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
|
||||
box.size = new Vector3(length, length / 3f, 0.2f);
|
||||
box.center = new Vector3(length / 2f, 0, 0);
|
||||
box.enabled = enableCollider;
|
||||
}
|
||||
}
|
||||
utilBone.gameObject.AddComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
static void AttachRigidbodyAndCollider2D(SkeletonUtilityBone utilBone, bool enableCollider = false) {
|
||||
if (utilBone.GetComponent<Collider2D>() == null) {
|
||||
if (utilBone.bone.Data.Length == 0) {
|
||||
var sphere = utilBone.gameObject.AddComponent<CircleCollider2D>();
|
||||
sphere.radius = 0.1f;
|
||||
sphere.enabled = enableCollider;
|
||||
}
|
||||
else {
|
||||
float length = utilBone.bone.Data.Length;
|
||||
var box = utilBone.gameObject.AddComponent<BoxCollider2D>();
|
||||
box.size = new Vector3(length, length / 3f, 0.2f);
|
||||
box.offset = new Vector3(length / 2f, 0, 0);
|
||||
box.enabled = enableCollider;
|
||||
}
|
||||
}
|
||||
utilBone.gameObject.AddComponent<Rigidbody2D>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3ae20b4bcc31f645afd6f5b64f82473
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,192 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using System.Collections.Generic;
|
||||
using Spine;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
using Icons = SpineEditorUtilities.Icons;
|
||||
|
||||
[CustomEditor(typeof(SkeletonUtility))]
|
||||
public class SkeletonUtilityInspector : UnityEditor.Editor {
|
||||
|
||||
SkeletonUtility skeletonUtility;
|
||||
Skeleton skeleton;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
SkeletonGraphic skeletonGraphic;
|
||||
|
||||
#if !NEW_PREFAB_SYSTEM
|
||||
bool isPrefab;
|
||||
#endif
|
||||
|
||||
readonly GUIContent SpawnHierarchyButtonLabel = new GUIContent("Spawn Hierarchy", Icons.skeleton);
|
||||
|
||||
void OnEnable () {
|
||||
skeletonUtility = (SkeletonUtility)target;
|
||||
skeletonRenderer = skeletonUtility.skeletonRenderer;
|
||||
skeletonGraphic = skeletonUtility.skeletonGraphic;
|
||||
skeleton = skeletonUtility.Skeleton;
|
||||
|
||||
if (skeleton == null) {
|
||||
if (skeletonRenderer != null) {
|
||||
skeletonRenderer.Initialize(false);
|
||||
skeletonRenderer.LateUpdate();
|
||||
}
|
||||
else if (skeletonGraphic != null) {
|
||||
skeletonGraphic.Initialize(false);
|
||||
skeletonGraphic.LateUpdate();
|
||||
}
|
||||
skeleton = skeletonUtility.Skeleton;
|
||||
}
|
||||
|
||||
if ((skeletonRenderer != null && !skeletonRenderer.valid) ||
|
||||
(skeletonGraphic != null && !skeletonGraphic.IsValid)) return;
|
||||
|
||||
#if !NEW_PREFAB_SYSTEM
|
||||
isPrefab |= PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab;
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
|
||||
#if !NEW_PREFAB_SYSTEM
|
||||
if (isPrefab) {
|
||||
GUILayout.Label(new GUIContent("Cannot edit Prefabs", Icons.warning));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
if ((skeletonRenderer != null && !skeletonRenderer.valid) ||
|
||||
(skeletonGraphic != null && !skeletonGraphic.IsValid)) {
|
||||
GUILayout.Label(new GUIContent("Spine Component invalid. Check Skeleton Data Asset.", Icons.warning));
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("boneRoot"), SpineInspectorUtility.TempContent("Skeleton Root"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("flipBy180DegreeRotation"), SpineInspectorUtility.TempContent("Flip by Rotation", null,
|
||||
"If true, Skeleton.ScaleX and Skeleton.ScaleY are followed " +
|
||||
"by 180 degree rotation. If false, negative Transform scale is used. " +
|
||||
"Note that using negative scale is consistent with previous behaviour (hence the default), " +
|
||||
"however causes serious problems with rigidbodies and physics. Therefore, it is recommended to " +
|
||||
"enable this parameter where possible. When creating hinge chains for a chain of skeleton bones " +
|
||||
"via SkeletonUtilityBone, it is mandatory to have this parameter enabled."));
|
||||
|
||||
bool hasRootBone = skeletonUtility.boneRoot != null;
|
||||
|
||||
if (!hasRootBone)
|
||||
EditorGUILayout.HelpBox("No hierarchy found. Use Spawn Hierarchy to generate GameObjects for bones.", MessageType.Info);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(hasRootBone)) {
|
||||
if (SpineInspectorUtility.LargeCenteredButton(SpawnHierarchyButtonLabel))
|
||||
SpawnHierarchyContextMenu();
|
||||
}
|
||||
|
||||
if (hasRootBone) {
|
||||
if (SpineInspectorUtility.CenteredButton(new GUIContent("Remove Hierarchy"))) {
|
||||
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Remove Hierarchy");
|
||||
Undo.DestroyObjectImmediate(skeletonUtility.boneRoot.gameObject);
|
||||
skeletonUtility.boneRoot = null;
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void SpawnHierarchyContextMenu () {
|
||||
var menu = new GenericMenu();
|
||||
|
||||
menu.AddItem(new GUIContent("Follow all bones"), false, SpawnFollowHierarchy);
|
||||
menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly);
|
||||
menu.AddSeparator("");
|
||||
menu.AddItem(new GUIContent("Override all bones"), false, SpawnOverrideHierarchy);
|
||||
menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly);
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
public static void AttachIcon (SkeletonUtilityBone boneComponent) {
|
||||
Skeleton skeleton = boneComponent.hierarchy.Skeleton;
|
||||
Texture2D icon = boneComponent.bone.Data.Length == 0 ? Icons.nullBone : Icons.boneNib;
|
||||
|
||||
foreach (IkConstraint c in skeleton.IkConstraints)
|
||||
if (c.Target == boneComponent.bone) {
|
||||
icon = Icons.constraintNib;
|
||||
break;
|
||||
}
|
||||
|
||||
typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2] {
|
||||
boneComponent.gameObject,
|
||||
icon
|
||||
});
|
||||
}
|
||||
|
||||
static void AttachIconsToChildren (Transform root) {
|
||||
if (root != null) {
|
||||
var utilityBones = root.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||
foreach (var utilBone in utilityBones)
|
||||
AttachIcon(utilBone);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnFollowHierarchy () {
|
||||
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Hierarchy");
|
||||
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnFollowHierarchyRootOnly () {
|
||||
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Root");
|
||||
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnOverrideHierarchy () {
|
||||
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Hierarchy");
|
||||
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
|
||||
void SpawnOverrideHierarchyRootOnly () {
|
||||
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Root");
|
||||
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5b90df955eb8c2429ac67c8b2de6c5c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 563 B |
@@ -1,46 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68defdbc95b30a74a9ad396bfc9a2277
|
||||
TextureImporter:
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
linearTexture: 1
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: .25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -3
|
||||
maxTextureSize: 1024
|
||||
textureSettings:
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
wrapMode: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: .5, y: .5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 1
|
||||
textureType: 2
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
sprites: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
@@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10e0b626d7ae7394a934ee9f2fb81b5a
|
||||
folderAsset: yes
|
||||
timeCreated: 1527569604
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 687d9be457ea4eb44bf09c35c95ee5cd
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -1,174 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
|
||||
using Editor = UnityEditor.Editor;
|
||||
using Icons = SpineEditorUtilities.Icons;
|
||||
|
||||
public class SkeletonBakingWindow : EditorWindow {
|
||||
const bool IsUtilityWindow = true;
|
||||
|
||||
[MenuItem("CONTEXT/SkeletonDataAsset/Skeleton Baking", false, 5000)]
|
||||
public static void Init (MenuCommand command) {
|
||||
var window = EditorWindow.GetWindow<SkeletonBakingWindow>(IsUtilityWindow);
|
||||
window.minSize = new Vector2(330f, 530f);
|
||||
window.maxSize = new Vector2(600f, 1000f);
|
||||
window.titleContent = new GUIContent("Skeleton Baking", Icons.spine);
|
||||
window.skeletonDataAsset = command.context as SkeletonDataAsset;
|
||||
window.Show();
|
||||
}
|
||||
|
||||
public SkeletonDataAsset skeletonDataAsset;
|
||||
[SpineSkin(dataField:"skeletonDataAsset")]
|
||||
public string skinToBake = "default";
|
||||
|
||||
// Settings
|
||||
bool bakeAnimations = false;
|
||||
bool bakeIK = true;
|
||||
SendMessageOptions bakeEventOptions;
|
||||
|
||||
SerializedObject so;
|
||||
Skin bakeSkin;
|
||||
|
||||
|
||||
void DataAssetChanged () {
|
||||
bakeSkin = null;
|
||||
}
|
||||
|
||||
void OnGUI () {
|
||||
so = so ?? new SerializedObject(this);
|
||||
|
||||
EditorGUIUtility.wideMode = true;
|
||||
EditorGUILayout.LabelField("Spine Skeleton Prefab Baking", EditorStyles.boldLabel);
|
||||
|
||||
const string BakingWarningMessage = "\nSkeleton baking is not the primary use case for Spine skeletons." +
|
||||
"\nUse baking if you have specialized uses, such as simplified skeletons with movement driven by physics." +
|
||||
|
||||
"\n\nBaked Skeletons do not support the following:" +
|
||||
"\n\tDisabled rotation or scale inheritance" +
|
||||
"\n\tLocal Shear" +
|
||||
"\n\tAll Constraint types" +
|
||||
"\n\tWeighted mesh verts with more than 4 bound bones" +
|
||||
|
||||
"\n\nBaked Animations do not support the following:" +
|
||||
"\n\tMesh Deform Keys" +
|
||||
"\n\tColor Keys" +
|
||||
"\n\tDraw Order Keys" +
|
||||
|
||||
"\n\nAnimation Curves are sampled at 60fps and are not realtime." +
|
||||
"\nConstraint animations are also baked into animation curves." +
|
||||
"\nSee SkeletonBaker.cs comments for full details.\n";
|
||||
|
||||
EditorGUILayout.HelpBox(BakingWarningMessage, MessageType.Info, true);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var skeletonDataAssetProperty = so.FindProperty("skeletonDataAsset");
|
||||
EditorGUILayout.PropertyField(skeletonDataAssetProperty, SpineInspectorUtility.TempContent("SkeletonDataAsset", Icons.spine));
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
so.ApplyModifiedProperties();
|
||||
DataAssetChanged();
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (skeletonDataAsset == null) return;
|
||||
var skeletonData = skeletonDataAsset.GetSkeletonData(false);
|
||||
if (skeletonData == null) return;
|
||||
bool hasExtraSkins = skeletonData.Skins.Count > 1;
|
||||
|
||||
using (new SpineInspectorUtility.BoxScope(false)) {
|
||||
EditorGUILayout.LabelField(skeletonDataAsset.name, EditorStyles.boldLabel);
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bones: " + skeletonData.Bones.Count, Icons.bone));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Slots: " + skeletonData.Slots.Count, Icons.slotRoot));
|
||||
|
||||
if (hasExtraSkins) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins: " + skeletonData.Skins.Count, Icons.skinsRoot));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Current skin attachments: " + (bakeSkin == null ? 0 : bakeSkin.Attachments.Count), Icons.skinPlaceholder));
|
||||
} else if (skeletonData.Skins.Count == 1) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins: 1 (only default Skin)", Icons.skinsRoot));
|
||||
}
|
||||
|
||||
int totalAttachments = 0;
|
||||
foreach (var s in skeletonData.Skins)
|
||||
totalAttachments += s.Attachments.Count;
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Total Attachments: " + totalAttachments, Icons.genericAttachment));
|
||||
}
|
||||
}
|
||||
using (new SpineInspectorUtility.BoxScope(false)) {
|
||||
EditorGUILayout.LabelField("Animations", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Animations: " + skeletonData.Animations.Count, Icons.animation));
|
||||
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
bakeAnimations = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bake Animations", Icons.animationRoot), bakeAnimations);
|
||||
using (new EditorGUI.DisabledScope(!bakeAnimations)) {
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
bakeIK = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bake IK", Icons.constraintIK), bakeIK);
|
||||
bakeEventOptions = (SendMessageOptions)EditorGUILayout.EnumPopup(SpineInspectorUtility.TempContent("Event Options", Icons.userEvent), bakeEventOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (!string.IsNullOrEmpty(skinToBake) && UnityEngine.Event.current.type == EventType.Repaint)
|
||||
bakeSkin = skeletonData.FindSkin(skinToBake) ?? skeletonData.DefaultSkin;
|
||||
|
||||
var prefabIcon = EditorGUIUtility.FindTexture("PrefabModel Icon");
|
||||
|
||||
if (hasExtraSkins) {
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(so.FindProperty("skinToBake"));
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
so.ApplyModifiedProperties();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent(string.Format("Bake Skeleton with Skin ({0})", (bakeSkin == null ? "default" : bakeSkin.Name)), prefabIcon))) {
|
||||
SkeletonBaker.BakeToPrefab(skeletonDataAsset, new ExposedList<Skin>(new[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
|
||||
}
|
||||
|
||||
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent(string.Format("Bake All ({0} skins)", skeletonData.Skins.Count), prefabIcon))) {
|
||||
SkeletonBaker.BakeToPrefab(skeletonDataAsset, skeletonData.Skins, "", bakeAnimations, bakeIK, bakeEventOptions);
|
||||
}
|
||||
} else {
|
||||
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Bake Skeleton", prefabIcon))) {
|
||||
SkeletonBaker.BakeToPrefab(skeletonDataAsset, new ExposedList<Skin>(new[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 868b0caae5b3e65408ece1ab400c4a99
|
||||
timeCreated: 1495203966
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,592 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
// With contributions from: Mitch Thompson
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#else
|
||||
#define NO_PREFAB_MESH
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AnimatedValues;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
using Editor = UnityEditor.Editor;
|
||||
using Icons = SpineEditorUtilities.Icons;
|
||||
|
||||
public class SkeletonDebugWindow : EditorWindow {
|
||||
|
||||
const bool IsUtilityWindow = true;
|
||||
internal static bool showBoneNames, showPaths = true, showShapes = true, showConstraints = true;
|
||||
|
||||
[MenuItem("CONTEXT/SkeletonRenderer/Open Skeleton Debug Window", false, 5000)]
|
||||
public static void Init () {
|
||||
var window = EditorWindow.GetWindow<SkeletonDebugWindow>(IsUtilityWindow);
|
||||
window.minSize = new Vector2(330f, 360f);
|
||||
window.maxSize = new Vector2(600f, 4000f);
|
||||
window.titleContent = new GUIContent("Skeleton Debug", Icons.spine);
|
||||
window.Show();
|
||||
window.OnSelectionChange();
|
||||
}
|
||||
|
||||
|
||||
static AnimBool showSkeleton = new AnimBool(true);
|
||||
static AnimBool showSlotsTree = new AnimBool(false);
|
||||
static AnimBool showConstraintsTree = new AnimBool(false);
|
||||
static AnimBool showDrawOrderTree = new AnimBool(false);
|
||||
static AnimBool showEventDataTree = new AnimBool(false);
|
||||
static AnimBool showDataTree = new AnimBool(false);
|
||||
static AnimBool showInspectBoneTree = new AnimBool(false);
|
||||
|
||||
Vector2 scrollPos;
|
||||
|
||||
GUIContent SlotsRootLabel, SkeletonRootLabel;
|
||||
GUIStyle BoldFoldoutStyle;
|
||||
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
Skeleton skeleton;
|
||||
Skin activeSkin;
|
||||
bool isPrefab;
|
||||
|
||||
SerializedProperty bpo;
|
||||
Bone bone;
|
||||
|
||||
[SpineBone(dataField:"skeletonRenderer")]
|
||||
public string boneName;
|
||||
|
||||
readonly Dictionary<Slot, List<Skin.SkinEntry>> attachmentTable = new Dictionary<Slot, List<Skin.SkinEntry>>();
|
||||
|
||||
static bool staticLostValues = true;
|
||||
|
||||
void OnSceneGUI (SceneView sceneView) {
|
||||
if (skeleton == null || skeletonRenderer == null || !skeletonRenderer.valid || isPrefab)
|
||||
return;
|
||||
|
||||
var transform = skeletonRenderer.transform;
|
||||
if (showPaths) SpineHandles.DrawPaths(transform, skeleton);
|
||||
if (showConstraints) SpineHandles.DrawConstraints(transform, skeleton);
|
||||
if (showBoneNames) SpineHandles.DrawBoneNames(transform, skeleton);
|
||||
if (showShapes) SpineHandles.DrawBoundingBoxes(transform, skeleton);
|
||||
|
||||
if (bone != null) {
|
||||
SpineHandles.DrawBone(skeletonRenderer.transform, bone, 1.5f, Color.cyan);
|
||||
Handles.Label(bone.GetWorldPosition(skeletonRenderer.transform) + (Vector3.down * 0.15f), bone.Data.Name, SpineHandles.BoneNameStyle);
|
||||
}
|
||||
}
|
||||
|
||||
void OnSelectionChange () {
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui -= this.OnSceneGUI;
|
||||
SceneView.duringSceneGui += this.OnSceneGUI;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
|
||||
SceneView.onSceneGUIDelegate += this.OnSceneGUI;
|
||||
#endif
|
||||
|
||||
bool noSkeletonRenderer = false;
|
||||
|
||||
var selectedObject = Selection.activeGameObject;
|
||||
if (selectedObject == null) {
|
||||
noSkeletonRenderer = true;
|
||||
} else {
|
||||
var selectedSkeletonRenderer = selectedObject.GetComponent<SkeletonRenderer>();
|
||||
if (selectedSkeletonRenderer == null) {
|
||||
noSkeletonRenderer = true;
|
||||
} else if (skeletonRenderer != selectedSkeletonRenderer) {
|
||||
|
||||
bone = null;
|
||||
if (skeletonRenderer != null && skeletonRenderer.SkeletonDataAsset != selectedSkeletonRenderer.SkeletonDataAsset)
|
||||
boneName = null;
|
||||
|
||||
skeletonRenderer = selectedSkeletonRenderer;
|
||||
skeletonRenderer.Initialize(false);
|
||||
skeletonRenderer.LateUpdate();
|
||||
skeleton = skeletonRenderer.skeleton;
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
isPrefab = false;
|
||||
#else
|
||||
isPrefab |= PrefabUtility.GetPrefabType(selectedObject) == PrefabType.Prefab;
|
||||
#endif
|
||||
UpdateAttachments();
|
||||
}
|
||||
}
|
||||
|
||||
if (noSkeletonRenderer) Clear();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void Clear () {
|
||||
skeletonRenderer = null;
|
||||
skeleton = null;
|
||||
attachmentTable.Clear();
|
||||
isPrefab = false;
|
||||
boneName = string.Empty;
|
||||
bone = null;
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui -= this.OnSceneGUI;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
Clear();
|
||||
}
|
||||
|
||||
static void FalseDropDown (string label, string stringValue, Texture2D icon = null, bool disabledGroup = false) {
|
||||
if (disabledGroup) EditorGUI.BeginDisabledGroup(true);
|
||||
var pos = EditorGUILayout.GetControlRect(true);
|
||||
pos = EditorGUI.PrefixLabel(pos, SpineInspectorUtility.TempContent(label));
|
||||
GUI.Button(pos, SpineInspectorUtility.TempContent(stringValue, icon), EditorStyles.popup);
|
||||
if (disabledGroup) EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
// Window GUI
|
||||
void OnGUI () {
|
||||
bool requireRepaint = false;
|
||||
|
||||
if (staticLostValues) {
|
||||
Clear();
|
||||
OnSelectionChange();
|
||||
staticLostValues = false;
|
||||
requireRepaint = true;
|
||||
}
|
||||
|
||||
if (SlotsRootLabel == null) {
|
||||
SlotsRootLabel = new GUIContent("Slots", Icons.slotRoot);
|
||||
SkeletonRootLabel = new GUIContent("Skeleton", Icons.skeleton);
|
||||
BoldFoldoutStyle = new GUIStyle(EditorStyles.foldout);
|
||||
BoldFoldoutStyle.fontStyle = FontStyle.Bold;
|
||||
BoldFoldoutStyle.stretchWidth = true;
|
||||
BoldFoldoutStyle.fixedWidth = 0;
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.ObjectField(SpineInspectorUtility.TempContent("Debug Selection", Icons.spine), skeletonRenderer, typeof(SkeletonRenderer), true);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if (skeleton == null || skeletonRenderer == null) {
|
||||
EditorGUILayout.HelpBox("No SkeletonRenderer Spine GameObject selected.", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPrefab) {
|
||||
EditorGUILayout.HelpBox("SkeletonDebug only debugs Spine GameObjects in the scene.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonRenderer.valid) {
|
||||
EditorGUILayout.HelpBox("Spine Component is invalid. Check SkeletonData Asset.", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeSkin != skeleton.Skin)
|
||||
UpdateAttachments();
|
||||
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
|
||||
using (new SpineInspectorUtility.BoxScope(false)) {
|
||||
if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetToSetupPose()"))) {
|
||||
skeleton.SetToSetupPose();
|
||||
requireRepaint = true;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.LabelField("Scene View", EditorStyles.boldLabel);
|
||||
using (new SpineInspectorUtility.LabelWidthScope()) {
|
||||
showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames);
|
||||
showPaths = EditorGUILayout.Toggle("Show Paths", showPaths);
|
||||
showShapes = EditorGUILayout.Toggle("Show Shapes", showShapes);
|
||||
showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints);
|
||||
}
|
||||
requireRepaint |= EditorGUI.EndChangeCheck();
|
||||
|
||||
|
||||
// Skeleton
|
||||
showSkeleton.target = EditorGUILayout.Foldout(showSkeleton.target, SkeletonRootLabel, BoldFoldoutStyle);
|
||||
if (showSkeleton.faded > 0) {
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
using (new EditorGUILayout.FadeGroupScope(showSkeleton.faded)) {
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
FalseDropDown(".Skin", skeleton.Skin != null ? skeletonRenderer.Skeleton.Skin.Name : "<None>", Icons.skin);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// Flip
|
||||
skeleton.ScaleX = EditorGUILayout.DelayedFloatField(".ScaleX", skeleton.ScaleX);
|
||||
skeleton.ScaleY = EditorGUILayout.DelayedFloatField(".ScaleY", skeleton.ScaleY);
|
||||
//EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(160f));
|
||||
////EditorGUILayout.LabelField("Scale", GUILayout.Width(EditorGUIUtility.labelWidth - 20f));
|
||||
//GUILayout.EndHorizontal();
|
||||
|
||||
// Color
|
||||
skeleton.SetColor(EditorGUILayout.ColorField(".R .G .B .A", skeleton.GetColor()));
|
||||
|
||||
requireRepaint |= EditorGUI.EndChangeCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bone
|
||||
showInspectBoneTree.target = EditorGUILayout.Foldout(showInspectBoneTree.target, SpineInspectorUtility.TempContent("Bone", Icons.bone), BoldFoldoutStyle);
|
||||
if (showInspectBoneTree.faded > 0) {
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
using (new EditorGUILayout.FadeGroupScope(showInspectBoneTree.faded)) {
|
||||
showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames);
|
||||
if (bpo == null) bpo = new SerializedObject(this).FindProperty("boneName");
|
||||
EditorGUILayout.PropertyField(bpo, SpineInspectorUtility.TempContent("Bone"));
|
||||
if (!string.IsNullOrEmpty(bpo.stringValue)) {
|
||||
if (bone == null || bone.Data.Name != bpo.stringValue) {
|
||||
bone = skeleton.FindBone(bpo.stringValue);
|
||||
}
|
||||
|
||||
if (bone != null) {
|
||||
using (new EditorGUI.DisabledGroupScope(true)) {
|
||||
var wm = EditorGUIUtility.wideMode;
|
||||
EditorGUIUtility.wideMode = true;
|
||||
EditorGUILayout.Slider("Local Rotation", ViewRound(bone.Rotation), -180f, 180f);
|
||||
EditorGUILayout.Vector2Field("Local Position", RoundVector2(bone.X, bone.Y));
|
||||
EditorGUILayout.Vector2Field("Local Scale", RoundVector2(bone.ScaleX, bone.ScaleY));
|
||||
EditorGUILayout.Vector2Field("Local Shear", RoundVector2(bone.ShearX, bone.ShearY));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
var boneParent = bone.Parent;
|
||||
if (boneParent != null) FalseDropDown("Parent", boneParent.Data.Name, Icons.bone);
|
||||
|
||||
const string RoundFormat = "0.##";
|
||||
var lw = EditorGUIUtility.labelWidth;
|
||||
var fw = EditorGUIUtility.fieldWidth;
|
||||
EditorGUIUtility.labelWidth *= 0.25f;
|
||||
EditorGUIUtility.fieldWidth *= 0.5f;
|
||||
EditorGUILayout.LabelField("LocalToWorld");
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.TextField(".A", bone.A.ToString(RoundFormat));
|
||||
EditorGUILayout.TextField(".B", bone.B.ToString(RoundFormat));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.TextField(".C", bone.C.ToString(RoundFormat));
|
||||
EditorGUILayout.TextField(".D", bone.D.ToString(RoundFormat));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUIUtility.labelWidth = lw * 0.5f;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.TextField(".WorldX", bone.WorldX.ToString(RoundFormat));
|
||||
EditorGUILayout.TextField(".WorldY", bone.WorldY.ToString(RoundFormat));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUIUtility.labelWidth = lw;
|
||||
EditorGUIUtility.fieldWidth = fw;
|
||||
EditorGUIUtility.wideMode = wm;
|
||||
|
||||
}
|
||||
}
|
||||
requireRepaint = true;
|
||||
} else {
|
||||
bone = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slots
|
||||
int preSlotsIndent = EditorGUI.indentLevel;
|
||||
showSlotsTree.target = EditorGUILayout.Foldout(showSlotsTree.target, SlotsRootLabel, BoldFoldoutStyle);
|
||||
if (showSlotsTree.faded > 0) {
|
||||
using (new EditorGUILayout.FadeGroupScope(showSlotsTree.faded)) {
|
||||
if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetSlotsToSetupPose()"))) {
|
||||
skeleton.SetSlotsToSetupPose();
|
||||
requireRepaint = true;
|
||||
}
|
||||
|
||||
int baseIndent = EditorGUI.indentLevel;
|
||||
foreach (KeyValuePair<Slot, List<Skin.SkinEntry>> pair in attachmentTable) {
|
||||
Slot slot = pair.Key;
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
EditorGUI.indentLevel = baseIndent + 1;
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60));
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
slot.SetColor(c);
|
||||
requireRepaint = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var skinEntry in pair.Value) {
|
||||
var attachment = skinEntry.Attachment;
|
||||
GUI.contentColor = slot.Attachment == attachment ? Color.white : Color.grey;
|
||||
EditorGUI.indentLevel = baseIndent + 2;
|
||||
var icon = Icons.GetAttachmentIcon(attachment);
|
||||
bool isAttached = (attachment == slot.Attachment);
|
||||
bool swap = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachment.Name, icon), attachment == slot.Attachment);
|
||||
if (isAttached != swap) {
|
||||
slot.Attachment = isAttached ? null : attachment;
|
||||
requireRepaint = true;
|
||||
}
|
||||
GUI.contentColor = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel = preSlotsIndent;
|
||||
|
||||
// Constraints
|
||||
const string NoneText = "<none>";
|
||||
showConstraintsTree.target = EditorGUILayout.Foldout(showConstraintsTree.target, SpineInspectorUtility.TempContent("Constraints", Icons.constraintRoot), BoldFoldoutStyle);
|
||||
if (showConstraintsTree.faded > 0) {
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
using (new EditorGUILayout.FadeGroupScope(showConstraintsTree.faded)) {
|
||||
const float MixMin = 0f;
|
||||
const float MixMax = 1f;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints);
|
||||
requireRepaint |= EditorGUI.EndChangeCheck();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("IK Constraints ({0})", skeleton.IkConstraints.Count), Icons.constraintIK), EditorStyles.boldLabel);
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
if (skeleton.IkConstraints.Count > 0) {
|
||||
foreach (var c in skeleton.IkConstraints) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintIK));
|
||||
FalseDropDown("Goal", c.Data.Target.Name, Icons.bone, true);
|
||||
using (new EditorGUI.DisabledGroupScope(true)) {
|
||||
EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Data.Uniform", tooltip: "Uniformly scales a bone when Ik stretches or compresses."), c.Data.Uniform);
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
c.Mix = EditorGUILayout.Slider("Mix", c.Mix, MixMin, MixMax);
|
||||
c.BendDirection = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bend Clockwise", tooltip: "IkConstraint.BendDirection == 1 if clockwise; -1 if counterclockwise."), c.BendDirection > 0) ? 1 : -1;
|
||||
c.Compress = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Compress", tooltip: "Compress single bone IK when the target too close. Not applied when parent bone has nonuniform scale."), c.Compress);
|
||||
c.Stretch = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Stretch", tooltip: "Stretch the parent bone when the target is out of range. Not applied when parent bone has nonuniform scale."), c.Stretch);
|
||||
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
} else {
|
||||
EditorGUILayout.LabelField(NoneText);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Transform Constraints ({0})", skeleton.TransformConstraints.Count), Icons.constraintTransform), EditorStyles.boldLabel);
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
if (skeleton.TransformConstraints.Count > 0) {
|
||||
foreach (var c in skeleton.TransformConstraints) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintTransform));
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
FalseDropDown("Goal", c.Data.Target.Name, Icons.bone);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
c.TranslateMix = EditorGUILayout.Slider("TranslateMix", c.TranslateMix, MixMin, MixMax);
|
||||
c.RotateMix = EditorGUILayout.Slider("RotateMix", c.RotateMix, MixMin, MixMax);
|
||||
c.ScaleMix = EditorGUILayout.Slider("ScaleMix", c.ScaleMix, MixMin, MixMax);
|
||||
c.ShearMix = EditorGUILayout.Slider("ShearMix", c.ShearMix, MixMin, MixMax);
|
||||
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
} else {
|
||||
EditorGUILayout.LabelField(NoneText);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Path Constraints ({0})", skeleton.PathConstraints.Count), Icons.constraintPath), EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
showPaths = EditorGUILayout.Toggle("Show Paths", showPaths);
|
||||
requireRepaint |= EditorGUI.EndChangeCheck();
|
||||
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
if (skeleton.PathConstraints.Count > 0) {
|
||||
foreach (var c in skeleton.PathConstraints) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintPath));
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
FalseDropDown("Path Slot", c.Data.Target.Name, Icons.slot);
|
||||
var activeAttachment = c.Target.Attachment;
|
||||
FalseDropDown("Active Path", activeAttachment != null ? activeAttachment.Name : "<None>", activeAttachment is PathAttachment ? Icons.path : null);
|
||||
EditorGUILayout.LabelField("PositionMode." + c.Data.PositionMode);
|
||||
EditorGUILayout.LabelField("SpacingMode." + c.Data.SpacingMode);
|
||||
EditorGUILayout.LabelField("RotateMode." + c.Data.RotateMode);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
c.RotateMix = EditorGUILayout.Slider("RotateMix", c.RotateMix, MixMin, MixMax);
|
||||
c.TranslateMix = EditorGUILayout.Slider("TranslateMix", c.TranslateMix, MixMin, MixMax);
|
||||
c.Position = EditorGUILayout.FloatField("Position", c.Position);
|
||||
c.Spacing = EditorGUILayout.FloatField("Spacing", c.Spacing);
|
||||
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
} else {
|
||||
EditorGUILayout.LabelField(NoneText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showDrawOrderTree.target = EditorGUILayout.Foldout(showDrawOrderTree.target, SpineInspectorUtility.TempContent("Draw Order and Separators", Icons.slotRoot), BoldFoldoutStyle);
|
||||
|
||||
//var separatorSlotNamesField =
|
||||
//SpineInspectorUtility.ge
|
||||
if (showDrawOrderTree.faded > 0) {
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
using (new EditorGUILayout.FadeGroupScope(showDrawOrderTree.faded)) {
|
||||
|
||||
const string SeparatorString = "------------- v SEPARATOR v -------------";
|
||||
|
||||
if (Application.isPlaying) {
|
||||
foreach (var slot in skeleton.DrawOrder) {
|
||||
if (skeletonRenderer.separatorSlots.Contains(slot)) EditorGUILayout.LabelField(SeparatorString);
|
||||
|
||||
using (new EditorGUI.DisabledScope(!slot.Bone.Active)) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (var slot in skeleton.DrawOrder) {
|
||||
var slotNames = SkeletonRendererInspector.GetSeparatorSlotNames(skeletonRenderer);
|
||||
for (int i = 0, n = slotNames.Length; i < n; i++) {
|
||||
if (string.Equals(slotNames[i], slot.Data.Name, System.StringComparison.Ordinal)) {
|
||||
EditorGUILayout.LabelField(SeparatorString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
using (new EditorGUI.DisabledScope(!slot.Bone.Active)) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showEventDataTree.target = EditorGUILayout.Foldout(showEventDataTree.target, SpineInspectorUtility.TempContent("Events", Icons.userEvent), BoldFoldoutStyle);
|
||||
if (showEventDataTree.faded > 0) {
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
using (new EditorGUILayout.FadeGroupScope(showEventDataTree.faded)) {
|
||||
if (skeleton.Data.Events.Count > 0) {
|
||||
foreach (var e in skeleton.Data.Events) {
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(e.Name, Icons.userEvent));
|
||||
}
|
||||
} else {
|
||||
EditorGUILayout.LabelField(NoneText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showDataTree.target = EditorGUILayout.Foldout(showDataTree.target, SpineInspectorUtility.TempContent("Data Counts", Icons.spine), BoldFoldoutStyle);
|
||||
if (showDataTree.faded > 0) {
|
||||
using (new SpineInspectorUtility.IndentScope()) {
|
||||
using (new EditorGUILayout.FadeGroupScope(showDataTree.faded)) {
|
||||
using (new SpineInspectorUtility.LabelWidthScope()) {
|
||||
var skeletonData = skeleton.Data;
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bones", Icons.bone, "Skeleton.Data.Bones"), new GUIContent(skeletonData.Bones.Count.ToString()));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Slots", Icons.slotRoot, "Skeleton.Data.Slots"), new GUIContent(skeletonData.Slots.Count.ToString()));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins", Icons.skinsRoot, "Skeleton.Data.Skins"), new GUIContent(skeletonData.Skins.Count.ToString()));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Events", Icons.userEvent, "Skeleton.Data.Events"), new GUIContent(skeletonData.Events.Count.ToString()));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("IK Constraints", Icons.constraintIK, "Skeleton.Data.IkConstraints"), new GUIContent(skeletonData.IkConstraints.Count.ToString()));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Transform Constraints", Icons.constraintTransform, "Skeleton.Data.TransformConstraints"), new GUIContent(skeletonData.TransformConstraints.Count.ToString()));
|
||||
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Path Constraints", Icons.constraintPath, "Skeleton.Data.PathConstraints"), new GUIContent(skeletonData.PathConstraints.Count.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsAnimating(showSlotsTree, showSkeleton, showConstraintsTree, showDrawOrderTree, showEventDataTree, showInspectBoneTree, showDataTree))
|
||||
Repaint();
|
||||
}
|
||||
|
||||
if (requireRepaint) {
|
||||
skeletonRenderer.LateUpdate();
|
||||
Repaint();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
static float ViewRound (float x) {
|
||||
const float Factor = 100f;
|
||||
const float Divisor = 1f/Factor;
|
||||
return Mathf.Round(x * Factor) * Divisor;
|
||||
}
|
||||
|
||||
static Vector2 RoundVector2 (float x, float y) {
|
||||
const float Factor = 100f;
|
||||
const float Divisor = 1f/Factor;
|
||||
return new Vector2(Mathf.Round(x * Factor) * Divisor, Mathf.Round(y * Factor) * Divisor);
|
||||
}
|
||||
|
||||
static bool IsAnimating (params AnimBool[] animBools) {
|
||||
foreach (var a in animBools)
|
||||
if (a.isAnimating) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateAttachments () {
|
||||
//skeleton = skeletonRenderer.skeleton;
|
||||
Skin defaultSkin = skeleton.Data.DefaultSkin;
|
||||
Skin skin = skeleton.Skin ?? defaultSkin;
|
||||
bool notDefaultSkin = skin != defaultSkin;
|
||||
|
||||
attachmentTable.Clear();
|
||||
for (int i = skeleton.Slots.Count - 1; i >= 0; i--) {
|
||||
var attachments = new List<Skin.SkinEntry>();
|
||||
attachmentTable.Add(skeleton.Slots.Items[i], attachments);
|
||||
// Add skin attachments.
|
||||
skin.GetAttachments(i, attachments);
|
||||
if (notDefaultSkin && defaultSkin != null) // Add default skin attachments.
|
||||
defaultSkin.GetAttachments(i, attachments);
|
||||
}
|
||||
|
||||
activeSkin = skeleton.Skin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7093e73ff3cf6c543ac5865980070b49
|
||||
timeCreated: 1494837950
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bf8fd476d074f449bbae932a1c8a360
|
||||
folderAsset: yes
|
||||
timeCreated: 1527569465
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,58 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Spine.Unity.Editor;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
[CustomEditor(typeof(SkeletonPartsRenderer))]
|
||||
public class SkeletonRenderPartInspector : UnityEditor.Editor {
|
||||
SpineInspectorUtility.SerializedSortingProperties sortingProperties;
|
||||
|
||||
void OnEnable () {
|
||||
sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(SpineInspectorUtility.GetRenderersSerializedObject(serializedObject));
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
SpineInspectorUtility.SortingPropertyFields(sortingProperties, true);
|
||||
|
||||
if (!serializedObject.isEditingMultipleObjects) {
|
||||
EditorGUILayout.Space();
|
||||
if (SpineInspectorUtility.LargeCenteredButton(new GUIContent("Select SkeletonRenderer", SpineEditorUtilities.Icons.spine))) {
|
||||
var thisSkeletonPartsRenderer = target as SkeletonPartsRenderer;
|
||||
var srs = thisSkeletonPartsRenderer.GetComponentInParent<SkeletonRenderSeparator>();
|
||||
if (srs != null && srs.partsRenderers.Contains(thisSkeletonPartsRenderer) && srs.SkeletonRenderer != null)
|
||||
Selection.activeGameObject = srs.SkeletonRenderer.gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30e43037bf4433645ad70266f34c1c8b
|
||||
timeCreated: 1458051036
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,323 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
|
||||
#define NEW_PREFAB_SYSTEM
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Spine.Unity;
|
||||
using Spine.Unity.Editor;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
|
||||
[CustomEditor(typeof(SkeletonRenderSeparator))]
|
||||
public class SkeletonRenderSeparatorInspector : UnityEditor.Editor {
|
||||
SkeletonRenderSeparator component;
|
||||
|
||||
// Properties
|
||||
SerializedProperty skeletonRenderer_, copyPropertyBlock_, copyMeshRendererFlags_, partsRenderers_;
|
||||
static bool partsRenderersExpanded = false;
|
||||
|
||||
// For separator field.
|
||||
SerializedObject skeletonRendererSerializedObject;
|
||||
SerializedProperty separatorNamesProp;
|
||||
static bool skeletonRendererExpanded = true;
|
||||
bool slotsReapplyRequired = false;
|
||||
bool partsRendererInitRequired = false;
|
||||
|
||||
void OnEnable () {
|
||||
if (component == null)
|
||||
component = target as SkeletonRenderSeparator;
|
||||
|
||||
skeletonRenderer_ = serializedObject.FindProperty("skeletonRenderer");
|
||||
copyPropertyBlock_ = serializedObject.FindProperty("copyPropertyBlock");
|
||||
copyMeshRendererFlags_ = serializedObject.FindProperty("copyMeshRendererFlags");
|
||||
|
||||
var partsRenderers = component.partsRenderers;
|
||||
partsRenderers_ = serializedObject.FindProperty("partsRenderers");
|
||||
partsRenderers_.isExpanded = partsRenderersExpanded || // last state
|
||||
partsRenderers.Contains(null) || // null items found
|
||||
partsRenderers.Count < 1 || // no parts renderers
|
||||
(skeletonRenderer_.objectReferenceValue != null && SkeletonRendererSeparatorCount + 1 > partsRenderers.Count); // not enough parts renderers
|
||||
}
|
||||
|
||||
int SkeletonRendererSeparatorCount {
|
||||
get {
|
||||
if (Application.isPlaying)
|
||||
return component.SkeletonRenderer.separatorSlots.Count;
|
||||
else
|
||||
return separatorNamesProp == null ? 0 : separatorNamesProp.arraySize;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
|
||||
// Restore mesh part for undo logic after undo of "Add Parts Renderer".
|
||||
// Triggers regeneration and assignment of the mesh filter's mesh.
|
||||
|
||||
bool isMeshFilterAlwaysNull = false;
|
||||
#if UNITY_EDITOR && NEW_PREFAB_SYSTEM
|
||||
// Don't store mesh or material at the prefab, otherwise it will permanently reload
|
||||
var prefabType = UnityEditor.PrefabUtility.GetPrefabAssetType(component);
|
||||
if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(component) &&
|
||||
(prefabType == UnityEditor.PrefabAssetType.Regular || prefabType == UnityEditor.PrefabAssetType.Variant)) {
|
||||
isMeshFilterAlwaysNull = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!isMeshFilterAlwaysNull && component.GetComponent<MeshFilter>() && component.GetComponent<MeshFilter>().sharedMesh == null) {
|
||||
component.OnDisable();
|
||||
component.OnEnable();
|
||||
}
|
||||
|
||||
var componentRenderers = component.partsRenderers;
|
||||
int totalParts;
|
||||
|
||||
using (new SpineInspectorUtility.LabelWidthScope()) {
|
||||
bool componentEnabled = component.enabled;
|
||||
bool checkBox = EditorGUILayout.Toggle("Enable Separator", componentEnabled);
|
||||
if (checkBox != componentEnabled)
|
||||
component.enabled = checkBox;
|
||||
if (component.SkeletonRenderer.disableRenderingOnOverride && !component.enabled)
|
||||
EditorGUILayout.HelpBox("By default, SkeletonRenderer's MeshRenderer is disabled while the SkeletonRenderSeparator takes over rendering. It is re-enabled when SkeletonRenderSeparator is disabled.", MessageType.Info);
|
||||
|
||||
EditorGUILayout.PropertyField(copyPropertyBlock_);
|
||||
EditorGUILayout.PropertyField(copyMeshRendererFlags_);
|
||||
}
|
||||
|
||||
// SkeletonRenderer Box
|
||||
using (new SpineInspectorUtility.BoxScope(false)) {
|
||||
// Fancy SkeletonRenderer foldout reference field
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var foldoutSkeletonRendererRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.PropertyField(foldoutSkeletonRendererRect, skeletonRenderer_);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
if (component.SkeletonRenderer != null) {
|
||||
skeletonRendererExpanded = EditorGUI.Foldout(foldoutSkeletonRendererRect, skeletonRendererExpanded, "");
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
int separatorCount = 0;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
if (component.SkeletonRenderer != null) {
|
||||
// Separators from SkeletonRenderer
|
||||
{
|
||||
bool skeletonRendererMismatch = skeletonRendererSerializedObject != null && skeletonRendererSerializedObject.targetObject != component.SkeletonRenderer;
|
||||
if (separatorNamesProp == null || skeletonRendererMismatch) {
|
||||
if (component.SkeletonRenderer != null) {
|
||||
skeletonRendererSerializedObject = new SerializedObject(component.SkeletonRenderer);
|
||||
separatorNamesProp = skeletonRendererSerializedObject.FindProperty("separatorSlotNames");
|
||||
separatorNamesProp.isExpanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (separatorNamesProp != null) {
|
||||
if (skeletonRendererExpanded) {
|
||||
EditorGUI.indentLevel++;
|
||||
SkeletonRendererInspector.SeparatorsField(separatorNamesProp);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
separatorCount = this.SkeletonRendererSeparatorCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (SkeletonRendererSeparatorCount == 0) {
|
||||
EditorGUILayout.HelpBox("Separators are empty. Change the size to 1 and choose a slot if you want the render to be separated.", MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
skeletonRendererSerializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (!Application.isPlaying)
|
||||
slotsReapplyRequired = true;
|
||||
}
|
||||
|
||||
|
||||
totalParts = separatorCount + 1;
|
||||
var counterStyle = skeletonRendererExpanded ? EditorStyles.label : EditorStyles.miniLabel;
|
||||
EditorGUILayout.LabelField(string.Format("{0}: separates into {1}.", SpineInspectorUtility.Pluralize(separatorCount, "separator", "separators"), SpineInspectorUtility.Pluralize(totalParts, "part", "parts") ), counterStyle);
|
||||
}
|
||||
|
||||
// Parts renderers
|
||||
using (new SpineInspectorUtility.BoxScope(false)) {
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(this.partsRenderers_, true);
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
// Null items warning
|
||||
bool nullItemsFound = componentRenderers.Contains(null);
|
||||
if (nullItemsFound)
|
||||
EditorGUILayout.HelpBox("Some items in the parts renderers list are null and may cause problems.\n\nYou can right-click on that element and choose 'Delete Array Element' to remove it.", MessageType.Warning);
|
||||
|
||||
// (Button) Match Separators count
|
||||
if (separatorNamesProp != null) {
|
||||
int currentRenderers = 0;
|
||||
foreach (var r in componentRenderers) {
|
||||
if (r != null)
|
||||
currentRenderers++;
|
||||
}
|
||||
int extraRenderersNeeded = totalParts - currentRenderers;
|
||||
|
||||
if (component.enabled && component.SkeletonRenderer != null && extraRenderersNeeded > 0) {
|
||||
EditorGUILayout.HelpBox(string.Format("Insufficient parts renderers. Some parts will not be rendered."), MessageType.Warning);
|
||||
string addMissingLabel = string.Format("Add the missing renderer{1} ({0}) ", extraRenderersNeeded, SpineInspectorUtility.PluralThenS(extraRenderersNeeded));
|
||||
if (GUILayout.Button(addMissingLabel, GUILayout.Height(30f))) {
|
||||
AddPartsRenderer(extraRenderersNeeded);
|
||||
DetectOrphanedPartsRenderers(component);
|
||||
partsRendererInitRequired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (partsRenderers_.isExpanded != partsRenderersExpanded) partsRenderersExpanded = partsRenderers_.isExpanded;
|
||||
if (partsRenderers_.isExpanded) {
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
// (Button) Destroy Renderers button
|
||||
if (componentRenderers.Count > 0) {
|
||||
if (GUILayout.Button("Clear Parts Renderers")) {
|
||||
// Do you really want to destroy all?
|
||||
Undo.RegisterCompleteObjectUndo(component, "Clear Parts Renderers");
|
||||
if (EditorUtility.DisplayDialog("Destroy Renderers", "Do you really want to destroy all the Parts Renderer GameObjects in the list?", "Destroy", "Cancel")) {
|
||||
foreach (var r in componentRenderers) {
|
||||
if (r != null)
|
||||
Undo.DestroyObjectImmediate(r.gameObject);
|
||||
}
|
||||
componentRenderers.Clear();
|
||||
// Do you also want to destroy orphans? (You monster.)
|
||||
DetectOrphanedPartsRenderers(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (Button) Add Part Renderer button
|
||||
if (GUILayout.Button("Add Parts Renderer")) {
|
||||
AddPartsRenderer(1);
|
||||
partsRendererInitRequired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (partsRendererInitRequired) {
|
||||
Undo.RegisterCompleteObjectUndo(component.GetComponent<MeshRenderer>(), "Add Parts Renderers");
|
||||
component.OnEnable();
|
||||
partsRendererInitRequired = false;
|
||||
}
|
||||
|
||||
if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) {
|
||||
component.SkeletonRenderer.ReapplySeparatorSlotNames();
|
||||
component.SkeletonRenderer.LateUpdate();
|
||||
SceneView.RepaintAll();
|
||||
slotsReapplyRequired = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPartsRenderer (int count) {
|
||||
var componentRenderers = component.partsRenderers;
|
||||
bool emptyFound = componentRenderers.Contains(null);
|
||||
if (emptyFound) {
|
||||
bool userClearEntries = EditorUtility.DisplayDialog("Empty entries found", "Null entries found. Do you want to remove null entries before adding the new renderer? ", "Clear Empty Entries", "Don't Clear");
|
||||
if (userClearEntries) componentRenderers.RemoveAll(x => x == null);
|
||||
}
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(component, "Add Parts Renderers");
|
||||
for (int i = 0; i < count; i++) {
|
||||
int index = componentRenderers.Count;
|
||||
var smr = SkeletonPartsRenderer.NewPartsRendererGameObject(component.transform, index.ToString());
|
||||
Undo.RegisterCreatedObjectUndo(smr.gameObject, "New Parts Renderer GameObject.");
|
||||
componentRenderers.Add(smr);
|
||||
|
||||
// increment renderer sorting order.
|
||||
if (index == 0) continue;
|
||||
var prev = componentRenderers[index - 1]; if (prev == null) continue;
|
||||
|
||||
var prevMeshRenderer = prev.GetComponent<MeshRenderer>();
|
||||
var currentMeshRenderer = smr.GetComponent<MeshRenderer>();
|
||||
if (prevMeshRenderer == null || currentMeshRenderer == null) continue;
|
||||
|
||||
int prevSortingLayer = prevMeshRenderer.sortingLayerID;
|
||||
int prevSortingOrder = prevMeshRenderer.sortingOrder;
|
||||
currentMeshRenderer.sortingLayerID = prevSortingLayer;
|
||||
currentMeshRenderer.sortingOrder = prevSortingOrder + SkeletonRenderSeparator.DefaultSortingOrderIncrement;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Detects orphaned parts renderers and offers to delete them.</summary>
|
||||
public void DetectOrphanedPartsRenderers (SkeletonRenderSeparator component) {
|
||||
var children = component.GetComponentsInChildren<SkeletonPartsRenderer>();
|
||||
|
||||
var orphans = new System.Collections.Generic.List<SkeletonPartsRenderer>();
|
||||
foreach (var r in children) {
|
||||
if (!component.partsRenderers.Contains(r))
|
||||
orphans.Add(r);
|
||||
}
|
||||
|
||||
if (orphans.Count > 0) {
|
||||
if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Unassigned renderers were found. Do you want to delete them? (These may belong to another Render Separator in the same hierarchy. If you don't have another Render Separator component in the children of this GameObject, it's likely safe to delete. Warning: This operation cannot be undone.)", "Delete", "Cancel")) {
|
||||
foreach (var o in orphans) {
|
||||
Undo.DestroyObjectImmediate(o.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region SkeletonRenderer Context Menu Item
|
||||
[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator")]
|
||||
static void AddRenderSeparatorComponent (MenuCommand cmd) {
|
||||
var skeletonRenderer = cmd.context as SkeletonRenderer;
|
||||
var newComponent = skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
|
||||
|
||||
Undo.RegisterCreatedObjectUndo(newComponent, "Add SkeletonRenderSeparator");
|
||||
}
|
||||
|
||||
// Validate
|
||||
[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator", true)]
|
||||
static bool ValidateAddRenderSeparatorComponent (MenuCommand cmd) {
|
||||
var skeletonRenderer = cmd.context as SkeletonRenderer;
|
||||
var separator = skeletonRenderer.GetComponent<SkeletonRenderSeparator>();
|
||||
bool separatorNotOnObject = separator == null;
|
||||
return separatorNotOnObject;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2a5062cfe5dd4344831cda4723128af
|
||||
timeCreated: 1458067064
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18ee2876d53412642bbfa1070a1b947f
|
||||
folderAsset: yes
|
||||
timeCreated: 1527569487
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,47 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated January 1, 2020. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2020, 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.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Spine.Unity.Deprecated;
|
||||
using System;
|
||||
|
||||
namespace Spine.Unity.Editor {
|
||||
using Editor = UnityEditor.Editor;
|
||||
|
||||
[Obsolete("The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. Will be removed in spine-unity 3.9.", false)]
|
||||
public class SlotBlendModesEditor : Editor {
|
||||
|
||||
[MenuItem("CONTEXT/SkeletonRenderer/Add Slot Blend Modes Component")]
|
||||
static void AddSlotBlendModesComponent (MenuCommand command) {
|
||||
var skeletonRenderer = (SkeletonRenderer)command.context;
|
||||
skeletonRenderer.gameObject.AddComponent<SlotBlendModes>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbec7dc66dca80a419477536c23b7a0d
|
||||
timeCreated: 1496449255
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user