如标题所示
切换动画就是使用了SetAnimation这个方法,在同一轨道上进行切换。
此外需要注意的时,我是通过配置的时间轴来切换动画的,因为此前有切换后抖动的问题,所以判断是否需要刷新动画时,我提前了一帧(0.02s)通过判断,并将TrackEntry的TrackTime设置为一个合适的负值。
期待大师的解答,谢谢
如标题所示
切换动画就是使用了SetAnimation这个方法,在同一轨道上进行切换。
此外需要注意的时,我是通过配置的时间轴来切换动画的,因为此前有切换后抖动的问题,所以判断是否需要刷新动画时,我提前了一帧(0.02s)通过判断,并将TrackEntry的TrackTime设置为一个合适的负值。
期待大师的解答,谢谢
在切换动画时,SkeletonAnimation回到SetUp的默认状态一帧,然后才会恢复正常,这通常是由于动画混合(crossfading)或动画状态更新顺序的问题。
根据你提供的信息,你使用了 SetAnimation
方法在同一轨道上切换动画,并且通过配置的时间轴来切换动画。为了避免抖动问题,你提前了一帧(0.02s)进行判断,并将 TrackEntry
的 TrackTime
设置为一个合适的负值。
以下是一些可能的解决方案:
设置混合时间为零:
你可以通过将 MixDuration
设置为零来避免动画之间的混合,从而防止回到默认状态一帧的问题。
TrackEntry entry = skeletonAnimation.AnimationState.SetAnimation(0, anim, false);
entry.MixDuration = 0;
skeletonAnimation.Update(0);
使用 SetEmptyAnimation
:
在切换动画之前,可以先使用 SetEmptyAnimation
方法来清空当前动画,这样可以确保动画切换时不会出现回到默认状态的问题。
skeletonAnimation.AnimationState.SetEmptyAnimation(0, 0);
skeletonAnimation.AnimationState.SetAnimation(0, anim, false);
更新骨骼的世界变换:
在切换动画后,调用 skeleton.UpdateWorldTransform()
方法来确保骨骼的变换立即生效。
skeletonAnimation.AnimationState.SetAnimation(0, anim, false);
skeletonAnimation.skeleton.UpdateWorldTransform();
避免在每帧调用 SetAnimation
:
确保 SetAnimation
方法只在需要切换动画时调用,而不是在每一帧中调用。
if (shouldSwitchAnimation) {
skeletonAnimation.AnimationState.SetAnimation(0, anim, false);
}
通过以上方法,你可以避免在切换动画时SkeletonAnimation回到SetUp的默认状态一帧的问题。如果问题依然存在,请检查你的动画配置和时间轴设置,确保它们与上述方法兼容。
以上三张图片为:
1.错误帧(切换动画的那一帧) 前的一帧 动画A
2.错误帧 动画B
3.错误帧(切换动画的那一帧) 后的一帧 动画B
@Bali_Jun Unfortunately machine translation was not very clear, so i don't completely understand your current setup.
Also, unforutnately the screenshots seem to have been uploading in the wrong format, I see three .bin
files, while I assume that you intended to upload .png
or .jpeg
or similar image files here.
此外需要注意的时,我是通过配置的时间轴来切换动画的
As some part is translated as "timeline" for me, are you saying that you are using the spine-unity Timeline Extensions UPM package? Or what exactly do you mean by the above sentence?
Could you please share some code of what you are doing? And please also share some screenshots which show your setup (especially if using the Timeline UPM package, please share your timeline animation setup).
Harald
切换动画的主要逻辑和简单
if (.....)
{
TrackEntry trackEntry = kvp.Value.AnimationState.GetCurrent(0);
trackEntry = kvp.Value.AnimationState.SetAnimation(0, spineAnimNameDic[animData.spineNum], true);
float timeScale = 1000f / (nextNodeData.startTime - curNodeData.startTime) * animData.loopTimes;
trackEntry.TimeScale = timeScale;
trackEntry.TrackTime = (nowMTime - curNodeData.startTime) / 1000f * timeScale;
}
以上内容我在每 fiexedFrame检查,并在合适的时候调用
TrackTime设置时是一个很小的负值,但错误的表现应该与此无关
此外MixDuration 全部设置为了0
至于我说的 "Spine动画突然回到了SetUp默认状态一帧“,所谓的SetUp默认状态就是 Skeleton Data Asset 文件在预览时,从Preview窗口看到的默认状态,也可以通过点击上方的Setup Pose来回到此预览状态
Thanks for the additional information.
Bali_Jun TrackTime设置时是一个很小的负值,但错误的表现应该与此无关
Why would a negative track time not be a problem?
You should limit the TrackTime to 0 via trackTime = Mathf.Max(0f, trackTime)
then.
Have you tried limiting the trackTime like this?
TrackEntry trackEntry = kvp.Value.AnimationState.GetCurrent(0); // <-- this line has no effect
trackEntry = kvp.Value.AnimationState.SetAnimation(0, spineAnimNameDic[animData.spineNum], true);
Here the first line has no effect. I assume that your original code does more than the code shown. What else happens in the original code?
In general I see nothing obviously wrong with your code, except if you set negative track times.
Bali_Jun 此外MixDuration 全部设置为了0
This will not solve any issues of showing the setup pose for a single frame.
Bali_Jun 另外,美术各个动画的首尾帧也都K为了关键帧,我也尝试了使用 skeletonAnimation.Update
Which skeletonAnimation.Update
? There are two method, one which accepts the deltaTime
Bali_Jun 除此之外,我想询问的是:假如说我希望在11.21s这个时间点开始播放动画A,但实际上最接近的一帧为11.22s,那么此时我是否需要微调 TrackEntry.TrackTime来达到一个更准确的表现效果?
I'm not sure what you would like to achieve. Skeleton animation in general is continuous and has no clear stepped frames, it's just seconds of playback time. If you want to precisely place something on a certain frame with 60fps, just set the TrackTime
to frame / 60.0f
, or frame / 30.0f
for 30fps.
Bali_Jun 假如我使用AddAnimation 添加了四个动画到队列中,那么只有当最后一个动画播放完毕时才会调用 OnComplete回调,这似乎和我在文档上看到的不一样,请问这是正常的吗
No, OnComplete
is called every time an animation completes a loop:
http://esotericsoftware.com/spine-api-reference#AnimationStateListener-complete
In general it is never necessary to avoid setting loop
to false and add animations multiple times to emulate the same behaviour. If you need this, something is wrong with your logic code. If you can create a minimal Unity project with minimal code that still shows your issues, we can have a look at what's going wrong. You can send the Unity project as zip package to contact@esotericsoftware.com, briefly mentioning this forum thread URL so that we know the context. Then we can have a look at it.
If you can't reproduce the issue with a minimal project, it's likely a problem with your code, then you might want to check with a debugger what values are passed to the SetAnimation method calls and what values are actually set at the TrackEntry.
Harald In general it is never necessary to avoid setting loop to false and add animations multiple times to emulate the same behaviour. If you need this, something is wrong with your logic code. If you can create a minimal Unity project with minimal code that still shows your issues, we can have a look at what's going wrong. You can send the Unity project as zip package to [contact@esotericsoftware.com](mailto:contact@esotericsoftware.com), briefly mentioning this forum thread URL so that we know the context. Then we can have a look at it.
我的意思是我在同一时间通过 AddAnimation 这个方法添加了四个动画到同一轨道上,使得他们能够按顺序播放,但是只有最后一个动画播放完毕时才会调用OnComplete 回调
例如
AddAnimation(0, "idle1", false, 0);
AddAnimation(0, "play1", false, 0);
AddAnimation(0, "idle2", false, 0);
AddAnimation(0, "play2", false, 0);
只有当 "play2" 动画播放完毕时,才会触发OnComplete回调
Bali_Jun In order for us to understand your situation, it would be best if you could send us a minimal Unity project, as Harald has already suggested, but from what you have explained, it seems correct that the Complete
event is only fired for the last animation. This is because the Complete
event is not fired when the animation changes, but when the animation has been played to the end without being interrupted. Since you are setting the new animation one frame in advance, if the next animation is set, the previous animation will not be able to play the animation to the end, so it is expected that the Complete
event will not be fired.
If you want to get the timing when the previous animation ends anyway, even if it is interrupted, you can use the End
event.
Misaki
抱歉,发送一个迷你Unity项目可能不太方便,还望你们谅解,因为我已经找到了其他可用的解决方法,所以无论有没有找到最终原因,我都很感谢你们的每一次回答。
不过首先要注意的是,在这个问题中,我并没有提前一帧改变动画状态,而只是使用AddAnimation,让动画自己按照顺序播放,这中间并没有尝试打断动画的播放。SkeletonAnimation的更新则是使用的fixedFrame。
不过我已经通过调试发现了问题原因:
在AnimationState的QueueEvents中,判断bool类型参数complete时使用了"complete = animationTime >= animationEnd && entry.animationLast < animationEnd;",正常情况下,在动画播放完毕时,complete能够正常变为true,而在我的调试中,每次似乎都缺少一些时间时间以达到complete为true的条件。
换句话说,通过AddAnimation添加到播放队列中的动画,其变化的时机似乎不对?
以下是一段连续的打印方式与打印结果:
if (entry.loop)
complete = duration == 0 || (trackLastWrapped > entry.trackTime % duration);
else
complete = animationTime >= animationEnd && entry.animationLast < animationEnd;
Debug.LogError("AT:" + animationTime + " End:" + animationEnd);
Debug.LogError("Last:" + entry.animationLast + " End:" + animationEnd);
AT:0.89972 End:1
Last:0.88888 End:1
AT:0.91056 End:1
Last:0.89972 End:1
AT:0.9214 End:1
Last:0.91056 End:1
AT:0.13224 End:1
Last:0.1213999 End:1
而正常情况下,应该有类似以下的情况,来触发OnComplete
AT:1.01028 End:1
Last:0.9994399 End:1
可从上面的打印来看,似乎跳过了一段时间
@Bali_Jun Please understand that we can't debug your code just from reading some sections of code. One reason is that that's highly inefficient, another is that it's likely that relevant code parts and how everything is set up is missing. We require a Unity project to test what is going wrong. The callback should always behave as the documentation reads. If it does not, either it is a bug in your code, or an issue on our end (which we would like to fix then). We can't tell without a Unity reproduction project though. We hope you understand.
Bali_Jun 目前来看,我将TrackTime设为一个很小的负值,在表现上是没有问题的 (很小的负值意味着下一帧trackTime将变为正数)
而我之所以这样做,是因为我要提前一帧设置新动画
而我之所以要提前一帧设置新动画,是因为如果不提前的话,每次重新设置新动画时,那一帧的表现并没有立即刷新,也就是说动画转换会比预计的延迟一帧。虽然不知道为什么会这样,但是这是很致命的表现。
Please note that if you call SetAnimation()
, the current animation will be mixed out immediately and replaced with the new animation, if you then set a negative TrackTime on the new animation, this sounds like an invalid state. This may lead to problems. If you want to set animations with a delay, there is AddAnimation()
which accepts a delay parameter for this purpose.