TextureProgressBar的progress_texture在其值改变时不会前进。

huangapple go评论81阅读模式
英文:

TextureProgressBar's progress_texture Doesn't Advance When Its value is Changed

问题

我在Godot 4.0中工作,遇到了TextureProgressBar的问题。为了重新创建这个问题,创建一个新项目,创建一个新场景作为Node2D,并添加子节点Timer和CanvasLayer。然后为CanvasLayer添加一个名为TextureProgressBar的子节点。在Node2D上添加一个gdscript文件,并将Timer的timeout()信号连接到它(我将计时器的名称更改为StartTimer,CanvasLayer更改为Loading Screen,TextureProgressBar更改为Loading Bar,这就是为什么在提供的代码中函数调用名称不同的原因)。然后将脚本文件中的代码替换为以下内容:

extends Node2D

func _ready():
	get_node("Loading Screen/Loading Bar").position = Vector2(200, 360)
	get_node("Loading Screen/Loading Bar").set_value(1)

func start_world_creation():
	start_progress_bar()

func start_progress_bar():
	for x in range(100):
		for y in range(100):
			for time_waster in range(20000):
				time_waster += 300
		get_node("Loading Screen/Loading Bar").value += 1
		print(get_node("Loading Screen/Loading Bar").value)

func _process(delta):
	pass

func _on_start_timer_timeout():
	start_world_creation()

最后,将以下图片分配给TextureProgressBar的相应位置:

TextureProgressBar的progress_texture在其值改变时不会前进。
TextureProgressBar的progress_texture在其值改变时不会前进。
TextureProgressBar的progress_texture在其值改变时不会前进。

由于某种原因,TextureProgressBar直到其值达到100才会更新。为什么会这样,我该如何修复它?根据文档,它应该在值每次增加1时更新,因为TextureProgressBar的步进值设置为1,最大值为100。

英文:

I am working in Godot 4.0, and having issues with the TextureProgressBar. In order to recreate this issue, make a new project, create a new scene as a Node2D, and add the children Timer and CanvasLayer. Then give the CanvasLayer its own child, a TextureProgressBar. Add a gdscript file to the Node2D and connect the Timer's timeout() signal to it (I changed the name of the timer to StartTimer, the CanvasLayer to Loading Screen, and the TextureProgressBar to Loading Bar, which is why the func call name is different in the provided code). Then replace the code in the script file with this:

extends Node2D

func _ready():
	get_node("Loading Screen/Loading Bar").position = Vector2(200, 360)
	get_node("Loading Screen/Loading Bar").set_value(1)

func start_world_creation():
	start_progress_bar()

func start_progress_bar():
	for x in 100:
		for y in 100:
			for time_waster in range(20000):
				time_waster += 300
		get_node("Loading Screen/Loading Bar").value += 1
		print(get_node("Loading Screen/Loading Bar").value)

func _process(delta):
	pass


func _on_start_timer_timeout():
	start_world_creation()

Finally, assign the images below to their respective places for the TextureProgressBar:
TextureProgressBar的progress_texture在其值改变时不会前进。
TextureProgressBar的progress_texture在其值改变时不会前进。
TextureProgressBar的progress_texture在其值改变时不会前进。

For some reason, the TextureProgressBar does not update until its value reaches 100. Why is this, and how can I fix it? According to the documentation, it should update with every integer increase in value, since the TextureProgressBar's step value is set to 1, and max_value is 100.

答案1

得分: 1

所以 Timer 的信号被触发,这段代码将在下一帧运行:

func _on_start_timer_timeout():
    start_world_creation()

调用这段代码,将在下一帧结束前完成:

func start_world_creation():
    start_progress_bar()

调用这段代码,将在下一帧结束前完成:

func start_progress_bar():
    for x in 100:
        for y in 100:
            for time_waster in range(20000):
                time_waster += 300
        get_node("Loading Screen/Loading Bar").value += 1
        print(get_node("Loading Screen/Loading Bar").value)

因此,当 Godot 最终进入下一帧时,TextureProgressBarvalue100.0,这也是默认的 max_value,所以你会看到它已满。

希望你能看到问题:time_waster 等正在让线程忙碌,所以 Godot 无法做其他事情。在这种情况下,使用 set_deferredcall_deferred 是无济于事的。

简单的解决方案是 await 一帧:

func start_progress_bar():
    for x in 100:
        for y in 100:
            for time_waster in range(20000):
                time_waster += 300
        get_node("Loading Screen/Loading Bar").value += 1
        print(get_node("Loading Screen/Loading Bar").value)
        await get_tree().process_frame

当执行到 await 时,Godot 会保存当前位置,并在指定信号(在这种情况下是 get_tree().process_frame)触发后继续执行。

另一种方法是在另一个 Thread 中运行代码。在这种情况下,你可以使用 set_deferredcall_deferred 来操作 UI。

如果 time_waster 等的代码是某个异步操作的替身,你可能还想寻找适当的信号(或创建一个信号),以便在异步操作完成后使用 await

警告:使用 await 有缺点:它不能被取消或断开连接。

因此:

  • 即使你知道信号永远不会被触发,你也无法让 Godot 忘记。
  • 如果该 object 已被释放,并且信号被触发,你将会遇到运行时错误。

如果遇到这种情况,通过连接一个 Callable 的继续执行(可以是匿名方法,也叫做 "lambda")到信号上来重构代码... 当对象被释放时,Godot 会断开连接它。

如果你需要检查一个 Node 是否即将被 queue_free 销毁,你可以检查 is_queued_for_deletion。如果你需要检查是否有对已释放的 Node 的引用,请使用 is_instance_valid。如果你需要在释放 Object 前执行某些操作,请让它重写 _notification 并对 NOTIFICATION_PREDELETE 作出反应。

英文:

So the signal of the Timer is emitted and this code will run before the next frame:

func _on_start_timer_timeout():
    start_world_creation()

Which is calling this code which will finish before the next frame:

func start_world_creation():
    start_progress_bar()

Which is calling this code which will finish before the next frame:

func start_progress_bar():
    for x in 100:
        for y in 100:
            for time_waster in range(20000):
                time_waster += 300
        get_node("Loading Screen/Loading Bar").value += 1
        print(get_node("Loading Screen/Loading Bar").value)

So when Godot finally gets around to the next frame, the value of the TextureProgressBar is at 100.0, which is also the default max_value, so you see it full.

Hopefully you can see the issue: The time_waster et.al. is keeping the thread busy, so Godot cannot do other stuff. In this situation using set_deferred or call_deferred won't help you.


The easier solution is to await a frame:

func start_progress_bar():
    for x in 100:
        for y in 100:
            for time_waster in range(20000):
                time_waster += 300
        get_node("Loading Screen/Loading Bar").value += 1
        print(get_node("Loading Screen/Loading Bar").value)
        await get_tree().process_frame

When the execution reaches await, Godot will save where it was, and continue after the specified signal (get_tree().process_frame in this case) is emitted.


Another thing you could do is run the code in another Thread. In this case you can use set_deferred or call_deferred to manipulate the UI.


If the time_waster et.al. code is an stand-in for some asynchronous operation, you might also want to look for an appropriate signal (or make one) so you can await until the asynchronous operation is completed.


WARNING: Using await has drawbacks: It cannot be cancelled or disconnected.

Thus:

  • Even if you know the signal will never be emitted, you cannot make Godot forget.
  • If the object was freed, and the signal is emitted, you have a runtime error.

If you run into that, refactor the code by connecting a Callable continuation (which can be an anonymous method a.k.a. "lambda") to the signal... When the object is freed, Godot will disconnect it.

If you need to check if a Node is about to be freed by queue_free, you can check is_queued_for_deletion. If you need to check if you have a reference to a Node that was freed, use is_instance_valid. If you need to do something before an Object is freed, have it override _notification and react to NOTIFICATION_PREDELETE.

huangapple
  • 本文由 发表于 2023年5月28日 02:06:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76348341.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定