[unity][csharp] API Cleanup: removed many redundant extension methods that are now obsolete with Skin and Attachment API of 3.8. Closes #1557. Fixed an error in Skin where Skin.setAttachment() did not work as expected (See #1485). C# implementation is different to ref-impl here as struct keys do not allow for changing key.Attachment later without removing and readding the entry to the dictionary.

This commit is contained in:
Harald Csaszar 2019-11-29 12:31:39 +01:00
parent de84c1000e
commit f2a8c9a3c1
15 changed files with 72 additions and 512 deletions

View File

@ -43,6 +43,11 @@
### Unity
* **Breaking changes**
* Removed all `Spine.Unity.AttachmentTools.SkinUtilities` Skin extension methods. These have become obsoleted and error-prone since the introduction of the new Skin API in 3.8. To fix any compile errors, replace any usage of `Skin` extension methods with their counterparts, e.g. replace occurrances of `skin.AddAttachments()` with `skin.AddSkin()`.
* Removed redundant `Spine.Unity.AttachmentTools` extension method `Skin.GetRepackedAttachments()`. Please use `Skin.GetRepackedSkin()` instead.
* Removed redundant `Spine.Unity.AttachmentTools.AttachmentCloneExtensions` extension methods `Attachment.GetCopy()` and `Attachment.GetLinkedMesh()`. To fix any compile errors, replace any occurrances with `Attachment.Copy()` and `Attachment.NewLinkedMesh()`.
* Removed redundant `Spine.SkeletonExtensions` extension methods `Skeleton.Set*ToSetupPose()`. Also removed less commonly used extension methods `TrackEntry.AllowImmediateQueue()` and `Attachment.IsRenderable()`.
* `Skin.Attachments` now replaces `Skin.GetAttachments()`, returning an `ICollection<SkinEntry>`. This makes access more consistent and intuitive. To fix any compile errors, replace any occurrances of `skin.GetAttachments()` by `skin.Attachments`.
* **Additions**
* Additional **Fix Draw Order** parameter at SkeletonRenderer, defaults to `disabled` (previous behaviour).

View File

@ -260,8 +260,7 @@ namespace Spine {
}
private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
foreach (var entryObj in skin.Attachments.Keys) {
var entry = (Skin.SkinEntry)entryObj;
foreach (var entry in skin.Attachments) {
if (entry.SlotIndex == slotIndex) SortPathConstraintAttachment(entry.Attachment, slotBone);
}
}

View File

@ -39,12 +39,13 @@ namespace Spine {
/// </summary>
public class Skin {
internal string name;
private OrderedDictionary<SkinEntry, Attachment> attachments = new OrderedDictionary<SkinEntry, Attachment>(SkinEntryComparer.Instance);
private OrderedDictionary<SkinKey, SkinEntry> attachments = new OrderedDictionary<SkinKey, SkinEntry>(SkinKeyComparer.Instance);
internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
public string Name { get { return name; } }
public OrderedDictionary<SkinEntry, Attachment> Attachments { get { return attachments; } }
///<summary>Returns all attachments contained in this skin.</summary>
public ICollection<SkinEntry> Attachments { get { return attachments.Values; } }
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<ConstraintData> Constraints { get { return constraints; } }
@ -58,7 +59,7 @@ namespace Spine {
public void SetAttachment (int slotIndex, string name, Attachment attachment) {
if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
if (slotIndex < 0) throw new ArgumentNullException("slotIndex", "slotIndex must be >= 0.");
attachments[new SkinEntry(slotIndex, name, attachment)] = attachment;
attachments[new SkinKey(slotIndex, name)] = new SkinEntry(slotIndex, name, attachment);
}
///<summary>Adds all attachments, bones, and constraints from the specified skin to this skin.</summary>
@ -69,7 +70,7 @@ namespace Spine {
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (SkinEntry entry in skin.attachments.Keys)
foreach (SkinEntry entry in skin.attachments.Values)
SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment);
}
@ -81,7 +82,7 @@ namespace Spine {
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (SkinEntry entry in skin.attachments.Keys) {
foreach (SkinEntry entry in skin.attachments.Values) {
if (entry.Attachment is MeshAttachment)
SetAttachment(entry.SlotIndex, entry.Name,
entry.Attachment != null ? ((MeshAttachment)entry.Attachment).NewLinkedMesh() : null);
@ -93,28 +94,23 @@ namespace Spine {
/// <summary>Returns the attachment for the specified slot index and name, or null.</summary>
/// <returns>May be null.</returns>
public Attachment GetAttachment (int slotIndex, string name) {
var lookup = new SkinEntry(slotIndex, name, null);
Attachment attachment = null;
bool containsKey = attachments.TryGetValue(lookup, out attachment);
return containsKey ? attachment : null;
var lookup = new SkinKey(slotIndex, name);
SkinEntry entry;
bool containsKey = attachments.TryGetValue(lookup, out entry);
return containsKey ? entry.Attachment : null;
}
/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
public void RemoveAttachment (int slotIndex, string name) {
if (slotIndex < 0) throw new ArgumentOutOfRangeException("slotIndex", "slotIndex must be >= 0");
var lookup = new SkinEntry(slotIndex, name, null);
var lookup = new SkinKey(slotIndex, name);
attachments.Remove(lookup);
}
///<summary>Returns all attachments contained in this skin.</summary>
public ICollection<SkinEntry> GetAttachments () {
return this.attachments.Keys;
}
/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
public void GetAttachments (int slotIndex, List<SkinEntry> attachments) {
foreach (SkinEntry entry in this.attachments.Keys)
foreach (SkinEntry entry in this.attachments.Values)
if (entry.SlotIndex == slotIndex) attachments.Add(entry);
}
@ -131,7 +127,7 @@ 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 (SkinEntry entry in oldSkin.attachments.Keys) {
foreach (SkinEntry entry in oldSkin.attachments.Values) {
int slotIndex = entry.SlotIndex;
Slot slot = skeleton.slots.Items[slotIndex];
if (slot.Attachment == entry.Attachment) {
@ -146,13 +142,11 @@ namespace Spine {
private readonly int slotIndex;
private readonly string name;
private readonly Attachment attachment;
internal readonly int hashCode;
public SkinEntry (int slotIndex, string name, Attachment attachment) {
this.slotIndex = slotIndex;
this.name = name;
this.attachment = attachment;
this.hashCode = this.name.GetHashCode() + this.slotIndex * 37;
}
public int SlotIndex {
@ -175,17 +169,43 @@ namespace Spine {
}
}
// Avoids boxing in the dictionary and is necessary to omit entry.attachment in the comparison.
class SkinEntryComparer : IEqualityComparer<SkinEntry> {
internal static readonly SkinEntryComparer Instance = new SkinEntryComparer();
/// Note: not present in libgdx reference implementation.
/// SkinKey contains slot index and name only, as a once-added SkinEntry cannot have its Attachment changed
/// (SkinEntry is a value type as opposed to the java reference implementation) without removing and
/// re-adding it to a dictionary, leading to problems in order and unnecessary operations.
private struct SkinKey {
private readonly int slotIndex;
private readonly string name;
bool IEqualityComparer<SkinEntry>.Equals (SkinEntry e1, SkinEntry e2) {
public SkinKey (int slotIndex, string name) {
this.slotIndex = slotIndex;
this.name = name;
}
public int SlotIndex {
get {
return slotIndex;
}
}
/// <summary>The name the attachment is associated with, equivalent to the skin placeholder name in the Spine editor.</summary>
public String Name {
get {
return name;
}
}
}
class SkinKeyComparer : IEqualityComparer<SkinKey> {
internal static readonly SkinKeyComparer Instance = new SkinKeyComparer();
bool IEqualityComparer<SkinKey>.Equals (SkinKey e1, SkinKey e2) {
if (e1.SlotIndex != e2.SlotIndex) return false;
if (!string.Equals(e1.Name, e2.Name, StringComparison.Ordinal)) return false;
return true;
}
int IEqualityComparer<SkinEntry>.GetHashCode (SkinEntry e) {
int IEqualityComparer<SkinKey>.GetHashCode (SkinKey e) {
return e.Name.GetHashCode() + e.SlotIndex * 37;
}
}

View File

@ -53,7 +53,7 @@ namespace Spine.Unity.Examples {
// OPTIONAL: Add all the attachments from the template skin.
var templateSkin = skeletonAnimation.Skeleton.Data.FindSkin(templateSkinName);
if (templateSkin != null)
equipsSkin.AddAttachments(templateSkin);
equipsSkin.AddSkin(templateSkin);
skeletonAnimation.Skeleton.Skin = equipsSkin;
RefreshSkeletonAttachments();
@ -69,8 +69,8 @@ namespace Spine.Unity.Examples {
// 1. Collect all the attachments of all active skins.
collectedSkin = collectedSkin ?? new Skin("Collected skin");
collectedSkin.Clear();
collectedSkin.AddAttachments(skeletonAnimation.Skeleton.Data.DefaultSkin);
collectedSkin.AddAttachments(equipsSkin);
collectedSkin.AddSkin(skeletonAnimation.Skeleton.Data.DefaultSkin);
collectedSkin.AddSkin(equipsSkin);
// 2. Create a repacked skin.
var repackedSkin = collectedSkin.GetRepackedSkin("Repacked skin", skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial, out runtimeMaterial, out runtimeAtlas);

View File

@ -119,8 +119,8 @@ namespace Spine.Unity.Examples {
// call Skin.GetRepackedSkin to get a cloned skin with cloned attachments that all use one texture.
if (repack) {
var repackedSkin = new Skin("repacked skin");
repackedSkin.AddAttachments(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders)
repackedSkin.AddAttachments(customSkin); // Include your new custom skin.
repackedSkin.AddSkin(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders)
repackedSkin.AddSkin(customSkin); // Include your new custom skin.
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); // Pack all the items in the skin.
skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton.
if (bbFollower != null) bbFollower.Initialize(true);

View File

@ -117,8 +117,8 @@ namespace Spine.Unity.Examples {
// call Skin.GetRepackedSkin to get a cloned skin with cloned attachments that all use one texture.
if (repack) {
var repackedSkin = new Skin("repacked skin");
repackedSkin.AddAttachments(skeleton.Data.DefaultSkin);
repackedSkin.AddAttachments(customSkin);
repackedSkin.AddSkin(skeleton.Data.DefaultSkin);
repackedSkin.AddSkin(customSkin);
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas);
skeleton.SetSkin(repackedSkin);
} else {

View File

@ -49,7 +49,7 @@ namespace Spine.Unity.Examples {
combinedSkin.Clear();
foreach (var skinName in skinsToCombine) {
var skin = skeleton.Data.FindSkin(skinName);
if (skin != null) combinedSkin.AddAttachments(skin);
if (skin != null) combinedSkin.AddSkin(skin);
}
skeleton.SetSkin(combinedSkin);

View File

@ -112,10 +112,9 @@ namespace Spine.Unity.Editor {
static void DrawPointsInSkin (Skin skin, Skeleton skeleton, Transform transform) {
foreach (var skinEntry in skin.Attachments) {
var attachment = skinEntry.Value as PointAttachment;
var attachment = skinEntry.Attachment as PointAttachment;
if (attachment != null) {
var skinKey = (Skin.SkinEntry)skinEntry.Key;
var slot = skeleton.Slots.Items[skinKey.SlotIndex];
var slot = skeleton.Slots.Items[skinEntry.SlotIndex];
DrawPointAttachmentWithLabel(attachment, slot.Bone, transform);
}
}

View File

@ -180,7 +180,7 @@ namespace Spine.Unity {
if (autoReset) {
var previousAnimations = this.previousAnimations;
for (int i = 0, n = previousAnimations.Count; i < n; i++)
previousAnimations[i].SetKeyedItemsToSetupPose(skeleton);
previousAnimations[i].Apply(skeleton, 0, 0, false, null, 0, MixBlend.Setup, MixDirection.Out); // SetKeyedItemsToSetupPose
previousAnimations.Clear();
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {

View File

@ -219,96 +219,6 @@ namespace Spine.Unity.AttachmentTools {
}
#region Runtime Repacking
/// <summary>
/// Fills the outputAttachments list with new attachment objects based on the attachments in sourceAttachments, but mapped to a new single texture using the same material.</summary>
/// <param name="sourceAttachments">The list of attachments to be repacked.</param>
/// <param name = "outputAttachments">The List(Attachment) to populate with the newly created Attachment objects.</param>
///
/// <param name="materialPropertySource">May be null. If no Material property source is provided, no special </param>
public static void GetRepackedAttachments (List<Attachment> sourceAttachments, List<Attachment> outputAttachments, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, string newAssetName = "Repacked Attachments", bool clearCache = false, bool useOriginalNonrenderables = true) {
if (sourceAttachments == null) throw new System.ArgumentNullException("sourceAttachments");
if (outputAttachments == null) throw new System.ArgumentNullException("outputAttachments");
// Use these to detect and use shared regions.
var existingRegions = new Dictionary<AtlasRegion, int>();
var regionIndexes = new List<int>();
var texturesToPack = new List<Texture2D>();
var originalRegions = new List<AtlasRegion>();
outputAttachments.Clear();
outputAttachments.AddRange(sourceAttachments);
int newRegionIndex = 0;
for (int i = 0, n = sourceAttachments.Count; i < n; i++) {
var originalAttachment = sourceAttachments[i];
if (IsRenderable(originalAttachment)) {
var newAttachment = originalAttachment.GetCopy(true);
var region = newAttachment.GetRegion();
int existingIndex;
if (existingRegions.TryGetValue(region, out existingIndex)) {
regionIndexes.Add(existingIndex); // Store the region index for the eventual new attachment.
} else {
originalRegions.Add(region);
texturesToPack.Add(region.ToTexture()); // Add the texture to the PackTextures argument
existingRegions.Add(region, newRegionIndex); // Add the region to the dictionary of known regions
regionIndexes.Add(newRegionIndex); // Store the region index for the eventual new attachment.
newRegionIndex++;
}
outputAttachments[i] = newAttachment;
} else {
outputAttachments[i] = useOriginalNonrenderables ? originalAttachment : originalAttachment.GetCopy(true);
regionIndexes.Add(NonrenderingRegion); // Output attachments pairs with regionIndexes list 1:1. Pad with a sentinel if the attachment doesn't have a region.
}
}
// Fill a new texture with the collected attachment textures.
var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps);
newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias;
newTexture.name = newAssetName;
// Copy settings
if (texturesToPack.Count > 0) {
var sourceTexture = texturesToPack[0];
newTexture.CopyTextureAttributesFrom(sourceTexture);
}
var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize);
// Rehydrate the repacked textures as a Material, Spine atlas and Spine.AtlasAttachments
Shader shader = materialPropertySource == null ? Shader.Find("Spine/Skeleton") : materialPropertySource.shader;
var newMaterial = new Material(shader);
if (materialPropertySource != null) {
newMaterial.CopyPropertiesFromMaterial(materialPropertySource);
newMaterial.shaderKeywords = materialPropertySource.shaderKeywords;
}
newMaterial.name = newAssetName;
newMaterial.mainTexture = newTexture;
var page = newMaterial.ToSpineAtlasPage();
page.name = newAssetName;
var repackedRegions = new List<AtlasRegion>();
for (int i = 0, n = originalRegions.Count; i < n; i++) {
var oldRegion = originalRegions[i];
var newRegion = UVRectToAtlasRegion(rects[i], oldRegion, page);
repackedRegions.Add(newRegion);
}
// Map the cloned attachments to the repacked atlas.
for (int i = 0, n = outputAttachments.Count; i < n; i++) {
var a = outputAttachments[i];
if (IsRenderable(a))
a.SetRegion(repackedRegions[regionIndexes[i]]);
}
// Clean up.
if (clearCache)
AtlasUtilities.ClearCache();
outputTexture = newTexture;
outputMaterial = newMaterial;
}
/// <summary>
/// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas
/// comprised of all the regions from the original skin.</summary>
@ -363,14 +273,13 @@ namespace Spine.Unity.AttachmentTools {
var originalRegions = new List<AtlasRegion>();
int newRegionIndex = 0;
foreach (var skinEntry in skinAttachments) {
var originalKey = skinEntry.Key;
var originalAttachment = skinEntry.Value;
foreach (var originalSkinEntry in skinAttachments) {
var originalAttachment = originalSkinEntry.Attachment;
Attachment newAttachment;
if (IsRenderable(originalAttachment)) {
newAttachment = originalAttachment.GetCopy(true);
var region = newAttachment.GetRegion();
if (originalAttachment is IHasRendererObject) {
var originalMeshAttachment = originalAttachment as MeshAttachment;
Attachment newAttachment = (originalMeshAttachment != null) ? originalMeshAttachment.NewLinkedMesh() : originalAttachment.Copy();
var region = ((IHasRendererObject)newAttachment).RendererObject as AtlasRegion;
int existingIndex;
if (existingRegions.TryGetValue(region, out existingIndex)) {
regionIndexes.Add(existingIndex); // Store the region index for the eventual new attachment.
@ -386,9 +295,9 @@ namespace Spine.Unity.AttachmentTools {
}
repackedAttachments.Add(newAttachment);
newSkin.SetAttachment(originalKey.SlotIndex, originalKey.Name, newAttachment);
newSkin.SetAttachment(originalSkinEntry.SlotIndex, originalSkinEntry.Name, newAttachment);
} else {
newSkin.SetAttachment(originalKey.SlotIndex, originalKey.Name, useOriginalNonrenderables ? originalAttachment : originalAttachment.GetCopy(true));
newSkin.SetAttachment(originalSkinEntry.SlotIndex, originalSkinEntry.Name, useOriginalNonrenderables ? originalAttachment : originalAttachment.Copy());
}
}
@ -436,7 +345,7 @@ namespace Spine.Unity.AttachmentTools {
// Map the cloned attachments to the repacked atlas.
for (int i = 0, n = repackedAttachments.Count; i < n; i++) {
var a = repackedAttachments[i];
if (IsRenderable(a))
if (a is IHasRendererObject)
a.SetRegion(repackedRegions[regionIndexes[i]]);
}
@ -523,10 +432,6 @@ namespace Spine.Unity.AttachmentTools {
}
}
static bool IsRenderable (Attachment a) {
return a is IHasRendererObject;
}
/// <summary>
/// Get a rect with flipped Y so that a Spine atlas rect gets converted to a Unity Sprite rect and vice versa.</summary>
static Rect SpineUnityFlipRect (this Rect rect, int textureHeight) {

View File

@ -34,44 +34,6 @@ using System.Collections;
namespace Spine.Unity.AttachmentTools {
public static class AttachmentCloneExtensions {
/// <summary>
/// Clones the attachment.</summary>
public static Attachment GetCopy (this Attachment o, bool cloneMeshesAsLinked) {
var meshAttachment = o as MeshAttachment;
if (meshAttachment != null && cloneMeshesAsLinked)
return meshAttachment.NewLinkedMesh();
return o.Copy();
}
#region Runtime Linked MeshAttachments
/// <summary>
/// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to the AtlasRegion provided.</summary>
public static MeshAttachment GetLinkedMesh (this MeshAttachment o, string newLinkedMeshName, AtlasRegion region) {
if (region == null) throw new System.ArgumentNullException("region");
MeshAttachment mesh = o.NewLinkedMesh();
mesh.SetRegion(region, false);
return mesh;
}
/// <summary>
/// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader.
/// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool)</summary>
public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Shader shader, Material materialPropertySource = null) {
var m = new Material(shader);
if (materialPropertySource != null) {
m.CopyPropertiesFromMaterial(materialPropertySource);
m.shaderKeywords = materialPropertySource.shaderKeywords;
}
return o.GetLinkedMesh(sprite.name, sprite.ToAtlasRegion());
}
/// <summary>
/// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader.
/// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool)</summary>
public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Material materialPropertySource) {
return o.GetLinkedMesh(sprite, materialPropertySource.shader, materialPropertySource);
}
#endregion
#region RemappedClone Convenience Methods
/// <summary>
@ -115,8 +77,7 @@ namespace Spine.Unity.AttachmentTools {
return newAttachment;
}
}
return o.GetCopy(true); // Non-renderable Attachments will return as normal cloned attachments.
return o.Copy();
}
#endregion
}

View File

@ -447,10 +447,6 @@ namespace Spine {
return va.bones != null && va.bones.Length > 0;
}
public static bool IsRenderable (this Attachment a) {
return a is IHasRendererObject;
}
#region Transform Modes
public static bool InheritsRotation (this TransformMode mode) {
const int RotationBit = 0;
@ -462,153 +458,5 @@ namespace Spine {
return ((int)mode & (1U << ScaleBit)) == 0;
}
#endregion
#region Posing
internal static void SetPropertyToSetupPose (this Skeleton skeleton, int propertyID) {
int tt = propertyID >> 24;
var timelineType = (TimelineType)tt;
int i = propertyID - (tt << 24);
Bone bone;
IkConstraint ikc;
PathConstraint pc;
switch (timelineType) {
// Bone
case TimelineType.Rotate:
bone = skeleton.bones.Items[i];
bone.rotation = bone.data.rotation;
break;
case TimelineType.Translate:
bone = skeleton.bones.Items[i];
bone.x = bone.data.x;
bone.y = bone.data.y;
break;
case TimelineType.Scale:
bone = skeleton.bones.Items[i];
bone.scaleX = bone.data.scaleX;
bone.scaleY = bone.data.scaleY;
break;
case TimelineType.Shear:
bone = skeleton.bones.Items[i];
bone.shearX = bone.data.shearX;
bone.shearY = bone.data.shearY;
break;
// Slot
case TimelineType.Attachment:
skeleton.SetSlotAttachmentToSetupPose(i);
break;
case TimelineType.Color:
skeleton.slots.Items[i].SetColorToSetupPose();
break;
case TimelineType.TwoColor:
skeleton.slots.Items[i].SetColorToSetupPose();
break;
case TimelineType.Deform:
skeleton.slots.Items[i].Deform.Clear();
break;
// Skeleton
case TimelineType.DrawOrder:
skeleton.SetDrawOrderToSetupPose();
break;
// IK Constraint
case TimelineType.IkConstraint:
ikc = skeleton.ikConstraints.Items[i];
ikc.mix = ikc.data.mix;
ikc.softness = ikc.data.softness;
ikc.bendDirection = ikc.data.bendDirection;
ikc.stretch = ikc.data.stretch;
break;
// TransformConstraint
case TimelineType.TransformConstraint:
var tc = skeleton.transformConstraints.Items[i];
var tcData = tc.data;
tc.rotateMix = tcData.rotateMix;
tc.translateMix = tcData.translateMix;
tc.scaleMix = tcData.scaleMix;
tc.shearMix = tcData.shearMix;
break;
// Path Constraint
case TimelineType.PathConstraintPosition:
pc = skeleton.pathConstraints.Items[i];
pc.position = pc.data.position;
break;
case TimelineType.PathConstraintSpacing:
pc = skeleton.pathConstraints.Items[i];
pc.spacing = pc.data.spacing;
break;
case TimelineType.PathConstraintMix:
pc = skeleton.pathConstraints.Items[i];
pc.rotateMix = pc.data.rotateMix;
pc.translateMix = pc.data.translateMix;
break;
}
}
/// <summary>Resets the DrawOrder to the Setup Pose's draw order</summary>
public static void SetDrawOrderToSetupPose (this Skeleton skeleton) {
var slotsItems = skeleton.slots.Items;
int n = skeleton.slots.Count;
var drawOrder = skeleton.drawOrder;
drawOrder.Clear(false);
drawOrder.EnsureCapacity(n);
drawOrder.Count = n;
System.Array.Copy(slotsItems, drawOrder.Items, n);
}
/// <summary>Resets all the slots on the skeleton to their Setup Pose attachments but does not reset slot colors.</summary>
public static void SetSlotAttachmentsToSetupPose (this Skeleton skeleton) {
var slotsItems = skeleton.slots.Items;
for (int i = 0; i < skeleton.slots.Count; i++) {
Slot slot = slotsItems[i];
string attachmentName = slot.data.attachmentName;
slot.Attachment = string.IsNullOrEmpty(attachmentName) ? null : skeleton.GetAttachment(i, attachmentName);
}
}
/// <summary>Resets the color of a slot to Setup Pose value.</summary>
public static void SetColorToSetupPose (this Slot slot) {
slot.r = slot.data.r;
slot.g = slot.data.g;
slot.b = slot.data.b;
slot.a = slot.data.a;
slot.r2 = slot.data.r2;
slot.g2 = slot.data.g2;
slot.b2 = slot.data.b2;
}
/// <summary>Sets a slot's attachment to setup pose. If you have the slotIndex, Skeleton.SetSlotAttachmentToSetupPose is faster.</summary>
public static void SetAttachmentToSetupPose (this Slot slot) {
var slotData = slot.data;
slot.Attachment = slot.bone.skeleton.GetAttachment(slotData.name, slotData.attachmentName);
}
/// <summary>Resets the attachment of slot at a given slotIndex to setup pose. This is faster than Slot.SetAttachmentToSetupPose.</summary>
public static void SetSlotAttachmentToSetupPose (this Skeleton skeleton, int slotIndex) {
var slot = skeleton.slots.Items[slotIndex];
string attachmentName = slot.data.attachmentName;
if (string.IsNullOrEmpty(attachmentName)) {
slot.Attachment = null;
} else {
var attachment = skeleton.GetAttachment(slotIndex, attachmentName);
slot.Attachment = attachment;
}
}
/// <summary>Resets Skeleton parts to Setup Pose according to a Spine.Animation's keyed items.</summary>
public static void SetKeyedItemsToSetupPose (this Animation animation, Skeleton skeleton) {
animation.Apply(skeleton, 0, 0, false, null, 0, MixBlend.Setup, MixDirection.Out);
}
public static void AllowImmediateQueue (this TrackEntry trackEntry) {
if (trackEntry.nextTrackLast < 0) trackEntry.nextTrackLast = 0;
}
#endregion
}
}

View File

@ -1,165 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
namespace Spine.Unity.AttachmentTools {
public static class SkinUtilities {
#region Skeleton Skin Extensions
/// <summary>
/// Convenience method for duplicating a skeleton's current active skin so changes to it will not affect other skeleton instances. .</summary>
public static Skin UnshareSkin (this Skeleton skeleton, bool includeDefaultSkin, bool unshareAttachments, AnimationState state = null) {
// 1. Copy the current skin and set the skeleton's skin to the new one.
var newSkin = skeleton.GetClonedSkin("cloned skin", includeDefaultSkin, unshareAttachments, true);
skeleton.SetSkin(newSkin);
// 2. Apply correct attachments: skeleton.SetToSetupPose + animationState.Apply
if (state != null) {
skeleton.SetToSetupPose();
state.Apply(skeleton);
}
// 3. Return unshared skin.
return newSkin;
}
public static Skin GetClonedSkin (this Skeleton skeleton, string newSkinName, bool includeDefaultSkin = false, bool cloneAttachments = false, bool cloneMeshesAsLinked = true) {
var newSkin = new Skin(newSkinName); // may have null name. Harmless.
var defaultSkin = skeleton.data.DefaultSkin;
var activeSkin = skeleton.skin;
if (includeDefaultSkin)
defaultSkin.CopyTo(newSkin, true, cloneAttachments, cloneMeshesAsLinked);
if (activeSkin != null)
activeSkin.CopyTo(newSkin, true, cloneAttachments, cloneMeshesAsLinked);
return newSkin;
}
#endregion
/// <summary>
/// Gets a shallow copy of the skin. The cloned skin's attachments are shared with the original skin.</summary>
public static Skin GetClone (this Skin original) {
var newSkin = new Skin(original.name + " clone");
var newSkinAttachments = newSkin.Attachments;
var newSkinBones = newSkin.Bones;
var newSkinConstraints = newSkin.Constraints;
foreach (var a in original.Attachments)
newSkinAttachments[a.Key] = a.Value;
newSkinBones.AddRange(original.bones);
newSkinConstraints.AddRange(original.constraints);
return newSkin;
}
/// <summary>Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced.</summary>
public static void SetAttachment (this Skin skin, string slotName, string keyName, Attachment attachment, Skeleton skeleton) {
int slotIndex = skeleton.FindSlotIndex(slotName);
if (skeleton == null) throw new System.ArgumentNullException("skeleton", "skeleton cannot be null.");
if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName");
skin.SetAttachment(slotIndex, keyName, attachment);
}
/// <summary>Adds skin items from another skin. For items that already exist, the previous values are replaced.</summary>
public static void AddAttachments (this Skin skin, Skin otherSkin) {
if (otherSkin == null) return;
otherSkin.CopyTo(skin, true, false);
}
/// <summary>Gets an attachment from the skin for the specified slot index and name.</summary>
public static Attachment GetAttachment (this Skin skin, string slotName, string keyName, Skeleton skeleton) {
int slotIndex = skeleton.FindSlotIndex(slotName);
if (skeleton == null) throw new System.ArgumentNullException("skeleton", "skeleton cannot be null.");
if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName");
return skin.GetAttachment(slotIndex, keyName);
}
/// <summary>Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced.</summary>
public static void SetAttachment (this Skin skin, int slotIndex, string keyName, Attachment attachment) {
skin.SetAttachment(slotIndex, keyName, attachment);
}
public static void RemoveAttachment (this Skin skin, string slotName, string keyName, SkeletonData skeletonData) {
int slotIndex = skeletonData.FindSlotIndex(slotName);
if (skeletonData == null) throw new System.ArgumentNullException("skeletonData", "skeletonData cannot be null.");
if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName");
skin.RemoveAttachment(slotIndex, keyName);
}
public static void Clear (this Skin skin) {
skin.Attachments.Clear();
}
//[System.Obsolete]
public static void Append (this Skin destination, Skin source) {
source.CopyTo(destination, true, false);
}
public static void CopyTo (this Skin source, Skin destination, bool overwrite, bool cloneAttachments, bool cloneMeshesAsLinked = true) {
var sourceAttachments = source.Attachments;
var destinationAttachments = destination.Attachments;
var destinationBones = destination.Bones;
var destinationConstraints = destination.Constraints;
if (cloneAttachments) {
if (overwrite) {
foreach (var e in sourceAttachments)
destinationAttachments[e.Key] = e.Value.GetCopy(cloneMeshesAsLinked);
} else {
foreach (var e in sourceAttachments) {
if (destinationAttachments.ContainsKey(e.Key)) continue;
destinationAttachments.Add(e.Key, e.Value.GetCopy(cloneMeshesAsLinked));
}
}
} else {
if (overwrite) {
foreach (var e in sourceAttachments)
destinationAttachments[e.Key] = e.Value;
} else {
foreach (var e in sourceAttachments) {
if (destinationAttachments.ContainsKey(e.Key)) continue;
destinationAttachments.Add(e.Key, e.Value);
}
}
}
foreach (BoneData data in source.bones)
if (!destinationBones.Contains(data)) destinationBones.Add(data);
foreach (ConstraintData data in source.constraints)
if (!destinationConstraints.Contains(data)) destinationConstraints.Add(data);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: f4692b9527684d048862210ba3f9834e
timeCreated: 1563321428
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -161,7 +161,7 @@ namespace Spine.Unity.Playables {
if (!isAnimationTransitionMatch) {
dummyAnimationState.ClearTracks();
fromTrack = dummyAnimationState.SetAnimation(0, fromAnimation, fromClipLoop);
fromTrack.AllowImmediateQueue();
if (fromTrack.nextTrackLast < 0) fromTrack.nextTrackLast = 0; // fromTrack.AllowImmediateQueue
if (toAnimation != null)
toTrack = dummyAnimationState.SetAnimation(0, toAnimation, clipData.loop);
}