同一节点中的碰撞检测和重叠检测? [第2部分]
Collision detection and overlapping detection in same node? [part 2]
的延续
我们如何从 body_set_force_integration_callback
中检测碰撞?
对于上下文,我们有 body RID
:
var _body:RID
然后我们使用 body_set_force_integration_callback
设置回调:
Physics2DServer.body_set_force_integration_callback(_body, self, "_body_moved", 0)
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
pass
在继续之前,我想指出 body_set_force_integration_callback
的最后一个参数是我们在 _user_data
中得到的。但是,如果我将它设置为 null
Godot 不会将两个参数传递给调用,在这种情况下我应该只使用 state
参数定义 _body_moved
。
如果 body 的状态处于活动状态(即未休眠),Godot 将调用我们的 _body_moved
每个物理帧。
注意:我们需要调用body_set_max_contacts_reported
设置我们要上报的联系人数量,例如:
Physics2DServer.body_set_max_contacts_reported(_body, 32)
现在,在Physics2DDirectBodyState
我们获取联系人,我们可以询问每个联系人的一些事情,包括body:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
for index in state.get_contact_count():
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
如果是PhysicsBody2D
的body,那么instance
就有了。
如果我们想要实现 body_entered
和 body_exited
,我们需要跟踪主体。我会保留实例的字典(即 PhysicsBody2D
),我也会用它来报告 get_colliding_bodies
。
然后我们需要跟踪 body_shape_entered
和 body_shape_exited
的形状,而不仅仅是物体。我们可以这样找到它们:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
for index in state.get_contact_count():
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
var body_shape_index:int = state.get_contact_collider_shape(index)
var local_shape_index:int = state.get_contact_local_shape(index)
注意他们不是 RID
。它们是形状在 body 中的位置(因此 0
是 body 的第一个形状,1
是第二个,依此类推)。这意味着我们不能与主体分开跟踪形状,因为如果不知道它们属于什么 body ,它们的形状索引就没有意义。这意味着我们不能像以前那样简单地使用两个物体数组。
此外,如果我们只有一种形状——这是先前答案的情况——我们可以忽略 local_shape_index
,因为它始终是 0
。在这种情况下,我只需要 body_shape_index:int
的 body:RID
索引的 Dictionary
。
如果我不接受那个假设,我很难决定数据结构。
- 我可以使用由
body:RID
索引的 Dictionary
索引的 Dictionary
索引由 local_shape_index:int
的 body_shape_index:int
索引,在这种情况下,我想要辅助方法来处理有了它,这促使我为它制作 class。
- 我可以使用由
body_shape_index:int
和 local_shape_index:int
的元组 body:RID
索引的 Dictionary
。除了没有元组类型,所以我会作弊并使用 Vector2
.
你知道吗?我会作弊并使用 Vector2
.
signal body_entered(body)
signal body_exited(body)
signal body_shape_entered(body_rid, body, body_shape_index, local_shape_index)
signal body_shape_exited(body_rid, body, body_shape_index, local_shape_index)
var colliding_instances:Dictionary = {}
var colliding_shapes:Dictionary = {}
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
var old_colliding_shapes:Dictionary = colliding_shapes
var new_colliding_shapes:Dictionary = {}
colliding_shapes = {}
var instances:Dictionary = {}
for index in state.get_contact_count():
# get contact information
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
var body_shape_index:int = state.get_contact_collider_shape(index)
var local_shape_index:int = state.get_contact_local_shape(index)
var vector := Vector2(body_shape_index, local_shape_index)
# add to instances
instances[body] = instance
# add to colliding_shapes
if not colliding_shapes.had(body):
colliding_shapes[body] = [vector]
else:
colliding_shapes[body].append(vector)
# remove from old_colliding_shapes or add to new_colliding_shapes
# there is room for optimization here
if (
old_colliding_shapes.has(body)
and old_colliding_shapes[body].has(vector)
):
old_colliding_shapes[body].erase(vector)
if old_colliding_shapes[body].size() == 0:
old_colliding_shapes.erase(body)
else:
if not new_colliding_shapes.had(body):
new_colliding_shapes[body] = [vector]
else:
new_colliding_shapes[body].append(vector)
for body in old_colliding_shapes.keys():
# get instance from old dictionary
var instance:Object = colliding_instances[body]
# emit
if not instances.has(body):
emit_signal("body_exited", body)
for vector in old_colliding_shapes[body]:
emit_signal(
"body_shape_exited",
body,
instance,
vector.x,
vector.y
)
for body in new_colliding_shapes.keys():
# get instance from new dictionary
var instance:Object = instances[body]
# emit
for vector in old_colliding_shapes[body]:
emit_signal(
"body_shape_entered",
body,
colliders[body],
vector.x,
vector.y
)
if not colliding_instances.has(body):
emit_signal("body_entered", body)
# swap instance dictionaries
colliding_instances = instances
func get_colliding_bodies() -> Array:
return colliding_instances.values()
变量old_colliding_shapes
以已知碰撞的形状开始,在迭代中我们将删除我们看到的每个形状。所以最后,它具有碰撞但不再碰撞的形状。
变量 new_colliding_bodies
开始为空,在迭代中我们添加了每个我们没有从 old_colliding_shapes
中删除的形状,所以最后它有我们没有删除的碰撞形状之前就知道了。
注意 old_colliding_shapes
和 new_colliding_bodies
是互斥的。如果 body 在一个中,则它不在另一个中,因为我们仅在 old_colliding_shapes
中不存在时才将 body 添加到 new_colliding_bodies
。但是由于它们有形状而不是主体,所以 body 可以出现在两者中。这就是为什么我需要额外检查以发出 "body_exited"
和 "body_entered"
.
我们如何从 body_set_force_integration_callback
中检测碰撞?
对于上下文,我们有 body RID
:
var _body:RID
然后我们使用 body_set_force_integration_callback
设置回调:
Physics2DServer.body_set_force_integration_callback(_body, self, "_body_moved", 0)
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
pass
在继续之前,我想指出 body_set_force_integration_callback
的最后一个参数是我们在 _user_data
中得到的。但是,如果我将它设置为 null
Godot 不会将两个参数传递给调用,在这种情况下我应该只使用 state
参数定义 _body_moved
。
如果 body 的状态处于活动状态(即未休眠),Godot 将调用我们的 _body_moved
每个物理帧。
注意:我们需要调用body_set_max_contacts_reported
设置我们要上报的联系人数量,例如:
Physics2DServer.body_set_max_contacts_reported(_body, 32)
现在,在Physics2DDirectBodyState
我们获取联系人,我们可以询问每个联系人的一些事情,包括body:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
for index in state.get_contact_count():
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
如果是PhysicsBody2D
的body,那么instance
就有了。
如果我们想要实现 body_entered
和 body_exited
,我们需要跟踪主体。我会保留实例的字典(即 PhysicsBody2D
),我也会用它来报告 get_colliding_bodies
。
然后我们需要跟踪 body_shape_entered
和 body_shape_exited
的形状,而不仅仅是物体。我们可以这样找到它们:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
for index in state.get_contact_count():
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
var body_shape_index:int = state.get_contact_collider_shape(index)
var local_shape_index:int = state.get_contact_local_shape(index)
注意他们不是 RID
。它们是形状在 body 中的位置(因此 0
是 body 的第一个形状,1
是第二个,依此类推)。这意味着我们不能与主体分开跟踪形状,因为如果不知道它们属于什么 body ,它们的形状索引就没有意义。这意味着我们不能像以前那样简单地使用两个物体数组。
此外,如果我们只有一种形状——这是先前答案的情况——我们可以忽略 local_shape_index
,因为它始终是 0
。在这种情况下,我只需要 body_shape_index:int
的 body:RID
索引的 Dictionary
。
如果我不接受那个假设,我很难决定数据结构。
- 我可以使用由
body:RID
索引的Dictionary
索引的Dictionary
索引由local_shape_index:int
的body_shape_index:int
索引,在这种情况下,我想要辅助方法来处理有了它,这促使我为它制作 class。 - 我可以使用由
body_shape_index:int
和local_shape_index:int
的元组body:RID
索引的Dictionary
。除了没有元组类型,所以我会作弊并使用Vector2
.
你知道吗?我会作弊并使用 Vector2
.
signal body_entered(body)
signal body_exited(body)
signal body_shape_entered(body_rid, body, body_shape_index, local_shape_index)
signal body_shape_exited(body_rid, body, body_shape_index, local_shape_index)
var colliding_instances:Dictionary = {}
var colliding_shapes:Dictionary = {}
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
var old_colliding_shapes:Dictionary = colliding_shapes
var new_colliding_shapes:Dictionary = {}
colliding_shapes = {}
var instances:Dictionary = {}
for index in state.get_contact_count():
# get contact information
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
var body_shape_index:int = state.get_contact_collider_shape(index)
var local_shape_index:int = state.get_contact_local_shape(index)
var vector := Vector2(body_shape_index, local_shape_index)
# add to instances
instances[body] = instance
# add to colliding_shapes
if not colliding_shapes.had(body):
colliding_shapes[body] = [vector]
else:
colliding_shapes[body].append(vector)
# remove from old_colliding_shapes or add to new_colliding_shapes
# there is room for optimization here
if (
old_colliding_shapes.has(body)
and old_colliding_shapes[body].has(vector)
):
old_colliding_shapes[body].erase(vector)
if old_colliding_shapes[body].size() == 0:
old_colliding_shapes.erase(body)
else:
if not new_colliding_shapes.had(body):
new_colliding_shapes[body] = [vector]
else:
new_colliding_shapes[body].append(vector)
for body in old_colliding_shapes.keys():
# get instance from old dictionary
var instance:Object = colliding_instances[body]
# emit
if not instances.has(body):
emit_signal("body_exited", body)
for vector in old_colliding_shapes[body]:
emit_signal(
"body_shape_exited",
body,
instance,
vector.x,
vector.y
)
for body in new_colliding_shapes.keys():
# get instance from new dictionary
var instance:Object = instances[body]
# emit
for vector in old_colliding_shapes[body]:
emit_signal(
"body_shape_entered",
body,
colliders[body],
vector.x,
vector.y
)
if not colliding_instances.has(body):
emit_signal("body_entered", body)
# swap instance dictionaries
colliding_instances = instances
func get_colliding_bodies() -> Array:
return colliding_instances.values()
变量old_colliding_shapes
以已知碰撞的形状开始,在迭代中我们将删除我们看到的每个形状。所以最后,它具有碰撞但不再碰撞的形状。
变量 new_colliding_bodies
开始为空,在迭代中我们添加了每个我们没有从 old_colliding_shapes
中删除的形状,所以最后它有我们没有删除的碰撞形状之前就知道了。
注意 old_colliding_shapes
和 new_colliding_bodies
是互斥的。如果 body 在一个中,则它不在另一个中,因为我们仅在 old_colliding_shapes
中不存在时才将 body 添加到 new_colliding_bodies
。但是由于它们有形状而不是主体,所以 body 可以出现在两者中。这就是为什么我需要额外检查以发出 "body_exited"
和 "body_entered"
.