Sfoglia il codice sorgente

Merge pull request #52724 from groud/improve_tilemap_physics

Rémi Verschelde 4 anni fa
parent
commit
1ab8f3f559

+ 30 - 0
doc/classes/TileData.xml

@@ -37,6 +37,20 @@
 				Returns how many polygons the tile has for TileSet physics layer with index [code]layer_id[/code].
 			</description>
 		</method>
+		<method name="get_constant_angular_velocity" qualifiers="const">
+			<return type="float" />
+			<argument index="0" name="layer_id" type="int" />
+			<description>
+				Returns the constant angular velocity applied to objects colliding with this tile.
+			</description>
+		</method>
+		<method name="get_constant_linear_velocity" qualifiers="const">
+			<return type="Vector2" />
+			<argument index="0" name="layer_id" type="int" />
+			<description>
+				Returns the constant linear velocity applied to objects colliding with this tile.
+			</description>
+		</method>
 		<method name="get_custom_data" qualifiers="const">
 			<return type="Variant" />
 			<argument index="0" name="layer_name" type="String" />
@@ -123,6 +137,22 @@
 				Sets the polygons count for TileSet physics layer with index [code]layer_id[/code].
 			</description>
 		</method>
+		<method name="set_constant_angular_velocity">
+			<return type="void" />
+			<argument index="0" name="layer_id" type="int" />
+			<argument index="1" name="velocity" type="float" />
+			<description>
+				Sets the constant angular velocity. This does not rotate the tile. This angular velocity is applied to objects colliding with this tile.
+			</description>
+		</method>
+		<method name="set_constant_linear_velocity">
+			<return type="void" />
+			<argument index="0" name="layer_id" type="int" />
+			<argument index="1" name="velocity" type="Vector2" />
+			<description>
+				Sets the constant linear velocity. This does not move the tile. This linear velocity is applied to objects colliding with this tile. This is useful to create conveyor belts.
+			</description>
+		</method>
 		<method name="set_custom_data">
 			<return type="void" />
 			<argument index="0" name="layer_name" type="String" />

+ 18 - 0
doc/classes/TileMap.xml

@@ -29,6 +29,13 @@
 				Clears all cells.
 			</description>
 		</method>
+		<method name="clear_layer">
+			<return type="void" />
+			<argument index="0" name="layer" type="int" />
+			<description>
+				Clears all cells on the given layer.
+			</description>
+		</method>
 		<method name="fix_invalid_tiles">
 			<return type="void" />
 			<description>
@@ -62,6 +69,13 @@
 				Returns the tile source ID of the cell on layer [code]layer[/code] at coordinates [code]coords[/code]. If [code]use_proxies[/code] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw alternative identifier. See [method TileSet.map_tile_proxy].
 			</description>
 		</method>
+		<method name="get_coords_for_body_rid">
+			<return type="Vector2i" />
+			<argument index="0" name="body" type="RID" />
+			<description>
+				Returns the coodinates of the tile for given physics body RID. Such RID can be retrieved from [member KinematicCollision2D.collider_rid], when colliding with a tile.
+			</description>
+		</method>
 		<method name="get_layer_name" qualifiers="const">
 			<return type="String" />
 			<argument index="0" name="layer" type="int" />
@@ -220,6 +234,10 @@
 		<member name="cell_quadrant_size" type="int" setter="set_quadrant_size" getter="get_quadrant_size" default="16">
 			The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size.
 		</member>
+		<member name="collision_animatable" type="bool" setter="set_collision_animatable" getter="is_collision_animatable" default="false">
+			If enabled, the TileMap will see its collisions synced to the physics tick and change its collision type from static to kinematic. This is required to create TileMap-based moving platform.
+			[b]Note:[/b] Enabling [code]collision_animatable[/code] may have a small performance impact, only do it if the TileMap is moving and has colliding tiles.
+		</member>
 		<member name="collision_visibility_mode" type="int" setter="set_collision_visibility_mode" getter="get_collision_visibility_mode" enum="TileMap.VisibilityMode" default="0">
 			Show or hide the TileMap's collision shapes. If set to [code]VISIBILITY_MODE_DEFAULT[/code], this depends on the show collision debug settings.
 		</member>

+ 68 - 23
editor/plugins/tiles/tile_data_editors.cpp

@@ -231,10 +231,14 @@ void GenericTilePolygonEditor::_zoom_changed() {
 
 void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
 	switch (p_item_pressed) {
-		case RESET_TO_DEFAULT_TILE:
+		case RESET_TO_DEFAULT_TILE: {
 			undo_redo->create_action(TTR("Edit Polygons"));
 			undo_redo->add_do_method(this, "clear_polygons");
-			undo_redo->add_do_method(this, "add_polygon", tile_set->get_tile_shape_polygon());
+			Vector<Vector2> polygon = tile_set->get_tile_shape_polygon();
+			for (int i = 0; i < polygon.size(); i++) {
+				polygon.write[i] = polygon[i] * tile_set->get_tile_size();
+			}
+			undo_redo->add_do_method(this, "add_polygon", polygon);
 			undo_redo->add_do_method(base_control, "update");
 			undo_redo->add_do_method(this, "emit_signal", "polygons_changed");
 			undo_redo->add_undo_method(this, "clear_polygons");
@@ -244,8 +248,8 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
 			undo_redo->add_undo_method(base_control, "update");
 			undo_redo->add_undo_method(this, "emit_signal", "polygons_changed");
 			undo_redo->commit_action(true);
-			break;
-		case CLEAR_TILE:
+		} break;
+		case CLEAR_TILE: {
 			undo_redo->create_action(TTR("Edit Polygons"));
 			undo_redo->add_do_method(this, "clear_polygons");
 			undo_redo->add_do_method(base_control, "update");
@@ -257,7 +261,7 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
 			undo_redo->add_undo_method(base_control, "update");
 			undo_redo->add_undo_method(this, "emit_signal", "polygons_changed");
 			undo_redo->commit_action(true);
-			break;
+		} break;
 		default:
 			break;
 	}
@@ -308,6 +312,9 @@ void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_cur
 	ERR_FAIL_COND(!tile_set.is_valid());
 
 	Vector<Point2> polygon = tile_set->get_tile_shape_polygon();
+	for (int i = 0; i < polygon.size(); i++) {
+		polygon.write[i] = polygon[i] * tile_set->get_tile_size();
+	}
 	Point2 snapped_point = r_point;
 
 	// Snap to polygon vertices.
@@ -539,7 +546,11 @@ void GenericTilePolygonEditor::set_tile_set(Ref<TileSet> p_tile_set) {
 		// Set the default tile shape
 		clear_polygons();
 		if (p_tile_set.is_valid()) {
-			add_polygon(p_tile_set->get_tile_shape_polygon());
+			Vector<Vector2> polygon = p_tile_set->get_tile_shape_polygon();
+			for (int i = 0; i < polygon.size(); i++) {
+				polygon.write[i] = polygon[i] * p_tile_set->get_tile_size();
+			}
+			add_polygon(polygon);
 		}
 	}
 	tile_set = p_tile_set;
@@ -1265,17 +1276,21 @@ void TileDataCollisionEditor::_polygons_changed() {
 }
 
 Variant TileDataCollisionEditor::_get_painted_value() {
+	Dictionary dict;
+	dict["linear_velocity"] = dummy_object->get("linear_velocity");
+	dict["angular_velocity"] = dummy_object->get("angular_velocity");
 	Array array;
 	for (int i = 0; i < polygon_editor->get_polygon_count(); i++) {
 		ERR_FAIL_COND_V(polygon_editor->get_polygon(i).size() < 3, Variant());
-		Dictionary dict;
-		dict["points"] = polygon_editor->get_polygon(i);
-		dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i));
-		dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i));
-		array.push_back(dict);
+		Dictionary polygon_dict;
+		polygon_dict["points"] = polygon_editor->get_polygon(i);
+		polygon_dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i));
+		polygon_dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i));
+		array.push_back(polygon_dict);
 	}
+	dict["polygons"] = array;
 
-	return array;
+	return dict;
 }
 
 void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
@@ -1291,6 +1306,8 @@ void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_
 	}
 
 	_polygons_changed();
+	dummy_object->set("linear_velocity", tile_data->get_constant_linear_velocity(physics_layer));
+	dummy_object->set("angular_velocity", tile_data->get_constant_angular_velocity(physics_layer));
 	for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) {
 		dummy_object->set(vformat("polygon_%d_one_way", i), tile_data->is_collision_polygon_one_way(physics_layer, i));
 		dummy_object->set(vformat("polygon_%d_one_way_margin", i), tile_data->get_collision_polygon_one_way_margin(physics_layer, i));
@@ -1306,13 +1323,16 @@ void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_so
 	TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
 	ERR_FAIL_COND(!tile_data);
 
-	Array array = p_value;
+	Dictionary dict = p_value;
+	tile_data->set_constant_linear_velocity(physics_layer, dict["linear_velocity"]);
+	tile_data->set_constant_angular_velocity(physics_layer, dict["angular_velocity"]);
+	Array array = dict["polygons"];
 	tile_data->set_collision_polygons_count(physics_layer, array.size());
 	for (int i = 0; i < array.size(); i++) {
-		Dictionary dict = array[i];
-		tile_data->set_collision_polygon_points(physics_layer, i, dict["points"]);
-		tile_data->set_collision_polygon_one_way(physics_layer, i, dict["one_way"]);
-		tile_data->set_collision_polygon_one_way_margin(physics_layer, i, dict["one_way_margin"]);
+		Dictionary polygon_dict = array[i];
+		tile_data->set_collision_polygon_points(physics_layer, i, polygon_dict["points"]);
+		tile_data->set_collision_polygon_one_way(physics_layer, i, polygon_dict["one_way"]);
+		tile_data->set_collision_polygon_one_way_margin(physics_layer, i, polygon_dict["one_way_margin"]);
 	}
 
 	polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate());
@@ -1322,15 +1342,19 @@ Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas
 	TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
 	ERR_FAIL_COND_V(!tile_data, Variant());
 
+	Dictionary dict;
+	dict["linear_velocity"] = tile_data->get_constant_linear_velocity(physics_layer);
+	dict["angular_velocity"] = tile_data->get_constant_angular_velocity(physics_layer);
 	Array array;
 	for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) {
-		Dictionary dict;
-		dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i);
-		dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i);
-		dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i);
-		array.push_back(dict);
+		Dictionary polygon_dict;
+		polygon_dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i);
+		polygon_dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i);
+		polygon_dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i);
+		array.push_back(polygon_dict);
 	}
-	return array;
+	dict["polygons"] = array;
+	return dict;
 }
 
 void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
@@ -1378,6 +1402,27 @@ TileDataCollisionEditor::TileDataCollisionEditor() {
 	polygon_editor->connect("polygons_changed", callable_mp(this, &TileDataCollisionEditor::_polygons_changed));
 	add_child(polygon_editor);
 
+	dummy_object->add_dummy_property("linear_velocity");
+	dummy_object->set("linear_velocity", Vector2());
+	dummy_object->add_dummy_property("angular_velocity");
+	dummy_object->set("angular_velocity", 0.0);
+
+	EditorProperty *linear_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::VECTOR2, "linear_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+	linear_velocity_editor->set_object_and_property(dummy_object, "linear_velocity");
+	linear_velocity_editor->set_label("linear_velocity");
+	linear_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
+	linear_velocity_editor->update_property();
+	add_child(linear_velocity_editor);
+	property_editors["linear_velocity"] = linear_velocity_editor;
+
+	EditorProperty *angular_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::FLOAT, "angular_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+	angular_velocity_editor->set_object_and_property(dummy_object, "angular_velocity");
+	angular_velocity_editor->set_label("angular_velocity");
+	angular_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
+	angular_velocity_editor->update_property();
+	add_child(angular_velocity_editor);
+	property_editors["angular_velocity"] = linear_velocity_editor;
+
 	_polygons_changed();
 }
 

+ 150 - 116
scene/2d/tile_map.cpp

@@ -452,6 +452,19 @@ int TileMap::get_layer_z_index(int p_layer) const {
 	return layers[p_layer].z_index;
 }
 
+void TileMap::set_collision_animatable(bool p_enabled) {
+	collision_animatable = p_enabled;
+	_clear_internals();
+	set_notify_local_transform(p_enabled);
+	set_physics_process_internal(p_enabled);
+	_recreate_internals();
+	emit_signal(SNAME("changed"));
+}
+
+bool TileMap::is_collision_animatable() const {
+	return collision_animatable;
+}
+
 void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) {
 	collision_visibility_mode = p_show_collision;
 	_clear_internals();
@@ -508,7 +521,6 @@ Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer,
 	// Call the create_quadrant method on plugins
 	if (tile_set.is_valid()) {
 		_rendering_create_quadrant(&q);
-		_physics_create_quadrant(&q);
 	}
 
 	return layers[p_layer].quadrant_map.insert(p_qk, q);
@@ -1092,24 +1104,67 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
 
 void TileMap::_physics_notification(int p_what) {
 	switch (p_what) {
+		case CanvasItem::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+			bool in_editor = false;
+#ifdef TOOLS_ENABLED
+			in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+			if (is_inside_tree() && collision_animatable && !in_editor) {
+				// Update tranform on the physics tick when in animatable mode.
+				last_valid_transform = new_transform;
+				set_notify_local_transform(false);
+				set_global_transform(new_transform);
+				set_notify_local_transform(true);
+			}
+		} break;
 		case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
-			// Update the bodies transforms.
-			if (is_inside_tree()) {
+			bool in_editor = false;
+#ifdef TOOLS_ENABLED
+			in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+			if (is_inside_tree() && (!collision_animatable || in_editor)) {
+				// Update the new transform directly if we are not in animatable mode.
+				Transform2D global_transform = get_global_transform();
 				for (int layer = 0; layer < (int)layers.size(); layer++) {
-					Transform2D global_transform = get_global_transform();
+					for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+						TileMapQuadrant &q = E->get();
 
+						for (RID body : q.bodies) {
+							Transform2D xform;
+							xform.set_origin(map_to_world(bodies_coords[body]));
+							xform = global_transform * xform;
+							PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+						}
+					}
+				}
+			}
+		} break;
+		case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+			bool in_editor = false;
+#ifdef TOOLS_ENABLED
+			in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+			if (is_inside_tree() && !in_editor && collision_animatable) {
+				// Only active when animatable. Send the new transform to the physics...
+				new_transform = get_global_transform();
+				for (int layer = 0; layer < (int)layers.size(); layer++) {
 					for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
 						TileMapQuadrant &q = E->get();
 
-						Transform2D xform;
-						xform.set_origin(map_to_world(E->key() * get_effective_quadrant_size(layer)));
-						xform = global_transform * xform;
+						for (RID body : q.bodies) {
+							Transform2D xform;
+							xform.set_origin(map_to_world(bodies_coords[body]));
+							xform = new_transform * xform;
 
-						for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
-							PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+							PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
 						}
 					}
 				}
+
+				// ... but then revert changes.
+				set_notify_local_transform(false);
+				set_global_transform(last_valid_transform);
+				set_notify_local_transform(true);
 			}
 		} break;
 	}
@@ -1120,29 +1175,23 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
 	ERR_FAIL_COND(!tile_set.is_valid());
 
 	Transform2D global_transform = get_global_transform();
+	last_valid_transform = global_transform;
+	new_transform = global_transform;
 	PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
+	RID space = get_world_2d()->get_space();
 
 	SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
 	while (q_list_element) {
 		TileMapQuadrant &q = *q_list_element->self();
 
-		Vector2 quadrant_pos = map_to_world(q.coords * get_effective_quadrant_size(q.layer));
-
-		LocalVector<int> body_shape_count;
-		body_shape_count.resize(q.bodies.size());
-
-		// Clear shapes.
-		for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
-			ps->body_clear_shapes(q.bodies[body_index]);
-			body_shape_count[body_index] = 0;
-
-			// Position the bodies.
-			Transform2D xform;
-			xform.set_origin(quadrant_pos);
-			xform = global_transform * xform;
-			ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+		// Clear bodies.
+		for (RID body : q.bodies) {
+			bodies_coords.erase(body);
+			ps->free(body);
 		}
+		q.bodies.clear();
 
+		// Recreate bodies and shapes.
 		for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
 			TileMapCell c = get_cell(q.layer, E_cell->get(), true);
 
@@ -1158,26 +1207,53 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
 				if (atlas_source) {
 					TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
 
-					for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
-						int &body_shape_index = body_shape_count[body_index];
+					for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) {
+						Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer);
+						uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer);
+						uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer);
 
-						// Add the shapes again.
-						for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) {
-							bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index);
-							float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index);
+						// Create the body.
+						RID body = ps->body_create();
+						bodies_coords[body] = E_cell->get();
+						ps->body_set_mode(body, collision_animatable ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC);
+						ps->body_set_space(body, space);
 
-							int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index);
-							for (int shape_index = 0; shape_index < shapes_count; shape_index++) {
-								Transform2D xform = Transform2D();
-								xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
+						Transform2D xform;
+						xform.set_origin(map_to_world(E_cell->get()));
+						xform = global_transform * xform;
+						ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+
+						ps->body_attach_object_instance_id(body, get_instance_id());
+						ps->body_set_collision_layer(body, physics_layer);
+						ps->body_set_collision_mask(body, physics_mask);
+						ps->body_set_pickable(body, false);
+						ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer));
+						ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer));
+
+						if (!physics_material.is_valid()) {
+							ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
+							ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
+						} else {
+							ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce());
+							ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction());
+						}
 
+						q.bodies.push_back(body);
+
+						// Add the shapes to the body.
+						int body_shape_index = 0;
+						for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(tile_set_physics_layer); polygon_index++) {
+							// Iterate over the polygons.
+							bool one_way_collision = tile_data->is_collision_polygon_one_way(tile_set_physics_layer, polygon_index);
+							float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(tile_set_physics_layer, polygon_index);
+							int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index);
+							for (int shape_index = 0; shape_index < shapes_count; shape_index++) {
 								// Add decomposed convex shapes.
-								Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index);
-								ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform);
-								ps->body_set_shape_metadata(q.bodies[body_index], body_shape_index, E_cell->get());
-								ps->body_set_shape_as_one_way_collision(q.bodies[body_index], body_shape_index, one_way_collision, one_way_collision_margin);
+								Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index);
+								ps->body_add_shape(body, shape->get_rid());
+								ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin);
 
-								++body_shape_index;
+								body_shape_index++;
 							}
 						}
 					}
@@ -1189,54 +1265,11 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
 	}
 }
 
-void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) {
-	ERR_FAIL_COND(!tile_set.is_valid());
-
-	//Get the TileMap's gobla transform.
-	Transform2D global_transform;
-	if (is_inside_tree()) {
-		global_transform = get_global_transform();
-	}
-
-	// Clear all bodies.
-	p_quadrant->bodies.clear();
-
-	// Create the body and set its parameters.
-	for (int layer = 0; layer < tile_set->get_physics_layers_count(); layer++) {
-		RID body = PhysicsServer2D::get_singleton()->body_create();
-		PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC);
-
-		PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, get_instance_id());
-		PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer));
-		PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer));
-
-		Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer);
-		if (!physics_material.is_valid()) {
-			PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
-			PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
-		} else {
-			PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce());
-			PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction());
-		}
-
-		if (is_inside_tree()) {
-			RID space = get_world_2d()->get_space();
-			PhysicsServer2D::get_singleton()->body_set_space(body, space);
-
-			Transform2D xform;
-			xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)));
-			xform = global_transform * xform;
-			PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
-		}
-
-		p_quadrant->bodies.push_back(body);
-	}
-}
-
 void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
 	// Remove a quadrant.
-	for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
-		PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]);
+	for (RID body : p_quadrant->bodies) {
+		bodies_coords.erase(body);
+		PhysicsServer2D::get_singleton()->free(body);
 	}
 	p_quadrant->bodies.clear();
 }
@@ -1252,7 +1285,7 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
 	bool show_collision = false;
 	switch (collision_visibility_mode) {
 		case TileMap::VISIBILITY_MODE_DEFAULT:
-			show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint());
+			show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_collisions_hint());
 			break;
 		case TileMap::VISIBILITY_MODE_FORCE_HIDE:
 			show_collision = false;
@@ -1266,39 +1299,28 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
 	}
 
 	RenderingServer *rs = RenderingServer::get_singleton();
-
-	Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+	PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
 
 	Color debug_collision_color = get_tree()->get_debug_collisions_color();
-	for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
-		TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true);
-
-		Transform2D xform;
-		xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
-		rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+	Vector<Color> color;
+	color.push_back(debug_collision_color);
 
-		if (tile_set->has_source(c.source_id)) {
-			TileSetSource *source = *tile_set->get_source(c.source_id);
-
-			if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
-				continue;
-			}
-
-			TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
-			if (atlas_source) {
-				TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+	Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+	Transform2D qudrant_xform;
+	qudrant_xform.set_origin(quadrant_pos);
+	Transform2D global_transform_inv = (get_global_transform() * qudrant_xform).affine_inverse();
 
-				for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
-					for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) {
-						// Draw the debug polygon.
-						Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index);
-						if (polygon.size() >= 3) {
-							Vector<Color> color;
-							color.push_back(debug_collision_color);
-							rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color);
-						}
-					}
-				}
+	for (RID body : p_quadrant->bodies) {
+		Transform2D xform = Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)) * global_transform_inv;
+		rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+		for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) {
+			const RID &shape = ps->body_get_shape(body, shape_index);
+			PhysicsServer2D::ShapeType type = ps->shape_get_type(shape);
+			if (type == PhysicsServer2D::SHAPE_CONVEX_POLYGON) {
+				Vector<Vector2> polygon = ps->shape_get_data(shape);
+				rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color);
+			} else {
+				WARN_PRINT("Wrong shape type for a tile, should be SHAPE_CONVEX_POLYGON.");
 			}
 		}
 		rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D());
@@ -1858,6 +1880,11 @@ Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) {
 	return &layers[p_layer].quadrant_map;
 }
 
+Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) {
+	ERR_FAIL_COND_V_MSG(!bodies_coords.has(p_physics_body), Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body));
+	return bodies_coords[p_physics_body];
+}
+
 void TileMap::fix_invalid_tiles() {
 	ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
 
@@ -3007,6 +3034,8 @@ void TileMap::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index);
 	ClassDB::bind_method(D_METHOD("get_layer_z_index", "layer"), &TileMap::get_layer_z_index);
 
+	ClassDB::bind_method(D_METHOD("set_collision_animatable", "enabled"), &TileMap::set_collision_animatable);
+	ClassDB::bind_method(D_METHOD("is_collision_animatable"), &TileMap::is_collision_animatable);
 	ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode);
 	ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode);
 
@@ -3018,10 +3047,14 @@ void TileMap::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords);
 	ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile);
 
+	ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid);
+
 	ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
-	ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles);
+	ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer);
 	ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear);
 
+	ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles);
+
 	ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells);
 	ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect);
 
@@ -3037,6 +3070,7 @@ void TileMap::_bind_methods() {
 
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
 

+ 12 - 1
scene/2d/tile_map.h

@@ -202,6 +202,7 @@ private:
 	// Properties.
 	Ref<TileSet> tile_set;
 	int quadrant_size = 16;
+	bool collision_animatable = false;
 	VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
 	VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
 
@@ -229,6 +230,9 @@ private:
 	LocalVector<TileMapLayer> layers;
 	int selected_layer = -1;
 
+	// Mapping for RID to coords.
+	Map<RID, Vector2i> bodies_coords;
+
 	// Quadrants and internals management.
 	Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const;
 
@@ -259,9 +263,10 @@ private:
 	void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant);
 	void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
 
+	Transform2D last_valid_transform;
+	Transform2D new_transform;
 	void _physics_notification(int p_what);
 	void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
-	void _physics_create_quadrant(TileMapQuadrant *p_quadrant);
 	void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant);
 	void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
 
@@ -325,6 +330,9 @@ public:
 	void set_selected_layer(int p_layer_id); // For editor use.
 	int get_selected_layer() const;
 
+	void set_collision_animatable(bool p_enabled);
+	bool is_collision_animatable() const;
+
 	void set_collision_visibility_mode(VisibilityMode p_show_collision);
 	VisibilityMode get_collision_visibility_mode();
 
@@ -364,6 +372,9 @@ public:
 	virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
 	virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
 
+	// For finding tiles from collision.
+	Vector2i get_coords_for_body_rid(RID p_physics_body);
+
 	// Fixing a nclearing methods.
 	void fix_invalid_tiles();
 

+ 59 - 15
scene/resources/tile_set.cpp

@@ -3124,7 +3124,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
 }
 
 bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) {
-	Vector<String> components = String(p_name).split("/", true, 3);
+	Vector<String> components = String(p_name).split("/", true, 2);
 
 	// Compute the vector2i if we have coordinates.
 	Vector<String> coords_split = components[0].split(":");
@@ -4316,9 +4316,26 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const {
 }
 
 // Physics
-int TileData::get_collision_polygons_count(int p_layer_id) const {
-	ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
-	return physics[p_layer_id].polygons.size();
+void TileData::set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity) {
+	ERR_FAIL_INDEX(p_layer_id, physics.size());
+	physics.write[p_layer_id].linear_velocity = p_velocity;
+	emit_signal(SNAME("changed"));
+}
+
+Vector2 TileData::get_constant_linear_velocity(int p_layer_id) const {
+	ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector2());
+	return physics[p_layer_id].linear_velocity;
+}
+
+void TileData::set_constant_angular_velocity(int p_layer_id, real_t p_velocity) {
+	ERR_FAIL_INDEX(p_layer_id, physics.size());
+	physics.write[p_layer_id].angular_velocity = p_velocity;
+	emit_signal(SNAME("changed"));
+}
+
+real_t TileData::get_constant_angular_velocity(int p_layer_id) const {
+	ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0);
+	return physics[p_layer_id].angular_velocity;
 }
 
 void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) {
@@ -4329,6 +4346,11 @@ void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count
 	emit_signal(SNAME("changed"));
 }
 
+int TileData::get_collision_polygons_count(int p_layer_id) const {
+	ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+	return physics[p_layer_id].polygons.size();
+}
+
 void TileData::add_collision_polygon(int p_layer_id) {
 	ERR_FAIL_INDEX(p_layer_id, physics.size());
 	physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData());
@@ -4530,11 +4552,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
 		// Physics layers.
 		int layer_index = components[0].trim_prefix("physics_layer_").to_int();
 		ERR_FAIL_COND_V(layer_index < 0, false);
-		if (components.size() == 2 && components[1] == "polygons_count") {
-			if (p_value.get_type() != Variant::INT) {
-				return false;
-			}
-
+		if (components.size() == 2) {
 			if (layer_index >= physics.size()) {
 				if (tile_set) {
 					return false;
@@ -4542,8 +4560,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
 					physics.resize(layer_index + 1);
 				}
 			}
-			set_collision_polygons_count(layer_index, p_value);
-			return true;
+			if (components[1] == "linear_velocity") {
+				set_constant_linear_velocity(layer_index, p_value);
+				return true;
+			} else if (components[1] == "angular_velocity") {
+				set_constant_angular_velocity(layer_index, p_value);
+				return true;
+			} else if (components[1] == "polygons_count") {
+				if (p_value.get_type() != Variant::INT) {
+					return false;
+				}
+				set_collision_polygons_count(layer_index, p_value);
+				return true;
+			}
 		} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
 			int polygon_index = components[1].trim_prefix("polygon_").to_int();
 			ERR_FAIL_COND_V(polygon_index < 0, false);
@@ -4645,9 +4674,18 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const {
 			if (layer_index >= physics.size()) {
 				return false;
 			}
-			if (components.size() == 2 && components[1] == "polygons_count") {
-				r_ret = get_collision_polygons_count(layer_index);
-				return true;
+
+			if (components.size() == 2) {
+				if (components[1] == "linear_velocity") {
+					r_ret = get_constant_linear_velocity(layer_index);
+					return true;
+				} else if (components[1] == "angular_velocity") {
+					r_ret = get_constant_angular_velocity(layer_index);
+					return true;
+				} else if (components[1] == "polygons_count") {
+					r_ret = get_collision_polygons_count(layer_index);
+					return true;
+				}
 			} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
 				int polygon_index = components[1].trim_prefix("polygon_").to_int();
 				ERR_FAIL_COND_V(polygon_index < 0, false);
@@ -4718,6 +4756,8 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
 		// Physics layers.
 		p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
 		for (int i = 0; i < physics.size(); i++) {
+			p_list->push_back(PropertyInfo(Variant::VECTOR2, vformat("physics_layer_%d/linear_velocity", i), PROPERTY_HINT_NONE));
+			p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/angular_velocity", i), PROPERTY_HINT_NONE));
 			p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
 
 			for (int j = 0; j < physics[i].polygons.size(); j++) {
@@ -4807,8 +4847,12 @@ void TileData::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder);
 
 	// Physics.
-	ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count);
+	ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity);
+	ClassDB::bind_method(D_METHOD("get_constant_linear_velocity", "layer_id"), &TileData::get_constant_linear_velocity);
+	ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "layer_id", "velocity"), &TileData::set_constant_angular_velocity);
+	ClassDB::bind_method(D_METHOD("get_constant_angular_velocity", "layer_id"), &TileData::get_constant_angular_velocity);
 	ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count);
+	ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count);
 	ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon);
 	ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon);
 	ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points);

+ 7 - 1
scene/resources/tile_set.h

@@ -645,6 +645,8 @@ private:
 			float one_way_margin = 1.0;
 		};
 
+		Vector2 linear_velocity;
+		float angular_velocity;
 		Vector<PolygonShapeTileData> polygons;
 	};
 	Vector<PhysicsLayerTileData> physics;
@@ -718,8 +720,12 @@ public:
 	Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const;
 
 	// Physics
-	int get_collision_polygons_count(int p_layer_id) const;
+	void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);
+	Vector2 get_constant_linear_velocity(int p_layer_id) const;
+	void set_constant_angular_velocity(int p_layer_id, real_t p_velocity);
+	real_t get_constant_angular_velocity(int p_layer_id) const;
 	void set_collision_polygons_count(int p_layer_id, int p_shapes_count);
+	int get_collision_polygons_count(int p_layer_id) const;
 	void add_collision_polygon(int p_layer_id);
 	void remove_collision_polygon(int p_layer_id, int p_polygon_index);
 	void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon);