Browse Source

-Add support for one-way collision in 2D (only works for kinematic body so far)
-Solve drawing order bug introduced in previous commit: solves #1214

Juan Linietsky 10 years ago
parent
commit
9012cd408e

BIN
demos/2d/kinematic_char/colworld.scn


+ 3 - 2
demos/2d/kinematic_char/player.gd

@@ -15,6 +15,7 @@ const GRAVITY = 500.0
 #consider "floor".
 const FLOOR_ANGLE_TOLERANCE = 40
 const WALK_FORCE = 600
+const WALK_MIN_SPEED=10
 const WALK_MAX_SPEED = 200
 const STOP_FORCE = 1300
 const JUMP_SPEED = 200
@@ -40,12 +41,12 @@ func _fixed_process(delta):
 	var stop=true
 	
 	if (walk_left):
-		if (velocity.x<=0 and velocity.x > -WALK_MAX_SPEED):
+		if (velocity.x<=WALK_MIN_SPEED and velocity.x > -WALK_MAX_SPEED):
 			force.x-=WALK_FORCE			
 			stop=false
 		
 	elif (walk_right):
-		if (velocity.x>=0 and velocity.x < WALK_MAX_SPEED):
+		if (velocity.x>=-WALK_MIN_SPEED and velocity.x < WALK_MAX_SPEED):
 			force.x+=WALK_FORCE
 			stop=false
 	

BIN
demos/2d/kinematic_char/player.scn


File diff suppressed because it is too large
+ 35 - 17
demos/2d/platformer/stage.xml


+ 22 - 2
scene/2d/physics_body_2d.cpp

@@ -55,16 +55,32 @@ Vector2 PhysicsBody2D::get_one_way_collision_direction() const{
 }
 
 
+void PhysicsBody2D::set_one_way_collision_max_depth(float p_depth) {
+
+	one_way_collision_max_depth=p_depth;
+	Physics2DServer::get_singleton()->body_set_one_way_collision_max_depth(get_rid(),p_depth);
+
+}
+
+float PhysicsBody2D::get_one_way_collision_max_depth() const{
+
+	return one_way_collision_max_depth;
+}
+
+
 void PhysicsBody2D::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"),&PhysicsBody2D::set_layer_mask);
 	ObjectTypeDB::bind_method(_MD("get_layer_mask"),&PhysicsBody2D::get_layer_mask);
 	ObjectTypeDB::bind_method(_MD("set_one_way_collision_direction","dir"),&PhysicsBody2D::set_one_way_collision_direction);
 	ObjectTypeDB::bind_method(_MD("get_one_way_collision_direction"),&PhysicsBody2D::get_one_way_collision_direction);
+	ObjectTypeDB::bind_method(_MD("set_one_way_collision_max_depth","depth"),&PhysicsBody2D::set_one_way_collision_max_depth);
+	ObjectTypeDB::bind_method(_MD("get_one_way_collision_max_depth"),&PhysicsBody2D::get_one_way_collision_max_depth);
 	ObjectTypeDB::bind_method(_MD("add_collision_exception_with","body:PhysicsBody2D"),&PhysicsBody2D::add_collision_exception_with);
 	ObjectTypeDB::bind_method(_MD("remove_collision_exception_with","body:PhysicsBody2D"),&PhysicsBody2D::remove_collision_exception_with);
 	ADD_PROPERTY(PropertyInfo(Variant::INT,"layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask"));
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"one_way_collision/direction"),_SCS("set_one_way_collision_direction"),_SCS("get_one_way_collision_direction"));
+	ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2,"one_way_collision/direction"),_SCS("set_one_way_collision_direction"),_SCS("get_one_way_collision_direction"));
+	ADD_PROPERTYNZ(PropertyInfo(Variant::REAL,"one_way_collision/max_depth"),_SCS("set_one_way_collision_max_depth"),_SCS("get_one_way_collision_max_depth"));
 }
 
 void PhysicsBody2D::set_layer_mask(uint32_t p_mask) {
@@ -81,6 +97,7 @@ uint32_t PhysicsBody2D::get_layer_mask() const {
 PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode) : CollisionObject2D( Physics2DServer::get_singleton()->body_create(p_mode), false) {
 
 	mask=1;
+	set_one_way_collision_max_depth(0);
 
 }
 
@@ -947,7 +964,7 @@ Vector2 KinematicBody2D::move(const Vector2& p_motion) {
 
 			//if (d<margin)
 			///	continue;
-			recover_motion+=(b-a)*0.2;
+			recover_motion+=(b-a)*0.4;
 		}
 
 		if (recover_motion==Vector2()) {
@@ -978,6 +995,7 @@ Vector2 KinematicBody2D::move(const Vector2& p_motion) {
 		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
@@ -1009,9 +1027,11 @@ Vector2 KinematicBody2D::move(const Vector2& p_motion) {
 		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;

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

@@ -40,6 +40,7 @@ class PhysicsBody2D : public CollisionObject2D {
 
 	uint32_t mask;
 	Vector2 one_way_collision_direction;
+	float one_way_collision_max_depth;
 protected:
 
 	void _notification(int p_what);
@@ -57,6 +58,9 @@ public:
 	void set_one_way_collision_direction(const Vector2& p_dir);
 	Vector2 get_one_way_collision_direction() const;
 
+	void set_one_way_collision_max_depth(float p_dir);
+	float get_one_way_collision_max_depth() const;
+
 	PhysicsBody2D();
 
 };

+ 1 - 0
servers/physics_2d/body_2d_sw.cpp

@@ -647,6 +647,7 @@ Body2DSW::Body2DSW() : CollisionObject2DSW(TYPE_BODY), active_list(this), inerti
 	area_linear_damp=0;
 	contact_count=0;
 	gravity_scale=1.0;
+	one_way_collision_max_depth=0.1;
 
 	still_time=0;
 	continuous_cd_mode=Physics2DServer::CCD_MODE_DISABLED;

+ 4 - 0
servers/physics_2d/body_2d_sw.h

@@ -68,6 +68,7 @@ class Body2DSW : public CollisionObject2DSW {
 	real_t applied_torque;
 
 	Vector2 one_way_collision_direction;
+	float one_way_collision_max_depth;
 
 
 	SelfList<Body2DSW> active_list;
@@ -221,6 +222,9 @@ public:
 	void set_one_way_collision_direction(const Vector2& p_dir) { one_way_collision_direction=p_dir; }
 	Vector2 get_one_way_collision_direction() const { return one_way_collision_direction; }
 
+	void set_one_way_collision_max_depth(float p_depth) { one_way_collision_max_depth=p_depth; }
+	float get_one_way_collision_max_depth() const { return one_way_collision_max_depth; }
+
 	void set_space(Space2DSW *p_space);
 
 	void update_inertias();

+ 30 - 0
servers/physics_2d/physics_2d_server_sw.cpp

@@ -138,6 +138,21 @@ void Physics2DServerSW::_shape_col_cbk(const Vector2& p_point_A,const Vector2& p
 	if (cbk->max==0)
 		return;
 
+	if (cbk->valid_dir!=Vector2()) {
+		if (p_point_A.distance_squared_to(p_point_B)>cbk->valid_depth*cbk->valid_depth) {
+			return;
+		}
+		if (cbk->valid_dir.dot((p_point_A-p_point_B).normalized())<0.7071) {
+/*			print_line("A: "+p_point_A);
+			print_line("B: "+p_point_B);
+			print_line("discard too angled "+rtos(cbk->valid_dir.dot((p_point_A-p_point_B))));
+			print_line("resnorm: "+(p_point_A-p_point_B).normalized());
+			print_line("distance: "+rtos(p_point_A.distance_to(p_point_B)));
+*/
+			return;
+		}
+	}
+
 	if (cbk->amount == cbk->max) {
 		//find least deep
 		float min_depth=1e20;
@@ -875,6 +890,21 @@ Vector2 Physics2DServerSW::body_get_one_way_collision_direction(RID p_body) cons
 
 }
 
+void Physics2DServerSW::body_set_one_way_collision_max_depth(RID p_body,float p_max_depth) {
+
+	Body2DSW *body = body_owner.get(p_body);
+	ERR_FAIL_COND(!body);
+	body->set_one_way_collision_max_depth(p_max_depth);
+
+}
+
+float Physics2DServerSW::body_get_one_way_collision_max_depth(RID p_body) const {
+
+	Body2DSW *body = body_owner.get(p_body);
+	ERR_FAIL_COND_V(!body,0);
+	return body->get_one_way_collision_max_depth();
+
+}
 
 void Physics2DServerSW::body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata) {
 

+ 6 - 0
servers/physics_2d/physics_2d_server_sw.h

@@ -71,6 +71,8 @@ public:
 
 	struct CollCbkData {
 
+		Vector2 valid_dir;
+		float valid_depth;
 		int max;
 		int amount;
 		Vector2 *ptr;
@@ -208,6 +210,10 @@ public:
 	virtual void body_set_one_way_collision_direction(RID p_body,const Vector2& p_direction);
 	virtual Vector2 body_get_one_way_collision_direction(RID p_body) const;
 
+	virtual void body_set_one_way_collision_max_depth(RID p_body,float p_max_depth);
+	virtual float body_get_one_way_collision_max_depth(RID p_body) const;
+
+
 	virtual void body_set_force_integration_callback(RID p_body,Object *p_receiver,const StringName& p_method,const Variant& p_udata=Variant());
 	virtual bool body_collide_shape(RID p_body, int p_body_shape,RID p_shape, const Matrix32& p_shape_xform,const Vector2& p_motion,Vector2 *r_results,int p_result_max,int &r_result_count);
 

+ 78 - 3
servers/physics_2d/space_2d_sw.cpp

@@ -98,7 +98,7 @@ bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2& p_from, const Vec
 		if (shape->intersect_segment(local_from,local_to,shape_point,shape_normal)) {
 
 
-			//print_line("inters sgment!");
+
 			Matrix32 xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
 			shape_point=xform.xform(shape_point);
 
@@ -217,6 +217,16 @@ bool Physics2DDirectSpaceStateSW::cast_motion(const RID& p_shape, const Matrix32
 		int shape_idx=space->intersection_query_subindex_results[i];
 
 
+		/*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() && p_motion.dot(body->get_one_way_collision_direction())<=CMP_EPSILON) {
+				print_line("failed in motion dir");
+				continue;
+			}
+		}*/
+
+
 		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(shape,p_xform,p_motion,col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,p_margin)) {
@@ -227,6 +237,14 @@ bool Physics2DDirectSpaceStateSW::cast_motion(const RID& p_shape, const Matrix32
 		//test initial overlap
 		if (CollisionSolver2DSW::solve(shape,p_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,p_margin)) {
 
+			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;
+				}
+			}
+
 			return false;
 		}
 
@@ -253,6 +271,29 @@ bool Physics2DDirectSpaceStateSW::cast_motion(const RID& p_shape, const Matrix32
 			}
 		}
 
+		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(shape,p_xform,p_motion*(hi+space->contact_max_allowed_penetration),col_obj->get_shape(shape_idx),col_obj_xform,Vector2(),Physics2DServerSW::_shape_col_cbk,&cbk,&sep,p_margin);
+				if (!collided || cbk.amount==0) {					
+					continue;
+				}
+
+			}
+		}
+
+
 		if (low<best_safe) {
 			best_safe=low;
 			best_unsafe=hi;
@@ -311,14 +352,23 @@ bool Physics2DDirectSpaceStateSW::collide_shape(RID p_shape, const Matrix32& p_s
 
 		if (p_exclude.has( col_obj->get_self() ))
 			continue;
-
+		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(shape,p_shape_xform,p_motion,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2(),cbkres,cbkptr,NULL,p_margin)) {
-			collided=true;
+			collided=p_result_max==0 || cbk.amount>0;
 		}
 
 	}
 
+
 	r_result_count=cbk.amount;
 
 	return collided;
@@ -334,6 +384,8 @@ struct _RestCallbackData2D {
 	Vector2 best_contact;
 	Vector2 best_normal;
 	float best_len;
+	Vector2 valid_dir;
+	float valid_depth;
 };
 
 static void _rest_cbk_result(const Vector2& p_point_A,const Vector2& p_point_B,void *p_userdata) {
@@ -341,11 +393,23 @@ static void _rest_cbk_result(const Vector2& p_point_A,const Vector2& p_point_B,v
 
 	_RestCallbackData2D *rd=(_RestCallbackData2D*)p_userdata;
 
+	if (rd->valid_dir!=Vector2()) {
+
+		if (rd->valid_dir!=Vector2()) {
+			if (p_point_A.distance_squared_to(p_point_B)>rd->valid_depth*rd->valid_depth)
+				return;
+			if (rd->valid_dir.dot((p_point_A-p_point_B).normalized())<Math_PI*0.25)
+				return;
+		}
+
+	}
+
 	Vector2 contact_rel = p_point_B - p_point_A;
 	float len = contact_rel.length();
 	if (len <= rd->best_len)
 		return;
 
+
 	rd->best_len=len;
 	rd->best_contact=p_point_B;
 	rd->best_normal=contact_rel/len;
@@ -385,6 +449,17 @@ bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Matrix32& p_shape
 		if (p_exclude.has( col_obj->get_self() ))
 			continue;
 
+		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(shape,p_shape_xform,p_motion,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);

+ 3 - 0
servers/physics_2d_server.cpp

@@ -503,6 +503,9 @@ void Physics2DServer::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("body_set_one_way_collision_direction","normal"),&Physics2DServer::body_set_one_way_collision_direction);
 	ObjectTypeDB::bind_method(_MD("body_get_one_way_collision_direction"),&Physics2DServer::body_get_one_way_collision_direction);
 
+	ObjectTypeDB::bind_method(_MD("body_set_one_way_collision_max_depth","normal"),&Physics2DServer::body_set_one_way_collision_max_depth);
+	ObjectTypeDB::bind_method(_MD("body_get_one_way_collision_max_depth"),&Physics2DServer::body_get_one_way_collision_max_depth);
+
 
 	ObjectTypeDB::bind_method(_MD("body_set_omit_force_integration","body","enable"),&Physics2DServer::body_set_omit_force_integration);
 	ObjectTypeDB::bind_method(_MD("body_is_omitting_force_integration","body"),&Physics2DServer::body_is_omitting_force_integration);

+ 3 - 0
servers/physics_2d_server.h

@@ -445,6 +445,9 @@ public:
 	virtual void body_set_one_way_collision_direction(RID p_body,const Vector2& p_direction)=0;
 	virtual Vector2 body_get_one_way_collision_direction(RID p_body) const=0;
 
+	virtual void body_set_one_way_collision_max_depth(RID p_body,float p_max_depth)=0;
+	virtual float body_get_one_way_collision_max_depth(RID p_body) const=0;
+
 	//missing remove
 	virtual void body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold)=0;
 	virtual float body_get_contacts_reported_depth_treshold(RID p_body) const=0;

+ 1 - 1
servers/visual/visual_server_raster.h

@@ -392,7 +392,7 @@ class VisualServerRaster : public VisualServer {
 
 		CanvasItem() {
 			E=NULL;
-			z=CANVAS_ITEM_Z_MAX/2;
+			z=0;
 			opacity=1;
 			self_opacity=1;
 			sort_y=false;

Some files were not shown because too many files changed in this diff