mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-10 09:08:42 +08:00
Merge remote-tracking branch 'esotericsoftware/master' into unity-skeletonrenderer
This commit is contained in:
commit
bf4511c8de
@ -35,7 +35,7 @@ using System.Collections.Generic;
|
||||
namespace Spine {
|
||||
public class AnimationStateData {
|
||||
internal SkeletonData skeletonData;
|
||||
private Dictionary<KeyValuePair<Animation, Animation>, float> animationToMixTime = new Dictionary<KeyValuePair<Animation, Animation>, float>();
|
||||
private Dictionary<AnimationPair, float> animationToMixTime = new Dictionary<AnimationPair, float>(AnimationPairComparer.Instance);
|
||||
internal float defaultMix;
|
||||
|
||||
public SkeletonData SkeletonData { get { return skeletonData; } }
|
||||
@ -56,16 +56,41 @@ namespace Spine {
|
||||
public void SetMix (Animation from, Animation to, float duration) {
|
||||
if (from == null) throw new ArgumentNullException("from cannot be null.");
|
||||
if (to == null) throw new ArgumentNullException("to cannot be null.");
|
||||
KeyValuePair<Animation, Animation> key = new KeyValuePair<Animation, Animation>(from, to);
|
||||
AnimationPair key = new AnimationPair(from, to);
|
||||
animationToMixTime.Remove(key);
|
||||
animationToMixTime.Add(key, duration);
|
||||
}
|
||||
|
||||
public float GetMix (Animation from, Animation to) {
|
||||
KeyValuePair<Animation, Animation> key = new KeyValuePair<Animation, Animation>(from, to);
|
||||
AnimationPair key = new AnimationPair(from, to);
|
||||
float duration;
|
||||
if (animationToMixTime.TryGetValue(key, out duration)) return duration;
|
||||
return defaultMix;
|
||||
}
|
||||
|
||||
struct AnimationPair {
|
||||
public readonly Animation a1;
|
||||
public readonly Animation a2;
|
||||
|
||||
public AnimationPair (Animation a1, Animation a2) {
|
||||
this.a1 = a1;
|
||||
this.a2 = a2;
|
||||
}
|
||||
}
|
||||
|
||||
// Avoids boxing in the dictionary.
|
||||
class AnimationPairComparer : IEqualityComparer<AnimationPair> {
|
||||
internal static readonly AnimationPairComparer Instance = new AnimationPairComparer();
|
||||
|
||||
bool IEqualityComparer<AnimationPair>.Equals (AnimationPair x, AnimationPair y) {
|
||||
return ReferenceEquals(x.a1, y.a1) && ReferenceEquals(x.a2, y.a2);
|
||||
}
|
||||
|
||||
int IEqualityComparer<AnimationPair>.GetHashCode (AnimationPair obj) {
|
||||
// from Tuple.CombineHashCodes // return (((h1 << 5) + h1) ^ h2);
|
||||
int h1 = obj.a1.GetHashCode();
|
||||
return (((h1 << 5) + h1) ^ obj.a2.GetHashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,13 +61,13 @@ namespace Spine {
|
||||
public void FindNamesForSlot (int slotIndex, List<String> names) {
|
||||
if (names == null) throw new ArgumentNullException("names cannot be null.");
|
||||
foreach (AttachmentKeyTuple key in attachments.Keys)
|
||||
if (key.SlotIndex == slotIndex) names.Add(key.Name);
|
||||
if (key.slotIndex == slotIndex) names.Add(key.name);
|
||||
}
|
||||
|
||||
public void FindAttachmentsForSlot (int slotIndex, List<Attachment> attachments) {
|
||||
if (attachments == null) throw new ArgumentNullException("attachments cannot be null.");
|
||||
foreach (KeyValuePair<AttachmentKeyTuple, Attachment> entry in this.attachments)
|
||||
if (entry.Key.SlotIndex == slotIndex) attachments.Add(entry.Value);
|
||||
if (entry.Key.slotIndex == slotIndex) attachments.Add(entry.Value);
|
||||
}
|
||||
|
||||
override public String ToString () {
|
||||
@ -77,37 +77,37 @@ namespace Spine {
|
||||
/// <summary>Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.</summary>
|
||||
internal void AttachAll (Skeleton skeleton, Skin oldSkin) {
|
||||
foreach (KeyValuePair<AttachmentKeyTuple, Attachment> entry in oldSkin.attachments) {
|
||||
int slotIndex = entry.Key.SlotIndex;
|
||||
int slotIndex = entry.Key.slotIndex;
|
||||
Slot slot = skeleton.slots.Items[slotIndex];
|
||||
if (slot.attachment == entry.Value) {
|
||||
Attachment attachment = GetAttachment(slotIndex, entry.Key.Name);
|
||||
Attachment attachment = GetAttachment(slotIndex, entry.Key.name);
|
||||
if (attachment != null) slot.Attachment = attachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Avoids boxing in the dictionary.
|
||||
private class AttachmentKeyTupleComparer : IEqualityComparer<AttachmentKeyTuple> {
|
||||
internal static readonly AttachmentKeyTupleComparer Instance = new AttachmentKeyTupleComparer();
|
||||
struct AttachmentKeyTuple {
|
||||
public readonly int slotIndex;
|
||||
public readonly string name;
|
||||
internal readonly int nameHashCode;
|
||||
|
||||
bool IEqualityComparer<AttachmentKeyTuple>.Equals (AttachmentKeyTuple o1, AttachmentKeyTuple o2) {
|
||||
return o1.SlotIndex == o2.SlotIndex && o1.NameHashCode == o2.NameHashCode && o1.Name == o2.Name;
|
||||
}
|
||||
|
||||
int IEqualityComparer<AttachmentKeyTuple>.GetHashCode (AttachmentKeyTuple o) {
|
||||
return o.SlotIndex;
|
||||
public AttachmentKeyTuple (int slotIndex, string name) {
|
||||
this.slotIndex = slotIndex;
|
||||
this.name = name;
|
||||
nameHashCode = this.name.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private class AttachmentKeyTuple {
|
||||
public readonly int SlotIndex;
|
||||
public readonly string Name;
|
||||
public readonly int NameHashCode;
|
||||
// Avoids boxing in the dictionary.
|
||||
class AttachmentKeyTupleComparer : IEqualityComparer<AttachmentKeyTuple> {
|
||||
internal static readonly AttachmentKeyTupleComparer Instance = new AttachmentKeyTupleComparer();
|
||||
|
||||
public AttachmentKeyTuple (int slotIndex, string name) {
|
||||
SlotIndex = slotIndex;
|
||||
Name = name;
|
||||
NameHashCode = Name.GetHashCode();
|
||||
bool IEqualityComparer<AttachmentKeyTuple>.Equals (AttachmentKeyTuple o1, AttachmentKeyTuple o2) {
|
||||
return o1.slotIndex == o2.slotIndex && o1.nameHashCode == o2.nameHashCode && o1.name == o2.name;
|
||||
}
|
||||
|
||||
int IEqualityComparer<AttachmentKeyTuple>.GetHashCode (AttachmentKeyTuple o) {
|
||||
return o.slotIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d6edb328bfcef44a9d1bf852e5724bc
|
||||
timeCreated: 1427642224
|
||||
licenseType: Free
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -147,9 +147,14 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
GUILayout.Space(32);
|
||||
if (GUILayout.Button(new GUIContent("Generate Mecanim Controller"), GUILayout.Width(195), GUILayout.Height(20)))
|
||||
SkeletonBaker.GenerateMecanimAnimationClips(m_skeletonDataAsset);
|
||||
//GUILayout.Label(new GUIContent("Alternative to SkeletonAnimation, not a requirement.", SpineEditorUtilities.Icons.warning));
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.LabelField("SkeletonAnimator is the Mecanim alternative to SkeletonAnimation. It is not required.", EditorStyles.miniLabel);
|
||||
} else {
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(32);
|
||||
if (GUILayout.Button(new GUIContent("Update Controller Animations"), GUILayout.Width(195), GUILayout.Height(20)))
|
||||
SkeletonBaker.GenerateMecanimAnimationClips(m_skeletonDataAsset);
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
@ -228,6 +233,7 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
|
||||
|
||||
}
|
||||
|
||||
void DrawReimportButton () {
|
||||
EditorGUI.BeginDisabledGroup(skeletonJSON.objectReferenceValue == null);
|
||||
if (GUILayout.Button(new GUIContent("Attempt Reimport", SpineEditorUtilities.Icons.warning))) {
|
||||
@ -302,10 +308,14 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
if (!showAnimationList)
|
||||
return;
|
||||
|
||||
if (GUILayout.Button(new GUIContent("Setup Pose", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) {
|
||||
StopAnimation();
|
||||
m_skeletonAnimation.skeleton.SetToSetupPose();
|
||||
m_requireRefresh = true;
|
||||
if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) {
|
||||
if (GUILayout.Button(new GUIContent("Setup Pose", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) {
|
||||
StopAnimation();
|
||||
m_skeletonAnimation.skeleton.SetToSetupPose();
|
||||
m_requireRefresh = true;
|
||||
}
|
||||
} else {
|
||||
EditorGUILayout.HelpBox("Animations can be previewed if you expand the Preview window below.", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField("Name", "Duration");
|
||||
@ -498,6 +508,10 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
private Color m_originColor = new Color(0.3f, 0.3f, 0.3f, 1);
|
||||
|
||||
private void StopAnimation () {
|
||||
if (m_skeletonAnimation == null) {
|
||||
Debug.LogWarning("Animation was stopped but preview doesn't exist. It's possible that the Preview Panel is closed.");
|
||||
}
|
||||
|
||||
m_skeletonAnimation.state.ClearTrack(0);
|
||||
m_playing = false;
|
||||
}
|
||||
@ -562,7 +576,7 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
m_initialized = true;
|
||||
AdjustCameraGoals(true);
|
||||
} catch {
|
||||
|
||||
// WARNING: Suppresses errors.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -861,6 +875,7 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
if (position.Contains(current.mousePosition)) {
|
||||
|
||||
m_orthoGoal += current.delta.y;
|
||||
m_orthoGoal = Mathf.Max(0.01f, m_orthoGoal);
|
||||
GUIUtility.hotControl = controlID;
|
||||
current.Use();
|
||||
}
|
||||
@ -959,4 +974,4 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
tex = this.m_previewUtility.EndStaticPreview();
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,14 +40,8 @@ using Spine;
|
||||
[AddComponentMenu("Spine/BoneFollower")]
|
||||
public class BoneFollower : MonoBehaviour {
|
||||
|
||||
[System.NonSerialized]
|
||||
public bool
|
||||
valid;
|
||||
#region Inspector
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
public Bone bone;
|
||||
public bool followZPosition = true;
|
||||
public bool followBoneRotation = true;
|
||||
|
||||
public SkeletonRenderer SkeletonRenderer {
|
||||
get { return skeletonRenderer; }
|
||||
set {
|
||||
@ -55,15 +49,21 @@ public class BoneFollower : MonoBehaviour {
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
|
||||
|
||||
[SpineBone(dataField: "skeletonRenderer")]
|
||||
public String boneName;
|
||||
|
||||
public bool followZPosition = true;
|
||||
public bool followBoneRotation = true;
|
||||
public bool resetOnAwake = true;
|
||||
protected Transform cachedTransform;
|
||||
protected Transform skeletonTransform;
|
||||
#endregion
|
||||
|
||||
[NonSerialized]
|
||||
public bool valid;
|
||||
|
||||
[NonSerialized]
|
||||
public Bone bone;
|
||||
Transform skeletonTransform;
|
||||
|
||||
public void HandleResetRenderer (SkeletonRenderer skeletonRenderer) {
|
||||
Reset();
|
||||
@ -71,21 +71,21 @@ public class BoneFollower : MonoBehaviour {
|
||||
|
||||
public void Reset () {
|
||||
bone = null;
|
||||
cachedTransform = transform;
|
||||
valid = skeletonRenderer != null && skeletonRenderer.valid;
|
||||
if (!valid)
|
||||
return;
|
||||
skeletonTransform = skeletonRenderer.transform;
|
||||
|
||||
if (!valid) return;
|
||||
|
||||
skeletonTransform = skeletonRenderer.transform;
|
||||
skeletonRenderer.OnRebuild -= HandleResetRenderer;
|
||||
skeletonRenderer.OnRebuild += HandleResetRenderer;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor)
|
||||
DoUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
//cleanup
|
||||
if (skeletonRenderer != null)
|
||||
skeletonRenderer.OnRebuild -= HandleResetRenderer;
|
||||
}
|
||||
@ -112,33 +112,33 @@ public class BoneFollower : MonoBehaviour {
|
||||
if (bone == null) {
|
||||
Debug.LogError("Bone not found: " + boneName, this);
|
||||
return;
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Spine.Skeleton skeleton = skeletonRenderer.skeleton;
|
||||
Skeleton skeleton = skeletonRenderer.skeleton;
|
||||
float flipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
|
||||
Transform thisTransform = this.transform;
|
||||
|
||||
if (cachedTransform.parent == skeletonTransform) {
|
||||
cachedTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : cachedTransform.localPosition.z);
|
||||
// Recommended setup: Use local transform properties if Spine GameObject is parent
|
||||
if (thisTransform.parent == skeletonTransform) {
|
||||
thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z);
|
||||
|
||||
if (followBoneRotation) {
|
||||
Vector3 rotation = cachedTransform.localRotation.eulerAngles;
|
||||
cachedTransform.localRotation = Quaternion.Euler(rotation.x, rotation.y, bone.WorldRotationX * flipRotation);
|
||||
Vector3 rotation = thisTransform.localRotation.eulerAngles;
|
||||
thisTransform.localRotation = Quaternion.Euler(rotation.x, rotation.y, bone.WorldRotationX * flipRotation);
|
||||
}
|
||||
|
||||
// For special cases: Use transform world properties if transform relationship is complicated
|
||||
} else {
|
||||
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f));
|
||||
if (!followZPosition)
|
||||
targetWorldPosition.z = cachedTransform.position.z;
|
||||
targetWorldPosition.z = thisTransform.position.z;
|
||||
|
||||
cachedTransform.position = targetWorldPosition;
|
||||
thisTransform.position = targetWorldPosition;
|
||||
|
||||
if (followBoneRotation) {
|
||||
Vector3 rotation = skeletonTransform.rotation.eulerAngles;
|
||||
|
||||
cachedTransform.rotation = Quaternion.Euler(rotation.x, rotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.WorldRotationX * flipRotation));
|
||||
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
|
||||
thisTransform.rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.WorldRotationX * flipRotation));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
* SkeletonBaker added by Mitch Thompson
|
||||
* Full irrevocable rights and permissions granted to Esoteric Software
|
||||
*****************************************************************************/
|
||||
#define SPINE_SKELETON_ANIMATOR
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
@ -44,6 +46,133 @@ using Spine;
|
||||
|
||||
/// </summary>
|
||||
public static class SkeletonBaker {
|
||||
|
||||
#region SkeletonAnimator's Mecanim Clips
|
||||
#if SPINE_SKELETON_ANIMATOR
|
||||
public static void GenerateMecanimAnimationClips (SkeletonDataAsset skeletonDataAsset) {
|
||||
//skeletonDataAsset.Clear();
|
||||
var data = skeletonDataAsset.GetSkeletonData(true);
|
||||
if (data == null) {
|
||||
Debug.LogError("SkeletonData failed!", skeletonDataAsset);
|
||||
return;
|
||||
}
|
||||
|
||||
string dataPath = AssetDatabase.GetAssetPath(skeletonDataAsset);
|
||||
string controllerPath = dataPath.Replace("_SkeletonData", "_Controller").Replace(".asset", ".controller");
|
||||
|
||||
#if UNITY_5
|
||||
UnityEditor.Animations.AnimatorController controller;
|
||||
|
||||
if (skeletonDataAsset.controller != null) {
|
||||
controller = (UnityEditor.Animations.AnimatorController)skeletonDataAsset.controller;
|
||||
controllerPath = AssetDatabase.GetAssetPath(controller);
|
||||
} else {
|
||||
if (File.Exists(controllerPath)) {
|
||||
if (EditorUtility.DisplayDialog("Controller Overwrite Warning", "Unknown Controller already exists at: " + controllerPath, "Update", "Overwrite")) {
|
||||
controller = (UnityEditor.Animations.AnimatorController)AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController));
|
||||
} else {
|
||||
controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
} else {
|
||||
controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
UnityEditorInternal.AnimatorController controller;
|
||||
|
||||
if (skeletonDataAsset.controller != null) {
|
||||
controller = (UnityEditorInternal.AnimatorController)skeletonDataAsset.controller;
|
||||
controllerPath = AssetDatabase.GetAssetPath(controller);
|
||||
} else {
|
||||
if (File.Exists(controllerPath)) {
|
||||
if (EditorUtility.DisplayDialog("Controller Overwrite Warning", "Unknown Controller already exists at: " + controllerPath, "Update", "Overwrite")) {
|
||||
controller = (UnityEditorInternal.AnimatorController)AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController));
|
||||
} else {
|
||||
controller = (UnityEditorInternal.AnimatorController)UnityEditorInternal.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
} else {
|
||||
controller = (UnityEditorInternal.AnimatorController)UnityEditorInternal.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
skeletonDataAsset.controller = controller;
|
||||
EditorUtility.SetDirty(skeletonDataAsset);
|
||||
|
||||
UnityEngine.Object[] objs = AssetDatabase.LoadAllAssetsAtPath(controllerPath);
|
||||
|
||||
var unityAnimationClipTable = new Dictionary<string, AnimationClip>();
|
||||
var spineAnimationTable = new Dictionary<string, Spine.Animation>();
|
||||
|
||||
foreach (var o in objs) {
|
||||
//Debug.LogFormat("({0}){1} : {3} + {2} + {4}", o.GetType(), o.name, o.hideFlags, o.GetInstanceID(), o.GetHashCode());
|
||||
// There is a bug in Unity 5.3.3 (and likely before) that creates
|
||||
// a duplicate AnimationClip when you duplicate a Mecanim Animator State.
|
||||
// These duplicates seem to be identifiable by their HideFlags, so we'll exclude them.
|
||||
if (o is AnimationClip) {
|
||||
var clip = o as AnimationClip;
|
||||
if (!clip.HasFlag(HideFlags.HideInHierarchy)) {
|
||||
if (unityAnimationClipTable.ContainsKey(clip.name)) {
|
||||
Debug.LogWarningFormat("Duplicate AnimationClips were found named {0}", clip.name);
|
||||
}
|
||||
unityAnimationClipTable.Add(clip.name, clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var anim in data.Animations) {
|
||||
string name = anim.Name;
|
||||
spineAnimationTable.Add(name, anim);
|
||||
|
||||
if (unityAnimationClipTable.ContainsKey(name) == false) {
|
||||
//generate new dummy clip
|
||||
AnimationClip newClip = new AnimationClip();
|
||||
newClip.name = name;
|
||||
#if !(UNITY_5)
|
||||
AnimationUtility.SetAnimationType(newClip, ModelImporterAnimationType.Generic);
|
||||
#endif
|
||||
AssetDatabase.AddObjectToAsset(newClip, controller);
|
||||
unityAnimationClipTable.Add(name, newClip);
|
||||
}
|
||||
|
||||
AnimationClip clip = unityAnimationClipTable[name];
|
||||
|
||||
clip.SetCurve("", typeof(GameObject), "dummy", AnimationCurve.Linear(0, 0, anim.Duration, 0));
|
||||
var settings = AnimationUtility.GetAnimationClipSettings(clip);
|
||||
settings.stopTime = anim.Duration;
|
||||
|
||||
SetAnimationSettings(clip, settings);
|
||||
|
||||
AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]);
|
||||
|
||||
foreach (Timeline t in anim.Timelines) {
|
||||
if (t is EventTimeline) {
|
||||
ParseEventTimeline((EventTimeline)t, clip, SendMessageOptions.DontRequireReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(clip);
|
||||
|
||||
unityAnimationClipTable.Remove(name);
|
||||
}
|
||||
|
||||
//clear no longer used animations
|
||||
foreach (var clip in unityAnimationClipTable.Values) {
|
||||
AnimationClip.DestroyImmediate(clip, true);
|
||||
}
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
static bool HasFlag (this UnityEngine.Object o, HideFlags flagToCheck) {
|
||||
return (o.hideFlags & flagToCheck) == flagToCheck;
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Baking
|
||||
/// <summary>
|
||||
/// Interval between key sampling for Bezier curves, IK controlled bones, and Inherit Rotation effected bones.
|
||||
/// </summary>
|
||||
@ -89,8 +218,8 @@ public static class SkeletonBaker {
|
||||
}
|
||||
#endif
|
||||
|
||||
Dictionary<string, AnimationClip> existingClipTable = new Dictionary<string, AnimationClip>();
|
||||
List<string> unusedClipNames = new List<string>();
|
||||
var existingClipTable = new Dictionary<string, AnimationClip>();
|
||||
var unusedClipNames = new List<string>();
|
||||
Object[] animObjs = AssetDatabase.LoadAllAssetsAtPath(controllerPath);
|
||||
|
||||
foreach (Object o in animObjs) {
|
||||
@ -344,115 +473,6 @@ public static class SkeletonBaker {
|
||||
|
||||
}
|
||||
|
||||
public static void GenerateMecanimAnimationClips (SkeletonDataAsset skeletonDataAsset) {
|
||||
var data = skeletonDataAsset.GetSkeletonData(true);
|
||||
if (data == null) {
|
||||
Debug.LogError("SkeletonData failed!", skeletonDataAsset);
|
||||
return;
|
||||
}
|
||||
|
||||
string dataPath = AssetDatabase.GetAssetPath(skeletonDataAsset);
|
||||
string controllerPath = dataPath.Replace("_SkeletonData", "_Controller").Replace(".asset", ".controller");
|
||||
|
||||
|
||||
#if UNITY_5
|
||||
UnityEditor.Animations.AnimatorController controller;
|
||||
|
||||
if (skeletonDataAsset.controller != null) {
|
||||
controller = (UnityEditor.Animations.AnimatorController)skeletonDataAsset.controller;
|
||||
controllerPath = AssetDatabase.GetAssetPath(controller);
|
||||
} else {
|
||||
if (File.Exists(controllerPath)) {
|
||||
if (EditorUtility.DisplayDialog("Controller Overwrite Warning", "Unknown Controller already exists at: " + controllerPath, "Update", "Overwrite")) {
|
||||
controller = (UnityEditor.Animations.AnimatorController)AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController));
|
||||
} else {
|
||||
controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
} else {
|
||||
controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
UnityEditorInternal.AnimatorController controller;
|
||||
|
||||
if (skeletonDataAsset.controller != null) {
|
||||
controller = (UnityEditorInternal.AnimatorController)skeletonDataAsset.controller;
|
||||
controllerPath = AssetDatabase.GetAssetPath(controller);
|
||||
} else {
|
||||
if (File.Exists(controllerPath)) {
|
||||
if (EditorUtility.DisplayDialog("Controller Overwrite Warning", "Unknown Controller already exists at: " + controllerPath, "Update", "Overwrite")) {
|
||||
controller = (UnityEditorInternal.AnimatorController)AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController));
|
||||
} else {
|
||||
controller = (UnityEditorInternal.AnimatorController)UnityEditorInternal.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
} else {
|
||||
controller = (UnityEditorInternal.AnimatorController)UnityEditorInternal.AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
skeletonDataAsset.controller = controller;
|
||||
EditorUtility.SetDirty(skeletonDataAsset);
|
||||
|
||||
Object[] objs = AssetDatabase.LoadAllAssetsAtPath(controllerPath);
|
||||
|
||||
Dictionary<string, AnimationClip> clipTable = new Dictionary<string, AnimationClip>();
|
||||
Dictionary<string, Spine.Animation> animTable = new Dictionary<string, Spine.Animation>();
|
||||
|
||||
foreach (var o in objs) {
|
||||
if (o is AnimationClip) {
|
||||
clipTable.Add(o.name, (AnimationClip)o);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var anim in data.Animations) {
|
||||
string name = anim.Name;
|
||||
animTable.Add(name, anim);
|
||||
|
||||
if (clipTable.ContainsKey(name) == false) {
|
||||
//generate new dummy clip
|
||||
AnimationClip newClip = new AnimationClip();
|
||||
newClip.name = name;
|
||||
#if UNITY_5
|
||||
#else
|
||||
AnimationUtility.SetAnimationType(newClip, ModelImporterAnimationType.Generic);
|
||||
#endif
|
||||
|
||||
AssetDatabase.AddObjectToAsset(newClip, controller);
|
||||
clipTable.Add(name, newClip);
|
||||
}
|
||||
|
||||
AnimationClip clip = clipTable[name];
|
||||
|
||||
clip.SetCurve("", typeof(GameObject), "dummy", AnimationCurve.Linear(0, 0, anim.Duration, 0));
|
||||
var settings = AnimationUtility.GetAnimationClipSettings(clip);
|
||||
settings.stopTime = anim.Duration;
|
||||
|
||||
SetAnimationSettings(clip, settings);
|
||||
|
||||
AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]);
|
||||
|
||||
foreach (Timeline t in anim.Timelines) {
|
||||
if (t is EventTimeline) {
|
||||
ParseEventTimeline((EventTimeline)t, clip, SendMessageOptions.DontRequireReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(clip);
|
||||
|
||||
clipTable.Remove(name);
|
||||
}
|
||||
|
||||
//clear no longer used animations
|
||||
foreach (var clip in clipTable.Values) {
|
||||
AnimationClip.DestroyImmediate(clip, true);
|
||||
}
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
static Bone extractionBone;
|
||||
static Slot extractionSlot;
|
||||
|
||||
@ -747,17 +767,6 @@ public static class SkeletonBaker {
|
||||
return arr;
|
||||
}
|
||||
|
||||
static void SetAnimationSettings (AnimationClip clip, AnimationClipSettings settings) {
|
||||
#if UNITY_5
|
||||
AnimationUtility.SetAnimationClipSettings(clip, settings);
|
||||
#else
|
||||
MethodInfo methodInfo = typeof(AnimationUtility).GetMethod("SetAnimationClipSettings", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
methodInfo.Invoke(null, new object[] { clip, settings });
|
||||
|
||||
EditorUtility.SetDirty(clip);
|
||||
#endif
|
||||
}
|
||||
|
||||
static AnimationClip ExtractAnimation (string name, SkeletonData skeletonData, Dictionary<int, List<string>> slotLookup, bool bakeIK, SendMessageOptions eventOptions, AnimationClip clip = null) {
|
||||
var animation = skeletonData.FindAnimation(name);
|
||||
|
||||
@ -864,7 +873,7 @@ public static class SkeletonBaker {
|
||||
ae.functionName = ev.Data.Name;
|
||||
ae.messageOptions = eventOptions;
|
||||
|
||||
if (ev.String != "" && ev.String != null) {
|
||||
if (!string.IsNullOrEmpty(ev.String)) {
|
||||
ae.stringParameter = ev.String;
|
||||
} else {
|
||||
if (ev.Int == 0 && ev.Float == 0) {
|
||||
@ -1447,7 +1456,6 @@ public static class SkeletonBaker {
|
||||
AnimationUtility.SetEditorCurve(clip, yBind, new AnimationCurve());
|
||||
EditorCurveBinding zBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".z");
|
||||
AnimationUtility.SetEditorCurve(clip, zBind, curve);
|
||||
|
||||
}
|
||||
|
||||
static string GetPath (BoneData b) {
|
||||
@ -1461,4 +1469,18 @@ public static class SkeletonBaker {
|
||||
|
||||
return GetPathRecurse(b.Parent) + "/" + b.Name;
|
||||
}
|
||||
#endregion
|
||||
|
||||
static void SetAnimationSettings (AnimationClip clip, AnimationClipSettings settings) {
|
||||
#if UNITY_5
|
||||
AnimationUtility.SetAnimationClipSettings(clip, settings);
|
||||
#else
|
||||
MethodInfo methodInfo = typeof(AnimationUtility).GetMethod("SetAnimationClipSettings", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
methodInfo.Invoke(null, new object[] { clip, settings });
|
||||
|
||||
EditorUtility.SetDirty(clip);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -117,8 +117,8 @@ public class SkeletonRendererInspector : Editor {
|
||||
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
const float MinZSpacing = 0f;
|
||||
const float MaxZSpacing = 0.1f;
|
||||
const float MinZSpacing = -0.1f;
|
||||
const float MaxZSpacing = 0f;
|
||||
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
|
||||
|
||||
EditorGUILayout.PropertyField(normals);
|
||||
|
||||
@ -1135,10 +1135,13 @@ public class SpineEditorUtilities : AssetPostprocessor {
|
||||
static void EnableTK2D () {
|
||||
bool added = false;
|
||||
foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
|
||||
if (group == BuildTargetGroup.Unknown)
|
||||
continue;
|
||||
|
||||
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
|
||||
if (!defines.Contains(SPINE_TK2D_DEFINE)) {
|
||||
added = true;
|
||||
if (defines.EndsWith(";"))
|
||||
if (defines.EndsWith(";", System.StringComparison.Ordinal))
|
||||
defines = defines + SPINE_TK2D_DEFINE;
|
||||
else
|
||||
defines = defines + ";" + SPINE_TK2D_DEFINE;
|
||||
|
||||
@ -94,21 +94,20 @@ public class SkeletonUtilityInspector : Editor {
|
||||
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
|
||||
}
|
||||
|
||||
void OnSceneGUI () {
|
||||
if (skeleton == null) {
|
||||
OnEnable();
|
||||
return;
|
||||
}
|
||||
|
||||
float flipRotation = skeleton.FlipX ? -1 : 1;
|
||||
// MITCH
|
||||
//float flipRotation = skeleton.FlipX ? -1 : 1;
|
||||
const float flipRotation = 1;
|
||||
|
||||
foreach (Bone b in skeleton.Bones) {
|
||||
Vector3 vec = transform.TransformPoint(new Vector3(b.WorldX, b.WorldY, 0));
|
||||
|
||||
// MITCH
|
||||
Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX * flipRotation);
|
||||
Vector3 forward = transform.TransformDirection(rot * Vector3.right);
|
||||
forward *= flipRotation;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user