浏览代码

Merge pull request #53280 from nekomatata/test-body-motion-parameters

Rémi Verschelde 3 年之前
父节点
当前提交
5b270278c8

+ 67 - 36
doc/classes/KinematicCollision2D.xml

@@ -14,43 +14,74 @@
 			<return type="float" />
 			<argument index="0" name="up_direction" type="Vector2" default="Vector2(0, -1)" />
 			<description>
-				The collision angle according to [code]up_direction[/code], which is [code]Vector2.UP[/code] by default. This value is always positive.
+				Returns the collision angle according to [code]up_direction[/code], which is [code]Vector2.UP[/code] by default. This value is always positive.
+			</description>
+		</method>
+		<method name="get_collider" qualifiers="const">
+			<return type="Object" />
+			<description>
+				Returns the colliding body's attached [Object].
+			</description>
+		</method>
+		<method name="get_collider_id" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the unique instance ID of the colliding body's attached [Object]. See [method Object.get_instance_id].
+			</description>
+		</method>
+		<method name="get_collider_rid" qualifiers="const">
+			<return type="RID" />
+			<description>
+				Returns the colliding body's [RID] used by the [PhysicsServer2D].
+			</description>
+		</method>
+		<method name="get_collider_shape" qualifiers="const">
+			<return type="Object" />
+			<description>
+				Returns the colliding body's shape.
+			</description>
+		</method>
+		<method name="get_collider_shape_index" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the colliding body's shape index. See [CollisionObject2D].
+			</description>
+		</method>
+		<method name="get_collider_velocity" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the colliding body's velocity.
+			</description>
+		</method>
+		<method name="get_local_shape" qualifiers="const">
+			<return type="Object" />
+			<description>
+				Returns the moving object's colliding shape.
+			</description>
+		</method>
+		<method name="get_normal" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the colliding body's shape's normal at the point of collision.
+			</description>
+		</method>
+		<method name="get_position" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the point of collision in global coordinates.
+			</description>
+		</method>
+		<method name="get_remainder" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the moving object's remaining movement vector.
+			</description>
+		</method>
+		<method name="get_travel" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the moving object's travel before collision.
 			</description>
 		</method>
 	</methods>
-	<members>
-		<member name="collider" type="Object" setter="" getter="get_collider">
-			The colliding body.
-		</member>
-		<member name="collider_id" type="int" setter="" getter="get_collider_id" default="0">
-			The colliding body's unique instance ID. See [method Object.get_instance_id].
-		</member>
-		<member name="collider_rid" type="RID" setter="" getter="get_collider_rid">
-			The colliding body's [RID] used by the [PhysicsServer2D].
-		</member>
-		<member name="collider_shape" type="Object" setter="" getter="get_collider_shape">
-			The colliding body's shape.
-		</member>
-		<member name="collider_shape_index" type="int" setter="" getter="get_collider_shape_index" default="0">
-			The colliding shape's index. See [CollisionObject2D].
-		</member>
-		<member name="collider_velocity" type="Vector2" setter="" getter="get_collider_velocity" default="Vector2(0, 0)">
-			The colliding object's velocity.
-		</member>
-		<member name="local_shape" type="Object" setter="" getter="get_local_shape">
-			The moving object's colliding shape.
-		</member>
-		<member name="normal" type="Vector2" setter="" getter="get_normal" default="Vector2(0, 0)">
-			The colliding body's shape's normal at the point of collision.
-		</member>
-		<member name="position" type="Vector2" setter="" getter="get_position" default="Vector2(0, 0)">
-			The point of collision, in global coordinates.
-		</member>
-		<member name="remainder" type="Vector2" setter="" getter="get_remainder" default="Vector2(0, 0)">
-			The moving object's remaining movement vector.
-		</member>
-		<member name="travel" type="Vector2" setter="" getter="get_travel" default="Vector2(0, 0)">
-			The distance the moving object traveled before collision.
-		</member>
-	</members>
 </class>

+ 28 - 47
doc/classes/KinematicCollision3D.xml

@@ -15,108 +15,89 @@
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<argument index="1" name="up_direction" type="Vector3" default="Vector3(0, 1, 0)" />
 			<description>
-				The collision angle according to [code]up_direction[/code], which is [code]Vector3.UP[/code] by default. This value is always positive.
+				Returns the collision angle according to [code]up_direction[/code], which is [code]Vector3.UP[/code] by default. This value is always positive.
 			</description>
 		</method>
 		<method name="get_collider" qualifiers="const">
 			<return type="Object" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider by index (the latest by default).
+				Returns the colliding body's attached [Object] given a collision index (the deepest collision by default).
 			</description>
 		</method>
 		<method name="get_collider_id" qualifiers="const">
 			<return type="int" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider ID by index (the latest by default).
+				Returns the unique instance ID of the colliding body's attached [Object] given a collision index (the deepest collision by default). See [method Object.get_instance_id].
 			</description>
 		</method>
 		<method name="get_collider_rid" qualifiers="const">
 			<return type="RID" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider RID by index (the latest by default).
+				Returns the colliding body's [RID] used by the [PhysicsServer3D] given a collision index (the deepest collision by default).
 			</description>
 		</method>
 		<method name="get_collider_shape" qualifiers="const">
 			<return type="Object" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider shape by index (the latest by default).
+				Returns the colliding body's shape given a collision index (the deepest collision by default).
 			</description>
 		</method>
 		<method name="get_collider_shape_index" qualifiers="const">
 			<return type="int" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider shape index by index (the latest by default).
+				Returns the colliding body's shape index given a collision index (the deepest collision by default). See [CollisionObject3D].
 			</description>
 		</method>
 		<method name="get_collider_velocity" qualifiers="const">
 			<return type="Vector3" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider velocity by index (the latest by default).
+				Returns the colliding body's velocity given a collision index (the deepest collision by default).
+			</description>
+		</method>
+		<method name="get_collision_count" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the number of detected collisions.
 			</description>
 		</method>
 		<method name="get_local_shape" qualifiers="const">
 			<return type="Object" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider velocity by index (the latest by default).
+				Returns the moving object's colliding shape given a collision index (the deepest collision by default).
 			</description>
 		</method>
 		<method name="get_normal" qualifiers="const">
 			<return type="Vector3" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider normal by index (the latest by default).
+				Returns the colliding body's shape's normal at the point of collision given a collision index (the deepest collision by default).
 			</description>
 		</method>
 		<method name="get_position" qualifiers="const">
 			<return type="Vector3" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
-				Returns the collider collision point by index (the latest by default).
+				Returns the point of collision in global coordinates given a collision index (the deepest collision by default).
+			</description>
+		</method>
+		<method name="get_remainder" qualifiers="const">
+			<return type="Vector3" />
+			<description>
+				Returns the moving object's remaining movement vector.
+			</description>
+		</method>
+		<method name="get_travel" qualifiers="const">
+			<return type="Vector3" />
+			<description>
+				Returns the moving object's travel before collision.
 			</description>
 		</method>
 	</methods>
-	<members>
-		<member name="collider" type="Object" setter="" getter="get_best_collider">
-			The colliding body.
-		</member>
-		<member name="collider_id" type="int" setter="" getter="get_best_collider_id" default="0">
-			The colliding body's unique instance ID. See [method Object.get_instance_id].
-		</member>
-		<member name="collider_rid" type="RID" setter="" getter="get_best_collider_rid">
-			The colliding body's [RID] used by the [PhysicsServer3D].
-		</member>
-		<member name="collider_shape" type="Object" setter="" getter="get_best_collider_shape">
-			The colliding body's shape.
-		</member>
-		<member name="collider_shape_index" type="int" setter="" getter="get_best_collider_shape_index" default="0">
-			The colliding shape's index. See [CollisionObject3D].
-		</member>
-		<member name="collider_velocity" type="Vector3" setter="" getter="get_best_collider_velocity" default="Vector3(0, 0, 0)">
-			The colliding object's velocity.
-		</member>
-		<member name="collision_count" type="int" setter="" getter="get_collision_count" default="0">
-		</member>
-		<member name="local_shape" type="Object" setter="" getter="get_best_local_shape">
-			The moving object's colliding shape.
-		</member>
-		<member name="normal" type="Vector3" setter="" getter="get_best_normal" default="Vector3(0, 0, 0)">
-			The colliding body's shape's normal at the point of collision.
-		</member>
-		<member name="position" type="Vector3" setter="" getter="get_best_position" default="Vector3(0, 0, 0)">
-			The point of collision, in global coordinates.
-		</member>
-		<member name="remainder" type="Vector3" setter="" getter="get_remainder" default="Vector3(0, 0, 0)">
-			The moving object's remaining movement vector.
-		</member>
-		<member name="travel" type="Vector3" setter="" getter="get_travel" default="Vector3(0, 0, 0)">
-			The distance the moving object traveled before collision.
-		</member>
-	</members>
 </class>

+ 4 - 8
doc/classes/PhysicsServer2D.xml

@@ -584,14 +584,10 @@
 		<method name="body_test_motion">
 			<return type="bool" />
 			<argument index="0" name="body" type="RID" />
-			<argument index="1" name="from" type="Transform2D" />
-			<argument index="2" name="motion" type="Vector2" />
-			<argument index="3" name="margin" type="float" default="0.08" />
-			<argument index="4" name="result" type="PhysicsTestMotionResult2D" default="null" />
-			<argument index="5" name="collide_separation_ray" type="bool" default="false" />
-			<argument index="6" name="exclude" type="Array" default="[]" />
-			<description>
-				Returns [code]true[/code] if a collision would result from moving in the given direction from a given point in space. Margin increases the size of the shapes involved in the collision detection. [PhysicsTestMotionResult2D] can be passed to return additional information in.
+			<argument index="1" name="parameters" type="PhysicsTestMotionParameters2D" />
+			<argument index="2" name="result" type="PhysicsTestMotionResult2D" default="null" />
+			<description>
+				Returns [code]true[/code] if a collision would result from moving along a motion vector from a given point in space. [PhysicsTestMotionParameters2D] is passed to set motion parameters. [PhysicsTestMotionResult2D] can be passed to return additional information.
 			</description>
 		</method>
 		<method name="capsule_shape_create">

+ 4 - 9
doc/classes/PhysicsServer3D.xml

@@ -577,15 +577,10 @@
 		<method name="body_test_motion">
 			<return type="bool" />
 			<argument index="0" name="body" type="RID" />
-			<argument index="1" name="from" type="Transform3D" />
-			<argument index="2" name="motion" type="Vector3" />
-			<argument index="3" name="margin" type="float" default="0.001" />
-			<argument index="4" name="result" type="PhysicsTestMotionResult3D" default="null" />
-			<argument index="5" name="collide_separation_ray" type="bool" default="false" />
-			<argument index="6" name="exclude" type="Array" default="[]" />
-			<argument index="7" name="max_collisions" type="int" default="1" />
-			<description>
-				Returns [code]true[/code] if a collision would result from moving in the given direction from a given point in space. Margin increases the size of the shapes involved in the collision detection. [PhysicsTestMotionResult3D] can be passed to return additional information in.
+			<argument index="1" name="parameters" type="PhysicsTestMotionParameters3D" />
+			<argument index="2" name="result" type="PhysicsTestMotionResult3D" default="null" />
+			<description>
+				Returns [code]true[/code] if a collision would result from moving along a motion vector from a given point in space. [PhysicsTestMotionParameters3D] is passed to set motion parameters. [PhysicsTestMotionResult3D] can be passed to return additional information.
 			</description>
 		</method>
 		<method name="box_shape_create">

+ 29 - 0
doc/classes/PhysicsTestMotionParameters2D.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PhysicsTestMotionParameters2D" inherits="RefCounted" version="4.0">
+	<brief_description>
+		Parameters to be sent to a 2D body motion test.
+	</brief_description>
+	<description>
+		This class contains parameters used in [method PhysicsServer2D.body_test_motion].
+	</description>
+	<tutorials>
+	</tutorials>
+	<members>
+		<member name="collide_separation_ray" type="bool" setter="set_collide_separation_ray_enabled" getter="is_collide_separation_ray_enabled" default="false">
+			If set to [code]true[/code], shapes of type [constant PhysicsServer2D.SHAPE_SEPARATION_RAY] are used to detect collisions and can stop the motion. Can be useful when snapping to the ground.
+			If set to [code]false[/code], shapes of type [constant PhysicsServer2D.SHAPE_SEPARATION_RAY] are only used for separation when overlapping with other bodies. That's the main use for separation ray shapes.
+		</member>
+		<member name="exclude_bodies" type="Array" setter="set_exclude_bodies" getter="get_exclude_bodies" default="[]">
+			Optional array of body [RID] to exclude from collision.
+		</member>
+		<member name="from" type="Transform2D" setter="set_from" getter="get_from" default="Transform2D(1, 0, 0, 1, 0, 0)">
+			Transform in global space where the motion should start. Usually set to [member Node2D.global_transform] for the current body's transform.
+		</member>
+		<member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.08">
+			Increases the size of the shapes involved in the collision detection.
+		</member>
+		<member name="motion" type="Vector2" setter="set_motion" getter="get_motion" default="Vector2(0, 0)">
+			Motion vector to define the length and direction of the motion to test.
+		</member>
+	</members>
+</class>

+ 32 - 0
doc/classes/PhysicsTestMotionParameters3D.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PhysicsTestMotionParameters3D" inherits="RefCounted" version="4.0">
+	<brief_description>
+		Parameters to be sent to a 3D body motion test.
+	</brief_description>
+	<description>
+		This class contains parameters used in [method PhysicsServer3D.body_test_motion].
+	</description>
+	<tutorials>
+	</tutorials>
+	<members>
+		<member name="collide_separation_ray" type="bool" setter="set_collide_separation_ray_enabled" getter="is_collide_separation_ray_enabled" default="false">
+			If set to [code]true[/code], shapes of type [constant PhysicsServer3D.SHAPE_SEPARATION_RAY] are used to detect collisions and can stop the motion. Can be useful when snapping to the ground.
+			If set to [code]false[/code], shapes of type [constant PhysicsServer3D.SHAPE_SEPARATION_RAY] are only used for separation when overlapping with other bodies. That's the main use for separation ray shapes.
+		</member>
+		<member name="exclude_bodies" type="Array" setter="set_exclude_bodies" getter="get_exclude_bodies" default="[]">
+			Optional array of body [RID] to exclude from collision.
+		</member>
+		<member name="from" type="Transform3D" setter="set_from" getter="get_from" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
+			Transform in global space where the motion should start. Usually set to [member Node3D.global_transform] for the current body's transform.
+		</member>
+		<member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.001">
+			Increases the size of the shapes involved in the collision detection.
+		</member>
+		<member name="max_collisions" type="int" setter="set_max_collisions" getter="get_max_collisions" default="1">
+			Maximum number of returned collisions, between [code]1[/code] and [code]32[/code]. Always returns the deepest detected collisions.
+		</member>
+		<member name="motion" type="Vector3" setter="set_motion" getter="get_motion" default="Vector3(0, 0, 0)">
+			Motion vector to define the length and direction of the motion to test.
+		</member>
+	</members>
+</class>

+ 82 - 26
doc/classes/PhysicsTestMotionResult2D.xml

@@ -1,35 +1,91 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <class name="PhysicsTestMotionResult2D" inherits="RefCounted" version="4.0">
 	<brief_description>
+		Result from a 2D body motion test.
 	</brief_description>
 	<description>
+		This class contains the motion and collision result from [method PhysicsServer2D.body_test_motion].
 	</description>
 	<tutorials>
 	</tutorials>
-	<members>
-		<member name="collider" type="Object" setter="" getter="get_collider">
-		</member>
-		<member name="collider_id" type="int" setter="" getter="get_collider_id" default="0">
-		</member>
-		<member name="collider_rid" type="RID" setter="" getter="get_collider_rid">
-		</member>
-		<member name="collider_shape" type="int" setter="" getter="get_collider_shape" default="0">
-		</member>
-		<member name="collider_velocity" type="Vector2" setter="" getter="get_collider_velocity" default="Vector2(0, 0)">
-		</member>
-		<member name="collision_depth" type="float" setter="" getter="get_collision_depth" default="0.0">
-		</member>
-		<member name="collision_normal" type="Vector2" setter="" getter="get_collision_normal" default="Vector2(0, 0)">
-		</member>
-		<member name="collision_point" type="Vector2" setter="" getter="get_collision_point" default="Vector2(0, 0)">
-		</member>
-		<member name="collision_safe_fraction" type="float" setter="" getter="get_collision_safe_fraction" default="0.0">
-		</member>
-		<member name="collision_unsafe_fraction" type="float" setter="" getter="get_collision_unsafe_fraction" default="0.0">
-		</member>
-		<member name="remainder" type="Vector2" setter="" getter="get_remainder" default="Vector2(0, 0)">
-		</member>
-		<member name="travel" type="Vector2" setter="" getter="get_travel" default="Vector2(0, 0)">
-		</member>
-	</members>
+	<methods>
+		<method name="get_collider" qualifiers="const">
+			<return type="Object" />
+			<description>
+				Returns the colliding body's attached [Object], if a collision occured.
+			</description>
+		</method>
+		<method name="get_collider_id" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the unique instance ID of the colliding body's attached [Object], if a collision occured. See [method Object.get_instance_id].
+			</description>
+		</method>
+		<method name="get_collider_rid" qualifiers="const">
+			<return type="RID" />
+			<description>
+				Returns the colliding body's [RID] used by the [PhysicsServer2D], if a collision occured.
+			</description>
+		</method>
+		<method name="get_collider_shape" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the colliding body's shape index, if a collision occured. See [CollisionObject2D].
+			</description>
+		</method>
+		<method name="get_collider_velocity" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the colliding body's velocity, if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_depth" qualifiers="const">
+			<return type="float" />
+			<description>
+				Returns the length of overlap along the collision normal, if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_local_shape" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the moving object's colliding shape, if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_normal" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the colliding body's shape's normal at the point of collision, if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_point" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the point of collision in global coordinates, if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_safe_fraction" qualifiers="const">
+			<return type="float" />
+			<description>
+				Returns the maximum fraction of the motion that can occur without a collision, between [code]0[/code] and [code]1[/code].
+			</description>
+		</method>
+		<method name="get_collision_unsafe_fraction" qualifiers="const">
+			<return type="float" />
+			<description>
+				Returns the minimum fraction of the motion needed to collide, if a collision occured, between [code]0[/code] and [code]1[/code].
+			</description>
+		</method>
+		<method name="get_remainder" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the moving object's remaining movement vector.
+			</description>
+		</method>
+		<method name="get_travel" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+				Returns the moving object's travel before collision.
+			</description>
+		</method>
+	</methods>
 </class>

+ 47 - 28
doc/classes/PhysicsTestMotionResult3D.xml

@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <class name="PhysicsTestMotionResult3D" inherits="RefCounted" version="4.0">
 	<brief_description>
+		Result from a 3D body motion test.
 	</brief_description>
 	<description>
+		This class contains the motion and collision result from [method PhysicsServer3D.body_test_motion].
 	</description>
 	<tutorials>
 	</tutorials>
@@ -11,77 +13,94 @@
 			<return type="Object" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the colliding body's attached [Object] given a collision index (the deepest collision by default), if a collision occured.
 			</description>
 		</method>
 		<method name="get_collider_id" qualifiers="const">
 			<return type="int" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the unique instance ID of the colliding body's attached [Object] given a collision index (the deepest collision by default), if a collision occured. See [method Object.get_instance_id].
 			</description>
 		</method>
 		<method name="get_collider_rid" qualifiers="const">
 			<return type="RID" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the colliding body's [RID] used by the [PhysicsServer3D] given a collision index (the deepest collision by default), if a collision occured.
 			</description>
 		</method>
 		<method name="get_collider_shape" qualifiers="const">
 			<return type="int" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the colliding body's shape index given a collision index (the deepest collision by default), if a collision occured. See [CollisionObject3D].
 			</description>
 		</method>
 		<method name="get_collider_velocity" qualifiers="const">
 			<return type="Vector3" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the colliding body's velocity given a collision index (the deepest collision by default), if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_count" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the number of detected collisions.
 			</description>
 		</method>
 		<method name="get_collision_depth" qualifiers="const">
 			<return type="float" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the length of overlap along the collision normal given a collision index (the deepest collision by default), if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_local_shape" qualifiers="const">
+			<return type="int" />
+			<argument index="0" name="collision_index" type="int" default="0" />
+			<description>
+				Returns the moving object's colliding shape given a collision index (the deepest collision by default), if a collision occured.
 			</description>
 		</method>
 		<method name="get_collision_normal" qualifiers="const">
 			<return type="Vector3" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the colliding body's shape's normal at the point of collision given a collision index (the deepest collision by default), if a collision occured.
 			</description>
 		</method>
 		<method name="get_collision_point" qualifiers="const">
 			<return type="Vector3" />
 			<argument index="0" name="collision_index" type="int" default="0" />
 			<description>
+				Returns the point of collision in global coordinates given a collision index (the deepest collision by default), if a collision occured.
+			</description>
+		</method>
+		<method name="get_collision_safe_fraction" qualifiers="const">
+			<return type="float" />
+			<description>
+				Returns the maximum fraction of the motion that can occur without a collision, between [code]0[/code] and [code]1[/code].
+			</description>
+		</method>
+		<method name="get_collision_unsafe_fraction" qualifiers="const">
+			<return type="float" />
+			<description>
+				Returns the minimum fraction of the motion needed to collide, if a collision occured, between [code]0[/code] and [code]1[/code].
+			</description>
+		</method>
+		<method name="get_remainder" qualifiers="const">
+			<return type="Vector3" />
+			<description>
+				Returns the moving object's remaining movement vector.
+			</description>
+		</method>
+		<method name="get_travel" qualifiers="const">
+			<return type="Vector3" />
+			<description>
+				Returns the moving object's travel before collision.
 			</description>
 		</method>
 	</methods>
-	<members>
-		<member name="collider" type="Object" setter="" getter="get_best_collider">
-		</member>
-		<member name="collider_id" type="int" setter="" getter="get_best_collider_id" default="0">
-		</member>
-		<member name="collider_rid" type="RID" setter="" getter="get_best_collider_rid">
-		</member>
-		<member name="collider_shape" type="int" setter="" getter="get_best_collider_shape" default="0">
-		</member>
-		<member name="collider_velocity" type="Vector3" setter="" getter="get_best_collider_velocity" default="Vector3(0, 0, 0)">
-		</member>
-		<member name="collision_count" type="int" setter="" getter="get_collision_count" default="0">
-		</member>
-		<member name="collision_depth" type="float" setter="" getter="get_best_collision_depth" default="0.0">
-		</member>
-		<member name="collision_normal" type="Vector3" setter="" getter="get_best_collision_normal" default="Vector3(0, 0, 0)">
-		</member>
-		<member name="collision_point" type="Vector3" setter="" getter="get_best_collision_point" default="Vector3(0, 0, 0)">
-		</member>
-		<member name="remainder" type="Vector3" setter="" getter="get_remainder" default="Vector3(0, 0, 0)">
-		</member>
-		<member name="safe_fraction" type="float" setter="" getter="get_safe_fraction" default="0.0">
-		</member>
-		<member name="travel" type="Vector3" setter="" getter="get_travel" default="Vector3(0, 0, 0)">
-		</member>
-		<member name="unsafe_fraction" type="float" setter="" getter="get_unsafe_fraction" default="0.0">
-		</member>
-	</members>
 </class>

+ 1 - 1
doc/classes/TileMap.xml

@@ -73,7 +73,7 @@
 			<return type="Vector2i" />
 			<argument index="0" name="body" type="RID" />
 			<description>
-				Returns the coodinates of the tile for given physics body RID. Such RID can be retrieved from [member KinematicCollision2D.collider_rid], when colliding with a tile.
+				Returns the coodinates of the tile for given physics body RID. Such RID can be retrieved from [method KinematicCollision2D.get_collider_rid], when colliding with a tile.
 			</description>
 		</method>
 		<method name="get_layer_name" qualifiers="const">

+ 40 - 43
scene/2d/physics_body_2d.cpp

@@ -54,13 +54,14 @@ PhysicsBody2D::~PhysicsBody2D() {
 	}
 }
 
-Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin) {
-	PhysicsServer2D::MotionResult result;
-
+Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_linear_velocity, bool p_test_only, real_t p_margin) {
 	// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
 	double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
 
-	if (move_and_collide(p_motion * delta, result, p_margin, p_test_only)) {
+	PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+
+	PhysicsServer2D::MotionResult result;
+	if (move_and_collide(parameters, result, p_test_only)) {
 		// Create a new instance when the cached reference is invalid or still in use in script.
 		if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
 			motion_cache.instantiate();
@@ -74,18 +75,18 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t
 	return Ref<KinematicCollision2D>();
 }
 
-bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
+bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) {
 	if (is_only_update_transform_changes_enabled()) {
 		ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation.");
 	}
-	Transform2D gt = get_global_transform();
-	bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude);
+
+	bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result);
 
 	// Restore direction of motion to be along original motion,
 	// in order to avoid sliding due to recovery,
 	// but only if collision depth is low enough to avoid tunneling.
 	if (p_cancel_sliding) {
-		real_t motion_length = p_motion.length();
+		real_t motion_length = p_parameters.motion.length();
 		real_t precision = 0.001;
 
 		if (colliding) {
@@ -93,7 +94,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
 			// so even in normal resting cases the depth can be a bit more than the margin.
 			precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction);
 
-			if (r_result.collision_depth > (real_t)p_margin + precision) {
+			if (r_result.collision_depth > p_parameters.margin + precision) {
 				p_cancel_sliding = false;
 			}
 		}
@@ -102,7 +103,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
 			// When motion is null, recovery is the resulting motion.
 			Vector2 motion_normal;
 			if (motion_length > CMP_EPSILON) {
-				motion_normal = p_motion / motion_length;
+				motion_normal = p_parameters.motion / motion_length;
 			}
 
 			// Check depth of recovery.
@@ -111,15 +112,16 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
 			real_t recovery_length = recovery.length();
 			// Fixes cases where canceling slide causes the motion to go too deep into the ground,
 			// because we're only taking rest information into account and not general recovery.
-			if (recovery_length < (real_t)p_margin + precision) {
+			if (recovery_length < p_parameters.margin + precision) {
 				// Apply adjustment to motion.
 				r_result.travel = motion_normal * projected_length;
-				r_result.remainder = p_motion - r_result.travel;
+				r_result.remainder = p_parameters.motion - r_result.travel;
 			}
 		}
 	}
 
 	if (!p_test_only) {
+		Transform2D gt = p_parameters.from;
 		gt.elements[2] += r_result.travel;
 		set_global_transform(gt);
 	}
@@ -127,7 +129,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
 	return colliding;
 }
 
-bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
+bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
 	ERR_FAIL_COND_V(!is_inside_tree(), false);
 
 	PhysicsServer2D::MotionResult *r = nullptr;
@@ -139,7 +141,9 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion
 	// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
 	double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
 
-	return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r);
+	PhysicsServer2D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+
+	return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r);
 }
 
 TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() {
@@ -1083,10 +1087,11 @@ bool CharacterBody2D::move_and_slide() {
 	on_wall = false;
 
 	if (!current_platform_velocity.is_equal_approx(Vector2())) {
+		PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin);
+		parameters.exclude_bodies.insert(platform_rid);
+
 		PhysicsServer2D::MotionResult floor_result;
-		Set<RID> exclude;
-		exclude.insert(platform_rid);
-		if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, false, exclude)) {
+		if (move_and_collide(parameters, floor_result, false, false)) {
 			motion_results.push_back(floor_result);
 			_set_collision_direction(floor_result);
 		}
@@ -1138,11 +1143,13 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 	Vector2 last_travel;
 
 	for (int iteration = 0; iteration < max_slides; ++iteration) {
-		PhysicsServer2D::MotionResult result;
+		PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin);
 
-		Vector2 prev_position = get_global_transform().elements[2];
+		Vector2 prev_position = parameters.from.elements[2];
+
+		PhysicsServer2D::MotionResult result;
+		bool collided = move_and_collide(parameters, result, false, !sliding_enabled);
 
-		bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled);
 		last_motion = result.travel;
 
 		if (collided) {
@@ -1283,9 +1290,11 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
 
 	bool first_slide = true;
 	for (int iteration = 0; iteration < max_slides; ++iteration) {
+		PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin);
+
 		PhysicsServer2D::MotionResult result;
+		bool collided = move_and_collide(parameters, result, false, false);
 
-		bool collided = move_and_collide(motion, result, margin, false, false);
 		last_motion = result.travel;
 
 		if (collided) {
@@ -1327,10 +1336,11 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
 	// Snap by at least collision margin to keep floor state consistent.
 	real_t length = MAX(floor_snap_length, margin);
 
-	Transform2D gt = get_global_transform();
+	PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+	parameters.collide_separation_ray = true;
+
 	PhysicsServer2D::MotionResult result;
-	if (move_and_collide(-up_direction * length, result, margin, true, false, true)) {
-		bool apply = true;
+	if (move_and_collide(parameters, result, true, false)) {
 		if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
 			on_floor = true;
 			floor_normal = result.collision_normal;
@@ -1345,13 +1355,9 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
 					result.travel = Vector2();
 				}
 			}
-		} else {
-			apply = false;
-		}
 
-		if (apply) {
-			gt.elements[2] += result.travel;
-			set_global_transform(gt);
+			parameters.from.elements[2] += result.travel;
+			set_global_transform(parameters.from);
 		}
 	}
 }
@@ -1364,8 +1370,11 @@ bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin
 	// Snap by at least collision margin to keep floor state consistent.
 	real_t length = MAX(floor_snap_length, margin);
 
+	PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+	parameters.collide_separation_ray = true;
+
 	PhysicsServer2D::MotionResult result;
-	if (move_and_collide(-up_direction * length, result, margin, true, false, true)) {
+	if (move_and_collide(parameters, result, true, false)) {
 		if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
 			return true;
 		}
@@ -1806,16 +1815,4 @@ void KinematicCollision2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicCollision2D::get_collider_shape);
 	ClassDB::bind_method(D_METHOD("get_collider_shape_index"), &KinematicCollision2D::get_collider_shape_index);
 	ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicCollision2D::get_collider_velocity);
-
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "", "get_position");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "", "get_normal");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "travel"), "", "get_travel");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "remainder"), "", "get_remainder");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_local_shape");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_collider");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_collider_id");
-	ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_collider_rid");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_collider_shape");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_collider_shape_index");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collider_velocity"), "", "get_collider_velocity");
 }

+ 3 - 3
scene/2d/physics_body_2d.h

@@ -47,11 +47,11 @@ protected:
 
 	Ref<KinematicCollision2D> motion_cache;
 
-	Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_test_only = false, real_t p_margin = 0.08);
+	Ref<KinematicCollision2D> _move(const Vector2 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.08);
 
 public:
-	bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
-	bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
+	bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
+	bool test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
 
 	TypedArray<PhysicsBody2D> get_collision_exceptions();
 	void add_collision_exception_with(Node *p_node); //must be physicsbody

+ 43 - 83
scene/3d/physics_body_3d.cpp

@@ -91,12 +91,15 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) {
 	PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
 }
 
-Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin, int p_max_collisions) {
-	PhysicsServer3D::MotionResult result;
+Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_linear_velocity, bool p_test_only, real_t p_margin, int p_max_collisions) {
 	// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
 	double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
 
-	if (move_and_collide(p_motion * delta, result, p_margin, p_test_only, p_max_collisions)) {
+	PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+	parameters.max_collisions = p_max_collisions;
+
+	PhysicsServer3D::MotionResult result;
+	if (move_and_collide(parameters, result, p_test_only)) {
 		// Create a new instance when the cached reference is invalid or still in use in script.
 		if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
 			motion_cache.instantiate();
@@ -111,23 +114,22 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t
 	return Ref<KinematicCollision3D>();
 }
 
-bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, int p_max_collisions, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
-	Transform3D gt = get_global_transform();
-	bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_max_collisions, p_collide_separation_ray, p_exclude);
+bool PhysicsBody3D::move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) {
+	bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result);
 
 	// Restore direction of motion to be along original motion,
 	// in order to avoid sliding due to recovery,
 	// but only if collision depth is low enough to avoid tunneling.
 	if (p_cancel_sliding) {
-		real_t motion_length = p_motion.length();
+		real_t motion_length = p_parameters.motion.length();
 		real_t precision = 0.001;
 
 		if (colliding) {
 			// Can't just use margin as a threshold because collision depth is calculated on unsafe motion,
 			// so even in normal resting cases the depth can be a bit more than the margin.
-			precision += motion_length * (r_result.unsafe_fraction - r_result.safe_fraction);
+			precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction);
 
-			if (r_result.collisions[0].depth > (real_t)p_margin + precision) {
+			if (r_result.collisions[0].depth > p_parameters.margin + precision) {
 				p_cancel_sliding = false;
 			}
 		}
@@ -136,7 +138,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
 			// When motion is null, recovery is the resulting motion.
 			Vector3 motion_normal;
 			if (motion_length > CMP_EPSILON) {
-				motion_normal = p_motion / motion_length;
+				motion_normal = p_parameters.motion / motion_length;
 			}
 
 			// Check depth of recovery.
@@ -145,10 +147,10 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
 			real_t recovery_length = recovery.length();
 			// Fixes cases where canceling slide causes the motion to go too deep into the ground,
 			// because we're only taking rest information into account and not general recovery.
-			if (recovery_length < (real_t)p_margin + precision) {
+			if (recovery_length < p_parameters.margin + precision) {
 				// Apply adjustment to motion.
 				r_result.travel = motion_normal * projected_length;
-				r_result.remainder = p_motion - r_result.travel;
+				r_result.remainder = p_parameters.motion - r_result.travel;
 			}
 		}
 	}
@@ -160,6 +162,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
 	}
 
 	if (!p_test_only) {
+		Transform3D gt = p_parameters.from;
 		gt.origin += r_result.travel;
 		set_global_transform(gt);
 	}
@@ -167,7 +170,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
 	return colliding;
 }
 
-bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
+bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
 	ERR_FAIL_COND_V(!is_inside_tree(), false);
 
 	PhysicsServer3D::MotionResult *r = nullptr;
@@ -179,7 +182,9 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion
 	// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
 	double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
 
-	return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r, p_max_collisions);
+	PhysicsServer3D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+
+	return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r);
 }
 
 void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) {
@@ -1124,10 +1129,11 @@ bool CharacterBody3D::move_and_slide() {
 	last_motion = Vector3();
 
 	if (!current_platform_velocity.is_equal_approx(Vector3())) {
+		PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin);
+		parameters.exclude_bodies.insert(platform_rid);
+
 		PhysicsServer3D::MotionResult floor_result;
-		Set<RID> exclude;
-		exclude.insert(platform_rid);
-		if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, 1, false, false, exclude)) {
+		if (move_and_collide(parameters, floor_result, false, false)) {
 			motion_results.push_back(floor_result);
 
 			CollisionState result_state;
@@ -1181,8 +1187,12 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 	Vector3 total_travel;
 
 	for (int iteration = 0; iteration < max_slides; ++iteration) {
+		PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin);
+		parameters.max_collisions = 4;
+
 		PhysicsServer3D::MotionResult result;
-		bool collided = move_and_collide(motion, result, margin, false, 4, !sliding_enabled);
+		bool collided = move_and_collide(parameters, result, false, !sliding_enabled);
+
 		last_motion = result.travel;
 
 		if (collided) {
@@ -1411,9 +1421,11 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
 
 	bool first_slide = true;
 	for (int iteration = 0; iteration < max_slides; ++iteration) {
+		PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin);
+
 		PhysicsServer3D::MotionResult result;
+		bool collided = move_and_collide(parameters, result, false, false);
 
-		bool collided = move_and_collide(motion, result, margin, false, 1, false);
 		last_motion = result.travel;
 
 		if (collided) {
@@ -1462,9 +1474,12 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
 	// Snap by at least collision margin to keep floor state consistent.
 	real_t length = MAX(floor_snap_length, margin);
 
-	Transform3D gt = get_global_transform();
+	PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+	parameters.max_collisions = 4;
+	parameters.collide_separation_ray = true;
+
 	PhysicsServer3D::MotionResult result;
-	if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) {
+	if (move_and_collide(parameters, result, true, false)) {
 		CollisionState result_state;
 		// Apply direction for floor only.
 		_set_collision_direction(result, result_state, CollisionState(true, false, false));
@@ -1480,8 +1495,8 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
 				}
 			}
 
-			gt.origin += result.travel;
-			set_global_transform(gt);
+			parameters.from.origin += result.travel;
+			set_global_transform(parameters.from);
 		}
 	}
 }
@@ -1494,8 +1509,12 @@ bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin
 	// Snap by at least collision margin to keep floor state consistent.
 	real_t length = MAX(floor_snap_length, margin);
 
+	PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+	parameters.max_collisions = 4;
+	parameters.collide_separation_ray = true;
+
 	PhysicsServer3D::MotionResult result;
-	if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) {
+	if (move_and_collide(parameters, result, true, false)) {
 		CollisionState result_state;
 		// Don't apply direction for any type.
 		_set_collision_direction(result, result_state, CollisionState());
@@ -2002,42 +2021,6 @@ Vector3 KinematicCollision3D::get_collider_velocity(int p_collision_index) const
 	return result.collisions[p_collision_index].collider_velocity;
 }
 
-Vector3 KinematicCollision3D::get_best_position() const {
-	return result.collision_count ? get_position() : Vector3();
-}
-
-Vector3 KinematicCollision3D::get_best_normal() const {
-	return result.collision_count ? get_normal() : Vector3();
-}
-
-Object *KinematicCollision3D::get_best_local_shape() const {
-	return result.collision_count ? get_local_shape() : nullptr;
-}
-
-Object *KinematicCollision3D::get_best_collider() const {
-	return result.collision_count ? get_collider() : nullptr;
-}
-
-ObjectID KinematicCollision3D::get_best_collider_id() const {
-	return result.collision_count ? get_collider_id() : ObjectID();
-}
-
-RID KinematicCollision3D::get_best_collider_rid() const {
-	return result.collision_count ? get_collider_rid() : RID();
-}
-
-Object *KinematicCollision3D::get_best_collider_shape() const {
-	return result.collision_count ? get_collider_shape() : nullptr;
-}
-
-int KinematicCollision3D::get_best_collider_shape_index() const {
-	return result.collision_count ? get_collider_shape_index() : 0;
-}
-
-Vector3 KinematicCollision3D::get_best_collider_velocity() const {
-	return result.collision_count ? get_collider_velocity() : Vector3();
-}
-
 void KinematicCollision3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision3D::get_travel);
 	ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision3D::get_remainder);
@@ -2052,29 +2035,6 @@ void KinematicCollision3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_collider_shape", "collision_index"), &KinematicCollision3D::get_collider_shape, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("get_collider_shape_index", "collision_index"), &KinematicCollision3D::get_collider_shape_index, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("get_collider_velocity", "collision_index"), &KinematicCollision3D::get_collider_velocity, DEFVAL(0));
-
-	ClassDB::bind_method(D_METHOD("get_best_position"), &KinematicCollision3D::get_best_position);
-	ClassDB::bind_method(D_METHOD("get_best_normal"), &KinematicCollision3D::get_best_normal);
-	ClassDB::bind_method(D_METHOD("get_best_local_shape"), &KinematicCollision3D::get_best_local_shape);
-	ClassDB::bind_method(D_METHOD("get_best_collider"), &KinematicCollision3D::get_best_collider);
-	ClassDB::bind_method(D_METHOD("get_best_collider_id"), &KinematicCollision3D::get_best_collider_id);
-	ClassDB::bind_method(D_METHOD("get_best_collider_rid"), &KinematicCollision3D::get_best_collider_rid);
-	ClassDB::bind_method(D_METHOD("get_best_collider_shape"), &KinematicCollision3D::get_best_collider_shape);
-	ClassDB::bind_method(D_METHOD("get_best_collider_shape_index"), &KinematicCollision3D::get_best_collider_shape_index);
-	ClassDB::bind_method(D_METHOD("get_best_collider_velocity"), &KinematicCollision3D::get_best_collider_velocity);
-
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "travel"), "", "get_travel");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "remainder"), "", "get_remainder");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_count"), "", "get_collision_count");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "", "get_best_position");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "normal"), "", "get_best_normal");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_best_local_shape");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_best_collider");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_best_collider_id");
-	ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_best_collider_rid");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_best_collider_shape");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_best_collider_shape_index");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_best_collider_velocity");
 }
 
 ///////////////////////////////////////

+ 3 - 13
scene/3d/physics_body_3d.h

@@ -50,11 +50,11 @@ protected:
 
 	uint16_t locked_axis = 0;
 
-	Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
+	Ref<KinematicCollision3D> _move(const Vector3 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
 
 public:
-	bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, int p_max_collisions = 1, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
-	bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
+	bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
+	bool test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
 
 	void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
 	bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const;
@@ -487,16 +487,6 @@ public:
 	Object *get_collider_shape(int p_collision_index = 0) const;
 	int get_collider_shape_index(int p_collision_index = 0) const;
 	Vector3 get_collider_velocity(int p_collision_index = 0) const;
-
-	Vector3 get_best_position() const;
-	Vector3 get_best_normal() const;
-	Object *get_best_local_shape() const;
-	Object *get_best_collider() const;
-	ObjectID get_best_collider_id() const;
-	RID get_best_collider_rid() const;
-	Object *get_best_collider_shape() const;
-	int get_best_collider_shape_index() const;
-	Vector3 get_best_collider_velocity() const;
 };
 
 class PhysicalBone3D : public PhysicsBody3D {

+ 2 - 2
servers/physics_2d/physics_server_2d_sw.cpp

@@ -948,7 +948,7 @@ void PhysicsServer2DSW::body_set_pickable(RID p_body, bool p_pickable) {
 	body->set_pickable(p_pickable);
 }
 
-bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
+bool PhysicsServer2DSW::body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result) {
 	Body2DSW *body = body_owner.get_or_null(p_body);
 	ERR_FAIL_COND_V(!body, false);
 	ERR_FAIL_COND_V(!body->get_space(), false);
@@ -956,7 +956,7 @@ bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from,
 
 	_update_shapes();
 
-	return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude);
+	return body->get_space()->test_body_motion(body, p_parameters, r_result);
 }
 
 PhysicsDirectBodyState2D *PhysicsServer2DSW::body_get_direct_state(RID p_body) {

+ 1 - 1
servers/physics_2d/physics_server_2d_sw.h

@@ -246,7 +246,7 @@ public:
 
 	virtual void body_set_pickable(RID p_body, bool p_pickable) override;
 
-	virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override;
+	virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override;
 
 	// this function only works on physics process, errors and returns null otherwise
 	virtual PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override;

+ 2 - 2
servers/physics_2d/physics_server_2d_wrap_mt.h

@@ -254,9 +254,9 @@ public:
 
 	FUNC2(body_set_pickable, RID, bool);
 
-	bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override {
+	bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
 		ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
-		return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_collide_separation_ray, p_exclude);
+		return physics_2d_server->body_test_motion(p_body, p_parameters, r_result);
 	}
 
 	// this function only works on physics process, errors and returns null otherwise

+ 28 - 27
servers/physics_2d/space_2d_sw.cpp

@@ -525,7 +525,7 @@ int Space2DSW::_cull_aabb_for_body(Body2DSW *p_body, const Rect2 &p_aabb) {
 	return amount;
 }
 
-bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
+bool Space2DSW::test_body_motion(Body2DSW *p_body, const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult *r_result) {
 	//give me back regular physics engine logic
 	//this is madness
 	//and most people using this function will think
@@ -557,25 +557,25 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 	if (!shapes_found) {
 		if (r_result) {
 			*r_result = PhysicsServer2D::MotionResult();
-			r_result->travel = p_motion;
+			r_result->travel = p_parameters.motion;
 		}
 		return false;
 	}
 
 	// Undo the currently transform the physics server is aware of and apply the provided one
-	body_aabb = p_from.xform(p_body->get_inv_transform().xform(body_aabb));
-	body_aabb = body_aabb.grow(p_margin);
+	body_aabb = p_parameters.from.xform(p_body->get_inv_transform().xform(body_aabb));
+	body_aabb = body_aabb.grow(p_parameters.margin);
 
 	static const int max_excluded_shape_pairs = 32;
 	ExcludedShapeSW excluded_shape_pairs[max_excluded_shape_pairs];
 	int excluded_shape_pair_count = 0;
 
-	real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;
+	real_t min_contact_depth = p_parameters.margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;
 
-	real_t motion_length = p_motion.length();
-	Vector2 motion_normal = p_motion / motion_length;
+	real_t motion_length = p_parameters.motion.length();
+	Vector2 motion_normal = p_parameters.motion / motion_length;
 
-	Transform2D body_transform = p_from;
+	Transform2D body_transform = p_parameters.from;
 
 	bool recovered = false;
 
@@ -612,7 +612,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 
 				for (int i = 0; i < amount; i++) {
 					const CollisionObject2DSW *col_obj = intersection_query_results[i];
-					if (p_exclude.has(col_obj->get_self())) {
+					if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
 						continue;
 					}
 
@@ -624,7 +624,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 						cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
 
 						real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx);
-						cbk.valid_depth = MAX(owc_margin, p_margin); //user specified, but never less than actual margin or it won't work
+						cbk.valid_depth = MAX(owc_margin, p_parameters.margin); //user specified, but never less than actual margin or it won't work
 						cbk.invalid_by_dir = 0;
 
 						if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
@@ -649,7 +649,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 					bool did_collide = false;
 
 					Shape2DSW *against_shape = col_obj->get_shape(shape_idx);
-					if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), cbkres, cbkptr, nullptr, p_margin)) {
+					if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), cbkres, cbkptr, nullptr, p_parameters.margin)) {
 						did_collide = cbk.passed > current_passed; //more passed, so collision actually existed
 					}
 
@@ -714,7 +714,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 		// STEP 2 ATTEMPT MOTION
 
 		Rect2 motion_aabb = body_aabb;
-		motion_aabb.position += p_motion;
+		motion_aabb.position += p_parameters.motion;
 		motion_aabb = motion_aabb.merge(body_aabb);
 
 		int amount = _cull_aabb_for_body(p_body, motion_aabb);
@@ -728,7 +728,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 
 			// Colliding separation rays allows to properly snap to the ground,
 			// otherwise it's not needed in regular motion.
-			if (!p_collide_separation_ray && (body_shape->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY)) {
+			if (!p_parameters.collide_separation_ray && (body_shape->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY)) {
 				// When slide on slope is on, separation ray shape acts like a regular shape.
 				if (!static_cast<SeparationRayShape2DSW *>(body_shape)->get_slide_on_slope()) {
 					continue;
@@ -744,9 +744,10 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 
 			for (int i = 0; i < amount; i++) {
 				const CollisionObject2DSW *col_obj = intersection_query_results[i];
-				if (p_exclude.has(col_obj->get_self())) {
+				if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
 					continue;
 				}
+
 				int col_shape_idx = intersection_query_subindex_results[i];
 				Shape2DSW *against_shape = col_obj->get_shape(col_shape_idx);
 
@@ -765,7 +766,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 
 				Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(col_shape_idx);
 				//test initial overlap, does it collide if going all the way?
-				if (!CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion, against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, nullptr, 0)) {
+				if (!CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_parameters.motion, against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, nullptr, 0)) {
 					continue;
 				}
 
@@ -790,7 +791,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 					real_t fraction = low + (hi - low) * fraction_coeff;
 
 					Vector2 sep = motion_normal; //important optimization for this to work fast enough
-					bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion * fraction, against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, &sep, 0);
+					bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_parameters.motion * fraction, against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, &sep, 0);
 
 					if (collided) {
 						hi = fraction;
@@ -827,7 +828,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 					cbk.valid_depth = 10e20;
 
 					Vector2 sep = motion_normal; //important optimization for this to work fast enough
-					bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion * (hi + contact_max_allowed_penetration), col_obj->get_shape(col_shape_idx), col_obj_shape_xform, Vector2(), PhysicsServer2DSW::_shape_col_cbk, &cbk, &sep, 0);
+					bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_parameters.motion * (hi + contact_max_allowed_penetration), col_obj->get_shape(col_shape_idx), col_obj_shape_xform, Vector2(), PhysicsServer2DSW::_shape_col_cbk, &cbk, &sep, 0);
 					if (!collided || cbk.amount == 0) {
 						continue;
 					}
@@ -865,7 +866,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 
 		//it collided, let's get the rest info in unsafe advance
 		Transform2D ugt = body_transform;
-		ugt.elements[2] += p_motion * unsafe;
+		ugt.elements[2] += p_parameters.motion * unsafe;
 
 		_RestCallbackData2D rcd;
 		rcd.best_len = 0;
@@ -886,13 +887,13 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 			Transform2D body_shape_xform = ugt * p_body->get_shape_transform(j);
 			Shape2DSW *body_shape = p_body->get_shape(j);
 
-			body_aabb.position += p_motion * unsafe;
+			body_aabb.position += p_parameters.motion * unsafe;
 
 			int amount = _cull_aabb_for_body(p_body, body_aabb);
 
 			for (int i = 0; i < amount; i++) {
 				const CollisionObject2DSW *col_obj = intersection_query_results[i];
-				if (p_exclude.has(col_obj->get_self())) {
+				if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
 					continue;
 				}
 
@@ -917,7 +918,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 					rcd.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
 
 					real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx);
-					rcd.valid_depth = MAX(owc_margin, p_margin); //user specified, but never less than actual margin or it won't work
+					rcd.valid_depth = MAX(owc_margin, p_parameters.margin); //user specified, but never less than actual margin or it won't work
 
 					if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
 						const Body2DSW *b = static_cast<const Body2DSW *>(col_obj);
@@ -939,7 +940,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 				rcd.object = col_obj;
 				rcd.shape = shape_idx;
 				rcd.local_shape = j;
-				bool sc = CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), _rest_cbk_result, &rcd, nullptr, p_margin);
+				bool sc = CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), _rest_cbk_result, &rcd, nullptr, p_parameters.margin);
 				if (!sc) {
 					continue;
 				}
@@ -962,9 +963,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 				Vector2 rel_vec = r_result->collision_point - (body->get_transform().get_origin() + body->get_center_of_mass());
 				r_result->collider_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity();
 
-				r_result->travel = safe * p_motion;
-				r_result->remainder = p_motion - safe * p_motion;
-				r_result->travel += (body_transform.get_origin() - p_from.get_origin());
+				r_result->travel = safe * p_parameters.motion;
+				r_result->remainder = p_parameters.motion - safe * p_parameters.motion;
+				r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());
 			}
 
 			collided = true;
@@ -972,9 +973,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
 	}
 
 	if (!collided && r_result) {
-		r_result->travel = p_motion;
+		r_result->travel = p_parameters.motion;
 		r_result->remainder = Vector2();
-		r_result->travel += (body_transform.get_origin() - p_from.get_origin());
+		r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());
 	}
 
 	return collided;

+ 1 - 1
servers/physics_2d/space_2d_sw.h

@@ -187,7 +187,7 @@ public:
 
 	int get_collision_pairs() const { return collision_pairs; }
 
-	bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, PhysicsServer2D::MotionResult *r_result, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
+	bool test_body_motion(Body2DSW *p_body, const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult *r_result);
 
 	void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); }
 	_FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.is_empty(); }

+ 2 - 2
servers/physics_3d/physics_server_3d_sw.cpp

@@ -868,7 +868,7 @@ void PhysicsServer3DSW::body_set_ray_pickable(RID p_body, bool p_enable) {
 	body->set_ray_pickable(p_enable);
 }
 
-bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, MotionResult *r_result, int p_max_collisions, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
+bool PhysicsServer3DSW::body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result) {
 	Body3DSW *body = body_owner.get_or_null(p_body);
 	ERR_FAIL_COND_V(!body, false);
 	ERR_FAIL_COND_V(!body->get_space(), false);
@@ -876,7 +876,7 @@ bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from,
 
 	_update_shapes();
 
-	return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result, p_max_collisions, p_collide_separation_ray, p_exclude);
+	return body->get_space()->test_body_motion(body, p_parameters, r_result);
 }
 
 PhysicsDirectBodyState3D *PhysicsServer3DSW::body_get_direct_state(RID p_body) {

+ 1 - 1
servers/physics_3d/physics_server_3d_sw.h

@@ -242,7 +242,7 @@ public:
 
 	virtual void body_set_ray_pickable(RID p_body, bool p_enable) override;
 
-	virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, int p_max_collisions = 1, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override;
+	virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override;
 
 	// this function only works on physics process, errors and returns null otherwise
 	virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override;

+ 2 - 2
servers/physics_3d/physics_server_3d_wrap_mt.h

@@ -253,9 +253,9 @@ public:
 
 	FUNC2(body_set_ray_pickable, RID, bool);
 
-	bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, int p_max_collisions = 1, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) override {
+	bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
 		ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
-		return physics_3d_server->body_test_motion(p_body, p_from, p_motion, p_margin, r_result, p_max_collisions, p_collide_separation_ray, p_exclude);
+		return physics_3d_server->body_test_motion(p_body, p_parameters, r_result);
 	}
 
 	// this function only works on physics process, errors and returns null otherwise

+ 31 - 31
servers/physics_3d/space_3d_sw.cpp

@@ -620,7 +620,7 @@ int Space3DSW::_cull_aabb_for_body(Body3DSW *p_body, const AABB &p_aabb) {
 	return amount;
 }
 
-bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, int p_max_collisions, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
+bool Space3DSW::test_body_motion(Body3DSW *p_body, const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult *r_result) {
 	//give me back regular physics engine logic
 	//this is madness
 	//and most people using this function will think
@@ -628,7 +628,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 	//this took about a week to get right..
 	//but is it right? who knows at this point..
 
-	ERR_FAIL_INDEX_V(p_max_collisions, PhysicsServer3D::MotionResult::MAX_COLLISIONS, false);
+	ERR_FAIL_INDEX_V(p_parameters.max_collisions, PhysicsServer3D::MotionResult::MAX_COLLISIONS, false);
 
 	if (r_result) {
 		*r_result = PhysicsServer3D::MotionResult();
@@ -652,22 +652,22 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 
 	if (!shapes_found) {
 		if (r_result) {
-			r_result->travel = p_motion;
+			r_result->travel = p_parameters.motion;
 		}
 
 		return false;
 	}
 
 	// Undo the currently transform the physics server is aware of and apply the provided one
-	body_aabb = p_from.xform(p_body->get_inv_transform().xform(body_aabb));
-	body_aabb = body_aabb.grow(p_margin);
+	body_aabb = p_parameters.from.xform(p_body->get_inv_transform().xform(body_aabb));
+	body_aabb = body_aabb.grow(p_parameters.margin);
 
-	real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;
+	real_t min_contact_depth = p_parameters.margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;
 
-	real_t motion_length = p_motion.length();
-	Vector3 motion_normal = p_motion / motion_length;
+	real_t motion_length = p_parameters.motion.length();
+	Vector3 motion_normal = p_parameters.motion / motion_length;
 
-	Transform3D body_transform = p_from;
+	Transform3D body_transform = p_parameters.from;
 
 	bool recovered = false;
 
@@ -701,13 +701,13 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 
 				for (int i = 0; i < amount; i++) {
 					const CollisionObject3DSW *col_obj = intersection_query_results[i];
-					if (p_exclude.has(col_obj->get_self())) {
+					if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
 						continue;
 					}
 
 					int shape_idx = intersection_query_subindex_results[i];
 
-					if (CollisionSolver3DSW::solve_static(body_shape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), cbkres, cbkptr, nullptr, p_margin)) {
+					if (CollisionSolver3DSW::solve_static(body_shape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), cbkres, cbkptr, nullptr, p_parameters.margin)) {
 						collided = cbk.amount > 0;
 					}
 				}
@@ -757,7 +757,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 		// STEP 2 ATTEMPT MOTION
 
 		AABB motion_aabb = body_aabb;
-		motion_aabb.position += p_motion;
+		motion_aabb.position += p_parameters.motion;
 		motion_aabb = motion_aabb.merge(body_aabb);
 
 		int amount = _cull_aabb_for_body(p_body, motion_aabb);
@@ -771,7 +771,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 
 			// Colliding separation rays allows to properly snap to the ground,
 			// otherwise it's not needed in regular motion.
-			if (!p_collide_separation_ray && (body_shape->get_type() == PhysicsServer3D::SHAPE_SEPARATION_RAY)) {
+			if (!p_parameters.collide_separation_ray && (body_shape->get_type() == PhysicsServer3D::SHAPE_SEPARATION_RAY)) {
 				// When slide on slope is on, separation ray shape acts like a regular shape.
 				if (!static_cast<SeparationRayShape3DSW *>(body_shape)->get_slide_on_slope()) {
 					continue;
@@ -783,7 +783,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 			Transform3D body_shape_xform_inv = body_shape_xform.affine_inverse();
 			MotionShape3DSW mshape;
 			mshape.shape = body_shape;
-			mshape.motion = body_shape_xform_inv.basis.xform(p_motion);
+			mshape.motion = body_shape_xform_inv.basis.xform(p_parameters.motion);
 
 			bool stuck = false;
 
@@ -792,7 +792,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 
 			for (int i = 0; i < amount; i++) {
 				const CollisionObject3DSW *col_obj = intersection_query_results[i];
-				if (p_exclude.has(col_obj->get_self())) {
+				if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
 					continue;
 				}
 
@@ -821,7 +821,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 				for (int k = 0; k < 8; k++) { //steps should be customizable..
 					real_t fraction = low + (hi - low) * fraction_coeff;
 
-					mshape.motion = body_shape_xform_inv.basis.xform(p_motion * fraction);
+					mshape.motion = body_shape_xform_inv.basis.xform(p_parameters.motion * fraction);
 
 					Vector3 lA, lB;
 					Vector3 sep = motion_normal; //important optimization for this to work fast enough
@@ -883,13 +883,13 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 
 		//it collided, let's get the rest info in unsafe advance
 		Transform3D ugt = body_transform;
-		ugt.origin += p_motion * unsafe;
+		ugt.origin += p_parameters.motion * unsafe;
 
 		_RestResultData results[PhysicsServer3D::MotionResult::MAX_COLLISIONS];
 
 		_RestCallbackData rcd;
-		if (p_max_collisions > 1) {
-			rcd.max_results = p_max_collisions;
+		if (p_parameters.max_collisions > 1) {
+			rcd.max_results = p_parameters.max_collisions;
 			rcd.other_results = results;
 		}
 
@@ -907,20 +907,20 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 			Transform3D body_shape_xform = ugt * p_body->get_shape_transform(j);
 			Shape3DSW *body_shape = p_body->get_shape(j);
 
-			body_aabb.position += p_motion * unsafe;
+			body_aabb.position += p_parameters.motion * unsafe;
 
 			int amount = _cull_aabb_for_body(p_body, body_aabb);
 
 			for (int i = 0; i < amount; i++) {
 				const CollisionObject3DSW *col_obj = intersection_query_results[i];
-				if (p_exclude.has(col_obj->get_self())) {
+				if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
 					continue;
 				}
 				int shape_idx = intersection_query_subindex_results[i];
 
 				rcd.object = col_obj;
 				rcd.shape = shape_idx;
-				bool sc = CollisionSolver3DSW::solve_static(body_shape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), _rest_cbk_result, &rcd, nullptr, p_margin);
+				bool sc = CollisionSolver3DSW::solve_static(body_shape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), _rest_cbk_result, &rcd, nullptr, p_parameters.margin);
 				if (!sc) {
 					continue;
 				}
@@ -948,12 +948,12 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 					collision.collider_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(rel_vec);
 				}
 
-				r_result->travel = safe * p_motion;
-				r_result->remainder = p_motion - safe * p_motion;
-				r_result->travel += (body_transform.get_origin() - p_from.get_origin());
+				r_result->travel = safe * p_parameters.motion;
+				r_result->remainder = p_parameters.motion - safe * p_parameters.motion;
+				r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());
 
-				r_result->safe_fraction = safe;
-				r_result->unsafe_fraction = unsafe;
+				r_result->collision_safe_fraction = safe;
+				r_result->collision_unsafe_fraction = unsafe;
 
 				r_result->collision_count = rcd.result_count;
 			}
@@ -963,12 +963,12 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
 	}
 
 	if (!collided && r_result) {
-		r_result->travel = p_motion;
+		r_result->travel = p_parameters.motion;
 		r_result->remainder = Vector3();
-		r_result->travel += (body_transform.get_origin() - p_from.get_origin());
+		r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());
 
-		r_result->safe_fraction = 1.0;
-		r_result->unsafe_fraction = 1.0;
+		r_result->collision_safe_fraction = 1.0;
+		r_result->collision_unsafe_fraction = 1.0;
 	}
 
 	return collided;

+ 1 - 1
servers/physics_3d/space_3d_sw.h

@@ -207,7 +207,7 @@ public:
 	void set_elapsed_time(ElapsedTime p_time, uint64_t p_msec) { elapsed_time[p_time] = p_msec; }
 	uint64_t get_elapsed_time(ElapsedTime p_time) const { return elapsed_time[p_time]; }
 
-	bool test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, PhysicsServer3D::MotionResult *r_result, int p_max_collisions = 1, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
+	bool test_body_motion(Body3DSW *p_body, const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult *r_result);
 
 	Space3DSW();
 	~Space3DSW();

+ 56 - 22
servers/physics_server_2d.cpp

@@ -412,6 +412,49 @@ void PhysicsDirectSpaceState2D::_bind_methods() {
 
 ///////////////////////////////
 
+Vector<RID> PhysicsTestMotionParameters2D::get_exclude_bodies() const {
+	Vector<RID> exclude;
+	exclude.resize(parameters.exclude_bodies.size());
+
+	int body_index = 0;
+	for (RID body : parameters.exclude_bodies) {
+		exclude.write[body_index++] = body;
+	}
+
+	return exclude;
+}
+
+void PhysicsTestMotionParameters2D::set_exclude_bodies(const Vector<RID> &p_exclude) {
+	for (RID body : p_exclude) {
+		parameters.exclude_bodies.insert(body);
+	}
+}
+
+void PhysicsTestMotionParameters2D::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("get_from"), &PhysicsTestMotionParameters2D::get_from);
+	ClassDB::bind_method(D_METHOD("set_from"), &PhysicsTestMotionParameters2D::set_from);
+
+	ClassDB::bind_method(D_METHOD("get_motion"), &PhysicsTestMotionParameters2D::get_motion);
+	ClassDB::bind_method(D_METHOD("set_motion"), &PhysicsTestMotionParameters2D::set_motion);
+
+	ClassDB::bind_method(D_METHOD("get_margin"), &PhysicsTestMotionParameters2D::get_margin);
+	ClassDB::bind_method(D_METHOD("set_margin"), &PhysicsTestMotionParameters2D::set_margin);
+
+	ClassDB::bind_method(D_METHOD("is_collide_separation_ray_enabled"), &PhysicsTestMotionParameters2D::is_collide_separation_ray_enabled);
+	ClassDB::bind_method(D_METHOD("set_collide_separation_ray_enabled"), &PhysicsTestMotionParameters2D::set_collide_separation_ray_enabled);
+
+	ClassDB::bind_method(D_METHOD("get_exclude_bodies"), &PhysicsTestMotionParameters2D::get_exclude_bodies);
+	ClassDB::bind_method(D_METHOD("set_exclude_bodies"), &PhysicsTestMotionParameters2D::set_exclude_bodies);
+
+	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "from"), "set_from", "get_from");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion"), "set_motion", "get_motion");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin"), "set_margin", "get_margin");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_separation_ray"), "set_collide_separation_ray_enabled", "is_collide_separation_ray_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_bodies"), "set_exclude_bodies", "get_exclude_bodies");
+}
+
+///////////////////////////////
+
 Vector2 PhysicsTestMotionResult2D::get_travel() const {
 	return result.travel;
 }
@@ -448,6 +491,10 @@ int PhysicsTestMotionResult2D::get_collider_shape() const {
 	return result.collider_shape;
 }
 
+int PhysicsTestMotionResult2D::get_collision_local_shape() const {
+	return result.collision_local_shape;
+}
+
 real_t PhysicsTestMotionResult2D::get_collision_depth() const {
 	return result.collision_depth;
 }
@@ -470,36 +517,23 @@ void PhysicsTestMotionResult2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_collider_rid"), &PhysicsTestMotionResult2D::get_collider_rid);
 	ClassDB::bind_method(D_METHOD("get_collider"), &PhysicsTestMotionResult2D::get_collider);
 	ClassDB::bind_method(D_METHOD("get_collider_shape"), &PhysicsTestMotionResult2D::get_collider_shape);
+	ClassDB::bind_method(D_METHOD("get_collision_local_shape"), &PhysicsTestMotionResult2D::get_collision_local_shape);
 	ClassDB::bind_method(D_METHOD("get_collision_depth"), &PhysicsTestMotionResult2D::get_collision_depth);
 	ClassDB::bind_method(D_METHOD("get_collision_safe_fraction"), &PhysicsTestMotionResult2D::get_collision_safe_fraction);
 	ClassDB::bind_method(D_METHOD("get_collision_unsafe_fraction"), &PhysicsTestMotionResult2D::get_collision_unsafe_fraction);
-
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "travel"), "", "get_travel");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "remainder"), "", "get_remainder");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collision_point"), "", "get_collision_point");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collision_normal"), "", "get_collision_normal");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collider_velocity"), "", "get_collider_velocity");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id", PROPERTY_HINT_OBJECT_ID), "", "get_collider_id");
-	ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_collider_rid");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_collider");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape"), "", "get_collider_shape");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_depth"), "", "get_collision_depth");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_safe_fraction"), "", "get_collision_safe_fraction");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_unsafe_fraction"), "", "get_collision_unsafe_fraction");
 }
 
 ///////////////////////////////////////
 
-bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result, bool p_collide_separation_ray, const Vector<RID> &p_exclude) {
-	MotionResult *r = nullptr;
+bool PhysicsServer2D::_body_test_motion(RID p_body, const Ref<PhysicsTestMotionParameters2D> &p_parameters, const Ref<PhysicsTestMotionResult2D> &p_result) {
+	ERR_FAIL_COND_V(!p_parameters.is_valid(), false);
+
+	MotionResult *result_ptr = nullptr;
 	if (p_result.is_valid()) {
-		r = p_result->get_result_ptr();
-	}
-	Set<RID> exclude;
-	for (int i = 0; i < p_exclude.size(); i++) {
-		exclude.insert(p_exclude[i]);
+		result_ptr = p_result->get_result_ptr();
 	}
-	return body_test_motion(p_body, p_from, p_motion, p_margin, r, p_collide_separation_ray, exclude);
+
+	return body_test_motion(p_body, p_parameters->get_parameters(), result_ptr);
 }
 
 void PhysicsServer2D::_bind_methods() {
@@ -626,7 +660,7 @@ void PhysicsServer2D::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("body_set_force_integration_callback", "body", "callable", "userdata"), &PhysicsServer2D::body_set_force_integration_callback, DEFVAL(Variant()));
 
-	ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "collide_separation_ray", "exclude"), &PhysicsServer2D::_body_test_motion, DEFVAL(0.08), DEFVAL(Variant()), DEFVAL(false), DEFVAL(Array()));
+	ClassDB::bind_method(D_METHOD("body_test_motion", "body", "parameters", "result"), &PhysicsServer2D::_body_test_motion, DEFVAL(Variant()));
 
 	ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer2D::body_get_direct_state);
 

+ 46 - 14
servers/physics_server_2d.h

@@ -198,6 +198,7 @@ public:
 	PhysicsDirectSpaceState2D();
 };
 
+class PhysicsTestMotionParameters2D;
 class PhysicsTestMotionResult2D;
 
 class PhysicsServer2D : public Object {
@@ -205,7 +206,7 @@ class PhysicsServer2D : public Object {
 
 	static PhysicsServer2D *singleton;
 
-	virtual bool _body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>(), bool p_collide_separation_ray = false, const Vector<RID> &p_exclude = Vector<RID>());
+	virtual bool _body_test_motion(RID p_body, const Ref<PhysicsTestMotionParameters2D> &p_parameters, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>());
 
 protected:
 	static void _bind_methods();
@@ -465,6 +466,21 @@ public:
 	// this function only works on physics process, errors and returns null otherwise
 	virtual PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) = 0;
 
+	struct MotionParameters {
+		Transform2D from;
+		Vector2 motion;
+		real_t margin = 0.08;
+		bool collide_separation_ray = false;
+		Set<RID> exclude_bodies;
+
+		MotionParameters() {}
+
+		MotionParameters(const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08) :
+				from(p_from),
+				motion(p_motion),
+				margin(p_margin) {}
+	};
+
 	struct MotionResult {
 		Vector2 travel;
 		Vector2 remainder;
@@ -485,18 +501,7 @@ public:
 		}
 	};
 
-	virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) = 0;
-
-	struct SeparationResult {
-		real_t collision_depth;
-		Vector2 collision_point;
-		Vector2 collision_normal;
-		Vector2 collider_velocity;
-		int collision_local_shape;
-		ObjectID collider_id;
-		RID collider;
-		int collider_shape;
-	};
+	virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) = 0;
 
 	/* JOINT API */
 
@@ -579,11 +584,37 @@ public:
 	~PhysicsServer2D();
 };
 
+class PhysicsTestMotionParameters2D : public RefCounted {
+	GDCLASS(PhysicsTestMotionParameters2D, RefCounted);
+
+	PhysicsServer2D::MotionParameters parameters;
+
+protected:
+	static void _bind_methods();
+
+public:
+	const PhysicsServer2D::MotionParameters &get_parameters() const { return parameters; }
+
+	const Transform2D &get_from() const { return parameters.from; }
+	void set_from(const Transform2D &p_from) { parameters.from = p_from; }
+
+	const Vector2 &get_motion() const { return parameters.motion; }
+	void set_motion(const Vector2 &p_motion) { parameters.motion = p_motion; }
+
+	real_t get_margin() const { return parameters.margin; }
+	void set_margin(real_t p_margin) { parameters.margin = p_margin; }
+
+	bool is_collide_separation_ray_enabled() const { return parameters.collide_separation_ray; }
+	void set_collide_separation_ray_enabled(bool p_enabled) { parameters.collide_separation_ray = p_enabled; }
+
+	Vector<RID> get_exclude_bodies() const;
+	void set_exclude_bodies(const Vector<RID> &p_exclude);
+};
+
 class PhysicsTestMotionResult2D : public RefCounted {
 	GDCLASS(PhysicsTestMotionResult2D, RefCounted);
 
 	PhysicsServer2D::MotionResult result;
-	friend class PhysicsServer2D;
 
 protected:
 	static void _bind_methods();
@@ -601,6 +632,7 @@ public:
 	RID get_collider_rid() const;
 	Object *get_collider() const;
 	int get_collider_shape() const;
+	int get_collision_local_shape() const;
 	real_t get_collision_depth() const;
 	real_t get_collision_safe_fraction() const;
 	real_t get_collision_unsafe_fraction() const;

+ 67 - 70
servers/physics_server_3d.cpp

@@ -362,6 +362,53 @@ void PhysicsDirectSpaceState3D::_bind_methods() {
 
 ///////////////////////////////
 
+Vector<RID> PhysicsTestMotionParameters3D::get_exclude_bodies() const {
+	Vector<RID> exclude;
+	exclude.resize(parameters.exclude_bodies.size());
+
+	int body_index = 0;
+	for (RID body : parameters.exclude_bodies) {
+		exclude.write[body_index++] = body;
+	}
+
+	return exclude;
+}
+
+void PhysicsTestMotionParameters3D::set_exclude_bodies(const Vector<RID> &p_exclude) {
+	for (RID body : p_exclude) {
+		parameters.exclude_bodies.insert(body);
+	}
+}
+
+void PhysicsTestMotionParameters3D::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("get_from"), &PhysicsTestMotionParameters3D::get_from);
+	ClassDB::bind_method(D_METHOD("set_from"), &PhysicsTestMotionParameters3D::set_from);
+
+	ClassDB::bind_method(D_METHOD("get_motion"), &PhysicsTestMotionParameters3D::get_motion);
+	ClassDB::bind_method(D_METHOD("set_motion"), &PhysicsTestMotionParameters3D::set_motion);
+
+	ClassDB::bind_method(D_METHOD("get_margin"), &PhysicsTestMotionParameters3D::get_margin);
+	ClassDB::bind_method(D_METHOD("set_margin"), &PhysicsTestMotionParameters3D::set_margin);
+
+	ClassDB::bind_method(D_METHOD("get_max_collisions"), &PhysicsTestMotionParameters3D::get_max_collisions);
+	ClassDB::bind_method(D_METHOD("set_max_collisions"), &PhysicsTestMotionParameters3D::set_max_collisions);
+
+	ClassDB::bind_method(D_METHOD("is_collide_separation_ray_enabled"), &PhysicsTestMotionParameters3D::is_collide_separation_ray_enabled);
+	ClassDB::bind_method(D_METHOD("set_collide_separation_ray_enabled"), &PhysicsTestMotionParameters3D::set_collide_separation_ray_enabled);
+
+	ClassDB::bind_method(D_METHOD("get_exclude_bodies"), &PhysicsTestMotionParameters3D::get_exclude_bodies);
+	ClassDB::bind_method(D_METHOD("set_exclude_bodies"), &PhysicsTestMotionParameters3D::set_exclude_bodies);
+
+	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "from"), "set_from", "get_from");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion"), "set_motion", "get_motion");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin"), "set_margin", "get_margin");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_collisions"), "set_max_collisions", "get_max_collisions");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_separation_ray"), "set_collide_separation_ray_enabled", "is_collide_separation_ray_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_bodies"), "set_exclude_bodies", "get_exclude_bodies");
+}
+
+///////////////////////////////
+
 Vector3 PhysicsTestMotionResult3D::get_travel() const {
 	return result.travel;
 }
@@ -370,12 +417,12 @@ Vector3 PhysicsTestMotionResult3D::get_remainder() const {
 	return result.remainder;
 }
 
-real_t PhysicsTestMotionResult3D::get_safe_fraction() const {
-	return result.safe_fraction;
+real_t PhysicsTestMotionResult3D::get_collision_safe_fraction() const {
+	return result.collision_safe_fraction;
 }
 
-real_t PhysicsTestMotionResult3D::get_unsafe_fraction() const {
-	return result.unsafe_fraction;
+real_t PhysicsTestMotionResult3D::get_collision_unsafe_fraction() const {
+	return result.collision_unsafe_fraction;
 }
 
 int PhysicsTestMotionResult3D::get_collision_count() const {
@@ -417,48 +464,21 @@ int PhysicsTestMotionResult3D::get_collider_shape(int p_collision_index) const {
 	return result.collisions[p_collision_index].collider_shape;
 }
 
+int PhysicsTestMotionResult3D::get_collision_local_shape(int p_collision_index) const {
+	ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, 0);
+	return result.collisions[p_collision_index].local_shape;
+}
+
 real_t PhysicsTestMotionResult3D::get_collision_depth(int p_collision_index) const {
 	ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, 0.0);
 	return result.collisions[p_collision_index].depth;
 }
 
-Vector3 PhysicsTestMotionResult3D::get_best_collision_point() const {
-	return result.collision_count ? get_collision_point() : Vector3();
-}
-
-Vector3 PhysicsTestMotionResult3D::get_best_collision_normal() const {
-	return result.collision_count ? get_collision_normal() : Vector3();
-}
-
-Vector3 PhysicsTestMotionResult3D::get_best_collider_velocity() const {
-	return result.collision_count ? get_collider_velocity() : Vector3();
-}
-
-ObjectID PhysicsTestMotionResult3D::get_best_collider_id() const {
-	return result.collision_count ? get_collider_id() : ObjectID();
-}
-
-RID PhysicsTestMotionResult3D::get_best_collider_rid() const {
-	return result.collision_count ? get_collider_rid() : RID();
-}
-
-Object *PhysicsTestMotionResult3D::get_best_collider() const {
-	return result.collision_count ? get_collider() : nullptr;
-}
-
-int PhysicsTestMotionResult3D::get_best_collider_shape() const {
-	return result.collision_count ? get_collider_shape() : 0;
-}
-
-real_t PhysicsTestMotionResult3D::get_best_collision_depth() const {
-	return result.collision_count ? get_collision_depth() : 0.0;
-}
-
 void PhysicsTestMotionResult3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_travel"), &PhysicsTestMotionResult3D::get_travel);
 	ClassDB::bind_method(D_METHOD("get_remainder"), &PhysicsTestMotionResult3D::get_remainder);
-	ClassDB::bind_method(D_METHOD("get_safe_fraction"), &PhysicsTestMotionResult3D::get_safe_fraction);
-	ClassDB::bind_method(D_METHOD("get_unsafe_fraction"), &PhysicsTestMotionResult3D::get_unsafe_fraction);
+	ClassDB::bind_method(D_METHOD("get_collision_safe_fraction"), &PhysicsTestMotionResult3D::get_collision_safe_fraction);
+	ClassDB::bind_method(D_METHOD("get_collision_unsafe_fraction"), &PhysicsTestMotionResult3D::get_collision_unsafe_fraction);
 	ClassDB::bind_method(D_METHOD("get_collision_count"), &PhysicsTestMotionResult3D::get_collision_count);
 	ClassDB::bind_method(D_METHOD("get_collision_point", "collision_index"), &PhysicsTestMotionResult3D::get_collision_point, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("get_collision_normal", "collision_index"), &PhysicsTestMotionResult3D::get_collision_normal, DEFVAL(0));
@@ -467,44 +487,21 @@ void PhysicsTestMotionResult3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_collider_rid", "collision_index"), &PhysicsTestMotionResult3D::get_collider_rid, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("get_collider", "collision_index"), &PhysicsTestMotionResult3D::get_collider, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("get_collider_shape", "collision_index"), &PhysicsTestMotionResult3D::get_collider_shape, DEFVAL(0));
+	ClassDB::bind_method(D_METHOD("get_collision_local_shape", "collision_index"), &PhysicsTestMotionResult3D::get_collision_local_shape, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("get_collision_depth", "collision_index"), &PhysicsTestMotionResult3D::get_collision_depth, DEFVAL(0));
-
-	ClassDB::bind_method(D_METHOD("get_best_collision_point"), &PhysicsTestMotionResult3D::get_best_collision_point);
-	ClassDB::bind_method(D_METHOD("get_best_collision_normal"), &PhysicsTestMotionResult3D::get_best_collision_normal);
-	ClassDB::bind_method(D_METHOD("get_best_collider_velocity"), &PhysicsTestMotionResult3D::get_best_collider_velocity);
-	ClassDB::bind_method(D_METHOD("get_best_collider_id"), &PhysicsTestMotionResult3D::get_best_collider_id);
-	ClassDB::bind_method(D_METHOD("get_best_collider_rid"), &PhysicsTestMotionResult3D::get_best_collider_rid);
-	ClassDB::bind_method(D_METHOD("get_best_collider"), &PhysicsTestMotionResult3D::get_best_collider);
-	ClassDB::bind_method(D_METHOD("get_best_collider_shape"), &PhysicsTestMotionResult3D::get_best_collider_shape);
-	ClassDB::bind_method(D_METHOD("get_best_collision_depth"), &PhysicsTestMotionResult3D::get_best_collision_depth);
-
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "travel"), "", "get_travel");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "remainder"), "", "get_remainder");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "safe_fraction"), "", "get_safe_fraction");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unsafe_fraction"), "", "get_unsafe_fraction");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_count"), "", "get_collision_count");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collision_point"), "", "get_best_collision_point");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collision_normal"), "", "get_best_collision_normal");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_best_collider_velocity");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id", PROPERTY_HINT_OBJECT_ID), "", "get_best_collider_id");
-	ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_best_collider_rid");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_best_collider");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape"), "", "get_best_collider_shape");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_depth"), "", "get_best_collision_depth");
 }
 
 ///////////////////////////////////////
 
-bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin, const Ref<PhysicsTestMotionResult3D> &p_result, bool p_collide_separation_ray, const Vector<RID> &p_exclude, int p_max_collisions) {
-	MotionResult *r = nullptr;
+bool PhysicsServer3D::_body_test_motion(RID p_body, const Ref<PhysicsTestMotionParameters3D> &p_parameters, const Ref<PhysicsTestMotionResult3D> &p_result) {
+	ERR_FAIL_COND_V(!p_parameters.is_valid(), false);
+
+	MotionResult *result_ptr = nullptr;
 	if (p_result.is_valid()) {
-		r = p_result->get_result_ptr();
+		result_ptr = p_result->get_result_ptr();
 	}
-	Set<RID> exclude;
-	for (int i = 0; i < p_exclude.size(); i++) {
-		exclude.insert(p_exclude[i]);
-	}
-	return body_test_motion(p_body, p_from, p_motion, p_margin, r, p_max_collisions, p_collide_separation_ray, exclude);
+
+	return body_test_motion(p_body, p_parameters->get_parameters(), result_ptr);
 }
 
 RID PhysicsServer3D::shape_create(ShapeType p_shape) {
@@ -662,7 +659,7 @@ void PhysicsServer3D::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("body_set_ray_pickable", "body", "enable"), &PhysicsServer3D::body_set_ray_pickable);
 
-	ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "margin", "result", "collide_separation_ray", "exclude", "max_collisions"), &PhysicsServer3D::_body_test_motion, DEFVAL(0.001), DEFVAL(Variant()), DEFVAL(false), DEFVAL(Array()), DEFVAL(1));
+	ClassDB::bind_method(D_METHOD("body_test_motion", "body", "parameters", "result"), &PhysicsServer3D::_body_test_motion, DEFVAL(Variant()));
 
 	ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer3D::body_get_direct_state);
 

+ 54 - 16
servers/physics_server_3d.h

@@ -203,6 +203,7 @@ public:
 	virtual ~RenderingServerHandler() {}
 };
 
+class PhysicsTestMotionParameters3D;
 class PhysicsTestMotionResult3D;
 
 class PhysicsServer3D : public Object {
@@ -210,7 +211,7 @@ class PhysicsServer3D : public Object {
 
 	static PhysicsServer3D *singleton;
 
-	virtual bool _body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, const Ref<PhysicsTestMotionResult3D> &p_result = Ref<PhysicsTestMotionResult3D>(), bool p_collide_separation_ray = false, const Vector<RID> &p_exclude = Vector<RID>(), int p_max_collisions = 1);
+	virtual bool _body_test_motion(RID p_body, const Ref<PhysicsTestMotionParameters3D> &p_parameters, const Ref<PhysicsTestMotionResult3D> &p_result = Ref<PhysicsTestMotionResult3D>());
 
 protected:
 	static void _bind_methods();
@@ -483,6 +484,22 @@ public:
 	// this function only works on physics process, errors and returns null otherwise
 	virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) = 0;
 
+	struct MotionParameters {
+		Transform3D from;
+		Vector3 motion;
+		real_t margin = 0.001;
+		int max_collisions = 1;
+		bool collide_separation_ray = false;
+		Set<RID> exclude_bodies;
+
+		MotionParameters() {}
+
+		MotionParameters(const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001) :
+				from(p_from),
+				motion(p_motion),
+				margin(p_margin) {}
+	};
+
 	struct MotionCollision {
 		Vector3 position;
 		Vector3 normal;
@@ -501,15 +518,15 @@ public:
 	struct MotionResult {
 		Vector3 travel;
 		Vector3 remainder;
-		real_t safe_fraction = 0.0;
-		real_t unsafe_fraction = 0.0;
+		real_t collision_safe_fraction = 0.0;
+		real_t collision_unsafe_fraction = 0.0;
 
 		static const int MAX_COLLISIONS = 32;
 		MotionCollision collisions[MAX_COLLISIONS];
 		int collision_count = 0;
 	};
 
-	virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, int p_max_collisions = 1, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()) = 0;
+	virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) = 0;
 
 	/* SOFT BODY */
 
@@ -760,11 +777,40 @@ public:
 	~PhysicsServer3D();
 };
 
+class PhysicsTestMotionParameters3D : public RefCounted {
+	GDCLASS(PhysicsTestMotionParameters3D, RefCounted);
+
+	PhysicsServer3D::MotionParameters parameters;
+
+protected:
+	static void _bind_methods();
+
+public:
+	const PhysicsServer3D::MotionParameters &get_parameters() const { return parameters; }
+
+	const Transform3D &get_from() const { return parameters.from; }
+	void set_from(const Transform3D &p_from) { parameters.from = p_from; }
+
+	const Vector3 &get_motion() const { return parameters.motion; }
+	void set_motion(const Vector3 &p_motion) { parameters.motion = p_motion; }
+
+	real_t get_margin() const { return parameters.margin; }
+	void set_margin(real_t p_margin) { parameters.margin = p_margin; }
+
+	int get_max_collisions() const { return parameters.max_collisions; }
+	void set_max_collisions(int p_max_collisions) { parameters.max_collisions = p_max_collisions; }
+
+	bool is_collide_separation_ray_enabled() const { return parameters.collide_separation_ray; }
+	void set_collide_separation_ray_enabled(bool p_enabled) { parameters.collide_separation_ray = p_enabled; }
+
+	Vector<RID> get_exclude_bodies() const;
+	void set_exclude_bodies(const Vector<RID> &p_exclude);
+};
+
 class PhysicsTestMotionResult3D : public RefCounted {
 	GDCLASS(PhysicsTestMotionResult3D, RefCounted);
 
 	PhysicsServer3D::MotionResult result;
-	friend class PhysicsServer3D;
 
 protected:
 	static void _bind_methods();
@@ -774,8 +820,8 @@ public:
 
 	Vector3 get_travel() const;
 	Vector3 get_remainder() const;
-	real_t get_safe_fraction() const;
-	real_t get_unsafe_fraction() const;
+	real_t get_collision_safe_fraction() const;
+	real_t get_collision_unsafe_fraction() const;
 
 	int get_collision_count() const;
 
@@ -786,16 +832,8 @@ public:
 	RID get_collider_rid(int p_collision_index = 0) const;
 	Object *get_collider(int p_collision_index = 0) const;
 	int get_collider_shape(int p_collision_index = 0) const;
+	int get_collision_local_shape(int p_collision_index = 0) const;
 	real_t get_collision_depth(int p_collision_index = 0) const;
-
-	Vector3 get_best_collision_point() const;
-	Vector3 get_best_collision_normal() const;
-	Vector3 get_best_collider_velocity() const;
-	ObjectID get_best_collider_id() const;
-	RID get_best_collider_rid() const;
-	Object *get_best_collider() const;
-	int get_best_collider_shape() const;
-	real_t get_best_collision_depth() const;
 };
 
 typedef PhysicsServer3D *(*CreatePhysicsServer3DCallback)();

+ 2 - 0
servers/register_server_types.cpp

@@ -208,12 +208,14 @@ void register_server_types() {
 
 	GDREGISTER_VIRTUAL_CLASS(PhysicsDirectBodyState2D);
 	GDREGISTER_VIRTUAL_CLASS(PhysicsDirectSpaceState2D);
+	GDREGISTER_CLASS(PhysicsTestMotionParameters2D);
 	GDREGISTER_CLASS(PhysicsTestMotionResult2D);
 	GDREGISTER_CLASS(PhysicsShapeQueryParameters2D);
 
 	GDREGISTER_CLASS(PhysicsShapeQueryParameters3D);
 	GDREGISTER_VIRTUAL_CLASS(PhysicsDirectBodyState3D);
 	GDREGISTER_VIRTUAL_CLASS(PhysicsDirectSpaceState3D);
+	GDREGISTER_CLASS(PhysicsTestMotionParameters3D);
 	GDREGISTER_CLASS(PhysicsTestMotionResult3D);
 
 	// Physics 2D