- 已编辑
PSA: Spine Animations as Unity asset references
Since Spine-Unity's spine-csharp
core is mostly engine agnostic, Spine's core data objects like Skeleton data and Animations are loaded at runtime directly from the exported Spine skeleton json (.json) or binary (.skel.bytes). They are pure C# objects and aren't assets in Unity, which limits their user-interface visibility in Unity Editor.
This means that we've historically had to resort to storing strings in our MonoBehaviours and ScriptableObjects to serialize the animations we choose. Then use those strings directly for SetAnimation
calls, or use strings to find the Animation objects using Skeleton.Data.FindAnimation
.
These are still good options depending on the situation.
However, coming very soon:
AnimationReferenceAsset
This is a new Spine-Unity asset type that keeps an animation name and a reference to a SkeletonDataAsset where the animation comes from.
You can use them in inspectors.
At runtime, the Spine.Animation
reference is loaded and cached into the ScriptableObject, so it can act as one-away from a reference to an Animation without having to deal with loading.
In code, you can use them directly in AnimationState calls, because of its implicit Spine.Animation cast/type conversion.
using UnityEngine;
using Spine.Unity;
public class SampleAnimationReferenceScript : MonoBehaviour
{
public AnimationReferenceAsset run;
void Start()
{
var skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.AnimationState.SetAnimation(0, run, true); //optionally: //skeletonAnimation.AnimationState.SetAnimation(0, run.Animation, true);
Debug.Log("This animation is " + run.Animation.Duration + " seconds long.");
}
}
You can also use its .Animation
property.
How do I create them?
A complete set of reference assets can be generated from your skeleton data with the press of a button in your SkeletonDataAsset's inspector.
You can also create them one by one by going to Assets>Create>Spine>Animation Reference Asset
, or duplicating existing ones and changing the values in their fields.
Just to clarify
This does not replace or affect the existing ways you can store and use animations and animation names.
If the system you created in your scripts makes more sense, by all means continue to use them.
This is just an additional inspector-friendly and code-friendly option that now comes with the runtime.
This is also not to be confused with dummy UnityEngine.AnimationClips.
Spine-Unity's Mecanim integration creates a controller and dummy AnimationClips (which are required by Mecanim uses for its state machine states).
What about other stuff? Bones? Slots? IK Constraints?
It became apparent that things like bones and slots could not be given the same implementation since they belong to skeleton instances, whereas objects like Animations belong to the shared SkeletonData.
Is it impossible? That remains to be seen. But it's not obvious to us at least.
We will continue to look into improving this aspect of Spine-Unity.
For now this also fulfills an important need that our Spine-Unity Timeline/Playable feature has had for a while.
The new asset's source code is also a total of about 20 lines of code so if you're curious, we encourage you to open it up when it's released.
If you have any ideas, suggestions, questions, words of caution, or haikus, let us know!
I am interested in how you solve when some animations are renamed in spine project?
I implemented the similar solution myself because I want to be sure that when animator renames some animation in Spine project, I can fix that just on one place (animation asset) and not to check the whole Unity project to replace strings.
This doesn't resolve that automatically since Spine's animations are only identified by name (it doesn't have guids or whatever). The animator could do anything with the animations, really. Rename them. Duplicate them. Swap names of two animations.
But because it's a centralized asset, you're right. You would only need to change the value on the asset, instead of on the component or prefab or wherever you were storing it.
It seems it came up pretty unnoticed but I find this REALLY cool !
- 已编辑
Not Working. Im Created Reference Asset and I attach to this script, but getting an error NullReferenceException: Object reference not set to an instance of an object SampleAnimationReferenceScript.Start ()
We could not reproduce this problem. Could you post the code you are using? In which line is the exception thrown?
using UnityEngine;
using Spine.Unity;
public class SampleAnimationReferenceScript : MonoBehaviour
{
public AnimationReferenceAsset run;
void Start()
{
var skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.AnimationState.SetAnimation(0, run, true); (ERROR IN THIS LINE NullReferenceException: Object reference not set to an instance )
//optionally: //skeletonAnimation.AnimationState.SetAnimation(0, run.Animation, true);
Debug.Log("This animation is " + run.Animation.Duration + " seconds long.");
}
}
P.S. I insert assets into the link field
I just tested your code and it works as expected here.
Please:
- check if your script is attached at the GameObject with the
SkeletonAnimation
component, - check that the AnimationReferenceAsset is of the same SkeletonDataAsset that the
SkeletonAnimation
is referencing, - select the AnimationReferenceAsset in the Project panel and in the Inspector check that all parameters are showing the proper values.
You can post screenshots here if you are unsure.
Oh. Now I understand everything. I apparently misinterpreted AnimationReferenceAsset as a tool that did not need SkeletonAnimation. The fact is, I was hoping that ReferenceAsset would make it possible in Runtime to easily change animations (different entities with DIFFERENT CHARACTERS) inside the prefab using the Scriptable Object. This would allow in a mobile project to save memory and diversify the gaming environment.
Please tell me. How to change SkeletonDataAsset using a script?
This forum thread shows how this can be done:
Spine Multi Script
Please also check out the links on this posting by Nate regarding Skins, in case you are not aware that Skins can be combined and repacked at runtime as well:
Combining Skins
Great thanks, I'll try to figure it out and unsubscribe
Yes, it worked. Harald, thanks a lot. You helped me out like that!
Glad it helped, thanks for letting us know!
I'm probably gonna give it a try :
It seems to me like this could be an awesome starting point for animation-driven gameplay !
It looks easy to add more properties to the AnimationReferenceAsset class like "delayBeforeTrigger", "damagesModifier", "fxPrefab" or whatever animation specific variables.
Do you guys already see a big "NO GO" in that idea ?
The general idea does not sound bad. For timing related things, I think Spine events would be more suitable, since they can be placed more precisely. If however precision is less important than easy modification, then it would still make sense.
A damage modifier
property might be well suitable, even though I would personally rather separate game data from the animation. It may just become a bit cluttered when you add any parameter to the animation, such as jump height
, damage
, and so on.
Also consider whether you really want to modify third-party code, as future (major) updates might require you to apply the same changes multiple times.
I'm currently using it and even if I have to say that I'm not always sure how to deal with AddAnimation and timings (but it's not entirely related to AnimAssets ^^) it works pretty well so far.
Harald :A
damage modifier
property might be well suitable, even though I would personally rather separate game data from the animation. It may just become a bit cluttered when you add any parameter to the animation, such asjump height
,damage
, and so on.
I actually don't bind everything to animation, general damages are tied to the enemy instance class, in my case it's really about "quick attack" is reducing enemy base damage by 20% when "strong-ass attack" boost base damage by 50% !
I can also specify animations that should use RootMotion or not, I can tie them to other scriptable objects. A lot of cool stuff actually
The thing that would really take it to the next level is if I manage to keep track of what AnimAsset is played or queued and when ! I'm not too comfortable with trackEntry yet >
Sounds good!
RemDust :The thing that would really take it to the next level is if I manage to keep track of what AnimAsset is played or queued and when ! I'm not too comfortable with trackEntry yet
There are two solutions for this:
1) modifying TrackEntry or any of it's members to hold a back-reference to the AnimationReferenceAsset
, which is not recommended.
2) creating and populating a Dictionary<>
that maps from Animation or string
(for use with TrackEntry.Animation
or TrackEntry.Animation.Name
) to the AnimationReferenceAsset
.
Harald :2) creating and populating a
Dictionary<>
that maps from Animation orstring
(for use withTrackEntry.Animation
orTrackEntry.Animation.Name
) to theAnimationReferenceAsset
.
This is actually something I was considering, I'm gonna give it a shot and let you know how good it works.
Right now I can't really wrap my head around how I can implement that with AddAnimation. With SetAnim it's pretty easy because I have the exact moment the animation begins :grinteeth:
But this is related to another post I wrote so I won't get into that here :zipper:
RemDust :But this is related to another post I wrote so I won't get into that here
We hope to resolve all your questions on the other thread then .