在 KinematicBody2D 上调用 get_position 会导致 get_global_transform 错误
Calling get_position on KinematicBody2D causes a get_global_transform error
总结
我正在尝试使用 Gut for my player
in gdscript but calling get_position()
on the player
(a KinematicBody2D) 编写单元测试导致以下错误:
ERROR: get_global_transform: Condition "!is_inside_tree()" is true. Returned: get_transform()
At: scene/2d/canvas_item.cpp:467.
ERROR: body_test_motion: Condition "!body->get_space()" is true. Returned: false
At: servers/physics_2d/physics_2d_server_sw.cpp:1046.
ERROR: get_global_transform: Condition "!is_inside_tree()" is true. Returned: get_transform()
At: scene/2d/canvas_item.cpp:467.
ERROR: body_test_ray_separation: Condition "!body->get_space()" is true. Returned: false
At: servers/physics_2d/physics_2d_server_sw.cpp:1058.
预期
我原以为在 _physics_process()
之后我可以得到我的 player
的更新 position
但它 returns Vector2(0, 0)
.
资源
这是我第一次使用 GDScript,所以我一直在混合使用以下资源来尝试编写第一个测试:
失败的解决方案
我想我可能需要让 player
成为其他东西的 child,但我不确定如何在我的测试中做到这一点。
我尝试在 Gut issues 中搜索如何在测试中使用 add_child()
,但找不到答案。
我也试过将 player
变成 Area2D
的 child 但这似乎也不起作用:
var player := Player.new(input)
var area := Area2D.new()
area.add_child(player)
代码
这是从命令行调用 gut
时导致错误的代码:
test/unit/actors/test_player.gd
extends "res://addons/gut/test.gd"
func test_can_create_player() -> void:
var input = MockInput.new()
var player := Player.new(input)
assert_not_null(player)
func test_can_move_player_up() -> void:
var input = MockInput.new()
input.press("ui_up")
var player := Player.new(input)
simulate(player, 1, .1)
assert_eq(player.get_position(), Vector2(200, 0))
test/mock_input.gd
class_name MockInput
var _pressed: Dictionary = {}
func press(key: String) -> void:
_pressed[key] = true
func release(key: String) -> void:
_pressed[key] = false
func is_action_pressed(key: String) -> bool:
if _pressed.has(key):
return _pressed.get(key)
return false
entities/actors/player.gd
extends KinematicBody2D
class_name Player
export var speed: int = 200
var _input: Object = Input
var _velocity: Vector2 = Vector2()
func _init(input: Object = Input):
_input = input
func _physics_process(_delta: float) -> void:
_velocity = Vector2()
if _input.is_action_pressed("ui_right"):
_velocity.x += 1
if _input.is_action_pressed("ui_left"):
_velocity.x -= 1
if _input.is_action_pressed("ui_down"):
_velocity.y += 1
if _input.is_action_pressed("ui_up"):
_velocity.y -= 1
_velocity = _velocity.normalized() * speed
_velocity = move_and_slide(_velocity)
我发现了一些对我有用的东西(我第一次错过这个有点生气)。
如我所料,player
需要是另一个 Node
的 child。
在 test/unit/actors/test_player.gd
中定义 player
变量后,可以将其添加为测试本身的 child 使用:
self.add_child_autofree(player)
完整的单元测试如下:
func test_can_move_player_up() -> void:
var input = MockInput.new()
input.press("ui_up")
var player := Player.new(input)
self.add_child_autofree(player)
simulate(player, 1, 0)
assert_eq(player.get_position().round(), Vector2(0, -30))
错误告诉你 !is_inside_tree()
。问题不仅仅在于节点没有 parent。问题是它不在 SceneTree
.
内
您可以创建自己的 SceneTree
实例。您甚至可以扩展它。例如,这会起作用:
var node := KinematicBody2D.new()
var tree := SceneTree.new()
tree.get_root().add_child(node)
yield(tree, "idle_frame")
print(node.get_global_transform())
现在,关于 Gut……您的测试将 运行 在 SceneTree
内进行。因此,您可以将要测试的节点添加为 children。请记住进行适当的拆卸(删除节点)。因为 Gut 不会为你做这件事,如果 children 节点从一个测试到另一个测试徘徊,那么它们就不再是单元了。
附录:你可以使用add_child_autofree
添加一个child,并在测试结束后立即将其排队等待释放。
答案的其余部分是我回顾 Gut 运行s 如何测试以确认它们 运行 在 SceneTree 中。
文件 gut_cmdln.gd
is actually an extended SceneTree
. It will load gut.gd
(here) and add it as a node to itself (here).
反过来,gut.gd
is in charge of running the tests, which is handled by _test_the_scripts
. What I want you to look is this:
var test_script = the_script.get_new()
var script_result = null
_setup_script(test_script)
那里the_script
是一个TestScript
object. The get_new
call will return an instance. And _setup_script
…
func _setup_script(test_script):
test_script.gut = self
test_script.set_logger(_lgr)
add_child(test_script)
_test_script_objects.append(test_script)
将其添加为 child(共 gud.gd
)。
之后 _test_the_scripts
将进入循环(here) and call _run_test
or _run_parameterized_test
(调用 _run_test
)以实际 运行 测试。并且 _run_test
将使用 call
在实例上 运行 测试方法(有额外的步骤)。
万岁!您的测试将在自定义 SceneTree
.
中 运行ning
总结
我正在尝试使用 Gut for my player
in gdscript but calling get_position()
on the player
(a KinematicBody2D) 编写单元测试导致以下错误:
ERROR: get_global_transform: Condition "!is_inside_tree()" is true. Returned: get_transform()
At: scene/2d/canvas_item.cpp:467.
ERROR: body_test_motion: Condition "!body->get_space()" is true. Returned: false
At: servers/physics_2d/physics_2d_server_sw.cpp:1046.
ERROR: get_global_transform: Condition "!is_inside_tree()" is true. Returned: get_transform()
At: scene/2d/canvas_item.cpp:467.
ERROR: body_test_ray_separation: Condition "!body->get_space()" is true. Returned: false
At: servers/physics_2d/physics_2d_server_sw.cpp:1058.
预期
我原以为在 _physics_process()
之后我可以得到我的 player
的更新 position
但它 returns Vector2(0, 0)
.
资源
这是我第一次使用 GDScript,所以我一直在混合使用以下资源来尝试编写第一个测试:
失败的解决方案
我想我可能需要让 player
成为其他东西的 child,但我不确定如何在我的测试中做到这一点。
我尝试在 Gut issues 中搜索如何在测试中使用 add_child()
,但找不到答案。
我也试过将 player
变成 Area2D
的 child 但这似乎也不起作用:
var player := Player.new(input)
var area := Area2D.new()
area.add_child(player)
代码
这是从命令行调用 gut
时导致错误的代码:
test/unit/actors/test_player.gd
extends "res://addons/gut/test.gd"
func test_can_create_player() -> void:
var input = MockInput.new()
var player := Player.new(input)
assert_not_null(player)
func test_can_move_player_up() -> void:
var input = MockInput.new()
input.press("ui_up")
var player := Player.new(input)
simulate(player, 1, .1)
assert_eq(player.get_position(), Vector2(200, 0))
test/mock_input.gd
class_name MockInput
var _pressed: Dictionary = {}
func press(key: String) -> void:
_pressed[key] = true
func release(key: String) -> void:
_pressed[key] = false
func is_action_pressed(key: String) -> bool:
if _pressed.has(key):
return _pressed.get(key)
return false
entities/actors/player.gd
extends KinematicBody2D
class_name Player
export var speed: int = 200
var _input: Object = Input
var _velocity: Vector2 = Vector2()
func _init(input: Object = Input):
_input = input
func _physics_process(_delta: float) -> void:
_velocity = Vector2()
if _input.is_action_pressed("ui_right"):
_velocity.x += 1
if _input.is_action_pressed("ui_left"):
_velocity.x -= 1
if _input.is_action_pressed("ui_down"):
_velocity.y += 1
if _input.is_action_pressed("ui_up"):
_velocity.y -= 1
_velocity = _velocity.normalized() * speed
_velocity = move_and_slide(_velocity)
我发现了一些对我有用的东西(我第一次错过这个有点生气)。
如我所料,player
需要是另一个 Node
的 child。
在 test/unit/actors/test_player.gd
中定义 player
变量后,可以将其添加为测试本身的 child 使用:
self.add_child_autofree(player)
完整的单元测试如下:
func test_can_move_player_up() -> void:
var input = MockInput.new()
input.press("ui_up")
var player := Player.new(input)
self.add_child_autofree(player)
simulate(player, 1, 0)
assert_eq(player.get_position().round(), Vector2(0, -30))
错误告诉你 !is_inside_tree()
。问题不仅仅在于节点没有 parent。问题是它不在 SceneTree
.
您可以创建自己的 SceneTree
实例。您甚至可以扩展它。例如,这会起作用:
var node := KinematicBody2D.new()
var tree := SceneTree.new()
tree.get_root().add_child(node)
yield(tree, "idle_frame")
print(node.get_global_transform())
现在,关于 Gut……您的测试将 运行 在 SceneTree
内进行。因此,您可以将要测试的节点添加为 children。请记住进行适当的拆卸(删除节点)。因为 Gut 不会为你做这件事,如果 children 节点从一个测试到另一个测试徘徊,那么它们就不再是单元了。
附录:你可以使用add_child_autofree
添加一个child,并在测试结束后立即将其排队等待释放。
答案的其余部分是我回顾 Gut 运行s 如何测试以确认它们 运行 在 SceneTree 中。
文件 gut_cmdln.gd
is actually an extended SceneTree
. It will load gut.gd
(here) and add it as a node to itself (here).
反过来,gut.gd
is in charge of running the tests, which is handled by _test_the_scripts
. What I want you to look is this:
var test_script = the_script.get_new()
var script_result = null
_setup_script(test_script)
那里the_script
是一个TestScript
object. The get_new
call will return an instance. And _setup_script
…
func _setup_script(test_script):
test_script.gut = self
test_script.set_logger(_lgr)
add_child(test_script)
_test_script_objects.append(test_script)
将其添加为 child(共 gud.gd
)。
之后 _test_the_scripts
将进入循环(here) and call _run_test
or _run_parameterized_test
(调用 _run_test
)以实际 运行 测试。并且 _run_test
将使用 call
在实例上 运行 测试方法(有额外的步骤)。
万岁!您的测试将在自定义 SceneTree
.