Переглянути джерело

-Rewritten KinematicBody2D::move to MUCH more efficient code.
-KinematicBody2D::move now properly recognizes collision exceptions and masks, fixes #1649
-Removed object type masking for KinematicBody2D
-Added a test_motion() function to RigidBody2D, allowing simlar behavior to KinematicBody2D::move there.

Juan Linietsky 10 роки тому
батько
коміт
28c4afeb57

+ 2 - 1
demos/2d/kinematic_char/colworld.gd

@@ -14,4 +14,5 @@ func _ready():
 
 func _on_princess_body_enter( body ):
 	#the name of this editor-generated callback is unfortunate
-	get_node("youwin").show()
+	if (body.get_name()=="player"):
+		get_node("youwin").show()

BIN
demos/2d/kinematic_char/colworld.scn


+ 34 - 108
scene/2d/physics_body_2d.cpp

@@ -341,6 +341,16 @@ struct _RigidBody2DInOut {
 	int local_shape;
 };
 
+
+bool RigidBody2D::_test_motion(const Vector2& p_motion,float p_margin,const Ref<Physics2DTestMotionResult>& p_result) {
+
+	Physics2DServer::MotionResult *r=NULL;
+	if (p_result.is_valid())
+		r=p_result->get_result_ptr();
+	return Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,p_margin,r);
+
+}
+
 void RigidBody2D::_direct_state_changed(Object *p_state) {
 
 	//eh.. fuck
@@ -791,6 +801,8 @@ void RigidBody2D::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set_can_sleep","able_to_sleep"),&RigidBody2D::set_can_sleep);
 	ObjectTypeDB::bind_method(_MD("is_able_to_sleep"),&RigidBody2D::is_able_to_sleep);
 
+	ObjectTypeDB::bind_method(_MD("test_motion","motion","margin","result:Physics2DTestMotionResult"),&RigidBody2D::_test_motion,DEFVAL(0.08),DEFVAL(Variant()));
+
 	ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&RigidBody2D::_direct_state_changed);
 	ObjectTypeDB::bind_method(_MD("_body_enter_tree"),&RigidBody2D::_body_enter_tree);
 	ObjectTypeDB::bind_method(_MD("_body_exit_tree"),&RigidBody2D::_body_exit_tree);
@@ -888,20 +900,25 @@ Variant KinematicBody2D::_get_collider() const {
 }
 
 
-bool KinematicBody2D::_ignores_mode(Physics2DServer::BodyMode p_mode) const {
+Vector2 KinematicBody2D::move(const Vector2& p_motion) {
 
-	switch(p_mode) {
-		case Physics2DServer::BODY_MODE_STATIC: return !collide_static;
-		case Physics2DServer::BODY_MODE_KINEMATIC: return !collide_kinematic;
-		case Physics2DServer::BODY_MODE_RIGID: return !collide_rigid;
-		case Physics2DServer::BODY_MODE_CHARACTER: return !collide_character;
-	}
+#if 1
+	Physics2DServer::MotionResult result;
+	colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,margin,&result);
 
-	return true;
-}
+	collider_metadata=result.collider_metadata;
+	collider_shape=result.collider_shape;
+	collider_vel=result.collider_velocity;
+	collision=result.collision_point;
+	normal=result.collision_normal;
+	collider=result.collider_id;
 
-Vector2 KinematicBody2D::move(const Vector2& p_motion) {
+	Matrix32 gt = get_global_transform();
+	gt.elements[2]+=result.motion;
+	set_global_transform(gt);
+	return result.remainder;
 
+#else
 	//give me back regular physics engine logic
 	//this is madness
 	//and most people using this function will think
@@ -1051,7 +1068,7 @@ Vector2 KinematicBody2D::move(const Vector2& p_motion) {
 	set_global_transform(gt);
 
 	return p_motion-motion;
-
+#endif
 }
 
 Vector2 KinematicBody2D::move_to(const Vector2& p_position) {
@@ -1059,58 +1076,22 @@ Vector2 KinematicBody2D::move_to(const Vector2& p_position) {
 	return move(p_position-get_global_pos());
 }
 
-bool KinematicBody2D::can_move_to(const Vector2& p_position, bool p_discrete) {
+bool KinematicBody2D::test_move(const Vector2& p_motion) {
 
 	ERR_FAIL_COND_V(!is_inside_tree(),false);
-	Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(get_world_2d()->get_space());
-	ERR_FAIL_COND_V(!dss,false);
 
-	uint32_t mask=0;
-	if (collide_static)
-		mask|=Physics2DDirectSpaceState::TYPE_MASK_STATIC_BODY;
-	if (collide_kinematic)
-		mask|=Physics2DDirectSpaceState::TYPE_MASK_KINEMATIC_BODY;
-	if (collide_rigid)
-		mask|=Physics2DDirectSpaceState::TYPE_MASK_RIGID_BODY;
-	if (collide_character)
-		mask|=Physics2DDirectSpaceState::TYPE_MASK_CHARACTER_BODY;
-
-	Vector2 motion = p_position-get_global_pos();
-	Matrix32 xform=get_global_transform();
-
-	if (p_discrete) {
-
-		xform.elements[2]+=motion;
-		motion=Vector2();
-	}
-
-	Set<RID> exclude;
-	exclude.insert(get_rid());
-
-	//fill exclude list..
-	for(int i=0;i<get_shape_count();i++) {
+	return Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,margin);
 
 
-		bool col = dss->intersect_shape(get_shape(i)->get_rid(), xform * get_shape_transform(i),motion,0,NULL,0,exclude,get_layer_mask(),mask);
-		if (col)
-			return false;
-	}
-
-	return true;
 }
 
-bool KinematicBody2D::is_colliding() const {
-
-	ERR_FAIL_COND_V(!is_inside_tree(),false);
-
-	return colliding;
-}
 Vector2 KinematicBody2D::get_collision_pos() const {
 
 	ERR_FAIL_COND_V(!colliding,Vector2());
 	return collision;
 
 }
+
 Vector2 KinematicBody2D::get_collision_normal() const {
 
 	ERR_FAIL_COND_V(!colliding,Vector2());
@@ -1143,43 +1124,10 @@ Variant KinematicBody2D::get_collider_metadata() const {
 
 }
 
-void KinematicBody2D::set_collide_with_static_bodies(bool p_enable) {
-
-	collide_static=p_enable;
-}
-bool KinematicBody2D::can_collide_with_static_bodies() const {
-
-	return collide_static;
-}
-
-void KinematicBody2D::set_collide_with_rigid_bodies(bool p_enable) {
-
-	collide_rigid=p_enable;
-
-}
-bool KinematicBody2D::can_collide_with_rigid_bodies() const {
-
 
-	return collide_rigid;
-}
-
-void KinematicBody2D::set_collide_with_kinematic_bodies(bool p_enable) {
-
-	collide_kinematic=p_enable;
-
-}
-bool KinematicBody2D::can_collide_with_kinematic_bodies() const {
+bool KinematicBody2D::is_colliding() const{
 
-	return collide_kinematic;
-}
-
-void KinematicBody2D::set_collide_with_character_bodies(bool p_enable) {
-
-	collide_character=p_enable;
-}
-bool KinematicBody2D::can_collide_with_character_bodies() const {
-
-	return collide_character;
+	return colliding;
 }
 
 void KinematicBody2D::set_collision_margin(float p_margin) {
@@ -1198,7 +1146,7 @@ void KinematicBody2D::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("move","rel_vec"),&KinematicBody2D::move);
 	ObjectTypeDB::bind_method(_MD("move_to","position"),&KinematicBody2D::move_to);
 
-	ObjectTypeDB::bind_method(_MD("can_move_to","position","discrete"),&KinematicBody2D::can_move_to,DEFVAL(false));
+	ObjectTypeDB::bind_method(_MD("test_move","rel_vec"),&KinematicBody2D::test_move);
 
 	ObjectTypeDB::bind_method(_MD("is_colliding"),&KinematicBody2D::is_colliding);
 
@@ -1209,26 +1157,9 @@ void KinematicBody2D::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_collider_shape"),&KinematicBody2D::get_collider_shape);
 	ObjectTypeDB::bind_method(_MD("get_collider_metadata"),&KinematicBody2D::get_collider_metadata);
 
-
-	ObjectTypeDB::bind_method(_MD("set_collide_with_static_bodies","enable"),&KinematicBody2D::set_collide_with_static_bodies);
-	ObjectTypeDB::bind_method(_MD("can_collide_with_static_bodies"),&KinematicBody2D::can_collide_with_static_bodies);
-
-	ObjectTypeDB::bind_method(_MD("set_collide_with_kinematic_bodies","enable"),&KinematicBody2D::set_collide_with_kinematic_bodies);
-	ObjectTypeDB::bind_method(_MD("can_collide_with_kinematic_bodies"),&KinematicBody2D::can_collide_with_kinematic_bodies);
-
-	ObjectTypeDB::bind_method(_MD("set_collide_with_rigid_bodies","enable"),&KinematicBody2D::set_collide_with_rigid_bodies);
-	ObjectTypeDB::bind_method(_MD("can_collide_with_rigid_bodies"),&KinematicBody2D::can_collide_with_rigid_bodies);
-
-	ObjectTypeDB::bind_method(_MD("set_collide_with_character_bodies","enable"),&KinematicBody2D::set_collide_with_character_bodies);
-	ObjectTypeDB::bind_method(_MD("can_collide_with_character_bodies"),&KinematicBody2D::can_collide_with_character_bodies);
-
 	ObjectTypeDB::bind_method(_MD("set_collision_margin","pixels"),&KinematicBody2D::set_collision_margin);
 	ObjectTypeDB::bind_method(_MD("get_collision_margin","pixels"),&KinematicBody2D::get_collision_margin);
 
-	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/static"),_SCS("set_collide_with_static_bodies"),_SCS("can_collide_with_static_bodies"));
-	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/kinematic"),_SCS("set_collide_with_kinematic_bodies"),_SCS("can_collide_with_kinematic_bodies"));
-	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/rigid"),_SCS("set_collide_with_rigid_bodies"),_SCS("can_collide_with_rigid_bodies"));
-	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/character"),_SCS("set_collide_with_character_bodies"),_SCS("can_collide_with_character_bodies"));
 	ADD_PROPERTY( PropertyInfo(Variant::REAL,"collision/margin",PROPERTY_HINT_RANGE,"0.001,256,0.001"),_SCS("set_collision_margin"),_SCS("get_collision_margin"));
 
 
@@ -1236,11 +1167,6 @@ void KinematicBody2D::_bind_methods() {
 
 KinematicBody2D::KinematicBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_KINEMATIC){
 
-	collide_static=true;
-	collide_rigid=true;
-	collide_kinematic=true;
-	collide_character=true;
-
 	colliding=false;
 	collider=0;
 

+ 4 - 18
scene/2d/physics_body_2d.h

@@ -188,6 +188,7 @@ private:
 	void _body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape);
 	void _direct_state_changed(Object *p_state);
 
+	bool _test_motion(const Vector2& p_motion,float p_margin=0.08,const Ref<Physics2DTestMotionResult>& p_result=Ref<Physics2DTestMotionResult>());
 
 protected:
 
@@ -249,6 +250,8 @@ public:
 	void set_applied_force(const Vector2& p_force);
 	Vector2 get_applied_force() const;
 
+
+
 	Array get_colliding_bodies() const; //function for script
 
 	RigidBody2D();
@@ -266,11 +269,6 @@ class KinematicBody2D : public PhysicsBody2D {
 	OBJ_TYPE(KinematicBody2D,PhysicsBody2D);
 
 	float margin;
-	bool collide_static;
-	bool collide_rigid;
-	bool collide_kinematic;
-	bool collide_character;
-
 	bool colliding;
 	Vector2 collision;
 	Vector2 normal;
@@ -290,7 +288,7 @@ public:
 	Vector2 move(const Vector2& p_motion);
 	Vector2 move_to(const Vector2& p_position);
 
-	bool can_move_to(const Vector2& p_position,bool p_discrete=false);
+	bool test_move(const Vector2& p_motion);
 	bool is_colliding() const;
 	Vector2 get_collision_pos() const;
 	Vector2 get_collision_normal() const;
@@ -299,18 +297,6 @@ public:
 	int get_collider_shape() const;
 	Variant get_collider_metadata() const;
 
-	void set_collide_with_static_bodies(bool p_enable);
-	bool can_collide_with_static_bodies() const;
-
-	void set_collide_with_rigid_bodies(bool p_enable);
-	bool can_collide_with_rigid_bodies() const;
-
-	void set_collide_with_kinematic_bodies(bool p_enable);
-	bool can_collide_with_kinematic_bodies() const;
-
-	void set_collide_with_character_bodies(bool p_enable);
-	bool can_collide_with_character_bodies() const;
-
 	void set_collision_margin(float p_margin);
 	float get_collision_margin() const;
 

+ 12 - 0
servers/physics_2d/physics_2d_server_sw.cpp

@@ -959,6 +959,18 @@ void Physics2DServerSW::body_set_pickable(RID p_body,bool p_pickable) {
 
 }
 
+bool Physics2DServerSW::body_test_motion(RID p_body,const Vector2& p_motion,float p_margin,MotionResult *r_result) {
+
+	Body2DSW *body = body_owner.get(p_body);
+	ERR_FAIL_COND_V(!body,false);
+	ERR_FAIL_COND_V(!body->get_space(),false);
+	ERR_FAIL_COND_V(body->get_space()->is_locked(),false);
+
+	return body->get_space()->test_body_motion(body,p_motion,p_margin,r_result);
+
+}
+
+
 /* JOINT API */
 
 void Physics2DServerSW::joint_set_param(RID p_joint, JointParam p_param, real_t p_value) {

+ 3 - 0
servers/physics_2d/physics_2d_server_sw.h

@@ -223,6 +223,9 @@ public:
 
 	virtual void body_set_pickable(RID p_body,bool p_pickable);
 
+	virtual bool body_test_motion(RID p_body,const Vector2& p_motion,float p_margin=0.001,MotionResult *r_result=NULL);
+
+
 	/* JOINT API */
 
 	virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value);

+ 504 - 0
servers/physics_2d/space_2d_sw.cpp

@@ -556,7 +556,511 @@ Physics2DDirectSpaceStateSW::Physics2DDirectSpaceStateSW() {
 
 
 
+bool Space2DSW::test_body_motion(Body2DSW *p_body,const Vector2&p_motion,float p_margin,Physics2DServer::MotionResult *r_result) {
 
+	//give me back regular physics engine logic
+	//this is madness
+	//and most people using this function will think
+	//what it does is simpler than using physics
+	//this took about a week to get right..
+	//but is it right? who knows at this point..
+
+	Rect2 body_aabb;
+
+	for(int i=0;i<p_body->get_shape_count();i++) {
+
+		if (i==0)
+			body_aabb=p_body->get_shape_aabb(i);
+		else
+			body_aabb=body_aabb.merge(p_body->get_shape_aabb(i));
+	}
+
+	body_aabb=body_aabb.grow(p_margin);
+
+	{
+		//add motion
+
+		Rect2 motion_aabb=body_aabb;
+		motion_aabb.pos+=p_motion;
+		body_aabb=body_aabb.merge(motion_aabb);
+	}
+
+
+	int amount = broadphase->cull_aabb(body_aabb,intersection_query_results,INTERSECTION_QUERY_MAX,intersection_query_subindex_results);
+
+	for(int i=0;i<amount;i++) {
+
+		bool keep=true;
+
+		if (intersection_query_results[i]==p_body)
+			keep=false;
+		else if (intersection_query_results[i]->get_type()==CollisionObject2DSW::TYPE_AREA)
+			keep=false;
+		else if ((static_cast<Body2DSW*>(intersection_query_results[i])->get_layer_mask()&p_body->get_layer_mask())==0)
+			keep=false;
+		else if (static_cast<Body2DSW*>(intersection_query_results[i])->has_exception(p_body->get_self()) || p_body->has_exception(intersection_query_results[i]->get_self()))
+			keep=false;
+		else if (static_cast<Body2DSW*>(intersection_query_results[i])->is_shape_set_as_trigger(intersection_query_subindex_results[i]))
+			keep=false;
+
+		if (!keep) {
+
+			if (i<amount-1) {
+				SWAP(intersection_query_results[i],intersection_query_results[amount-1]);
+				SWAP(intersection_query_subindex_results[i],intersection_query_subindex_results[amount-1]);
+
+			}
+
+			amount--;
+			i--;
+
+		}
+	}
+
+	Matrix32 body_transform = p_body->get_transform();
+
+	{
+		//STEP 1, FREE BODY IF STUCK
+
+		const int max_results = 32;
+		int recover_attempts=4;
+		Vector2 sr[max_results*2];
+
+		do {
+
+			Physics2DServerSW::CollCbkData cbk;
+			cbk.max=max_results;
+			cbk.amount=0;
+			cbk.ptr=sr;
+
+
+			CollisionSolver2DSW::CallbackResult cbkres=NULL;
+
+			Physics2DServerSW::CollCbkData *cbkptr=NULL;
+			cbkptr=&cbk;
+			cbkres=Physics2DServerSW::_shape_col_cbk;
+
+			bool collided=false;
+
+
+			for(int j=0;j<p_body->get_shape_count();j++) {
+				if (p_body->is_shape_set_as_trigger(j))
+					continue;
+
+				Matrix32 body_shape_xform = body_transform * p_body->get_shape_transform(j);
+				Shape2DSW *body_shape = p_body->get_shape(j);
+				for(int i=0;i<amount;i++) {
+
+					const CollisionObject2DSW *col_obj=intersection_query_results[i];
+					int shape_idx=intersection_query_subindex_results[i];
+
+					if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) {
+
+						const Body2DSW *body=static_cast<const Body2DSW*>(col_obj);
+						cbk.valid_dir=body->get_one_way_collision_direction();
+						cbk.valid_depth=body->get_one_way_collision_max_depth();
+					} else {
+						cbk.valid_dir=Vector2();
+						cbk.valid_depth=0;
+					}
+
+					if (CollisionSolver2DSW::solve(body_shape,body_shape_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2(),cbkres,cbkptr,NULL,p_margin)) {
+						collided=cbk.amount>0;
+					}
+				}
+			}
+
+
+			if (!collided)
+				break;
+
+			Vector2 recover_motion;
+
+			for(int i=0;i<cbk.amount;i++) {
+
+				Vector2 a = sr[i*2+0];
+				Vector2 b = sr[i*2+1];
+
+			//	float d = a.distance_to(b);
+
+				//if (d<margin)
+				///	continue;
+				recover_motion+=(b-a)*0.4;
+			}
+
+			if (recover_motion==Vector2()) {
+				collided=false;
+				break;
+			}
+
+			body_transform.elements[2]+=recover_motion;
+
+			recover_attempts--;
+
+		} while (recover_attempts);
+	}
+
+
+
+	float safe = 1.0;
+	float unsafe = 1.0;
+	int best_shape=-1;
+
+	{
+		// STEP 2 ATTEMPT MOTION
+
+
+
+		for(int j=0;j<p_body->get_shape_count();j++) {
+
+			if (p_body->is_shape_set_as_trigger(j))
+				continue;
+
+			Matrix32 body_shape_xform = body_transform * p_body->get_shape_transform(j);
+			Shape2DSW *body_shape = p_body->get_shape(j);
+
+			bool stuck=false;
+
+			float best_safe=1;
+			float best_unsafe=1;
+
+			for(int i=0;i<amount;i++) {
+
+				const CollisionObject2DSW *col_obj=intersection_query_results[i];
+				int shape_idx=intersection_query_subindex_results[i];
+
+
+				Matrix32 col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
+				//test initial overlap, does it collide if going all the way?
+				if (!CollisionSolver2DSW::solve(body_shape,body_shape_xform,p_motion,col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,0)) {
+					continue;
+				}
+
+
+				//test initial overlap
+				if (CollisionSolver2DSW::solve(body_shape,body_shape_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,0)) {
+
+					if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) {
+						//if one way collision direction ignore initial overlap
+						const Body2DSW *body=static_cast<const Body2DSW*>(col_obj);
+						if (body->get_one_way_collision_direction()!=Vector2()) {
+							continue;
+						}
+					}
+
+					stuck=true;
+					break;
+				}
+
+
+				//just do kinematic solving
+				float low=0;
+				float hi=1;
+				Vector2 mnormal=p_motion.normalized();
+
+				for(int i=0;i<8;i++) { //steps should be customizable..
+
+					//Matrix32 xfa = p_xform;
+					float ofs = (low+hi)*0.5;
+
+					Vector2 sep=mnormal; //important optimization for this to work fast enough
+					bool collided = CollisionSolver2DSW::solve(body_shape,body_shape_xform,p_motion*ofs,col_obj->get_shape(shape_idx),col_obj_xform,Vector2(),NULL,NULL,&sep,0);
+
+					if (collided) {
+
+						hi=ofs;
+					} else {
+
+						low=ofs;
+					}
+				}
+
+				if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) {
+
+					const Body2DSW *body=static_cast<const Body2DSW*>(col_obj);
+					if (body->get_one_way_collision_direction()!=Vector2()) {
+
+						Vector2 cd[2];
+						Physics2DServerSW::CollCbkData cbk;
+						cbk.max=1;
+						cbk.amount=0;
+						cbk.ptr=cd;
+						cbk.valid_dir=body->get_one_way_collision_direction();
+						cbk.valid_depth=body->get_one_way_collision_max_depth();
+
+						Vector2 sep=mnormal; //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(shape_idx),col_obj_xform,Vector2(),Physics2DServerSW::_shape_col_cbk,&cbk,&sep,0);
+						if (!collided || cbk.amount==0) {
+							continue;
+						}
+
+					}
+				}
+
+
+				if (low<best_safe) {
+					best_safe=low;
+					best_unsafe=hi;
+				}
+			}
+
+			if (stuck) {
+
+				safe=0;
+				unsafe=0;
+				best_shape=j; //sadly it's the best
+				break;
+			}
+			if (best_safe==1.0) {
+				continue;
+			}
+			if (best_safe < safe) {
+
+				safe=best_safe;
+				unsafe=best_unsafe;
+				best_shape=j;
+			}
+		}
+	}
+
+	bool collided=false;
+	if (safe>=1) {
+		//not collided
+		collided=false;
+		if (r_result) {
+
+			r_result->motion=p_motion+(body_transform.elements[2]-p_body->get_transform().elements[2]);
+			r_result->remainder=Vector2();
+		}
+
+	} else {
+
+		//it collided, let's get the rest info in unsafe advance
+		Matrix32 ugt = body_transform;
+		ugt.elements[2]+=p_motion*unsafe;
+
+		_RestCallbackData2D rcd;
+		rcd.best_len=0;
+		rcd.best_object=NULL;
+		rcd.best_shape=0;
+
+		Matrix32 body_shape_xform = ugt * p_body->get_shape_transform(best_shape);
+		Shape2DSW *body_shape = p_body->get_shape(best_shape);
+
+
+		for(int i=0;i<amount;i++) {
+
+
+			const CollisionObject2DSW *col_obj=intersection_query_results[i];
+			int shape_idx=intersection_query_subindex_results[i];
+
+			if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) {
+
+				const Body2DSW *body=static_cast<const Body2DSW*>(col_obj);
+				rcd.valid_dir=body->get_one_way_collision_direction();
+				rcd.valid_depth=body->get_one_way_collision_max_depth();
+			} else {
+				rcd.valid_dir=Vector2();
+				rcd.valid_depth=0;
+			}
+
+
+			rcd.object=col_obj;
+			rcd.shape=shape_idx;
+			bool sc = CollisionSolver2DSW::solve(body_shape,body_shape_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2() ,_rest_cbk_result,&rcd,NULL,p_margin);
+			if (!sc)
+				continue;
+
+		}
+
+		if (rcd.best_len!=0) {
+
+			if (r_result) {
+				r_result->collider=rcd.best_object->get_self();
+				r_result->collider_id=rcd.best_object->get_instance_id();
+				r_result->collider_shape=rcd.best_shape;
+				r_result->collision_normal=rcd.best_normal;
+				r_result->collision_point=rcd.best_contact;
+				r_result->collider_metadata=rcd.best_object->get_shape_metadata(rcd.best_shape);
+
+				const Body2DSW *body = static_cast<const Body2DSW*>(rcd.best_object);
+				Vector2 rel_vec = r_result->collision_point-body->get_transform().get_origin();
+				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->motion=safe*p_motion+(body_transform.elements[2]-p_body->get_transform().elements[2]);
+				r_result->remainder=p_motion - safe * p_motion;
+			}
+
+			collided=true;
+		} else {
+			if (r_result) {
+
+				r_result->motion=p_motion+(body_transform.elements[2]-p_body->get_transform().elements[2]);
+				r_result->remainder=Vector2();
+			}
+
+			collided=false;
+
+		}
+	}
+
+	return collided;
+
+
+#if 0
+	//give me back regular physics engine logic
+	//this is madness
+	//and most people using this function will think
+	//what it does is simpler than using physics
+	//this took about a week to get right..
+	//but is it right? who knows at this point..
+
+
+	colliding=false;
+	ERR_FAIL_COND_V(!is_inside_tree(),Vector2());
+	Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(get_world_2d()->get_space());
+	ERR_FAIL_COND_V(!dss,Vector2());
+	const int max_shapes=32;
+	Vector2 sr[max_shapes*2];
+	int res_shapes;
+
+	Set<RID> exclude;
+	exclude.insert(get_rid());
+
+
+	//recover first
+	int recover_attempts=4;
+
+	bool collided=false;
+	uint32_t mask=0;
+	if (collide_static)
+		mask|=Physics2DDirectSpaceState::TYPE_MASK_STATIC_BODY;
+	if (collide_kinematic)
+		mask|=Physics2DDirectSpaceState::TYPE_MASK_KINEMATIC_BODY;
+	if (collide_rigid)
+		mask|=Physics2DDirectSpaceState::TYPE_MASK_RIGID_BODY;
+	if (collide_character)
+		mask|=Physics2DDirectSpaceState::TYPE_MASK_CHARACTER_BODY;
+
+//	print_line("motion: "+p_motion+" margin: "+rtos(margin));
+
+	//print_line("margin: "+rtos(margin));
+	do {
+
+		//motion recover
+		for(int i=0;i<get_shape_count();i++) {
+
+			if (is_shape_set_as_trigger(i))
+				continue;
+			if (dss->collide_shape(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i),Vector2(),margin,sr,max_shapes,res_shapes,exclude,get_layer_mask(),mask))
+				collided=true;
+
+		}
+
+		if (!collided)
+			break;
+
+		Vector2 recover_motion;
+
+		for(int i=0;i<res_shapes;i++) {
+
+			Vector2 a = sr[i*2+0];
+			Vector2 b = sr[i*2+1];
+
+			float d = a.distance_to(b);
+
+			//if (d<margin)
+			///	continue;
+			recover_motion+=(b-a)*0.4;
+		}
+
+		if (recover_motion==Vector2()) {
+			collided=false;
+			break;
+		}
+
+		Matrix32 gt = get_global_transform();
+		gt.elements[2]+=recover_motion;
+		set_global_transform(gt);
+
+		recover_attempts--;
+
+	} while (recover_attempts);
+
+
+	//move second
+	float safe = 1.0;
+	float unsafe = 1.0;
+	int best_shape=-1;
+
+	for(int i=0;i<get_shape_count();i++) {
+
+		if (is_shape_set_as_trigger(i))
+			continue;
+
+		float lsafe,lunsafe;
+		bool valid = dss->cast_motion(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), p_motion, 0,lsafe,lunsafe,exclude,get_layer_mask(),mask);
+		//print_line("shape: "+itos(i)+" travel:"+rtos(ltravel));
+		if (!valid) {
+
+			safe=0;
+			unsafe=0;
+			best_shape=i; //sadly it's the best
+			break;
+		}
+		if (lsafe==1.0) {
+			continue;
+		}
+		if (lsafe < safe) {
+
+			safe=lsafe;
+			unsafe=lunsafe;
+			best_shape=i;
+		}
+	}
+
+
+	//print_line("best shape: "+itos(best_shape)+" motion "+p_motion);
+
+	if (safe>=1) {
+		//not collided
+		colliding=false;
+	} else {
+
+		//it collided, let's get the rest info in unsafe advance
+		Matrix32 ugt = get_global_transform();
+		ugt.elements[2]+=p_motion*unsafe;
+		Physics2DDirectSpaceState::ShapeRestInfo rest_info;
+		bool c2 = dss->rest_info(get_shape(best_shape)->get_rid(), ugt*get_shape_transform(best_shape), Vector2(), margin,&rest_info,exclude,get_layer_mask(),mask);
+		if (!c2) {
+			//should not happen, but floating point precision is so weird..
+
+			colliding=false;
+		} else {
+
+
+			//print_line("Travel: "+rtos(travel));
+			colliding=true;
+			collision=rest_info.point;
+			normal=rest_info.normal;
+			collider=rest_info.collider_id;
+			collider_vel=rest_info.linear_velocity;
+			collider_shape=rest_info.shape;
+			collider_metadata=rest_info.metadata;
+		}
+
+	}
+
+	Vector2 motion=p_motion*safe;
+	Matrix32 gt = get_global_transform();
+	gt.elements[2]+=motion;
+	set_global_transform(gt);
+
+	return p_motion-motion;
+
+#endif
+	return false;
+}
 
 
 

+ 2 - 0
servers/physics_2d/space_2d_sw.h

@@ -165,6 +165,8 @@ public:
 
 	int get_collision_pairs() const { return collision_pairs; }
 
+	bool test_body_motion(Body2DSW *p_body, const Vector2&p_motion, float p_margin, Physics2DServer::MotionResult *r_result);
+
 	Physics2DDirectSpaceStateSW *get_direct_state();
 
 	Space2DSW();

+ 75 - 0
servers/physics_2d_server.cpp

@@ -421,13 +421,86 @@ void Physics2DShapeQueryResult::_bind_methods() {
 
 }
 
+///////////////////////////////
 
+/*bool Physics2DTestMotionResult::is_colliding() const {
 
+	return colliding;
+}*/
+Vector2 Physics2DTestMotionResult::get_motion() const{
 
+	return result.motion;
+}
+Vector2 Physics2DTestMotionResult::get_motion_remainder() const{
+
+	return result.remainder;
+}
+
+Vector2 Physics2DTestMotionResult::get_collision_point() const{
+
+	return result.collision_point;
+}
+Vector2 Physics2DTestMotionResult::get_collision_normal() const{
+
+	return result.collision_normal;
+}
+Vector2 Physics2DTestMotionResult::get_collider_velocity() const{
+
+	return result.collider_velocity;
+}
+ObjectID Physics2DTestMotionResult::get_collider_id() const{
+
+	return result.collider_id;
+}
+RID Physics2DTestMotionResult::get_collider_rid() const{
+
+	return result.collider;
+}
+
+Object* Physics2DTestMotionResult::get_collider() const {
+	return ObjectDB::get_instance(result.collider_id);
+}
+
+int Physics2DTestMotionResult::get_collider_shape() const{
+
+	return result.collider_shape;
+}
+
+void Physics2DTestMotionResult::_bind_methods() {
+
+	//ObjectTypeDB::bind_method(_MD("is_colliding"),&Physics2DTestMotionResult::is_colliding);
+	ObjectTypeDB::bind_method(_MD("get_motion"),&Physics2DTestMotionResult::get_motion);
+	ObjectTypeDB::bind_method(_MD("get_motion_remainder"),&Physics2DTestMotionResult::get_motion_remainder);
+	ObjectTypeDB::bind_method(_MD("get_collision_point"),&Physics2DTestMotionResult::get_collision_point);
+	ObjectTypeDB::bind_method(_MD("get_collision_normal"),&Physics2DTestMotionResult::get_collision_normal);
+	ObjectTypeDB::bind_method(_MD("get_collider_velocity"),&Physics2DTestMotionResult::get_collider_velocity);
+	ObjectTypeDB::bind_method(_MD("get_collider_id"),&Physics2DTestMotionResult::get_collider_id);
+	ObjectTypeDB::bind_method(_MD("get_collider_rid"),&Physics2DTestMotionResult::get_collider_rid);
+	ObjectTypeDB::bind_method(_MD("get_collider"),&Physics2DTestMotionResult::get_collider);
+	ObjectTypeDB::bind_method(_MD("get_collider_shape"),&Physics2DTestMotionResult::get_collider_shape);
+
+}
+
+Physics2DTestMotionResult::Physics2DTestMotionResult(){
+
+	colliding=false;
+	result.collider_id=0;
+	result.collider_shape=0;
+}
 
 
 ///////////////////////////////////////
 
+
+
+bool Physics2DServer::_body_test_motion(RID p_body,const Vector2& p_motion,float p_margin,const Ref<Physics2DTestMotionResult>& p_result) {
+
+	MotionResult *r=NULL;
+	if (p_result.is_valid())
+		r=p_result->get_result_ptr();
+	return body_test_motion(p_body,p_motion,p_margin,r);
+}
+
 void Physics2DServer::_bind_methods() {
 
 
@@ -543,6 +616,8 @@ void Physics2DServer::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("body_set_force_integration_callback","body","receiver","method"),&Physics2DServer::body_set_force_integration_callback);
 
+	ObjectTypeDB::bind_method(_MD("body_test_motion","body","motion","margin","result:Physics2DTestMotionResult"),&Physics2DServer::_body_test_motion,DEFVAL(0.08),DEFVAL(Variant()));
+
 	/* JOINT API */
 
 	ObjectTypeDB::bind_method(_MD("joint_set_param","joint","param","value"),&Physics2DServer::joint_set_param);

+ 50 - 0
servers/physics_2d_server.h

@@ -230,6 +230,7 @@ public:
 	Physics2DShapeQueryResult();
 };
 
+class Physics2DTestMotionResult;
 
 class Physics2DServer : public Object {
 
@@ -237,6 +238,8 @@ class Physics2DServer : public Object {
 
 	static Physics2DServer * singleton;
 
+	virtual bool _body_test_motion(RID p_body,const Vector2& p_motion,float p_margin=0.08,const Ref<Physics2DTestMotionResult>& p_result=Ref<Physics2DTestMotionResult>());
+
 protected:
 	static void _bind_methods();
 
@@ -468,6 +471,22 @@ public:
 
 	virtual void body_set_pickable(RID p_body,bool p_pickable)=0;
 
+	struct MotionResult {
+
+		Vector2 motion;
+		Vector2 remainder;
+
+		Vector2 collision_point;
+		Vector2 collision_normal;
+		Vector2 collider_velocity;
+		ObjectID collider_id;
+		RID collider;
+		int collider_shape;
+		Variant collider_metadata;
+	};
+
+	virtual bool body_test_motion(RID p_body,const Vector2& p_motion,float p_margin=0.001,MotionResult *r_result=NULL)=0;
+
 	/* JOINT API */
 
 	enum JointType {
@@ -532,6 +551,37 @@ public:
 	~Physics2DServer();
 };
 
+
+class Physics2DTestMotionResult : public Reference {
+
+	OBJ_TYPE( Physics2DTestMotionResult, Reference );
+
+	Physics2DServer::MotionResult result;
+	bool colliding;
+friend class Physics2DServer;
+
+protected:
+	static void _bind_methods();
+public:
+
+	Physics2DServer::MotionResult* get_result_ptr() const { return const_cast<Physics2DServer::MotionResult*>(&result); }
+
+	//bool is_colliding() const;
+	Vector2 get_motion() const;
+	Vector2 get_motion_remainder() const;
+
+	Vector2 get_collision_point() const;
+	Vector2 get_collision_normal() const;
+	Vector2 get_collider_velocity() const;
+	ObjectID get_collider_id() const;
+	RID get_collider_rid() const;
+	Object* get_collider() const;
+	int get_collider_shape() const;
+
+	Physics2DTestMotionResult();
+};
+
+
 VARIANT_ENUM_CAST( Physics2DServer::ShapeType );
 VARIANT_ENUM_CAST( Physics2DServer::SpaceParameter );
 VARIANT_ENUM_CAST( Physics2DServer::AreaParameter );

+ 1 - 0
servers/register_server_types.cpp

@@ -55,6 +55,7 @@ void register_server_types() {
 	ObjectTypeDB::register_virtual_type<Physics2DDirectBodyState>();
 	ObjectTypeDB::register_virtual_type<Physics2DDirectSpaceState>();
 	ObjectTypeDB::register_virtual_type<Physics2DShapeQueryResult>();
+	ObjectTypeDB::register_virtual_type<Physics2DTestMotionResult>();
 	ObjectTypeDB::register_type<Physics2DShapeQueryParameters>();
 
 	ObjectTypeDB::register_type<PhysicsShapeQueryParameters>();