Godot:如何将 KinematicBody2D 节点从一个场景移动到另一个场景

发布于 2025-01-10 05:30:17 字数 2030 浏览 4 评论 0原文

我想将我的 Player 节点 (KinematicBody2D) 从一个场景移动到另一个场景。我的代码成功移动 Node2D 节点,但移动 KinematicBody2D 节点失败。

我有两个具有以下结构的位置场景(继承自基本位置场景):

FirstLocation (inherits Location)
  - YSort
    - Player

在基本 Location 场景中,我有两种方法:

func add_player(player: Player):
    get_node("YSort").add_child(player)

func remove_player() -> Player:
    var player = get_node("YSort/Player")
    get_node("YSort").remove_child(player)
    return player

GameWorld 场景中,我将可能的位置存储在其中字典和玩家的移动发生在change_location()函数内:

onready var _locations = {
    Location.FOO: $CurrentLocation,
    Location.BAR: load("res://locations/Bar.tscn").instance(),
}

onready var _current_location = $CurrentLocation

func change_location(location: int):
    var player = _current_location.remove_player()

    remove_child(_current_location)
    _current_location = _locations[location]
    add_child(_current_location)

    _current_location.add_player(player)
  • 位置的切换有效
  • 如果玩家是普通的Node2D,则玩家的移动也有效。

但是,当 Player 是 KinematicBody2D 时,游戏就会崩溃,没有任何提示说明问题的原因。

当我注释掉最后一行时,代码可以正常工作而不会崩溃:

    _current_location.add_player(player)

...但是当然,玩家不会被添加到其他场景中。

  • 我确认玩家确实已从场景中移除。
  • 我使用虚拟场景(仅包含 KinematicBody2D 作为根节点和简单的 Sprite 作为单个子节点)而不是实际的更复杂的 Player 场景进行测试,以确保它与我的 Player 场景中可能拥有的任何其他代码无关。 Node2D 作为 root 工作,KinematicBody2D 崩溃。 一定是侥幸。再次测试,现在两者都可以工作,所以我的 Player 对象中一定有不同的东西。
  • 我尝试将 Player 节点添加为 Location 节点的直接子节点(中间没有 YSort 节点) - 不,仍然崩溃。
  • 我尝试在将玩家添加到新场景之前/之后设置玩家的位置/全局位置 - 没有区别。
  • 我能够创建新的 Player 场景实例并将其添加到新位置。

KinematicBody2D 中的什么可能阻止我将其从一个场景移动到另一个场景?


找到了解决方案,但我不知道它为什么有效。

我正在执行位置更改作为对 Area2D.body_entered 信号的响应。我触发了自己的信号“location_change”,然后在代码的另一部分调用了 change_location() 函数来响应它。

在进行位置更改之前添加一个微小的超时(0.00000001秒)解决了这个问题。但是我不知道为什么,而且我很确定添加超时并不是解决这个问题的正确方法。

I want to move my Player node (KinematicBody2D) from one scene to another. My code successfully moves Node2D node, but fails with KinematicBody2D node.

I have two Location scenes (inheriting from base Location scene) with the following structure:

FirstLocation (inherits Location)
  - YSort
    - Player

In the base Location scene I have two methods:

func add_player(player: Player):
    get_node("YSort").add_child(player)

func remove_player() -> Player:
    var player = get_node("YSort/Player")
    get_node("YSort").remove_child(player)
    return player

In GameWorld scene I store the possible locations inside a dictionary and the moving of the player happens inside change_location() function:

onready var _locations = {
    Location.FOO: $CurrentLocation,
    Location.BAR: load("res://locations/Bar.tscn").instance(),
}

onready var _current_location = $CurrentLocation

func change_location(location: int):
    var player = _current_location.remove_player()

    remove_child(_current_location)
    _current_location = _locations[location]
    add_child(_current_location)

    _current_location.add_player(player)
  • The switching of the location works
  • The moving of the player also works in case the player is plain Node2D.

But when Player is KinematicBody2D then the game simply crashes, giving me no hint as to what's causing the problem.

The code works without crashing when I comment out the last line:

    _current_location.add_player(player)

...but of course then the player simply doesn't get added to the other scene.

  • I verified that the player does get removed from the scene.
  • I tested with a dummy scene (which only contains KinematicBody2D as root node and a simple Sprite as a single child) instead of my actual more complex Player scene to make sure it's not related to any other code I might have in my Player scene. Node2D as root works, KinematicBody2D crashes. Must have been a fluke. Tested again and now both work, so there must be something different in my Player object.
  • I tried adding the Player node as a direct child of Location node (not having a YSort node in the middle) - nope, still crashes.
  • I tried setting the position/global_position of player before/after adding it to new scene - no difference.
  • I'm able to create new Player scene instance and add it to new location.

What might it be in KinematicBody2D that prevents me from moving it from scene to scene?


Found a solution, but I don't know why it works.

I was performing the location change as a response to Area2D.body_entered signal. I triggered my own signal "location_change" and then called the change_location() function in another part of the code in response to it.

Adding a tiny timeout (0.00000001 seconds) before doing the location change solved the issue. However I have no idea why, and I'm pretty sure adding timeouts is not a proper way to solve this problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

雨夜星沙 2025-01-17 05:30:17

我很难想象当时的情况。但是,考虑到问题是在删除和添加物理体时发生的,并且涉及到“body_entered”,并且您找到的解决方案是添加超时......

听起来问题是删除物理体而戈多仍在解决物理反应。然后解决方案就是等到下一帧。

您可以等到下一个图形帧,这样:

yield(get_tree(), "idle_frame")

或者直到下一个物理帧,这样:

yield(get_tree(), "physics_frame")

这比任意的小超时要好。


或者,您也可以延迟信号连接。

如果您从 UI 进行连接,“将信号连接到方法”对话框中将会有一个“高级”切换开关。启用“高级”切换将显示一些额外信息,包括延迟连接。

如果您要连接表单代码,则可以通过传递 CONNECT_DEFERRED 标志来完成相同的操作。

I'm having trouble visualizing the situation. However, given that the problem happens when removing and adding a physics body, and that "body_entered" was involved, and that the solution you found was adding a time out…

Sounds like the issue was removing the physics body while Godot was still resolving physics response. And then the solution was to wait until the next frame.

You could wait until the next graphics frame with this:

yield(get_tree(), "idle_frame")

Or until the next physics frame with this:

yield(get_tree(), "physics_frame")

Which would be better than some arbitrary small timeout.


Alternatively, you could also make the signal connection deferred.

If you are connecting from the UI, there will be an "advanced" toggle in the "Connect a Signal to a Method" dialog. Enabling "advanced" toggle will reveal some extra including making the connection deferred.

If you are connecting form code, you can accomplish the same thing by passing the CONNECT_DEFERRED flag.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文