Browse Source

Area2D can now detect overlap with other areas

this should make everything simpler, specially for newcomers to Godot
Juan Linietsky 10 năm trước cách đây
mục cha
commit
a969e2e6f1

+ 217 - 18
scene/2d/area_2d.cpp

@@ -216,6 +216,119 @@ void Area2D::_body_inout(int p_status,const RID& p_body, int p_instance, int p_b
 }
 
 
+
+void Area2D::_area_enter_tree(ObjectID p_id) {
+
+	Object *obj = ObjectDB::get_instance(p_id);
+	Node *node = obj ? obj->cast_to<Node>() : NULL;
+	ERR_FAIL_COND(!node);
+
+	Map<ObjectID,AreaState>::Element *E=area_map.find(p_id);
+	ERR_FAIL_COND(!E);
+	ERR_FAIL_COND(E->get().in_tree);
+
+	E->get().in_tree=true;
+	emit_signal(SceneStringNames::get_singleton()->area_enter,node);
+	for(int i=0;i<E->get().shapes.size();i++) {
+
+		emit_signal(SceneStringNames::get_singleton()->area_enter_shape,p_id,node,E->get().shapes[i].area_shape,E->get().shapes[i].self_shape);
+	}
+
+}
+
+void Area2D::_area_exit_tree(ObjectID p_id) {
+
+	Object *obj = ObjectDB::get_instance(p_id);
+	Node *node = obj ? obj->cast_to<Node>() : NULL;
+	ERR_FAIL_COND(!node);
+	Map<ObjectID,AreaState>::Element *E=area_map.find(p_id);
+	ERR_FAIL_COND(!E);
+	ERR_FAIL_COND(!E->get().in_tree);
+	E->get().in_tree=false;
+	emit_signal(SceneStringNames::get_singleton()->area_exit,node);
+	for(int i=0;i<E->get().shapes.size();i++) {
+
+		emit_signal(SceneStringNames::get_singleton()->area_exit_shape,p_id,node,E->get().shapes[i].area_shape,E->get().shapes[i].self_shape);
+	}
+
+}
+
+void Area2D::_area_inout(int p_status,const RID& p_area, int p_instance, int p_area_shape,int p_self_shape) {
+
+	bool area_in = p_status==Physics2DServer::AREA_BODY_ADDED;
+	ObjectID objid=p_instance;
+
+	Object *obj = ObjectDB::get_instance(objid);
+	Node *node = obj ? obj->cast_to<Node>() : NULL;
+
+	Map<ObjectID,AreaState>::Element *E=area_map.find(objid);
+
+	ERR_FAIL_COND(!area_in && !E);
+
+	locked=true;
+
+	if (area_in) {
+		if (!E) {
+
+			E = area_map.insert(objid,AreaState());
+			E->get().rc=0;
+			E->get().in_tree=node && node->is_inside_tree();
+			if (node) {
+				node->connect(SceneStringNames::get_singleton()->enter_tree,this,SceneStringNames::get_singleton()->_area_enter_tree,make_binds(objid));
+				node->connect(SceneStringNames::get_singleton()->exit_tree,this,SceneStringNames::get_singleton()->_area_exit_tree,make_binds(objid));
+				if (E->get().in_tree) {
+					emit_signal(SceneStringNames::get_singleton()->area_enter,node);
+				}
+			}
+
+		}
+		E->get().rc++;
+		if (node)
+			E->get().shapes.insert(AreaShapePair(p_area_shape,p_self_shape));
+
+
+		if (!node || E->get().in_tree) {
+			emit_signal(SceneStringNames::get_singleton()->area_enter_shape,objid,node,p_area_shape,p_self_shape);
+		}
+
+	} else {
+
+		E->get().rc--;
+
+		if (node)
+			E->get().shapes.erase(AreaShapePair(p_area_shape,p_self_shape));
+
+		bool eraseit=false;
+
+		if (E->get().rc==0) {
+
+			if (node) {
+				node->disconnect(SceneStringNames::get_singleton()->enter_tree,this,SceneStringNames::get_singleton()->_area_enter_tree);
+				node->disconnect(SceneStringNames::get_singleton()->exit_tree,this,SceneStringNames::get_singleton()->_area_exit_tree);
+				if (E->get().in_tree)
+					emit_signal(SceneStringNames::get_singleton()->area_exit,obj);
+
+			}
+
+			eraseit=true;
+
+		}
+		if (!node || E->get().in_tree) {
+			emit_signal(SceneStringNames::get_singleton()->area_exit_shape,objid,obj,p_area_shape,p_self_shape);
+		}
+
+		if (eraseit)
+			area_map.erase(E);
+
+	}
+
+	locked=false;
+
+
+}
+
+
+
 void Area2D::_clear_monitoring() {
 
 	if (locked) {
@@ -223,27 +336,56 @@ void Area2D::_clear_monitoring() {
 	}
 	ERR_FAIL_COND(locked);
 
-	Map<ObjectID,BodyState> bmcopy = body_map;
-	body_map.clear();
-	//disconnect all monitored stuff
+	{
+		Map<ObjectID,BodyState> bmcopy = body_map;
+		body_map.clear();
+		//disconnect all monitored stuff
 
-	for (Map<ObjectID,BodyState>::Element *E=bmcopy.front();E;E=E->next()) {
+		for (Map<ObjectID,BodyState>::Element *E=bmcopy.front();E;E=E->next()) {
 
-		Object *obj = ObjectDB::get_instance(E->key());
-		Node *node = obj ? obj->cast_to<Node>() : NULL;
-		ERR_CONTINUE(!node);
-		if (!E->get().in_tree)
-			continue;
+			Object *obj = ObjectDB::get_instance(E->key());
+			Node *node = obj ? obj->cast_to<Node>() : NULL;
+			ERR_CONTINUE(!node);
+			if (!E->get().in_tree)
+				continue;
 
-		for(int i=0;i<E->get().shapes.size();i++) {
+			for(int i=0;i<E->get().shapes.size();i++) {
 
-			emit_signal(SceneStringNames::get_singleton()->body_exit_shape,E->key(),node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape);
+				emit_signal(SceneStringNames::get_singleton()->body_exit_shape,E->key(),node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape);
+			}
+
+			emit_signal(SceneStringNames::get_singleton()->body_exit,obj);
+
+			node->disconnect(SceneStringNames::get_singleton()->enter_tree,this,SceneStringNames::get_singleton()->_body_enter_tree);
+			node->disconnect(SceneStringNames::get_singleton()->exit_tree,this,SceneStringNames::get_singleton()->_body_exit_tree);
 		}
 
-		emit_signal(SceneStringNames::get_singleton()->body_exit,obj);
+	}
+
+	{
 
-		node->disconnect(SceneStringNames::get_singleton()->enter_tree,this,SceneStringNames::get_singleton()->_body_enter_tree);
-		node->disconnect(SceneStringNames::get_singleton()->exit_tree,this,SceneStringNames::get_singleton()->_body_exit_tree);
+		Map<ObjectID,AreaState> bmcopy = area_map;
+		area_map.clear();
+		//disconnect all monitored stuff
+
+		for (Map<ObjectID,AreaState>::Element *E=bmcopy.front();E;E=E->next()) {
+
+			Object *obj = ObjectDB::get_instance(E->key());
+			Node *node = obj ? obj->cast_to<Node>() : NULL;
+			ERR_CONTINUE(!node);
+			if (!E->get().in_tree)
+				continue;
+
+			for(int i=0;i<E->get().shapes.size();i++) {
+
+				emit_signal(SceneStringNames::get_singleton()->area_exit_shape,E->key(),node,E->get().shapes[i].area_shape,E->get().shapes[i].self_shape);
+			}
+
+			emit_signal(SceneStringNames::get_singleton()->area_exit,obj);
+
+			node->disconnect(SceneStringNames::get_singleton()->enter_tree,this,SceneStringNames::get_singleton()->_area_enter_tree);
+			node->disconnect(SceneStringNames::get_singleton()->exit_tree,this,SceneStringNames::get_singleton()->_area_exit_tree);
+		}
 	}
 
 }
@@ -276,8 +418,10 @@ void Area2D::set_enable_monitoring(bool p_enable) {
 	if (monitoring) {
 
 		Physics2DServer::get_singleton()->area_set_monitor_callback(get_rid(),this,"_body_inout");
+		Physics2DServer::get_singleton()->area_set_area_monitor_callback(get_rid(),this,"_area_inout");
 	} else {
 		Physics2DServer::get_singleton()->area_set_monitor_callback(get_rid(),NULL,StringName());
+		Physics2DServer::get_singleton()->area_set_area_monitor_callback(get_rid(),NULL,StringName());
 		_clear_monitoring();
 
 	}
@@ -288,6 +432,26 @@ bool Area2D::is_monitoring_enabled() const {
 	return monitoring;
 }
 
+void Area2D::set_monitorable(bool p_enable) {
+
+	if (locked) {
+		ERR_EXPLAIN("This function can't be used during the in/out signal.");
+	}
+	ERR_FAIL_COND(locked);
+
+	if (p_enable==monitorable)
+		return;
+
+	monitorable=p_enable;
+
+	Physics2DServer::get_singleton()->area_set_monitorable(get_rid(),monitorable);
+}
+
+bool Area2D::is_monitorable() const {
+
+	return monitorable;
+}
+
 Array Area2D::get_overlapping_bodies() const {
 
 	ERR_FAIL_COND_V(!monitoring,Array());
@@ -307,12 +471,33 @@ Array Area2D::get_overlapping_bodies() const {
 	return ret;
 }
 
+Array Area2D::get_overlapping_areas() const {
+
+	ERR_FAIL_COND_V(!monitoring,Array());
+	Array ret;
+	ret.resize(area_map.size());
+	int idx=0;
+	for (const Map<ObjectID,AreaState>::Element *E=area_map.front();E;E=E->next()) {
+		Object *obj = ObjectDB::get_instance(E->key());
+		if (!obj) {
+			ret.resize( ret.size() -1 ); //ops
+		} else {
+			ret[idx++]=obj;
+		}
+
+	}
+
+	return ret;
+}
 
 void Area2D::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("_body_enter_tree","id"),&Area2D::_body_enter_tree);
 	ObjectTypeDB::bind_method(_MD("_body_exit_tree","id"),&Area2D::_body_exit_tree);
 
+	ObjectTypeDB::bind_method(_MD("_area_enter_tree","id"),&Area2D::_area_enter_tree);
+	ObjectTypeDB::bind_method(_MD("_area_exit_tree","id"),&Area2D::_area_exit_tree);
+
 	ObjectTypeDB::bind_method(_MD("set_space_override_mode","enable"),&Area2D::set_space_override_mode);
 	ObjectTypeDB::bind_method(_MD("get_space_override_mode"),&Area2D::get_space_override_mode);
 
@@ -337,15 +522,26 @@ void Area2D::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area2D::set_enable_monitoring);
 	ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area2D::is_monitoring_enabled);
 
+	ObjectTypeDB::bind_method(_MD("set_monitorable","enable"),&Area2D::set_monitorable);
+	ObjectTypeDB::bind_method(_MD("is_monitorable"),&Area2D::is_monitorable);
+
 	ObjectTypeDB::bind_method(_MD("get_overlapping_bodies"),&Area2D::get_overlapping_bodies);
+	ObjectTypeDB::bind_method(_MD("get_overlapping_areas"),&Area2D::get_overlapping_areas);
 
 	ObjectTypeDB::bind_method(_MD("_body_inout"),&Area2D::_body_inout);
+	ObjectTypeDB::bind_method(_MD("_area_inout"),&Area2D::_area_inout);
+
+
+	ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body",PROPERTY_HINT_RESOURCE_TYPE,"PhysicsBody2D"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape")));
+	ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body",PROPERTY_HINT_RESOURCE_TYPE,"PhysicsBody2D"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape")));
+	ADD_SIGNAL( MethodInfo("body_enter:",PropertyInfo(Variant::OBJECT,"body",PROPERTY_HINT_RESOURCE_TYPE,"PhysicsBody2D")));
+	ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body",PROPERTY_HINT_RESOURCE_TYPE,"PhysicsBody2D")));
 
+	ADD_SIGNAL( MethodInfo("area_enter_shape",PropertyInfo(Variant::INT,"area_id"),PropertyInfo(Variant::OBJECT,"area",PROPERTY_HINT_RESOURCE_TYPE,"Area2D"),PropertyInfo(Variant::INT,"area_shape"),PropertyInfo(Variant::INT,"area_shape")));
+	ADD_SIGNAL( MethodInfo("area_exit_shape",PropertyInfo(Variant::INT,"area_id"),PropertyInfo(Variant::OBJECT,"area",PROPERTY_HINT_RESOURCE_TYPE,"Area2D"),PropertyInfo(Variant::INT,"area_shape"),PropertyInfo(Variant::INT,"area_shape")));
+	ADD_SIGNAL( MethodInfo("area_enter",PropertyInfo(Variant::OBJECT,"area",PROPERTY_HINT_RESOURCE_TYPE,"Area2D")));
+	ADD_SIGNAL( MethodInfo("area_exit",PropertyInfo(Variant::OBJECT,"area",PROPERTY_HINT_RESOURCE_TYPE,"Area2D")));
 
-	ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape")));
-	ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape")));
-	ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body")));
-	ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body")));
 
 	ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"space_override",PROPERTY_HINT_ENUM,"Disabled,Combine,Replace"),_SCS("set_space_override_mode"),_SCS("get_space_override_mode"));
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"gravity_point"),_SCS("set_gravity_is_point"),_SCS("is_gravity_a_point"));
@@ -355,6 +551,7 @@ void Area2D::_bind_methods() {
 	ADD_PROPERTY( PropertyInfo(Variant::REAL,"angular_damp",PROPERTY_HINT_RANGE,"0,1024,0.001"),_SCS("set_angular_damp"),_SCS("get_angular_damp"));
 	ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"priority",PROPERTY_HINT_RANGE,"0,128,1"),_SCS("set_priority"),_SCS("get_priority"));
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"monitoring"),_SCS("set_enable_monitoring"),_SCS("is_monitoring_enabled"));
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"monitorable"),_SCS("set_monitorable"),_SCS("is_monitorable"));
 
 }
 
@@ -369,7 +566,9 @@ Area2D::Area2D() : CollisionObject2D(Physics2DServer::get_singleton()->area_crea
 	locked=false;
 	priority=0;
 	monitoring=false;
+	monitorable=false;
 	set_enable_monitoring(true);
+	set_monitorable(true);
 
 }
 

+ 35 - 0
scene/2d/area_2d.h

@@ -53,6 +53,7 @@ private:
 	real_t angular_damp;
 	int priority;
 	bool monitoring;
+	bool monitorable;
 	bool locked;
 
 	void _body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape);
@@ -84,6 +85,36 @@ private:
 
 	Map<ObjectID,BodyState> body_map;
 
+
+
+	void _area_inout(int p_status,const RID& p_area, int p_instance, int p_area_shape,int p_self_shape);
+
+	void _area_enter_tree(ObjectID p_id);
+	void _area_exit_tree(ObjectID p_id);
+
+	struct AreaShapePair {
+
+		int area_shape;
+		int self_shape;
+		bool operator<(const AreaShapePair& p_sp) const {
+			if (area_shape==p_sp.area_shape)
+				return self_shape < p_sp.self_shape;
+			else
+				return area_shape < p_sp.area_shape;
+		}
+
+		AreaShapePair() {}
+		AreaShapePair(int p_bs, int p_as) { area_shape=p_bs; self_shape=p_as; }
+	};
+
+	struct AreaState {
+
+		int rc;
+		bool in_tree;
+		VSet<AreaShapePair> shapes;
+	};
+
+	Map<ObjectID,AreaState> area_map;
 	void _clear_monitoring();
 
 
@@ -117,7 +148,11 @@ public:
 	void set_enable_monitoring(bool p_enable);
 	bool is_monitoring_enabled() const;
 
+	void set_monitorable(bool p_enable);
+	bool is_monitorable() const;
+
 	Array get_overlapping_bodies() const; //function for script
+	Array get_overlapping_areas() const; //function for script
 
 
 	Area2D();

+ 6 - 0
scene/scene_string_names.cpp

@@ -64,6 +64,9 @@ SceneStringNames::SceneStringNames() {
 	body_exit_shape = StaticCString::create("body_exit_shape");
 	body_exit = StaticCString::create("body_exit");
 
+	area_enter_shape = StaticCString::create("area_enter_shape");
+	area_exit_shape = StaticCString::create("area_exit_shape");
+
 
 	idle=StaticCString::create("idle");
 	iteration=StaticCString::create("iteration");
@@ -103,6 +106,9 @@ SceneStringNames::SceneStringNames() {
 	_body_enter_tree = StaticCString::create("_body_enter_tree");
 	_body_exit_tree = StaticCString::create("_body_exit_tree");
 
+	_area_enter_tree = StaticCString::create("_area_enter_tree");
+	_area_exit_tree = StaticCString::create("_area_exit_tree");
+
 	_input_event=StaticCString::create("_input_event");
 
 	changed=StaticCString::create("changed");

+ 7 - 0
scene/scene_string_names.h

@@ -83,6 +83,10 @@ public:
 	StringName body_exit_shape;
 	StringName body_exit;
 
+	StringName area_enter_shape;
+	StringName area_exit_shape;
+
+
 
 	StringName _get_gizmo_geometry;
 	StringName _can_gizmo_scale;
@@ -124,6 +128,9 @@ public:
 	StringName _body_enter_tree;
 	StringName _body_exit_tree;
 
+	StringName _area_enter_tree;
+	StringName _area_exit_tree;
+
 	StringName changed;
 	StringName _shader_changed;
 

+ 70 - 1
servers/physics_2d/area_2d_sw.cpp

@@ -31,6 +31,7 @@
 #include "body_2d_sw.h"
 
 Area2DSW::BodyKey::BodyKey(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) { rid=p_body->get_self(); instance_id=p_body->get_instance_id(); body_shape=p_body_shape; area_shape=p_area_shape; }
+Area2DSW::BodyKey::BodyKey(Area2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape) { rid=p_body->get_self(); instance_id=p_body->get_instance_id(); body_shape=p_body_shape; area_shape=p_area_shape; }
 
 void Area2DSW::_shapes_changed() {
 
@@ -57,6 +58,7 @@ void Area2DSW::set_space(Space2DSW *p_space) {
 	}
 
 	monitored_bodies.clear();
+	monitored_areas.clear();
 
 	_set_space(p_space);
 }
@@ -76,11 +78,31 @@ void Area2DSW::set_monitor_callback(ObjectID p_id, const StringName& p_method) {
 	monitor_callback_method=p_method;
 
 	monitored_bodies.clear();
+	monitored_areas.clear();
 
 	_shape_changed();
 
 }
 
+void Area2DSW::set_area_monitor_callback(ObjectID p_id, const StringName& p_method) {
+
+
+	if (p_id==area_monitor_callback_id) {
+		area_monitor_callback_method=p_method;
+		return;
+	}
+
+	_unregister_shapes();
+
+	area_monitor_callback_id=p_id;
+	area_monitor_callback_method=p_method;
+
+	monitored_bodies.clear();
+	monitored_areas.clear();
+
+	_shape_changed();
+
+}
 
 
 void Area2DSW::set_space_override_mode(Physics2DServer::AreaSpaceOverrideMode p_mode) {
@@ -134,6 +156,15 @@ void Area2DSW::_queue_monitor_update() {
 
 }
 
+void Area2DSW::set_monitorable(bool p_monitorable) {
+
+	if (monitorable==p_monitorable)
+		return;
+
+	monitorable=p_monitorable;
+	_set_static(!monitorable);
+}
+
 void Area2DSW::call_queries() {
 
 	if (monitor_callback_id && !monitored_bodies.empty()) {
@@ -170,13 +201,49 @@ void Area2DSW::call_queries() {
 
 	monitored_bodies.clear();
 
+	if (area_monitor_callback_id && !monitored_areas.empty()) {
+
+
+		Variant res[5];
+		Variant *resptr[5];
+		for(int i=0;i<5;i++)
+			resptr[i]=&res[i];
+
+		Object *obj = ObjectDB::get_instance(area_monitor_callback_id);
+		if (!obj) {
+			monitored_areas.clear();
+			area_monitor_callback_id=0;
+			return;
+		}
+
+
+
+		for (Map<BodyKey,BodyState>::Element *E=monitored_areas.front();E;E=E->next()) {
+
+			if (E->get().state==0)
+				continue; //nothing happened
+
+			res[0]=E->get().state>0 ? Physics2DServer::AREA_BODY_ADDED : Physics2DServer::AREA_BODY_REMOVED;
+			res[1]=E->key().rid;
+			res[2]=E->key().instance_id;
+			res[3]=E->key().body_shape;
+			res[4]=E->key().area_shape;
+
+
+			Variant::CallError ce;
+			obj->call(area_monitor_callback_method,(const Variant**)resptr,5,ce);
+		}
+	}
+
+	monitored_areas.clear();
+
 	//get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
 
 }
 
 Area2DSW::Area2DSW() : CollisionObject2DSW(TYPE_AREA), monitor_query_list(this), moved_list(this)  {
 
-	_set_static(true); //areas are never active
+	_set_static(true); //areas are not active by default
 	space_override_mode=Physics2DServer::AREA_SPACE_OVERRIDE_DISABLED;
 	gravity=9.80665;
 	gravity_vector=Vector2(0,-1);
@@ -187,6 +254,8 @@ Area2DSW::Area2DSW() : CollisionObject2DSW(TYPE_AREA), monitor_query_list(this),
 	linear_damp=0.1;
 	priority=0;
 	monitor_callback_id=0;
+	area_monitor_callback_id=0;
+	monitorable=false;
 
 
 }

+ 34 - 0
servers/physics_2d/area_2d_sw.h

@@ -50,10 +50,14 @@ class Area2DSW : public CollisionObject2DSW{
 	float linear_damp;
 	float angular_damp;
 	int priority;
+	bool monitorable;
 
 	ObjectID monitor_callback_id;
 	StringName monitor_callback_method;
 
+	ObjectID area_monitor_callback_id;
+	StringName area_monitor_callback_method;
+
 	SelfList<Area2DSW> monitor_query_list;
 	SelfList<Area2DSW> moved_list;
 
@@ -80,6 +84,7 @@ class Area2DSW : public CollisionObject2DSW{
 
 		_FORCE_INLINE_ BodyKey() {}
 		BodyKey(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
+		BodyKey(Area2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
 	};
 
 	struct BodyState {
@@ -91,6 +96,7 @@ class Area2DSW : public CollisionObject2DSW{
 	};
 
 	Map<BodyKey,BodyState> monitored_bodies;
+	Map<BodyKey,BodyState> monitored_areas;
 
 	//virtual void shape_changed_notify(Shape2DSW *p_shape);
 	//virtual void shape_deleted_notify(Shape2DSW *p_shape);
@@ -108,9 +114,16 @@ public:
 	void set_monitor_callback(ObjectID p_id, const StringName& p_method);
 	_FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback_id; }
 
+	void set_area_monitor_callback(ObjectID p_id, const StringName& p_method);
+	_FORCE_INLINE_ bool has_area_monitor_callback() const { return area_monitor_callback_id; }
+
+
 	_FORCE_INLINE_ void add_body_to_query(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
 	_FORCE_INLINE_ void remove_body_from_query(Body2DSW *p_body, uint32_t p_body_shape,uint32_t p_area_shape);
 
+	_FORCE_INLINE_ void add_area_to_query(Area2DSW *p_area, uint32_t p_area_shape,uint32_t p_self_shape);
+	_FORCE_INLINE_ void remove_area_from_query(Area2DSW *p_area, uint32_t p_area_shape,uint32_t p_self_shape);
+
 	void set_param(Physics2DServer::AreaParameter p_param, const Variant& p_value);
 	Variant get_param(Physics2DServer::AreaParameter p_param) const;
 
@@ -142,6 +155,9 @@ public:
 	_FORCE_INLINE_ void remove_constraint( Constraint2DSW* p_constraint) { constraints.erase(p_constraint); }
 	_FORCE_INLINE_ const Set<Constraint2DSW*>& get_constraints() const { return constraints; }
 
+	void set_monitorable(bool p_monitorable);
+	_FORCE_INLINE_ bool is_monitorable() const { return monitorable; }
+
 	void set_transform(const Matrix32& p_transform);
 
 	void set_space(Space2DSW *p_space);
@@ -168,6 +184,24 @@ void Area2DSW::remove_body_from_query(Body2DSW *p_body, uint32_t p_body_shape,ui
 		_queue_monitor_update();
 }
 
+void Area2DSW::add_area_to_query(Area2DSW *p_area, uint32_t p_area_shape,uint32_t p_self_shape) {
+
+
+	BodyKey bk(p_area,p_area_shape,p_self_shape);
+	monitored_areas[bk].inc();
+	if (!monitor_query_list.in_list())
+		_queue_monitor_update();
+
+
+}
+void Area2DSW::remove_area_from_query(Area2DSW *p_area, uint32_t p_area_shape,uint32_t p_self_shape) {
+
+
+	BodyKey bk(p_area,p_area_shape,p_self_shape);
+	monitored_areas[bk].dec();
+	if (!monitor_query_list.in_list())
+		_queue_monitor_update();
+}
 
 
 

+ 69 - 0
servers/physics_2d/area_pair_2d_sw.cpp

@@ -94,3 +94,72 @@ AreaPair2DSW::~AreaPair2DSW() {
 	body->remove_constraint(this);
 	area->remove_constraint(this);
 }
+
+
+//////////////////////////////////
+
+
+
+bool Area2Pair2DSW::setup(float p_step) {
+
+	bool result = CollisionSolver2DSW::solve(area_a->get_shape(shape_a),area_a->get_transform() * area_a->get_shape_transform(shape_a),Vector2(),area_b->get_shape(shape_b),area_b->get_transform() * area_b->get_shape_transform(shape_b),Vector2(),NULL,this);
+
+	if (result!=colliding) {
+
+		if (result) {
+
+			if (area_b->has_area_monitor_callback() && area_a->is_monitorable())
+				area_b->add_area_to_query(area_a,shape_a,shape_b);
+
+			if (area_a->has_area_monitor_callback() && area_b->is_monitorable())
+				area_a->add_area_to_query(area_b,shape_b,shape_a);
+
+		} else {
+
+			if (area_b->has_area_monitor_callback() && area_a->is_monitorable())
+				area_b->remove_area_from_query(area_a,shape_a,shape_b);
+
+			if (area_a->has_area_monitor_callback() && area_b->is_monitorable())
+				area_a->remove_area_from_query(area_b,shape_b,shape_a);
+		}
+
+		colliding=result;
+
+	}
+
+	return false; //never do any post solving
+}
+
+void Area2Pair2DSW::solve(float p_step) {
+
+
+}
+
+
+Area2Pair2DSW::Area2Pair2DSW(Area2DSW *p_area_a,int p_shape_a, Area2DSW *p_area_b,int p_shape_b) {
+
+
+	area_a=p_area_a;
+	area_b=p_area_b;
+	shape_a=p_shape_a;
+	shape_b=p_shape_b;
+	colliding=false;
+	area_a->add_constraint(this);
+	area_b->add_constraint(this);
+
+}
+
+Area2Pair2DSW::~Area2Pair2DSW() {
+
+	if (colliding) {
+
+		if (area_b->has_area_monitor_callback() && area_a->is_monitorable())
+			area_b->remove_area_from_query(area_a,shape_a,shape_b);
+
+		if (area_a->has_area_monitor_callback() && area_b->is_monitorable())
+			area_a->remove_area_from_query(area_b,shape_b,shape_a);
+	}
+
+	area_a->remove_constraint(this);
+	area_b->remove_constraint(this);
+}

+ 18 - 0
servers/physics_2d/area_pair_2d_sw.h

@@ -49,5 +49,23 @@ public:
 	~AreaPair2DSW();
 };
 
+
+class Area2Pair2DSW : public Constraint2DSW {
+
+	Area2DSW *area_a;
+	Area2DSW *area_b;
+	int shape_a;
+	int shape_b;
+	bool colliding;
+public:
+
+	bool setup(float p_step);
+	void solve(float p_step);
+
+	Area2Pair2DSW(Area2DSW *p_area_a,int p_shape_a, Area2DSW *p_area_b,int p_shape_b);
+	~Area2Pair2DSW();
+};
+
+
 #endif // AREA_PAIR_2D_SW_H
 

+ 18 - 0
servers/physics_2d/physics_2d_server_sw.cpp

@@ -463,6 +463,16 @@ Matrix32 Physics2DServerSW::area_get_transform(RID p_area) const {
 	return area->get_transform();
 };
 
+void Physics2DServerSW::area_set_monitorable(RID p_area,bool p_monitorable) {
+
+	Area2DSW *area = area_owner.get(p_area);
+	ERR_FAIL_COND(!area);
+
+	area->set_monitorable(p_monitorable);
+
+}
+
+
 void Physics2DServerSW::area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method) {
 
 	Area2DSW *area = area_owner.get(p_area);
@@ -473,6 +483,14 @@ void Physics2DServerSW::area_set_monitor_callback(RID p_area,Object *p_receiver,
 
 }
 
+void Physics2DServerSW::area_set_area_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method) {
+
+
+	Area2DSW *area = area_owner.get(p_area);
+	ERR_FAIL_COND(!area);
+
+	area->set_area_monitor_callback(p_receiver?p_receiver->get_instance_ID():0,p_method);
+}
 
 /* BODY API */
 

+ 2 - 1
servers/physics_2d/physics_2d_server_sw.h

@@ -133,9 +133,10 @@ public:
 
 	virtual Variant area_get_param(RID p_parea,AreaParameter p_param) const;
 	virtual Matrix32 area_get_transform(RID p_area) const;
+	virtual void area_set_monitorable(RID p_area,bool p_monitorable);
 
 	virtual void area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method);
-
+	virtual void area_set_area_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method);
 
 	/* BODY API */
 

+ 11 - 4
servers/physics_2d/space_2d_sw.cpp

@@ -527,13 +527,20 @@ void* Space2DSW::_broadphase_pair(CollisionObject2DSW *A,int p_subindex_A,Collis
 
 	if (type_A==CollisionObject2DSW::TYPE_AREA) {
 
-		ERR_FAIL_COND_V(type_B!=CollisionObject2DSW::TYPE_BODY,NULL);
 		Area2DSW *area=static_cast<Area2DSW*>(A);
-		Body2DSW *body=static_cast<Body2DSW*>(B);
+		if (type_B==CollisionObject2DSW::TYPE_AREA) {
+
+			Area2DSW *area_b=static_cast<Area2DSW*>(B);
+			Area2Pair2DSW *area2_pair = memnew(Area2Pair2DSW(area_b,p_subindex_B,area,p_subindex_A) );
+			return area2_pair;
+		} else {
+
+			Body2DSW *body=static_cast<Body2DSW*>(B);
+			AreaPair2DSW *area_pair = memnew(AreaPair2DSW(body,p_subindex_B,area,p_subindex_A) );
+			return area_pair;
+		}
 
-		AreaPair2DSW *area_pair = memnew(AreaPair2DSW(body,p_subindex_B,area,p_subindex_A) );
 
-		return area_pair;
 	} else {
 
 

+ 3 - 0
servers/physics_2d_server.h

@@ -341,7 +341,10 @@ public:
 	virtual Variant area_get_param(RID p_parea,AreaParameter p_param) const=0;
 	virtual Matrix32 area_get_transform(RID p_area) const=0;
 
+	virtual void area_set_monitorable(RID p_area,bool p_monitorable)=0;
+
 	virtual void area_set_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method)=0;
+	virtual void area_set_area_monitor_callback(RID p_area,Object *p_receiver,const StringName& p_method)=0;
 
 	/* BODY API */
 

+ 10 - 1
tools/editor/connections_dialog.cpp

@@ -607,6 +607,14 @@ void ConnectionsDialog::_remove_confirm() {
 
 }
 */
+
+struct _ConnectionsDialogMethodInfoSort {
+
+	_FORCE_INLINE_ bool operator()(const MethodInfo& a, const MethodInfo& b) const {
+		return a.name < b.name;
+	}
+};
+
 void ConnectionsDialog::update_tree() {
 	
 	if (!is_visible())
@@ -623,7 +631,8 @@ void ConnectionsDialog::update_tree() {
 
 	node->get_signal_list(&node_signals);
 
-	
+	//node_signals.sort_custom<_ConnectionsDialogMethodInfoSort>();
+
 	for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) {