Tilemap fix displaced textures and shapes and added center texture and compatibility mode

This commit fix #22989 #15249 #28206. Main problem is that tilemap displace textures in different tile origins in a strange way and doesn´t respect coincidence between texture and shapes in not uniform tiles. This issue is present in godot 3.0 and godot 3.1. To maintain compatibility are added a compatibility mode and a center texture option. Other related issues and pull request: #28896 #29487 #29519 #29961. Idications of #30204 are added
This commit is contained in:
Ranoller 2019-07-01 22:43:52 +02:00
parent d40dbd6d11
commit 14e2a99129
4 changed files with 207 additions and 22 deletions

View file

@ -274,6 +274,15 @@
<member name="cell_y_sort" type="bool" setter="set_y_sort_mode" getter="is_y_sort_mode_enabled" default="false">
If [code]true[/code], the TileMap's children will be drawn in order of their Y coordinate.
</member>
<member name="compatibility_mode" type="bool" setter="set_compatibility_mode" getter="is_compatibility_mode_enabled">
If [code]true[/code], the compatibility with the tilemaps made in Godot 3.1 or earlier is maintained (textures move when the tile origin changes and rotate if the texture size is not homogeneous). This mode presents problems when doing [code]flip_h[/code], [code]flip_v[/code] and [code]transpose[/code] tile operations on non-homogeneous isometric tiles (e.g. 2:1), in which the texture could not coincide with the collision, thus it is not recommended for isometric or non-square tiles.
If [code]false[/code], the textures do not move when doing [code]flip_h[/code], [code]flip_v[/code] operations if no offset is used, nor when changing the tile origin.
The compatibility mode doesn't work with the [member centered_textures] option, because displacing textures with the [member cell_tile_origin] option or in irregular tiles is not relevant when centering those textures.
</member>
<member name="centered_textures" type="bool" setter="set_centered_textures" getter="is_centered_textures_enabled">
If [code]true[/code], the textures will be centered in the middle of each tile. This is useful for certain isometric or top-down modes when textures are made larger or smaller than the tiles (e.g. to avoid flickering on tile edges). The offset is still applied, but from the center of the tile. If used, [member compatibility_mode] is ignored.
If [code]false[/code], the texture position start in the top-left corner unless [member compatibility_mode] is enabled.
</member>
<member name="collision_bounce" type="float" setter="set_collision_bounce" getter="get_collision_bounce" default="0.0">
Bounce value for static body collisions (see [code]collision_use_kinematic[/code]).
</member>

View file

@ -782,8 +782,9 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p
r.position += (r.size + Vector2(spacing, spacing)) * offset;
}
Size2 sc = p_xform.get_scale();
/* For a future CheckBox to Center Texture:
Size2 cell_size = node->get_cell_size(); */
Size2 cell_size = node->get_cell_size();
bool centered_texture = node->is_centered_textures_enabled();
bool compatibility_mode_enabled = node->is_compatibility_mode_enabled();
Rect2 rect = Rect2();
rect.position = node->map_to_world(p_point) + node->get_cell_draw_offset();
@ -793,13 +794,24 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p
rect.size = r.size;
}
if (compatibility_mode_enabled && !centered_texture) {
if (rect.size.y > rect.size.x) {
if ((p_flip_h && (p_flip_v || p_transpose)) || (p_flip_v && !p_transpose))
tile_ofs.y += rect.size.y - rect.size.x;
} else if (rect.size.y < rect.size.x) {
if ((p_flip_v && (p_flip_h || p_transpose)) || (p_flip_h && !p_transpose))
tile_ofs.x += rect.size.x - rect.size.y;
}
}
if (p_transpose) {
SWAP(tile_ofs.x, tile_ofs.y);
/* For a future CheckBox to Center Texture:
rect.position.x += cell_size.x / 2 - rect.size.y / 2;
rect.position.y += cell_size.y / 2 - rect.size.x / 2;
} else {
rect.position += cell_size / 2 - rect.size / 2; */
if (centered_texture) {
rect.position.x += cell_size.x / 2 - rect.size.y / 2;
rect.position.y += cell_size.y / 2 - rect.size.x / 2;
}
} else if (centered_texture) {
rect.position += cell_size / 2 - rect.size / 2;
}
if (p_flip_h) {
@ -812,7 +824,44 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p
tile_ofs.y *= -1.0;
}
rect.position += tile_ofs;
if (compatibility_mode_enabled && !centered_texture) {
if (node->get_tile_origin() == TileMap::TILE_ORIGIN_TOP_LEFT) {
rect.position += tile_ofs;
} else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_BOTTOM_LEFT) {
rect.position += tile_ofs;
if (p_transpose) {
if (p_flip_h)
rect.position.x -= cell_size.x;
else
rect.position.x += cell_size.x;
} else {
if (p_flip_v)
rect.position.y -= cell_size.y;
else
rect.position.y += cell_size.y;
}
} else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_CENTER) {
rect.position += tile_ofs;
if (p_flip_h)
rect.position.x -= cell_size.x / 2;
else
rect.position.x += cell_size.x / 2;
if (p_flip_v)
rect.position.y -= cell_size.y / 2;
else
rect.position.y += cell_size.y / 2;
}
} else {
rect.position += tile_ofs;
}
rect.position = p_xform.xform(rect.position);
rect.size *= sc;

View file

@ -234,6 +234,25 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
Size2 s = p_sc;
Vector2 offset = p_offset;
if (compatibility_mode && !centered_textures) {
if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
offset.y += cell_size.y;
} else if (tile_origin == TILE_ORIGIN_CENTER) {
offset += cell_size / 2;
}
if (s.y > s.x) {
if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose)) {
offset.y += s.y - s.x;
}
} else if (s.y < s.x) {
if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose)) {
offset.x += s.x - s.y;
}
}
}
if (p_cell.transpose) {
SWAP(xform.elements[0].x, xform.elements[0].y);
SWAP(xform.elements[1].x, xform.elements[1].y);
@ -244,16 +263,36 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
if (p_cell.flip_h) {
xform.elements[0].x = -xform.elements[0].x;
xform.elements[1].x = -xform.elements[1].x;
offset.x = s.x - offset.x;
if (compatibility_mode && !centered_textures) {
if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
offset.x = s.x - offset.x;
} else if (tile_origin == TILE_ORIGIN_CENTER) {
offset.x = s.x - offset.x / 2;
}
} else {
offset.x = s.x - offset.x;
}
}
if (p_cell.flip_v) {
xform.elements[0].y = -xform.elements[0].y;
xform.elements[1].y = -xform.elements[1].y;
offset.y = s.y - offset.y;
if (compatibility_mode && !centered_textures) {
if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
offset.y = s.y - offset.y;
} else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
offset.y += s.y;
} else if (tile_origin == TILE_ORIGIN_CENTER) {
offset.y += s.y;
}
} else {
offset.y = s.y - offset.y;
}
}
if (centered_textures) {
offset += cell_size / 2 - s / 2;
}
/* For a future CheckBox to Center Texture:
offset += cell_size / 2 - s / 2; */
xform.elements[2] += offset;
}
@ -434,13 +473,24 @@ void TileMap::update_dirty_quadrants() {
rect.size.x += fp_adjust;
rect.size.y += fp_adjust;
if (compatibility_mode && !centered_textures) {
if (rect.size.y > rect.size.x) {
if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose))
tile_ofs.y += rect.size.y - rect.size.x;
} else if (rect.size.y < rect.size.x) {
if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose))
tile_ofs.x += rect.size.x - rect.size.y;
}
}
if (c.transpose) {
SWAP(tile_ofs.x, tile_ofs.y);
/* For a future CheckBox to Center Texture:
rect.position.x += cell_size.x / 2 - rect.size.y / 2;
rect.position.y += cell_size.y / 2 - rect.size.x / 2;
} else {
rect.position += cell_size / 2 - rect.size / 2; */
if (centered_textures) {
rect.position.x += cell_size.x / 2 - rect.size.y / 2;
rect.position.y += cell_size.y / 2 - rect.size.x / 2;
}
} else if (centered_textures) {
rect.position += cell_size / 2 - rect.size / 2;
}
if (c.flip_h) {
@ -453,7 +503,43 @@ void TileMap::update_dirty_quadrants() {
tile_ofs.y = -tile_ofs.y;
}
rect.position += tile_ofs;
if (compatibility_mode && !centered_textures) {
if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
rect.position += tile_ofs;
} else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
rect.position += tile_ofs;
if (c.transpose) {
if (c.flip_h)
rect.position.x -= cell_size.x;
else
rect.position.x += cell_size.x;
} else {
if (c.flip_v)
rect.position.y -= cell_size.y;
else
rect.position.y += cell_size.y;
}
} else if (tile_origin == TILE_ORIGIN_CENTER) {
rect.position += tile_ofs;
if (c.flip_h)
rect.position.x -= cell_size.x / 2;
else
rect.position.x += cell_size.x / 2;
if (c.flip_v)
rect.position.y -= cell_size.y / 2;
else
rect.position.y += cell_size.y / 2;
}
} else {
rect.position += tile_ofs;
}
Ref<Texture> normal_map = tile_set->tile_get_normal_map(c.id);
Color modulate = tile_set->tile_get_modulate(c.id);
@ -1156,10 +1242,7 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
coord_x = decode_uint16(&local[8]);
coord_y = decode_uint16(&local[10]);
}
/*
if (x<-20 || y <-20 || x>4000 || y>4000)
continue;
*/
set_cell(x, y, v, flip_h, flip_v, transpose, Vector2(coord_x, coord_y));
}
@ -1570,6 +1653,32 @@ bool TileMap::is_y_sort_mode_enabled() const {
return y_sort_mode;
}
void TileMap::set_compatibility_mode(bool p_enable) {
_clear_quadrants();
compatibility_mode = p_enable;
_recreate_quadrants();
emit_signal("settings_changed");
}
bool TileMap::is_compatibility_mode_enabled() const {
return compatibility_mode;
}
void TileMap::set_centered_textures(bool p_enable) {
_clear_quadrants();
centered_textures = p_enable;
_recreate_quadrants();
emit_signal("settings_changed");
}
bool TileMap::is_centered_textures_enabled() const {
return centered_textures;
}
Array TileMap::get_used_cells() const {
Array a;
@ -1707,6 +1816,12 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_y_sort_mode", "enable"), &TileMap::set_y_sort_mode);
ClassDB::bind_method(D_METHOD("is_y_sort_mode_enabled"), &TileMap::is_y_sort_mode_enabled);
ClassDB::bind_method(D_METHOD("set_compatibility_mode", "enable"), &TileMap::set_compatibility_mode);
ClassDB::bind_method(D_METHOD("is_compatibility_mode_enabled"), &TileMap::is_compatibility_mode_enabled);
ClassDB::bind_method(D_METHOD("set_centered_textures", "enable"), &TileMap::set_centered_textures);
ClassDB::bind_method(D_METHOD("is_centered_textures_enabled"), &TileMap::is_centered_textures_enabled);
ClassDB::bind_method(D_METHOD("set_collision_use_kinematic", "use_kinematic"), &TileMap::set_collision_use_kinematic);
ClassDB::bind_method(D_METHOD("get_collision_use_kinematic"), &TileMap::get_collision_use_kinematic);
@ -1775,6 +1890,8 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_half_offset", PROPERTY_HINT_ENUM, "Offset X,Offset Y,Disabled,Offset Negative X,Offset Negative Y"), "set_half_offset", "get_half_offset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_tile_origin", PROPERTY_HINT_ENUM, "Top Left,Center,Bottom Left"), "set_tile_origin", "get_tile_origin");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_y_sort"), "set_y_sort_mode", "is_y_sort_mode_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "compatibility_mode"), "set_compatibility_mode", "is_compatibility_mode_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered_textures"), "set_centered_textures", "is_centered_textures_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_clip_uv"), "set_clip_uv", "get_clip_uv");
ADD_GROUP("Collision", "collision_");
@ -1832,6 +1949,8 @@ TileMap::TileMap() {
use_kinematic = false;
navigation = NULL;
y_sort_mode = false;
compatibility_mode = false;
centered_textures = false;
occluder_light_mask = 1;
clip_uv = false;
format = FORMAT_1; //Always initialize with the lowest format

View file

@ -181,6 +181,8 @@ private:
bool used_size_cache_dirty;
bool quadrant_order_dirty;
bool y_sort_mode;
bool compatibility_mode;
bool centered_textures;
bool clip_uv;
float fp_adjust;
float friction;
@ -311,6 +313,12 @@ public:
void set_y_sort_mode(bool p_enable);
bool is_y_sort_mode_enabled() const;
void set_compatibility_mode(bool p_enable);
bool is_compatibility_mode_enabled() const;
void set_centered_textures(bool p_enable);
bool is_centered_textures_enabled() const;
Array get_used_cells() const;
Array get_used_cells_by_id(int p_id) const;
Rect2 get_used_rect(); // Not const because of cache