I meant "random" as in practically random, where the programmer would need to have the character play any of the animations in any order so any of the values that were keyed in one animation and not keyed on subsequent animations would persist. Nothing in computing is truly random, you could say. Or the universe, depending on your theory of physics. 🙂
It was unreasonable workflow expectation to tell users to make sure everything that's keyed on one animation is keyed on all other animations.
And forcing everyone to just key everything blindly as an alternative not only makes a mess in the dopesheet (making it less readable for the animator), but the performance on playing animations will scale very poorly at runtime since calculations will be done for all those unused dopesheet items/runtime timelines that don't do anything. You could manage this sort of thing if your skeleton has very, very few bones and slots, but people also do very complex setups with Spine.
On to the solution:
Managing a runtime skin, or setting the attachment every frame.
OPTION 1 : MANAGING A RUNTIME SKIN
Skeletons use Skins to determine what attachment objects should be used whenever an attachment lookup happens. Attachment lookups with Skeleton.SetAttachment()
calls, as well as during animations that key slot attachments.
A skin is a table (Dictionary in C#) of attachment names, slots and actual attachments.
Imagine its contents like this:
slot# skin entry name attachment object
================================================
3 "glove" {a red-colored glove.}
5 "shield" {a large shield.}
6 "left shoe" {the left shoe}
7 "right shoe" {the right shoe}
...
...
So with the skin above, whenever you key "glove" in Spine, or call skeleton.SetAttachment(gloveSlotName, "glove")
in code. (also skeleton.GetAttachment
), it will return the red glove attachment.
At runtime, you can change the entries in the skin using Skin.AddAttachment or this helper function (this comes with Spine-Unity for Spine 3.6):
/// <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 (Skin skin, string slotName, string keyName, Attachment attachment, Skeleton skeleton) {
int slotIndex = skeleton.FindSlotIndex(slotName);
if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName");
skin.AddAttachment(slotIndex, keyName, attachment);
}
In order for runtime skins to work, you need to set and animate the necessary customizable attachments using Skins and Skin Placeholders in Spine. The name of the "skin placeholders" corresponds to the skin entry name.
OPTION 2: SET SLOT ATTACHMENT EVERY FRAME
Just keep and cache a reference to the relevant slot.
int weaponSlotIndex = skeleton.FindSlotIndex("my weapon slot name");
Slot weaponSlot = skeleton.Slots.Items[weaponSlotIndex];
Attachment myWeaponAttachment = skeleton.GetAttachment(weaponSlotIndex, "my weapon attachment name");
and set it every frame in Update.
bool weaponIsActive;
weaponSlot.Attachment = weaponIsActive ? myWeaponAttachment : null;
Note the update should happen after the animation is applied to the skeleton, or else the animation might undo your changes every frame.
You can set the Script Execution Order so your Update happen after SkeletonAnimation's Update.
Or you can use the SkeletonAnimation.UpdateComplete callback.
void Start () {
skeletonAnimation.UpdateComplete += delegate (ISkeletonAnimation animated) {
weaponSlot.Attachment = weaponIsActive ? myWeaponAttachment : null;
};
}