如何开始&使用代码在AnimationTree中旅行?
我正在尝试通过在 AnimationTree
中的节点之间移动来为可扩展的人员设置动画,如下所示:
...
tool
export(String,"small", "mid", "full") var staff_mode = "small" setget set_staff_mode;
func set_staff_mode(new_val):
var ani_state;
if(self.has_node("/path/to/AnimationTree")):
ani_state=self.get_node("/path/to/AnimationTree")["parameters/playback"];
ani_state.start(staff_mode);
print(ani_state.is_playing());
ani_state.travel(new_val);
ani_state.stop();
staff_mode=new_val;
我尚未对 small
应用自动播放,因为我不想要一个工作人员上的循环动画
(它只扩展或压缩,没有空闲动画)
但由于某种原因它给出了错误:
如果状态机未运行,则无法进入“满”状态。也许你 需要为您所在州的节点之一启用加载时自动播放 机器还是先调用.start()?
编辑: 我忘了提及,但我的员工没有任何空闲动画,因此我需要在过渡完成后停止动画。
小型、中型和小型完整
(所有这些都是法杖的静态模式,具体取决于游戏法杖应该延伸多少)
都是 0.1 秒的单帧动画,我应用了 0.2 秒的 Xfade 时间来显示过渡
,我只需要从现有的动画状态过渡到另一个状态,然后停止
I'm trying to animate an expandable staff by travel between nodes in AnimationTree
Like this:
...
tool
export(String,"small", "mid", "full") var staff_mode = "small" setget set_staff_mode;
func set_staff_mode(new_val):
var ani_state;
if(self.has_node("/path/to/AnimationTree")):
ani_state=self.get_node("/path/to/AnimationTree")["parameters/playback"];
ani_state.start(staff_mode);
print(ani_state.is_playing());
ani_state.travel(new_val);
ani_state.stop();
staff_mode=new_val;
I haven't applied autoplay to small
because I don't want a looping animation on the staff
(it only expands or compresses, no idle animation)
but for some reason it gives the error:
Can't travel to 'full' if state machine is not playing. Maybe you
need to enable Autoplay on Load for one of the nodes in your state
machine or call .start() first?
Edit:
I forgot to mention but I don't have any idle animation for my staff so I need to stop the animation after the transition is complete.
small, mid & full
(all of them are static modes of the staff depending upon the game how much the staff should extend)
are all 0.1sec single frame animations and I applied Xfade Time of 0.2 secs to show the transition
I simply need to transition from an existing animation state to another and then stop
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
新答案
显然旧答案的解决方案不适用于非常短的动画。对于我的口味来说,这些变通办法似乎开销太大了。因此,作为替代方案,让我们摆脱
AnimationTree
并直接使用AnimationPlayer
。为此,我们将:现在我们可以简单地要求
AnimationPlayer
进行播放,如下所示:我选择使用枚举,因为这也允许我在动画之间“旅行”。我们的想法是,我们将创建一个 for 循环,在其中按顺序调用动画。像这样:
我还决定尽早设置
staff_mode
,这样我就可以避免property_list_changed_notify
。并发调用可能会导致动画提前停止,因为调用
play
会停止当前正在播放的动画以播放新动画。但是,我认为等待当前动画结束是不正确的。而且对于如此短的动画,这应该不是问题。使用 Tween 的版本
使用
Tween
将为您提供更好的控制,但它也需要更多工作,因为我们要对动画进行编码在代码中...我将使用interpolate_property
来完成此操作。值得庆幸的是,这是一个相当简单的动画,因此可以在不使代码太长的情况下进行管理。当然,你需要添加一个Tween节点。我们不会使用
AnimationPlayer
或AnimationTree
。Tween
将处理插值(您甚至可以通过添加interpolate_property
的可选参数来指定如何进行插值,我在这里没有传递该参数)。这是代码:
您在这里可以看到,我已经对源代码中
AnimationPlayer
的轨道中的值进行了编码。使用 Tween,我可以告诉它从轨道的任何值到每个状态的目标位置进行插值。我不知道与
AnimationPlayer
相比,它的性能是更好还是更差。旧答案
好吧,这个问题有两个方面:
旅行 应该播放动画,所以它不是瞬时的。因此,如果您调用
stop
它将无法运行,并且您会收到错误消息。啊,但是你也不能调用 start 并连续旅行。您需要等待动画开始。
我首先不会从一种状态转到相同的状态:
我们需要获取
AnimationTree
,因此我们需要进入场景树。这里我使用yield
所以方法返回,Godot 在收到"tree_entered"
信号后恢复执行:yield
的缺点是如果在收到信号之前Node
空闲,则可能会导致错误。因此,如果您不想使用yield
,我们可以这样做:这里
CONNECT_ONESHOT
确保该信号自动断开。此外,Godot 确保在释放Node
时断开所有信号,因此这不会出现与yield
相同的问题。但是,与yield
不同的是,它不会在方法中间启动,而是会再次调用该方法。好吧,我们得到了
AnimationTree
:并得到了
AnimationNodeStateMachinePlayback
:现在,如果它没有播放,我们需要让它播放:
现在的问题是:我们需要等待动画开始。
为了代替更好的解决方案,我们将汇集它:
之前我建议获取
AnimationPlayer
,这样我们就可以等待"animation_started"
,但是那不起作用。最后,现在我们知道它正在播放,我们可以使用
travel
,并更新状态:不要调用
stop
.您可能还想致电
property_list_changed_notify()
在最后,所以 Godot 读取了staff_mode
的新值,它可能没有注册,因为我们没有立即更改它(相反,我们之前产生了改变它)。 我想您也可以在yield
之前更早地更改该值。顺便说一句,如果您希望
mid
动画在< code>travel,将AnimationTree
中从mid
发出的连接从“Immidiate”更改为“AtEnd”。关于等待行程结束和停止的附录
我们可以以与等待
AnimationNodeStateMachinePlayback
开始播放类似的方式旋转等待。这次我们需要池化两件事:只要动画未处于最终状态并且尚未到达动画的结尾,我们就会让一帧通过并再次检查。像这样:
然后你可以调用
stop
。此外,我将添加对
is_playing
的检查。原因是此代码正在等待AnimationTree
完成我们告诉它的状态...但是如果您在完成之前再次调用 Travel,它将前往新的目的地,因此可能永远不会到达我们期望的状态,这会导致自旋永远等待。由于它可能没有达到我们预期的状态,我决定查询最终状态,而不是将
staff_mode
设置为new_val
。这部分代码现在看起来像这样:New Answer
Apparently the solution on the old answer does not work for very short animations. And the workarounds begin to seem to much overhead for my taste. So, as alternative, let us get rid of the
AnimationTree
and work directly withAnimationPlayer
. To do this, we will:Now we can simply ask the
AnimationPlayer
to play, something like this:I have opted to use an enum, because this will also allow me to "travel" between the animations. The idea is that we will make a for loop where we call the animations in order. Like this:
I have also decided to set
staff_mode
early so I can avoidproperty_list_changed_notify
.Concurrent calls may result in animation stopping early, since calling
play
stops the currently playing animation to play the new one. However, I don't think waiting for the current animation to end is correct. Also with so short animations, it should not be a problem.Version using Tween
Using
Tween
will give you finer control, but it is also more work, because we are going to encode the animations in code… Which I will be doing withinterpolate_property
. Thankfully this is a fairly simple animation, so can manage without making the code too long.Of course, you need to add a
Tween
node. We will not useAnimationPlayer
norAnimationTree
.Tween
will handle the interpolations (and you can even specify how to do the interpolations by adding the optional parameters ofinterpolate_property
, which I'm not passing here).This is the code:
What you can see here is that I have encoded the values from the tracks of from the
AnimationPlayer
in the source code. UsingTween
I get to tell it to interpolate from whatever value the track has to the target position of each state.I don't know if this performs better or worse compared to
AnimationPlayer
.Old Answer
Alright, there are two sides to this problem:
travel
is supposed to play the animation, so it is not instantaneous. Thus, if you callstop
it will not be able to travel, and you get the error message you got.Ah, but you cannot call start and travel back to back either. You need to wait the animation to start.
I'll start by not going from one state to the same:
We are going to need to get the
AnimationTree
, so we need to ge in the scene tree. Here I useyield
so the method returns, and Godot resumes the execute after it gets the"tree_entered"
signal:The drawback of
yield
, is that it can cause an error if theNode
is free before you get the signal. Thus, if you prefer to not useyield
, we can do this instead:Here
CONNECT_ONESHOT
ensures this signal is automatically disconnected. Also, Godot makes sure to disconnect any signals when freeing aNode
so this does not have the same issue asyield
. However, unlikeyield
it will not start in the middle of the method, instead it will call the method again.Alright, we get the
AnimationTree
:And get the
AnimationNodeStateMachinePlayback
:Now, if it is not playing, we need to make it playing:
And now the problem: we need to wait for the animation to start.
In lieu of a better solution, we going to pool for it:
Previously I was suggesting to get the
AnimationPlayer
so we can wait for"animation_started"
, but that does not work.Finally, now that we know it is playing, we can use
travel
, and update the state:Don't call
stop
.You might also want to call
property_list_changed_notify()
at the end, so Godot reads the new value ofstaff_mode
, which it might not have registered because we didn't change it right away (instead we yielded before changing it). I suppose you could alternatively change the value earlier, before anyyield
.By the way, if you want the
mid
animation to complete in thetravel
, change the connections going out ofmid
in theAnimationTree
from "Immidiate" to "AtEnd".Addendum on waiting for travel to end and stop
We can spin wait in a similar fashion as we did to wait for the
AnimationNodeStateMachinePlayback
to start playing. This time we need to pool two things:As long as the animation is not in the final state and as long as it has not reached the end of that animation, we let one frame pass and check again. Like this:
Then you can call
stop
.Furthermore, I'll add a check for
is_playing
. The reason is that this code is waiting for theAnimationTree
to complete the state we told it to… But if you call travel again before it finished, it will go to new destination, and thus might never reach the state we expected, which result in spin waiting for ever.And since it might not have arrived to the state we expected, I decided to query the final state instead of setting
staff_mode
tonew_val
. That part of the code now looks like this: