-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
This commit is contained in:
Juan Linietsky 2015-01-13 21:19:11 -03:00
parent a327eee762
commit 9012cd408e
14 changed files with 827 additions and 43 deletions

Binary file not shown.

View file

@ -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

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -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;

View file

@ -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();
};

View file

@ -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;

View file

@ -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();

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;