From c283db2130073ab4f22533313e0350325267e0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gilles=20Roudi=C3=A8re?= Date: Wed, 5 Jun 2024 12:03:24 +0200 Subject: [PATCH] Implement X-draw-order switch in TileMapLayer --- doc/classes/TileMap.xml | 4 +-- doc/classes/TileMapLayer.xml | 7 ++++-- scene/2d/tile_map_layer.cpp | 48 ++++++++++++++++++++++++++++++------ scene/2d/tile_map_layer.h | 9 +++++-- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 687c7194cdba..3f70810a7f69 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -486,8 +486,8 @@ Show or hide the TileMap's navigation meshes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings. - The TileMap's quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quandrant size groups together [code]16 * 16 = 256[/code] tiles. - The quadrant size does not apply on Y-sorted layers, as tiles are be grouped by Y position instead in that case. + The TileMap's quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quadrant size groups together [code]16 * 16 = 256[/code] tiles. + The quadrant size does not apply on Y-sorted layers, as tiles are grouped by Y position instead in that case. [b]Note:[/b] As quadrants are created according to the map's coordinate system, the quadrant's "square shape" might not look like square in the TileMap's local coordinate system. diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml index 1bff6d911be1..b9acef209529 100644 --- a/doc/classes/TileMapLayer.xml +++ b/doc/classes/TileMapLayer.xml @@ -264,8 +264,8 @@ Show or hide the [TileMapLayer]'s navigation meshes. If set to [constant DEBUG_VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings. - The [TileMapLayer]'s quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quandrant size groups together [code]16 * 16 = 256[/code] tiles. - The quadrant size does not apply on a Y-sorted [TileMapLayer], as tiles are be grouped by Y position instead in that case. + The [TileMapLayer]'s quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quadrant size groups together [code]16 * 16 = 256[/code] tiles. + The quadrant size does not apply on a Y-sorted [TileMapLayer], as tiles are grouped by Y position instead in that case. [b]Note:[/b] As quadrants are created according to the map's coordinate system, the quadrant's "square shape" might not look like square in the [TileMapLayer]'s local coordinate system. @@ -277,6 +277,9 @@ If [code]true[/code], this [TileMapLayer] collision shapes will be instantiated as kinematic bodies. This can be needed for moving [TileMapLayer] nodes (i.e. moving platforms). + + If [member CanvasItem.y_sort_enabled] is enabled, setting this to [code]true[/code] will reverse the order the tiles are drawn on the X-axis. + This Y-sort origin value is added to each tile's Y-sort origin value. This allows, for example, to fake a different height level. This can be useful for top-down view games. diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 0ac236eaa710..cdd7b1511033 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -205,11 +205,12 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) { // Check if anything changed that might change the quadrant shape. // If so, recreate everything. - bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] || - (is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_TILE_SET])); + bool quadrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_SET] || + (is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM])) || + (!is_y_sort_enabled() && dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE]); // Free all quadrants. - if (forced_cleanup || quandrant_shape_changed) { + if (forced_cleanup || quadrant_shape_changed) { for (const KeyValue> &kv : rendering_quadrant_map) { for (const RID &ci : kv.value->canvas_items) { if (ci.is_valid()) { @@ -264,9 +265,8 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) { rendering_quadrant->canvas_items.clear(); // Sort the quadrant cells. - if (is_y_sort_enabled()) { - // For compatibility reasons, we use another comparator for Y-sorted layers. - rendering_quadrant->cells.sort_custom(); + if (is_y_sort_enabled() && x_draw_order_reversed) { + rendering_quadrant->cells.sort_custom(); } else { rendering_quadrant->cells.sort(); } @@ -1770,6 +1770,8 @@ void TileMapLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileMapLayer::set_y_sort_origin); ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileMapLayer::get_y_sort_origin); + ClassDB::bind_method(D_METHOD("set_x_draw_order_reversed", "x_draw_order_reversed"), &TileMapLayer::set_x_draw_order_reversed); + ClassDB::bind_method(D_METHOD("is_x_draw_order_reversed"), &TileMapLayer::is_x_draw_order_reversed); ClassDB::bind_method(D_METHOD("set_rendering_quadrant_size", "size"), &TileMapLayer::set_rendering_quadrant_size); ClassDB::bind_method(D_METHOD("get_rendering_quadrant_size"), &TileMapLayer::get_rendering_quadrant_size); @@ -1796,6 +1798,7 @@ void TileMapLayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tile_set", "get_tile_set"); ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "x_draw_order_reversed"), "set_x_draw_order_reversed", "is_x_draw_order_reversed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rendering_quadrant_size"), "set_rendering_quadrant_size", "get_rendering_quadrant_size"); ADD_GROUP("Physics", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled"); @@ -1814,6 +1817,18 @@ void TileMapLayer::_bind_methods() { BIND_ENUM_CONSTANT(DEBUG_VISIBILITY_MODE_FORCE_SHOW); } +void TileMapLayer::_validate_property(PropertyInfo &p_property) const { + if (is_y_sort_enabled()) { + if (p_property.name == "rendering_quadrant_size") { + p_property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } else { + if (p_property.name == "x_draw_order_reversed") { + p_property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } +} + void TileMapLayer::_update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter) { // Set a default texture filter for the whole tilemap. CanvasItem::_update_self_texture_filter(p_texture_filter); @@ -2772,6 +2787,7 @@ void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) { _queue_internal_update(); emit_signal(CoreStringName(changed)); + notify_property_list_changed(); _update_notify_local_transform(); } @@ -2789,6 +2805,20 @@ int TileMapLayer::get_y_sort_origin() const { return y_sort_origin; } +void TileMapLayer::set_x_draw_order_reversed(bool p_x_draw_order_reversed) { + if (x_draw_order_reversed == p_x_draw_order_reversed) { + return; + } + x_draw_order_reversed = p_x_draw_order_reversed; + dirty.flags[DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED] = true; + _queue_internal_update(); + emit_signal(CoreStringName(changed)); +} + +bool TileMapLayer::is_x_draw_order_reversed() const { + return x_draw_order_reversed; +} + void TileMapLayer::set_z_index(int p_z_index) { if (get_z_index() == p_z_index) { return; @@ -2813,10 +2843,9 @@ void TileMapLayer::set_rendering_quadrant_size(int p_size) { if (rendering_quadrant_size == p_size) { return; } - dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] = true; ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); - rendering_quadrant_size = p_size; + dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] = true; _queue_internal_update(); emit_signal(CoreStringName(changed)); } @@ -2840,6 +2869,9 @@ bool TileMapLayer::is_collision_enabled() const { } void TileMapLayer::set_use_kinematic_bodies(bool p_use_kinematic_bodies) { + if (use_kinematic_bodies == p_use_kinematic_bodies) { + return; + } use_kinematic_bodies = p_use_kinematic_bodies; dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] = p_use_kinematic_bodies; _queue_internal_update(); diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 57c83d7c4c15..19c6fc128bfa 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -160,8 +160,8 @@ struct CellData { } }; -// For compatibility reasons, we use another comparator for Y-sorted layers. -struct CellDataYSortedComparator { +// We use another comparator for Y-sorted layers with reversed X drawing order. +struct CellDataYSortedXReversedComparator { _FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const { return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x); } @@ -245,6 +245,7 @@ class TileMapLayer : public Node2D { DIRTY_FLAGS_LAYER_SELF_MODULATE, DIRTY_FLAGS_LAYER_Y_SORT_ENABLED, DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN, + DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED, DIRTY_FLAGS_LAYER_Z_INDEX, DIRTY_FLAGS_LAYER_LIGHT_MASK, DIRTY_FLAGS_LAYER_TEXTURE_FILTER, @@ -280,6 +281,7 @@ class TileMapLayer : public Node2D { HighlightMode highlight_mode = HIGHLIGHT_MODE_DEFAULT; int y_sort_origin = 0; + bool x_draw_order_reversed = false; int rendering_quadrant_size = 16; bool collision_enabled = true; @@ -383,6 +385,7 @@ class TileMapLayer : public Node2D { void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter) override; virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat) override; @@ -470,6 +473,8 @@ class TileMapLayer : public Node2D { virtual void set_y_sort_enabled(bool p_y_sort_enabled) override; void set_y_sort_origin(int p_y_sort_origin); int get_y_sort_origin() const; + void set_x_draw_order_reversed(bool p_x_draw_order_reversed); + bool is_x_draw_order_reversed() const; virtual void set_z_index(int p_z_index) override; virtual void set_light_mask(int p_light_mask) override; void set_rendering_quadrant_size(int p_size);