mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
230542fb7f
@ -35,33 +35,47 @@ using Spine;
|
|||||||
|
|
||||||
public class CustomSkin : MonoBehaviour {
|
public class CustomSkin : MonoBehaviour {
|
||||||
|
|
||||||
|
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class SkinPair {
|
public class SkinPair {
|
||||||
|
/// <summary>SpineAttachment attachment path to help find the attachment.</summary>
|
||||||
|
/// <remarks>This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment.</remarks>
|
||||||
[SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")]
|
[SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")]
|
||||||
public string sourceAttachment;
|
[UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")]
|
||||||
|
public string sourceAttachmentPath;
|
||||||
|
|
||||||
[SpineSlot]
|
[SpineSlot]
|
||||||
public string targetSlot;
|
public string targetSlot;
|
||||||
|
|
||||||
|
/// <summary>The name of the skin placeholder/skin dictionary entry this attachment should be associated with.</summary>
|
||||||
|
/// <remarks>This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment</remarks>
|
||||||
[SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)]
|
[SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)]
|
||||||
public string targetAttachment;
|
public string targetAttachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Inspector
|
||||||
public SkeletonDataAsset skinSource;
|
public SkeletonDataAsset skinSource;
|
||||||
public SkinPair[] skinning;
|
|
||||||
|
[UnityEngine.Serialization.FormerlySerializedAs("skinning")]
|
||||||
|
public SkinPair[] skinItems;
|
||||||
|
|
||||||
public Skin customSkin;
|
public Skin customSkin;
|
||||||
|
#endregion
|
||||||
|
|
||||||
SkeletonRenderer skeletonRenderer;
|
SkeletonRenderer skeletonRenderer;
|
||||||
|
|
||||||
void Start () {
|
void Start () {
|
||||||
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||||
Skeleton skeleton = skeletonRenderer.skeleton;
|
Skeleton skeleton = skeletonRenderer.skeleton;
|
||||||
|
|
||||||
customSkin = new Skin("CustomSkin");
|
customSkin = new Skin("CustomSkin");
|
||||||
|
|
||||||
foreach (var pair in skinning) {
|
foreach (var pair in skinItems) {
|
||||||
var attachment = SpineAttachment.GetAttachment(pair.sourceAttachment, skinSource);
|
var attachment = SpineAttachment.GetAttachment(pair.sourceAttachmentPath, skinSource);
|
||||||
customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment);
|
customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The custom skin does not need to be added to the skeleton data for it to work.
|
||||||
|
// But it's useful for your script to keep a reference to it.
|
||||||
skeleton.SetSkin(customSkin);
|
skeleton.SetSkin(customSkin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ using Spine;
|
|||||||
public class SkeletonAnimationInspector : SkeletonRendererInspector {
|
public class SkeletonAnimationInspector : SkeletonRendererInspector {
|
||||||
protected SerializedProperty animationName, loop, timeScale, autoReset;
|
protected SerializedProperty animationName, loop, timeScale, autoReset;
|
||||||
protected bool m_isPrefab;
|
protected bool m_isPrefab;
|
||||||
protected GUIContent autoResetLabel;
|
protected bool wasAnimationNameChanged;
|
||||||
|
|
||||||
protected override void OnEnable () {
|
protected override void OnEnable () {
|
||||||
base.OnEnable();
|
base.OnEnable();
|
||||||
@ -56,43 +56,45 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
|
|||||||
SkeletonAnimation component = (SkeletonAnimation)target;
|
SkeletonAnimation component = (SkeletonAnimation)target;
|
||||||
if (!component.valid)
|
if (!component.valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (wasAnimationNameChanged) {
|
||||||
|
if (!Application.isPlaying) {
|
||||||
|
component.state.ClearTrack(0);
|
||||||
|
component.skeleton.SetToSetupPose();
|
||||||
|
}
|
||||||
|
|
||||||
//catch case where SetAnimation was used to set track 0 without using AnimationName
|
Spine.Animation animationToUse = component.skeleton.Data.FindAnimation(animationName.stringValue);
|
||||||
|
|
||||||
|
if (!Application.isPlaying) {
|
||||||
|
if (animationToUse != null) animationToUse.Apply(component.skeleton, 0f, 0f, false, null);
|
||||||
|
component.Update();
|
||||||
|
component.LateUpdate();
|
||||||
|
SceneView.RepaintAll();
|
||||||
|
} else {
|
||||||
|
if (animationToUse != null)
|
||||||
|
component.state.SetAnimation(0, animationToUse, loop.boolValue);
|
||||||
|
else
|
||||||
|
component.state.ClearTrack(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
wasAnimationNameChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflect animationName serialized property in the inspector even if SetAnimation API was used.
|
||||||
if (Application.isPlaying) {
|
if (Application.isPlaying) {
|
||||||
TrackEntry currentState = component.state.GetCurrent(0);
|
TrackEntry current = component.state.GetCurrent(0);
|
||||||
if (currentState != null) {
|
if (current != null) {
|
||||||
if (component.AnimationName != animationName.stringValue) {
|
if (component.AnimationName != animationName.stringValue) {
|
||||||
animationName.stringValue = currentState.Animation.Name;
|
animationName.stringValue = current.Animation.Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
//TODO: Refactor this to use GenericMenu and callbacks to avoid interfering with control by other behaviours.
|
EditorGUILayout.PropertyField(animationName);
|
||||||
// Animation name.
|
wasAnimationNameChanged |= EditorGUI.EndChangeCheck();
|
||||||
{
|
|
||||||
String[] animations = new String[component.skeleton.Data.Animations.Count + 1];
|
|
||||||
animations[0] = "<None>";
|
|
||||||
int animationIndex = 0;
|
|
||||||
for (int i = 0; i < animations.Length - 1; i++) {
|
|
||||||
String name = component.skeleton.Data.Animations.Items[i].Name;
|
|
||||||
animations[i + 1] = name;
|
|
||||||
if (name == animationName.stringValue)
|
|
||||||
animationIndex = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
animationIndex = EditorGUILayout.Popup("Animation", animationIndex, animations);
|
|
||||||
|
|
||||||
String selectedAnimationName = animationIndex == 0 ? null : animations[animationIndex];
|
|
||||||
if (component.AnimationName != selectedAnimationName) {
|
|
||||||
component.AnimationName = selectedAnimationName;
|
|
||||||
animationName.stringValue = selectedAnimationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUILayout.PropertyField(loop);
|
EditorGUILayout.PropertyField(loop);
|
||||||
EditorGUILayout.PropertyField(timeScale);
|
EditorGUILayout.PropertyField(timeScale);
|
||||||
component.timeScale = Math.Max(component.timeScale, 0);
|
component.timeScale = Math.Max(component.timeScale, 0);
|
||||||
|
|||||||
@ -5,17 +5,19 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Spine;
|
using Spine;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public struct SpineDrawerValuePair {
|
public struct SpineDrawerValuePair {
|
||||||
public string str;
|
public string str;
|
||||||
public SerializedProperty property;
|
public SerializedProperty property;
|
||||||
|
|
||||||
public SpineDrawerValuePair(string val, SerializedProperty property) {
|
public SpineDrawerValuePair (string val, SerializedProperty property) {
|
||||||
this.str = val;
|
this.str = val;
|
||||||
this.property = property;
|
this.property = property;
|
||||||
}
|
}
|
||||||
@ -23,6 +25,7 @@ public struct SpineDrawerValuePair {
|
|||||||
|
|
||||||
public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
|
public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
|
||||||
protected SkeletonDataAsset skeletonDataAsset;
|
protected SkeletonDataAsset skeletonDataAsset;
|
||||||
|
|
||||||
protected T TargetAttribute { get { return (T)attribute; } }
|
protected T TargetAttribute { get { return (T)attribute; } }
|
||||||
|
|
||||||
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
|
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
|
||||||
@ -72,7 +75,7 @@ public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineA
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
GenericMenu menu = new GenericMenu();
|
GenericMenu menu = new GenericMenu();
|
||||||
PopulateMenu (menu, property, this.TargetAttribute, data);
|
PopulateMenu(menu, property, this.TargetAttribute, data);
|
||||||
menu.ShowAsContext();
|
menu.ShowAsContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +87,7 @@ public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineA
|
|||||||
pair.property.serializedObject.ApplyModifiedProperties();
|
pair.property.serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
|
||||||
return 18;
|
return 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +99,7 @@ public class SpineSlotDrawer : SpineTreeItemDrawerBase<SpineSlot> {
|
|||||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
|
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
|
||||||
for (int i = 0; i < data.Slots.Count; i++) {
|
for (int i = 0; i < data.Slots.Count; i++) {
|
||||||
string name = data.Slots.Items[i].Name;
|
string name = data.Slots.Items[i].Name;
|
||||||
if (name.StartsWith(targetAttribute.startsWith)) {
|
if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) {
|
||||||
if (targetAttribute.containsBoundingBoxes) {
|
if (targetAttribute.containsBoundingBoxes) {
|
||||||
|
|
||||||
int slotIndex = i;
|
int slotIndex = i;
|
||||||
@ -139,7 +142,7 @@ public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
|
|||||||
|
|
||||||
for (int i = 0; i < data.Skins.Count; i++) {
|
for (int i = 0; i < data.Skins.Count; i++) {
|
||||||
string name = data.Skins.Items[i].Name;
|
string name = data.Skins.Items[i].Name;
|
||||||
if (name.StartsWith(targetAttribute.startsWith))
|
if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
|
||||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,9 +153,13 @@ public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
|
|||||||
public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
|
public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
|
||||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
|
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
|
||||||
var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
|
var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
|
||||||
|
|
||||||
|
// <None> item
|
||||||
|
menu.AddItem(new GUIContent("<None>"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
|
||||||
|
|
||||||
for (int i = 0; i < animations.Count; i++) {
|
for (int i = 0; i < animations.Count; i++) {
|
||||||
string name = animations.Items[i].Name;
|
string name = animations.Items[i].Name;
|
||||||
if (name.StartsWith(targetAttribute.startsWith))
|
if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
|
||||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,7 +172,7 @@ public class SpineEventNameDrawer : SpineTreeItemDrawerBase<SpineEvent> {
|
|||||||
var events = skeletonDataAsset.GetSkeletonData(false).Events;
|
var events = skeletonDataAsset.GetSkeletonData(false).Events;
|
||||||
for (int i = 0; i < events.Count; i++) {
|
for (int i = 0; i < events.Count; i++) {
|
||||||
string name = events.Items[i].Name;
|
string name = events.Items[i].Name;
|
||||||
if (name.StartsWith(targetAttribute.startsWith))
|
if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
|
||||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,8 +296,8 @@ public class SpineBoneDrawer : SpineTreeItemDrawerBase<SpineBone> {
|
|||||||
public class SpineAtlasRegionDrawer : PropertyDrawer {
|
public class SpineAtlasRegionDrawer : PropertyDrawer {
|
||||||
Component component;
|
Component component;
|
||||||
SerializedProperty atlasProp;
|
SerializedProperty atlasProp;
|
||||||
|
|
||||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
|
||||||
if (property.propertyType != SerializedPropertyType.String) {
|
if (property.propertyType != SerializedPropertyType.String) {
|
||||||
EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
|
EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
|
||||||
return;
|
return;
|
||||||
@ -321,7 +328,7 @@ public class SpineAtlasRegionDrawer : PropertyDrawer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selector (SerializedProperty property) {
|
void Selector (SerializedProperty property) {
|
||||||
GenericMenu menu = new GenericMenu();
|
GenericMenu menu = new GenericMenu();
|
||||||
AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
|
AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
|
||||||
@ -337,7 +344,7 @@ public class SpineAtlasRegionDrawer : PropertyDrawer {
|
|||||||
|
|
||||||
menu.ShowAsContext();
|
menu.ShowAsContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HandleSelect (object val) {
|
static void HandleSelect (object val) {
|
||||||
var pair = (SpineDrawerValuePair)val;
|
var pair = (SpineDrawerValuePair)val;
|
||||||
pair.property.stringValue = pair.str;
|
pair.property.stringValue = pair.str;
|
||||||
|
|||||||
@ -68,6 +68,7 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
|
[SpineAnimation]
|
||||||
private String _animationName;
|
private String _animationName;
|
||||||
|
|
||||||
public String AnimationName {
|
public String AnimationName {
|
||||||
@ -104,39 +105,25 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
|||||||
public bool loop;
|
public bool loop;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.
|
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
|
||||||
/// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</summary>
|
/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
|
||||||
#if UNITY_5
|
#if UNITY_5
|
||||||
[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
|
[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
|
||||||
#endif
|
#endif
|
||||||
public float timeScale = 1;
|
public float timeScale = 1;
|
||||||
|
|
||||||
#region AutoReset
|
#region Runtime Instantiation
|
||||||
/**
|
/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
|
||||||
[Tooltip("Setting this to true makes the SkeletonAnimation behave similar to Spine editor. New animations will not inherit the pose from a previous animation. If you need to intermittently and programmatically pose your skeleton, leave this false.")]
|
/// <returns>The newly instantiated SkeletonAnimation</returns>
|
||||||
[SerializeField]
|
public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) {
|
||||||
protected bool autoReset = false;
|
return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Setting this to true makes the SkeletonAnimation behave similar to Spine editor.
|
|
||||||
/// New animations will not inherit the pose from a previous animation.
|
|
||||||
/// If you need to intermittently and programmatically pose your skeleton, leave this false.</summary>
|
|
||||||
public bool AutoReset {
|
|
||||||
get { return this.autoReset; }
|
|
||||||
set {
|
|
||||||
if (!autoReset && value) {
|
|
||||||
state.Start -= HandleNewAnimationAutoreset; // make sure there isn't a double-subscription.
|
|
||||||
state.Start += HandleNewAnimationAutoreset;
|
|
||||||
}
|
|
||||||
autoReset = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void HandleNewAnimationAutoreset (Spine.AnimationState state, int trackIndex) {
|
/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
|
||||||
if (!autoReset) return;
|
/// <returns>The newly instantiated SkeletonAnimation component.</returns>
|
||||||
if (skeleton != null) skeleton.SetToSetupPose();
|
public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) {
|
||||||
|
return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public override void Reset () {
|
public override void Reset () {
|
||||||
@ -146,16 +133,24 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
|||||||
|
|
||||||
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
|
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
|
||||||
|
|
||||||
/*
|
#if UNITY_EDITOR
|
||||||
if (autoReset) {
|
if (!string.IsNullOrEmpty(_animationName)) {
|
||||||
state.Start += HandleNewAnimationAutoreset;
|
if (Application.isPlaying) {
|
||||||
|
state.SetAnimation(0, _animationName, loop);
|
||||||
|
} else {
|
||||||
|
// Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above.
|
||||||
|
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
|
||||||
|
if (animationObject != null)
|
||||||
|
animationObject.Apply(skeleton, 0f, 0f, false, null);
|
||||||
|
}
|
||||||
|
Update(0);
|
||||||
}
|
}
|
||||||
*/
|
#else
|
||||||
|
if (!string.IsNullOrEmpty(_animationName)) {
|
||||||
if (_animationName != null && _animationName.Length > 0) {
|
|
||||||
state.SetAnimation(0, _animationName, loop);
|
state.SetAnimation(0, _animationName, loop);
|
||||||
Update(0);
|
Update(0);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Update () {
|
public virtual void Update () {
|
||||||
@ -185,4 +180,5 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
|||||||
_UpdateComplete(this);
|
_UpdateComplete(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Spine Extensions created by Mitch Thompson
|
* Spine Extensions by Mitch Thompson and John Dy
|
||||||
* Full irrevocable rights and permissions granted to Esoteric Software
|
* Full irrevocable rights and permissions granted to Esoteric Software
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
@ -11,6 +11,14 @@ using Spine;
|
|||||||
|
|
||||||
public static class SkeletonExtensions {
|
public static class SkeletonExtensions {
|
||||||
|
|
||||||
|
const float ByteToFloat = 1f / 255f;
|
||||||
|
|
||||||
|
#region Colors
|
||||||
|
public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); }
|
||||||
|
public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||||
|
public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||||
|
public static Color GetColor (this SkinnedMeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
|
||||||
|
|
||||||
public static void SetColor (this Skeleton skeleton, Color color) {
|
public static void SetColor (this Skeleton skeleton, Color color) {
|
||||||
skeleton.A = color.a;
|
skeleton.A = color.a;
|
||||||
skeleton.R = color.r;
|
skeleton.R = color.r;
|
||||||
@ -19,10 +27,10 @@ public static class SkeletonExtensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this Skeleton skeleton, Color32 color) {
|
public static void SetColor (this Skeleton skeleton, Color32 color) {
|
||||||
skeleton.A = color.a / 255f;
|
skeleton.A = color.a * ByteToFloat;
|
||||||
skeleton.R = color.r / 255f;
|
skeleton.R = color.r * ByteToFloat;
|
||||||
skeleton.G = color.g / 255f;
|
skeleton.G = color.g * ByteToFloat;
|
||||||
skeleton.B = color.b / 255f;
|
skeleton.B = color.b * ByteToFloat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this Slot slot, Color color) {
|
public static void SetColor (this Slot slot, Color color) {
|
||||||
@ -33,10 +41,10 @@ public static class SkeletonExtensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this Slot slot, Color32 color) {
|
public static void SetColor (this Slot slot, Color32 color) {
|
||||||
slot.A = color.a / 255f;
|
slot.A = color.a * ByteToFloat;
|
||||||
slot.R = color.r / 255f;
|
slot.R = color.r * ByteToFloat;
|
||||||
slot.G = color.g / 255f;
|
slot.G = color.g * ByteToFloat;
|
||||||
slot.B = color.b / 255f;
|
slot.B = color.b * ByteToFloat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this RegionAttachment attachment, Color color) {
|
public static void SetColor (this RegionAttachment attachment, Color color) {
|
||||||
@ -47,10 +55,10 @@ public static class SkeletonExtensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this RegionAttachment attachment, Color32 color) {
|
public static void SetColor (this RegionAttachment attachment, Color32 color) {
|
||||||
attachment.A = color.a / 255f;
|
attachment.A = color.a * ByteToFloat;
|
||||||
attachment.R = color.r / 255f;
|
attachment.R = color.r * ByteToFloat;
|
||||||
attachment.G = color.g / 255f;
|
attachment.G = color.g * ByteToFloat;
|
||||||
attachment.B = color.b / 255f;
|
attachment.B = color.b * ByteToFloat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this MeshAttachment attachment, Color color) {
|
public static void SetColor (this MeshAttachment attachment, Color color) {
|
||||||
@ -61,10 +69,10 @@ public static class SkeletonExtensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this MeshAttachment attachment, Color32 color) {
|
public static void SetColor (this MeshAttachment attachment, Color32 color) {
|
||||||
attachment.A = color.a / 255f;
|
attachment.A = color.a * ByteToFloat;
|
||||||
attachment.R = color.r / 255f;
|
attachment.R = color.r * ByteToFloat;
|
||||||
attachment.G = color.g / 255f;
|
attachment.G = color.g * ByteToFloat;
|
||||||
attachment.B = color.b / 255f;
|
attachment.B = color.b * ByteToFloat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this SkinnedMeshAttachment attachment, Color color) {
|
public static void SetColor (this SkinnedMeshAttachment attachment, Color color) {
|
||||||
@ -75,12 +83,14 @@ public static class SkeletonExtensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor (this SkinnedMeshAttachment attachment, Color32 color) {
|
public static void SetColor (this SkinnedMeshAttachment attachment, Color32 color) {
|
||||||
attachment.A = color.a / 255f;
|
attachment.A = color.a * ByteToFloat;
|
||||||
attachment.R = color.r / 255f;
|
attachment.R = color.r * ByteToFloat;
|
||||||
attachment.G = color.g / 255f;
|
attachment.G = color.g * ByteToFloat;
|
||||||
attachment.B = color.b / 255f;
|
attachment.B = color.b * ByteToFloat;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Bone Position
|
||||||
public static void SetPosition (this Bone bone, Vector2 position) {
|
public static void SetPosition (this Bone bone, Vector2 position) {
|
||||||
bone.X = position.x;
|
bone.X = position.x;
|
||||||
bone.Y = position.y;
|
bone.Y = position.y;
|
||||||
@ -91,10 +101,36 @@ public static class SkeletonExtensions {
|
|||||||
bone.Y = position.y;
|
bone.Y = position.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vector2 GetSkeletonSpacePosition (this Bone bone) {
|
||||||
|
// TODO: This changes in v3.0
|
||||||
|
return new Vector2(bone.worldX, bone.worldY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) {
|
||||||
|
return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Posing
|
||||||
|
/// <summary>
|
||||||
|
/// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds.
|
||||||
|
/// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that.</summary>
|
||||||
|
/// <param name = "skeleton">The skeleton to pose.</param>
|
||||||
|
/// <param name="animationName">The name of the animation to use.</param>
|
||||||
|
/// <param name = "time">The time of the pose within the animation.</param>
|
||||||
|
/// <param name = "loop">Wraps the time around if it is longer than the duration of the animation.</param>
|
||||||
|
public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop) {
|
||||||
|
// Fail loud when skeleton.data is null.
|
||||||
|
Spine.Animation animation = skeleton.data.FindAnimation(animationName);
|
||||||
|
if (animation == null) return;
|
||||||
|
animation.Apply(skeleton, 0, time, loop, null);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unity Sprite To Attachments
|
||||||
public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") {
|
public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") {
|
||||||
var att = sprite.ToRegionAttachment(shaderName);
|
var att = sprite.ToRegionAttachment(shaderName);
|
||||||
skeleton.FindSlot(slotName).Attachment = att;
|
skeleton.FindSlot(slotName).Attachment = att;
|
||||||
|
|
||||||
return att;
|
return att;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,4 +153,6 @@ public static class SkeletonExtensions {
|
|||||||
loader = null;
|
loader = null;
|
||||||
return att;
|
return att;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -76,6 +76,26 @@ public class SkeletonRenderer : MonoBehaviour {
|
|||||||
private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
|
private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
|
||||||
private SkeletonUtilitySubmeshRenderer[] submeshRenderers;
|
private SkeletonUtilitySubmeshRenderer[] submeshRenderers;
|
||||||
|
|
||||||
|
#region Runtime Instantiation
|
||||||
|
/// <summary>Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime.</summary>
|
||||||
|
/// <typeparam name="T">T should be SkeletonRenderer or any of its derived classes.</typeparam>
|
||||||
|
public static T AddSpineComponent<T> (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
|
||||||
|
|
||||||
|
var c = gameObject.AddComponent<T>();
|
||||||
|
|
||||||
|
if (skeletonDataAsset != null) {
|
||||||
|
c.skeletonDataAsset = skeletonDataAsset;
|
||||||
|
c.Reset(); // TODO: Method name will change.
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
|
||||||
|
return SkeletonRenderer.AddSpineComponent<T>(new GameObject("New Spine GameObject"), skeletonDataAsset);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public virtual void Awake () {
|
public virtual void Awake () {
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,14 +85,14 @@ public class SpineAttachment : SpineAttributeBase {
|
|||||||
/// Smart popup menu for Spine Attachments
|
/// Smart popup menu for Spine Attachments
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="currentSkinOnly">Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source.</param>
|
/// <param name="currentSkinOnly">Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source.</param>
|
||||||
/// <param name="returnAttachmentPath">Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName"</param>
|
/// <param name="returnAttachmentPath">Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system.</param>
|
||||||
/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
|
/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
|
||||||
/// <param name="slotField">If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot</param>
|
/// <param name="slotField">If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot</param>
|
||||||
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
|
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
|
||||||
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
|
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
|
||||||
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
|
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
|
||||||
/// </param>
|
/// </param>
|
||||||
public SpineAttachment(bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") {
|
public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") {
|
||||||
this.currentSkinOnly = currentSkinOnly;
|
this.currentSkinOnly = currentSkinOnly;
|
||||||
this.returnAttachmentPath = returnAttachmentPath;
|
this.returnAttachmentPath = returnAttachmentPath;
|
||||||
this.placeholdersOnly = placeholdersOnly;
|
this.placeholdersOnly = placeholdersOnly;
|
||||||
@ -100,11 +100,11 @@ public class SpineAttachment : SpineAttributeBase {
|
|||||||
this.dataField = dataField;
|
this.dataField = dataField;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Hierarchy GetHierarchy(string fullPath) {
|
public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) {
|
||||||
return new Hierarchy(fullPath);
|
return new SpineAttachment.Hierarchy(fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Spine.Attachment GetAttachment(string attachmentPath, Spine.SkeletonData skeletonData) {
|
public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) {
|
||||||
var hierarchy = SpineAttachment.GetHierarchy(attachmentPath);
|
var hierarchy = SpineAttachment.GetHierarchy(attachmentPath);
|
||||||
if (hierarchy.name == "")
|
if (hierarchy.name == "")
|
||||||
return null;
|
return null;
|
||||||
@ -112,16 +112,18 @@ public class SpineAttachment : SpineAttributeBase {
|
|||||||
return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name);
|
return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Spine.Attachment GetAttachment(string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
|
public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
|
||||||
return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
|
return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
|
||||||
public struct Hierarchy {
|
public struct Hierarchy {
|
||||||
public string skin;
|
public string skin;
|
||||||
public string slot;
|
public string slot;
|
||||||
public string name;
|
public string name;
|
||||||
|
|
||||||
public Hierarchy(string fullPath) {
|
public Hierarchy (string fullPath) {
|
||||||
string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
|
string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (chunks.Length == 0) {
|
if (chunks.Length == 0) {
|
||||||
skin = "";
|
skin = "";
|
||||||
@ -157,15 +159,11 @@ public class SpineBone : SpineAttributeBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) {
|
public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) {
|
||||||
if (renderer.skeleton == null)
|
return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName);
|
||||||
return null;
|
|
||||||
|
|
||||||
return renderer.skeleton.FindBone(boneName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) {
|
public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) {
|
||||||
var data = skeletonDataAsset.GetSkeletonData(true);
|
var data = skeletonDataAsset.GetSkeletonData(true);
|
||||||
|
|
||||||
return data.FindBone(boneName);
|
return data.FindBone(boneName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user