-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.
This commit is contained in:
Juan Linietsky 2015-04-19 20:50:55 -03:00
parent 1de1a04b78
commit 28c4afeb57
11 changed files with 692 additions and 132 deletions

View file

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

Binary file not shown.

View file

@ -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 {
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;
}
return true;
}
Vector2 KinematicBody2D::move(const Vector2& p_motion) {
#if 1
Physics2DServer::MotionResult result;
colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,margin,&result);
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;
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) {
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++) {
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 {
bool KinematicBody2D::test_move(const Vector2& p_motion) {
ERR_FAIL_COND_V(!is_inside_tree(),false);
return colliding;
return Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,margin);
}
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 {
bool KinematicBody2D::is_colliding() 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 {
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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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