AnimationPlayer seek() 使用线程

AnimationPlayer seek() using threading

我有一个附加到 AnimationPlayer 节点的脚本,它会进行一些长时间的计算,
所以为了避免游戏引擎在这些计算发生时挂起
我为那个函数创建了一个单独的线程,

但是尽管添加了 update=true

seek() 函数并没有更新动画

我已经将范围缩小到这个简单的例子:

extends AnimationPlayer
tool
export(float,0,1,0.1) var aniTimer = 0 setget aniTimer_Changed;
var thread;

func sep():
    self.seek(aniTimer,true);
    # calculations #

func aniTimer_Changed(new_val):
    aniTimer=new_val;
    thread = Thread.new();
    thread.start(self, "sep");

这是树的样子:

那么如何让 seek() 正常工作,或者是否有任何解决方法可以解决我想要实现的目标?

编辑:
我尝试应用@Theraot 给出的解决方案并修改它以循环遍历所有动画

像这样:

func sep(thread:Thread):
    var AnimationList=self.get_animation_list();
    for animation_name in AnimationList:

        self.current_animation=animation_name;
        var ongoing_animation=self.get_animation(animation_name);
        for track_indx in ongoing_animation.get_track_count():
            for key_indx in ongoing_animation.track_get_key_count(track_indx):
                var key_time=ongoing_animation.track_get_key_time(track_indx, key_indx);
                self.seek(key_time,true);

    Translate=false;
    property_list_changed_notify();
    thread.call_deferred("wait_to_finish")

func Translate_Changed(new_val):

    if _thread == null or _thread.is_active():
         _thread = Thread.new();

    _thread.start(self, "sep", _thread);

但是当我运行这是一个大动画时,它卡在中间并且整个 godot 游戏引擎挂起

我猜这是内存泄漏?

我想达到什么目的?

我实际上已经在节点上创建了自定义属性并将这些自定义属性添加为 AnimationPlayer

中的键

但这些自定义属性都会影响 positionrotation 和其他内置属性。

所以我认为通过 seek() 我可以看到所有这些组合的最终结果,然后将 positionrotation 和其他内置属性键入另一个 AnimationPlayer

经过测试,在我看来它可以使用一些注意事项:

  • 您需要妥善处理线程。我在下面解释。
  • 更改不会反映在“动画”面板中。但是您应该能够看到动画在节点上生效。

一旦正确完成,它就可以在 Godot 3.2 或更新版本上运行。然而,从 Godot 3.4 开始,我可以更加草率地处理线程,但它仍然可以工作。


首先,你需要在线程结束时调用wait_to_finish。不这样做会妨碍正确的清理工作。如果没有等待线程结束的代码,可以在线程代码末尾添加:

thread.call_deferred("wait_to_finish")

使用call_deferred(或set_deferredemit_signal)允许线程在主线程上发出操作。


顺便说一句,您可以在线程完成后再次调用 start 来重用该线程。所以你不需要每次都创建一个新的 Thread。所以你可以这样做:

if thread == null:
    thread = Thread.new()

但是!对于长时间的计算,线程将为 运行,当它为 运行 时,您不能对其调用 start。一个简单的解决方案是启动另一个:

if thread == null or thread.is_active():
    thread = Thread.new()

不知道有没有需要注意的竞争条件。考虑到两个线程运行同时存在的可能性

但是!这意味着当线程调用 thread.call_deferred("wait_to_finish") 时,那将不是正确的。所以我们要将 thread 作为参数传递给 thread.

我的代码是这样的:

tool
extends AnimationPlayer
export(float,0,1,0.1) var aniTimer = 0.0 setget aniTimer_Changed
var _thread:Thread

func sep(thread:Thread):
    seek(aniTimer, true)
    # calculations #
    thread.call_deferred("wait_to_finish")

func aniTimer_Changed(new_val):
    aniTimer=new_val
    if _thread == null or _thread.is_active():
        _thread = Thread.new()

    # warning-ignore:return_value_discarded
    _thread.start(self, "sep", _thread)