对于上下文,我们有一个身体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 不会向调用传递两个参数,在这种情况下我应该定义_body_moved
仅与state
范围。
戈多将召唤我们的_body_moved
如果身体状态处于活动状态(即未睡眠),则每个物理帧。
Note: 我们需要打电话body_set_max_contacts_reported
并设置我们想要报告的联系人数量,例如:
Physics2DServer.body_set_max_contacts_reported(_body, 32)
现在,在Physics2DDirectBodyState
我们得到联系人,我们可以询问每个联系人的一些信息,包括身体:
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
, then instance
将会拥有它。
如果我们想实现body_entered
and body_exited
,我们需要跟踪尸体。我将保留实例的字典(即PhysicsBody2D
)我将用它来报告get_colliding_bodies
too.
然后我们需要跟踪形状body_shape_entered
and 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
。它们是形状在身体中的位置(所以0
是身体的第一个形状,1
是第二个,依此类推)。这意味着我们无法将形状与物体分开来跟踪,因为如果不知道它们属于哪个物体,它们的形状索引就没有意义。这意味着我们不能像以前那样简单地使用两个主体数组。
另外,如果我们只有一种形状 - 这是之前答案的情况 - 我们可以忽略local_shape_index
因为它总是0
。在这种情况下我只需要一个Dictionary
索引为body:RID
of body_shape_index:int
.
如果我不接受这个假设,我就很难决定数据结构。
- 我可以用一个
Dictionary
索引为body:RID
of Dictionary
索引为body_shape_index:int
of local_shape_index:int
,在这种情况下,我需要辅助方法来处理它,这促使我为它创建一个类。
- 我可以用一个
Dictionary
索引为body:RID
的元组body_shape_index:int
and local_shape_index:int
。除了没有元组类型,所以我会作弊并使用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
and new_colliding_bodies
是互斥的。如果一个物体在一个物体中,那么它就不在另一个物体中,因为我们只是将物体添加到new_colliding_bodies
当它不在old_colliding_shapes
。但由于它们有形状而不是身体,所以两者都可以出现身体。这就是为什么我需要额外的检查来发出"body_exited"
and "body_entered"
.