Change TileMapEditor to TileMapLayerEditor

This commit is contained in:
Gilles Roudière 2024-01-19 17:25:14 +01:00
parent 4e990cd7e5
commit 5a999d67ec
16 changed files with 1506 additions and 1258 deletions

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TileMap" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<class name="TileMap" inherits="TileMapLayerGroup" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Node for 2D tile-based maps.
</brief_description>
@ -488,9 +488,6 @@
The quadrant size does not apply on Y-sorted layers, as tiles are be 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.
</member>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
The assigned [TileSet].
</member>
</members>
<signals>
<signal name="changed">

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TileMapLayerGroup" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Groups a set of tile map layers together, allowing them to share a provided [TileSet].
</brief_description>
<description>
Groups together tile map layers as part or the same map, replacing the [TileMap] node. Child layers will use this node's [member tile_set].
The editor also uses [TileMapLayerGroup] as a way to store which layers are selected in a given group. This allows highlighting the currently selected layers.
</description>
<tutorials>
</tutorials>
<members>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
The assigned [TileSet]. This TileSet will be applied to all child layers.
</member>
</members>
</class>

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* tile_map_editor.h */
/* tile_map_layer_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_MAP_EDITOR_H
#define TILE_MAP_EDITOR_H
#ifndef TILE_MAP_LAYER_EDITOR_H
#define TILE_MAP_LAYER_EDITOR_H
#include "tile_atlas_view.h"
@ -48,9 +48,13 @@
#include "scene/gui/tab_bar.h"
#include "scene/gui/tree.h"
class TileMapEditor;
class TileMapLayerEditor;
class TileMapLayerSubEditorPlugin : public Object {
protected:
ObjectID edited_tile_map_layer_id;
TileMapLayer *_get_edited_layer() const;
class TileMapSubEditorPlugin : public Object {
public:
struct TabData {
Control *toolbar = nullptr;
@ -64,11 +68,11 @@ public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; };
virtual void forward_canvas_draw_over_viewport(Control *p_overlay){};
virtual void tile_set_changed(){};
virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer){};
virtual void edit(ObjectID p_tile_map_layer_id){};
};
class TileMapEditorTilesPlugin : public TileMapSubEditorPlugin {
GDCLASS(TileMapEditorTilesPlugin, TileMapSubEditorPlugin);
class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
public:
enum {
@ -79,10 +83,6 @@ public:
};
private:
ObjectID tile_map_id;
int tile_map_layer = -1;
virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
///// Toolbar /////
HBoxContainer *toolbar = nullptr;
@ -237,18 +237,16 @@ public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
TileMapEditorTilesPlugin();
~TileMapEditorTilesPlugin();
virtual void edit(ObjectID p_tile_map_layer_id) override;
TileMapLayerEditorTilesPlugin();
~TileMapLayerEditorTilesPlugin();
};
class TileMapEditorTerrainsPlugin : public TileMapSubEditorPlugin {
GDCLASS(TileMapEditorTerrainsPlugin, TileMapSubEditorPlugin);
class TileMapLayerEditorTerrainsPlugin : public TileMapLayerSubEditorPlugin {
GDCLASS(TileMapLayerEditorTerrainsPlugin, TileMapLayerSubEditorPlugin);
private:
ObjectID tile_map_id;
int tile_map_layer = -1;
virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
// Toolbar.
HBoxContainer *toolbar = nullptr;
@ -331,28 +329,33 @@ public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
TileMapEditorTerrainsPlugin();
~TileMapEditorTerrainsPlugin();
virtual void edit(ObjectID p_tile_map_layer_id) override;
TileMapLayerEditorTerrainsPlugin();
~TileMapLayerEditorTerrainsPlugin();
};
class TileMapEditor : public VBoxContainer {
GDCLASS(TileMapEditor, VBoxContainer);
class TileMapLayerEditor : public VBoxContainer {
GDCLASS(TileMapLayerEditor, VBoxContainer);
private:
bool tileset_changed_needs_update = false;
ObjectID tile_map_id;
int tile_map_layer = -1;
ObjectID edited_tile_map_layer_id;
TileMapLayer *_get_edited_layer() const;
// Vector to keep plugins.
Vector<TileMapSubEditorPlugin *> tile_map_editor_plugins;
Vector<TileMapLayerSubEditorPlugin *> tile_map_editor_plugins;
// Toolbar.
HFlowContainer *tile_map_toolbar = nullptr;
OptionButton *layers_selection_button = nullptr;
Button *toggle_highlight_selected_layer_button = nullptr;
void _layers_selection_item_selected(int p_index);
Button *toggle_highlight_selected_layer_button = nullptr;
void _highlight_selected_layer_button_toggled(bool p_pressed);
Button *toggle_grid_button = nullptr;
void _on_grid_toggled(bool p_pressed);
@ -362,8 +365,8 @@ private:
// Bottom panel.
Label *missing_tileset_label = nullptr;
TabBar *tabs_bar = nullptr;
LocalVector<TileMapSubEditorPlugin::TabData> tabs_data;
LocalVector<TileMapSubEditorPlugin *> tabs_plugins;
LocalVector<TileMapLayerSubEditorPlugin::TabData> tabs_data;
LocalVector<TileMapLayerSubEditorPlugin *> tabs_plugins;
void _update_bottom_panel();
// TileMap.
@ -371,31 +374,33 @@ private:
Ref<Texture2D> warning_pattern_texture;
// CallBack.
void _tile_map_changed();
void _tile_map_layer_changed();
void _tab_changed(int p_tab_changed);
// Updates.
void _layers_select_next_or_previous(bool p_next);
void _update_layers_selection();
void _update_highlighting_toggle();
// Inspector undo/redo callback.
void _move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos);
protected:
void _notification(int p_what);
static void _bind_methods();
void _draw_shape(Control *p_control, Rect2 p_region, TileSet::TileShape p_shape, TileSet::TileOffsetAxis p_offset_axis, Color p_color);
public:
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(TileMap *p_tile_map);
void edit(TileMapLayer *p_tile_map_layer);
void update_layers_selector();
TileMapEditor();
~TileMapEditor();
TileMapLayerEditor();
~TileMapLayerEditor();
// Static functions.
static Vector<Vector2i> get_line(TileMap *p_tile_map, Vector2i p_from_cell, Vector2i p_to_cell);
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
};
#endif // TILE_MAP_EDITOR_H
#endif // TILE_MAP_LAYER_EDITOR_H

View file

@ -40,8 +40,8 @@
#include "editor/editor_string_names.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/2d/tile_map.h"
#include "scene/2d/tile_map_layer.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
@ -324,7 +324,7 @@ TilesEditorUtils::~TilesEditorUtils() {
singleton = nullptr;
}
void TileMapEditorPlugin::_tile_map_changed() {
void TileMapEditorPlugin::_tile_map_layer_changed() {
if (tile_map_changed_needs_update) {
return;
}
@ -332,23 +332,105 @@ void TileMapEditorPlugin::_tile_map_changed() {
callable_mp(this, &TileMapEditorPlugin::_update_tile_map).call_deferred();
}
void TileMapEditorPlugin::_update_tile_map() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
void TileMapEditorPlugin::_tile_map_layer_removed() {
// Workaround for TileMap, making sure the editor stays open when you delete the currently edited layer.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_group_id));
if (tile_map) {
Ref<TileSet> tile_set = tile_map->get_tileset();
if (tile_set.is_valid() && edited_tileset != tile_set->get_instance_id()) {
tile_set_plugin_singleton->edit(tile_map->get_tileset().ptr());
edit(tile_map);
}
}
void TileMapEditorPlugin::_update_tile_map() {
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
if (edited_layer) {
Ref<TileSet> tile_set = edited_layer->get_effective_tile_set();
if (tile_set.is_valid() && tile_set_id != tile_set->get_instance_id()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
edited_tileset = tile_set->get_instance_id();
tile_set_id = tile_set->get_instance_id();
} else if (tile_set.is_null()) {
tile_set_plugin_singleton->edit(nullptr);
tile_set_plugin_singleton->make_visible(false);
edited_tileset = ObjectID();
tile_set_id = ObjectID();
}
}
tile_map_changed_needs_update = false;
}
void TileMapEditorPlugin::_select_layer(const StringName &p_name) {
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
ERR_FAIL_NULL(edited_layer);
Node *parent = edited_layer->get_parent();
ERR_FAIL_NULL(parent);
TileMapLayer *new_layer = Object::cast_to<TileMapLayer>(parent->get_node_or_null(String(p_name)));
edit(new_layer);
}
void TileMapEditorPlugin::_edit_tile_map_layer(TileMapLayer *p_tile_map_layer) {
ERR_FAIL_NULL(p_tile_map_layer);
editor->edit(p_tile_map_layer);
// Update the selected layers in the TileMapLayerGroup parent node.
TileMapLayerGroup *tile_map_layer_group = Object::cast_to<TileMapLayerGroup>(p_tile_map_layer->get_parent());
if (tile_map_layer_group) {
Vector<StringName> selected;
selected.push_back(p_tile_map_layer->get_name());
tile_map_layer_group->set_selected_layers(selected);
}
// Update the object IDs.
tile_map_layer_id = p_tile_map_layer->get_instance_id();
p_tile_map_layer->connect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
p_tile_map_layer->connect("tree_exited", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
if (tile_map_layer_group) {
tile_map_group_id = tile_map_layer_group->get_instance_id();
tile_map_layer_group->connect("child_entered_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_layer_group->connect("child_exiting_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_layer_group->connect("child_order_changed", callable_mp(editor, &TileMapLayerEditor::update_layers_selector));
}
// Update the edited tileset.
Ref<TileSet> tile_set = p_tile_map_layer->get_effective_tile_set();
if (tile_set.is_valid()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
tile_set_id = tile_set->get_instance_id();
} else {
tile_set_plugin_singleton->edit(nullptr);
tile_set_plugin_singleton->make_visible(false);
}
}
void TileMapEditorPlugin::_edit_tile_map_layer_group(TileMapLayerGroup *p_tile_map_layer_group) {
ERR_FAIL_NULL(p_tile_map_layer_group);
Vector<StringName> selected_layers = p_tile_map_layer_group->get_selected_layers();
TileMapLayer *selected_layer = nullptr;
if (selected_layers.size() > 0) {
// Edit the selected layer.
selected_layer = Object::cast_to<TileMapLayer>(p_tile_map_layer_group->get_node_or_null(String(selected_layers[0])));
}
if (!selected_layer) {
// Edit the first layer found.
for (int i = 0; i < p_tile_map_layer_group->get_child_count(); i++) {
selected_layer = Object::cast_to<TileMapLayer>(p_tile_map_layer_group->get_child(i));
if (selected_layer) {
break;
}
}
}
if (selected_layer) {
_edit_tile_map_layer(selected_layer);
} else {
editor->edit(nullptr);
}
}
void TileMapEditorPlugin::_notification(int p_notification) {
if (p_notification == NOTIFICATION_EXIT_TREE) {
get_tree()->queue_delete(TilesEditorUtils::get_singleton());
@ -356,39 +438,42 @@ void TileMapEditorPlugin::_notification(int p_notification) {
}
void TileMapEditorPlugin::edit(Object *p_object) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tile_map->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed));
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
if (edited_layer) {
edited_layer->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
edited_layer->disconnect("tree_exited", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
}
tile_map = Object::cast_to<TileMap>(p_object);
if (tile_map) {
tile_map_id = tile_map->get_instance_id();
TileMapLayerGroup *tile_map_group = Object::cast_to<TileMapLayerGroup>(ObjectDB::get_instance(tile_map_group_id));
if (tile_map_group) {
tile_map_group->disconnect("child_entered_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_group->disconnect("child_exiting_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_group->disconnect("child_order_changed", callable_mp(editor, &TileMapLayerEditor::update_layers_selector));
}
tile_map_group_id = ObjectID();
tile_map_layer_id = ObjectID();
tile_set_id = ObjectID();
TileMapLayerGroup *tile_map_layer_group = Object::cast_to<TileMap>(p_object);
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_object);
if (tile_map_layer_group) {
_edit_tile_map_layer_group(tile_map_layer_group);
} else if (tile_map_layer) {
_edit_tile_map_layer(tile_map_layer);
} else {
tile_map_id = ObjectID();
// Deselect the layer in the group.
if (edited_layer) {
tile_map_layer_group = Object::cast_to<TileMapLayerGroup>(edited_layer->get_parent());
if (tile_map_layer_group) {
tile_map_layer_group->set_selected_layers(Vector<StringName>());
}
editor->edit(tile_map);
if (tile_map) {
tile_map->connect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed));
if (tile_map->get_tileset().is_valid()) {
tile_set_plugin_singleton->edit(tile_map->get_tileset().ptr());
tile_set_plugin_singleton->make_visible(true);
edited_tileset = tile_map->get_tileset()->get_instance_id();
}
} else if (edited_tileset.is_valid()) {
// Hide the TileSet editor, unless another TileSet is being edited.
if (tile_set_plugin_singleton->get_edited_tileset() == edited_tileset) {
tile_set_plugin_singleton->edit(nullptr);
tile_set_plugin_singleton->make_visible(false);
}
edited_tileset = ObjectID();
}
}
bool TileMapEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<TileMap>(p_object) != nullptr;
return Object::cast_to<TileMapLayer>(p_object) != nullptr || Object::cast_to<TileMapLayerGroup>(p_object) != nullptr;
}
void TileMapEditorPlugin::make_visible(bool p_visible) {
@ -427,10 +512,11 @@ TileMapEditorPlugin::TileMapEditorPlugin() {
}
tile_map_plugin_singleton = this;
editor = memnew(TileMapEditor);
editor = memnew(TileMapLayerEditor);
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
editor->connect("change_selected_layer_request", callable_mp(this, &TileMapEditorPlugin::_select_layer));
editor->hide();
button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileMap"), editor);

View file

@ -35,7 +35,7 @@
#include "scene/gui/box_container.h"
#include "tile_atlas_view.h"
#include "tile_map_editor.h"
#include "tile_map_layer_editor.h"
#include "tile_set_editor.h"
class TilesEditorUtils : public Object {
@ -113,15 +113,21 @@ public:
class TileMapEditorPlugin : public EditorPlugin {
GDCLASS(TileMapEditorPlugin, EditorPlugin);
TileMapEditor *editor = nullptr;
TileMapLayerEditor *editor = nullptr;
Button *button = nullptr;
ObjectID tile_map_id;
ObjectID tile_map_layer_id;
ObjectID tile_map_group_id; // Allow keeping the layer selector up to date.
bool tile_map_changed_needs_update = false;
ObjectID edited_tileset; // The TileSet associated with the TileMap.
ObjectID tile_set_id; // The TileSet associated with the TileMap.
void _tile_map_changed();
void _tile_map_layer_changed();
void _tile_map_layer_removed();
void _update_tile_map();
void _select_layer(const StringName &p_name);
void _edit_tile_map_layer(TileMapLayer *p_tile_map_layer);
void _edit_tile_map_layer_group(TileMapLayerGroup *p_tile_map_layer_group);
protected:
void _notification(int p_notification);

View file

@ -104,3 +104,12 @@ Validate extension JSON: Error: Field 'classes/GLTFBufferView/methods/get_byte_s
Validate extension JSON: Error: Field 'classes/GLTFBufferView/methods/get_indices': is_const changed value in new API, from false to true.
Change AudioStreamPlayer* is_autoplay_enabled and GLTFBufferView getters to be const.
GH-87379
--------
Validate extension JSON: API was removed: classes/TileMap/methods/get_tileset
Validate extension JSON: API was removed: classes/TileMap/methods/set_tileset
Validate extension JSON: API was removed: classes/TileMap/properties/tile_set
Moved to the parent TileMapLayerGroup class. No change should be necessary.

View file

@ -49,131 +49,8 @@
ERR_FAIL_INDEX_V(layer, (int)layers.size(), err_value); \
return layers[layer]->function(__VA_ARGS__);
Vector2i TileMap::transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) {
// Transform to stacked layout.
Vector2i output = p_coords;
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
SWAP(output.x, output.y);
}
switch (p_from_layout) {
case TileSet::TILE_LAYOUT_STACKED:
break;
case TileSet::TILE_LAYOUT_STACKED_OFFSET:
if (output.y % 2) {
output.x -= 1;
}
break;
case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
case TileSet::TILE_LAYOUT_STAIRS_DOWN:
if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if (output.y < 0 && bool(output.y % 2)) {
output = Vector2i(output.x + output.y / 2 - 1, output.y);
} else {
output = Vector2i(output.x + output.y / 2, output.y);
}
} else {
if (output.x < 0 && bool(output.x % 2)) {
output = Vector2i(output.x / 2 - 1, output.x + output.y * 2);
} else {
output = Vector2i(output.x / 2, output.x + output.y * 2);
}
}
break;
case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if ((output.x + output.y) < 0 && (output.x - output.y) % 2) {
output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x);
} else {
output = Vector2i((output.x + output.y) / 2, -output.x + output.y);
}
} else {
if ((output.x - output.y) < 0 && (output.x + output.y) % 2) {
output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y);
} else {
output = Vector2i((output.x - output.y) / 2, output.x + output.y);
}
}
break;
}
switch (p_to_layout) {
case TileSet::TILE_LAYOUT_STACKED:
break;
case TileSet::TILE_LAYOUT_STACKED_OFFSET:
if (output.y % 2) {
output.x += 1;
}
break;
case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
case TileSet::TILE_LAYOUT_STAIRS_DOWN:
if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if (output.y < 0 && (output.y % 2)) {
output = Vector2i(output.x - output.y / 2 + 1, output.y);
} else {
output = Vector2i(output.x - output.y / 2, output.y);
}
} else {
if (output.y % 2) {
if (output.y < 0) {
output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1);
} else {
output = Vector2i(2 * output.x + 1, -output.x + output.y / 2);
}
} else {
output = Vector2i(2 * output.x, -output.x + output.y / 2);
}
}
break;
case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if (output.y % 2) {
if (output.y > 0) {
output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1);
} else {
output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2);
}
} else {
output = Vector2i(output.x - output.y / 2, output.x + output.y / 2);
}
} else {
if (output.y % 2) {
if (output.y < 0) {
output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1);
} else {
output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2);
}
} else {
output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2);
}
}
break;
}
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
SWAP(output.x, output.y);
}
return output;
}
void TileMap::set_selected_layer(int p_layer_id) {
ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size());
if (selected_layer == p_layer_id) {
return;
}
selected_layer = p_layer_id;
void TileMap::_emit_changed() {
emit_signal(CoreStringNames::get_singleton()->changed);
// Update the layers modulation.
for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_SELECTED_LAYER);
}
}
int TileMap::get_selected_layer() const {
return selected_layer;
}
void TileMap::_notification(int p_what) {
@ -188,7 +65,6 @@ void TileMap::_notification(int p_what) {
if (is_inside_tree() && collision_animatable && !in_editor) {
// Update transform on the physics tick when in animatable mode.
last_valid_transform = new_transform;
print_line("Physics: ", new_transform);
set_notify_local_transform(false);
set_global_transform(new_transform);
set_notify_local_transform(true);
@ -207,7 +83,6 @@ void TileMap::_notification(int p_what) {
// Store last valid transform.
new_transform = get_global_transform();
print_line("local XFORM: ", last_valid_transform);
// ... but then revert changes.
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
@ -225,55 +100,6 @@ void TileMap::force_update(int p_layer) {
}
#endif
void TileMap::queue_internal_update() {
if (pending_update) {
return;
}
pending_update = true;
callable_mp(this, &TileMap::_internal_update).call_deferred();
}
void TileMap::_internal_update() {
// Other updates.
if (!pending_update) {
return;
}
// Update dirty quadrants on layers.
for (TileMapLayer *layer : layers) {
layer->internal_update();
}
pending_update = false;
}
void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
if (p_tileset == tile_set) {
return;
}
// Set the tileset, registering to its changes.
if (tile_set.is_valid()) {
tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed));
}
tile_set = p_tileset;
if (tile_set.is_valid()) {
tile_set->connect_changed(callable_mp(this, &TileMap::_tile_set_changed));
}
for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_TILE_SET);
}
emit_signal(CoreStringNames::get_singleton()->changed);
}
Ref<TileSet> TileMap::get_tileset() const {
return tile_set;
}
void TileMap::set_rendering_quadrant_size(int p_size) {
ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
@ -281,7 +107,7 @@ void TileMap::set_rendering_quadrant_size(int p_size) {
for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE);
}
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
}
int TileMap::get_rendering_quadrant_size() const {
@ -392,10 +218,11 @@ void TileMap::add_layer(int p_to_pos) {
for (uint32_t i = 0; i < layers.size(); i++) {
layers[i]->set_layer_index_in_tile_map_node(i);
}
queue_internal_update();
new_layer->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileMap::_emit_changed));
notify_property_list_changed();
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
update_configuration_warnings();
}
@ -412,14 +239,9 @@ void TileMap::move_layer(int p_layer, int p_to_pos) {
move_child(layer, i);
layers[i]->set_layer_index_in_tile_map_node(i);
}
queue_internal_update();
notify_property_list_changed();
if (selected_layer == p_layer) {
selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos;
}
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
update_configuration_warnings();
}
@ -433,14 +255,9 @@ void TileMap::remove_layer(int p_layer) {
for (uint32_t i = 0; i < layers.size(); i++) {
layers[i]->set_layer_index_in_tile_map_node(i);
}
queue_internal_update();
notify_property_list_changed();
if (selected_layer >= p_layer) {
selected_layer -= 1;
}
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
update_configuration_warnings();
}
@ -533,7 +350,7 @@ void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_colli
for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_COLLISION_VISIBILITY_MODE);
}
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
}
TileMap::VisibilityMode TileMap::get_collision_visibility_mode() const {
@ -548,7 +365,7 @@ void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navi
for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_NAVIGATION_VISIBILITY_MODE);
}
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
}
TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() const {
@ -563,7 +380,7 @@ void TileMap::set_y_sort_enabled(bool p_enable) {
for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED);
}
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
update_configuration_warnings();
}
@ -671,8 +488,9 @@ void TileMap::clear() {
}
void TileMap::update_internals() {
pending_update = true;
_internal_update();
for (TileMapLayer *layer : layers) {
layer->update_internals();
}
}
void TileMap::notify_runtime_tile_data_update(int p_layer) {
@ -721,10 +539,11 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
add_child(new_layer);
new_layer->set_name("Layer0");
new_layer->set_layer_index_in_tile_map_node(0);
new_layer->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileMap::_emit_changed));
layers.push_back(new_layer);
}
layers[0]->set_tile_data(format, p_value);
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
return true;
}
return false;
@ -745,11 +564,12 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
add_child(new_layer);
new_layer->set_name(vformat("Layer%d", index));
new_layer->set_layer_index_in_tile_map_node(index);
new_layer->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileMap::_emit_changed));
layers.push_back(new_layer);
}
notify_property_list_changed();
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
update_configuration_warnings();
}
@ -776,7 +596,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
return true;
} else if (components[1] == "tile_data") {
layers[index]->set_tile_data(format, p_value);
emit_signal(CoreStringNames::get_singleton()->changed);
_emit_changed();
return true;
} else {
return false;
@ -1014,93 +834,12 @@ void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
}
}
TypedArray<Vector2i> TileMap::get_surrounding_cells(const Vector2i &coords) {
TypedArray<Vector2i> TileMap::get_surrounding_cells(const Vector2i &p_coords) {
if (!tile_set.is_valid()) {
return TypedArray<Vector2i>();
}
TypedArray<Vector2i> around;
TileSet::TileShape shape = tile_set->get_tile_shape();
if (shape == TileSet::TILE_SHAPE_SQUARE) {
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_SIDE));
} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
} else {
if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
} else {
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_SIDE));
around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
}
}
return around;
}
void TileMap::draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform) {
if (!tile_set.is_valid()) {
return;
}
// Create a set.
Vector2i tile_size = tile_set->get_tile_size();
Vector<Vector2> polygon = tile_set->get_tile_shape_polygon();
TileSet::TileShape shape = tile_set->get_tile_shape();
for (const Vector2i &E : p_cells) {
Vector2 center = map_to_local(E);
#define DRAW_SIDE_IF_NEEDED(side, polygon_index_from, polygon_index_to) \
if (!p_cells.has(get_neighbor_cell(E, side))) { \
Vector2 from = p_transform.xform(center + polygon[polygon_index_from] * tile_size); \
Vector2 to = p_transform.xform(center + polygon[polygon_index_to] * tile_size); \
p_control->draw_line(from, to, p_color); \
}
if (shape == TileSet::TILE_SHAPE_SQUARE) {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, 2, 3);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_LEFT_SIDE, 3, 0);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_SIDE, 0, 1);
} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 2, 3);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 3, 0);
} else {
if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 2, 3);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_LEFT_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 5, 0);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, 4, 5);
} else {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, 4, 5);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 5, 0);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 2, 3);
}
}
}
#undef DRAW_SIDE_IF_NEEDED
return tile_set->get_surrounding_cells(p_coords);
}
Array TileMap::get_configuration_warnings() const {
@ -1171,9 +910,6 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_update", "layer"), &TileMap::force_update, DEFVAL(-1));
#endif // DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset);
ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset);
ClassDB::bind_method(D_METHOD("set_rendering_quadrant_size", "size"), &TileMap::set_rendering_quadrant_size);
ClassDB::bind_method(D_METHOD("get_rendering_quadrant_size"), &TileMap::get_rendering_quadrant_size);
@ -1244,7 +980,6 @@ void TileMap::_bind_methods() {
GDVIRTUAL_BIND(_use_tile_data_runtime_update, "layer", "coords");
GDVIRTUAL_BIND(_tile_data_runtime_update, "layer", "coords", "tile_data");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rendering_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_rendering_quadrant_size", "get_rendering_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");
@ -1261,28 +996,18 @@ void TileMap::_bind_methods() {
BIND_ENUM_CONSTANT(VISIBILITY_MODE_FORCE_SHOW);
}
void TileMap::_tile_set_changed() {
emit_signal(CoreStringNames::get_singleton()->changed);
for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_TILE_SET);
}
update_configuration_warnings();
}
TileMap::TileMap() {
TileMapLayer *new_layer = memnew(TileMapLayer);
add_child(new_layer);
new_layer->set_name("Layer0");
new_layer->set_layer_index_in_tile_map_node(0);
new_layer->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileMap::_emit_changed));
layers.push_back(new_layer);
default_layer = memnew(TileMapLayer);
}
TileMap::~TileMap() {
if (tile_set.is_valid()) {
tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed));
}
memdelete(default_layer);
}

View file

@ -31,7 +31,7 @@
#ifndef TILE_MAP_H
#define TILE_MAP_H
#include "scene/2d/node_2d.h"
#include "scene/2d/tile_map_layer_group.h"
#include "scene/resources/tile_set.h"
class Control;
@ -45,8 +45,8 @@ enum TileMapDataFormat {
FORMAT_MAX,
};
class TileMap : public Node2D {
GDCLASS(TileMap, Node2D);
class TileMap : public TileMapLayerGroup {
GDCLASS(TileMap, TileMapLayerGroup);
public:
enum VisibilityMode {
@ -64,7 +64,6 @@ private:
static constexpr float FP_ADJUST = 0.00001;
// Properties.
Ref<TileSet> tile_set;
int rendering_quadrant_size = 16;
bool collision_animatable = false;
VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
@ -73,14 +72,12 @@ private:
// Layers.
LocalVector<TileMapLayer *> layers;
TileMapLayer *default_layer; // Dummy layer to fetch default values.
int selected_layer = -1;
bool pending_update = false;
// Transforms for collision_animatable.
Transform2D last_valid_transform;
Transform2D new_transform;
void _tile_set_changed();
void _emit_changed();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@ -103,8 +100,6 @@ protected:
#endif
public:
static Vector2i transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout);
#ifdef TOOLS_ENABLED
virtual Rect2 _edit_get_rect() const override;
#endif
@ -113,13 +108,6 @@ public:
void force_update(int p_layer);
#endif
// Called by TileMapLayers.
void queue_internal_update();
void _internal_update();
void set_tileset(const Ref<TileSet> &p_tileset);
Ref<TileSet> get_tileset() const;
void set_rendering_quadrant_size(int p_size);
int get_rendering_quadrant_size() const;
@ -148,9 +136,6 @@ public:
void set_layer_navigation_map(int p_layer, RID p_map);
RID get_layer_navigation_map(int p_layer) const;
void set_selected_layer(int p_layer_id); // For editor use.
int get_selected_layer() const;
void set_collision_animatable(bool p_collision_animatable);
bool is_collision_animatable() const;
@ -224,8 +209,7 @@ public:
void notify_runtime_tile_data_update(int p_layer = -1);
// Helpers?
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &coords);
void draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D());
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &p_coords);
// Virtual function to modify the TileData at runtime.
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);

View file

@ -44,14 +44,6 @@ TileMap *TileMapLayer::_fetch_tilemap() const {
return TileMap::cast_to<TileMap>(get_parent());
}
Ref<TileSet> TileMapLayer::_fetch_tileset() const {
TileMap *tile_map_node = _fetch_tilemap();
if (!tile_map_node) {
return Ref<TileSet>();
}
return tile_map_node->get_tileset();
}
#ifdef DEBUG_ENABLED
/////////////////////////////// Debug //////////////////////////////////////////
constexpr int TILE_MAP_DEBUG_QUADRANT_SIZE = 16;
@ -63,7 +55,7 @@ Vector2i TileMapLayer::_coords_to_debug_quadrant_coords(const Vector2i &p_coords
}
void TileMapLayer::_debug_update() {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
RenderingServer *rs = RenderingServer::get_singleton();
// Check if we should cleanup everything.
@ -192,7 +184,7 @@ void TileMapLayer::_debug_quadrants_update_cell(CellData &r_cell_data, SelfList<
/////////////////////////////// Rendering //////////////////////////////////////
void TileMapLayer::_rendering_update() {
const TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
RenderingServer *rs = RenderingServer::get_singleton();
// Check if we should cleanup everything.
@ -206,17 +198,22 @@ void TileMapLayer::_rendering_update() {
// Modulate the layer.
Color layer_modulate = get_modulate();
int selected_layer = tile_map_node->get_selected_layer();
if (selected_layer >= 0 && layer_index_in_tile_map_node != selected_layer) {
int z_selected = tile_map_node->get_layer_z_index(selected_layer);
#ifdef TOOLS_ENABLED
const Vector<StringName> selected_layers = tile_map_node->get_selected_layers();
if (tile_map_node->is_highlighting_selected_layer() && selected_layers.size() == 1 && get_name() != selected_layers[0]) {
TileMapLayer *selected_layer = Object::cast_to<TileMapLayer>(tile_map_node->get_node_or_null(String(selected_layers[0])));
if (selected_layer) {
int z_selected = selected_layer->get_z_index();
int layer_z_index = get_z_index();
if (layer_z_index < z_selected || (layer_z_index == z_selected && layer_index_in_tile_map_node < selected_layer)) {
if (layer_z_index < z_selected || (layer_z_index == z_selected && get_index() < selected_layer->get_index())) {
layer_modulate = layer_modulate.darkened(0.5);
} else if (layer_z_index > z_selected || (layer_z_index == z_selected && layer_index_in_tile_map_node > selected_layer)) {
} else if (layer_z_index > z_selected || (layer_z_index == z_selected && get_index() > selected_layer->get_index())) {
layer_modulate = layer_modulate.darkened(0.5);
layer_modulate.a *= 0.3;
}
}
}
#endif // TOOLS_ENABLED
rs->canvas_item_set_modulate(get_canvas_item(), layer_modulate);
}
@ -228,7 +225,7 @@ void TileMapLayer::_rendering_update() {
// Check if anything changed that might change the quadrant shape.
// If so, recreate everything.
bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_TILE_MAP_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_TILE_MAP_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]));
(is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET]));
// Free all quadrants.
if (forced_cleanup || quandrant_shape_changed) {
@ -247,7 +244,7 @@ void TileMapLayer::_rendering_update() {
if (!forced_cleanup) {
// List all quadrants to update, recreating them if needed.
if (dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] || _rendering_was_cleaned_up) {
if (dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] || _rendering_was_cleaned_up) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
CellData &cell_data = kv.value;
@ -427,7 +424,7 @@ void TileMapLayer::_rendering_update() {
_rendering_occluders_clear_cell(kv.value);
}
} else {
if (_rendering_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) {
if (_rendering_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET]) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_rendering_occluders_update_cell(kv.value);
@ -448,7 +445,7 @@ void TileMapLayer::_rendering_update() {
void TileMapLayer::_rendering_notification(int p_what) {
RenderingServer *rs = RenderingServer::get_singleton();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (p_what == NOTIFICATION_TRANSFORM_CHANGED || p_what == NOTIFICATION_ENTER_CANVAS || p_what == NOTIFICATION_VISIBILITY_CHANGED) {
if (tile_set.is_valid()) {
Transform2D tilemap_xform = get_global_transform();
@ -469,7 +466,7 @@ void TileMapLayer::_rendering_notification(int p_what) {
void TileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list) {
const TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
// Check if the cell is valid and retrieve its y_sort_origin.
bool is_valid = false;
@ -569,7 +566,7 @@ void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) {
}
void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
RenderingServer *rs = RenderingServer::get_singleton();
// Free unused occluders then resize the occluders array.
@ -638,7 +635,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
#ifdef DEBUG_ENABLED
void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND(!tile_set.is_valid());
if (!Engine::get_singleton()->is_editor_hint()) {
@ -687,7 +684,7 @@ void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Ve
/////////////////////////////// Physics //////////////////////////////////////
void TileMapLayer::_physics_update() {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
// Check if we should cleanup everything.
bool forced_cleanup = in_destructor || !enabled || !is_inside_tree() || !tile_set.is_valid();
@ -697,7 +694,7 @@ void TileMapLayer::_physics_update() {
_physics_clear_cell(kv.value);
}
} else {
if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_physics_update_cell(kv.value);
@ -717,7 +714,7 @@ void TileMapLayer::_physics_update() {
}
void TileMapLayer::_physics_notification(int p_what) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
Transform2D gl_transform = get_global_transform();
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
@ -771,7 +768,7 @@ void TileMapLayer::_physics_clear_cell(CellData &r_cell_data) {
void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
const TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
Transform2D gl_transform = get_global_transform();
RID space = get_world_2d()->get_space();
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
@ -889,7 +886,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) {
// Draw the debug collision shapes.
TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND(!tile_set.is_valid());
if (!get_tree()) {
@ -945,7 +942,7 @@ void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vect
void TileMapLayer::_navigation_update() {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
NavigationServer2D *ns = NavigationServer2D::get_singleton();
// Check if we should cleanup everything.
@ -982,7 +979,7 @@ void TileMapLayer::_navigation_update() {
_navigation_clear_cell(kv.value);
}
} else {
if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_navigation_update_cell(kv.value);
@ -1002,7 +999,7 @@ void TileMapLayer::_navigation_update() {
}
void TileMapLayer::_navigation_notification(int p_what) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
if (tile_set.is_valid()) {
Transform2D tilemap_xform = get_global_transform();
@ -1037,7 +1034,7 @@ void TileMapLayer::_navigation_clear_cell(CellData &r_cell_data) {
void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
const TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
NavigationServer2D *ns = NavigationServer2D::get_singleton();
Transform2D gl_xform = get_global_transform();
@ -1136,7 +1133,7 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V
return;
}
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
RenderingServer *rs = RenderingServer::get_singleton();
const NavigationServer2D *ns2d = NavigationServer2D::get_singleton();
@ -1222,7 +1219,7 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V
/////////////////////////////// Scenes //////////////////////////////////////
void TileMapLayer::_scenes_update() {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
// Check if we should cleanup everything.
bool forced_cleanup = in_destructor || !enabled || !is_inside_tree() || !tile_set.is_valid();
@ -1233,7 +1230,7 @@ void TileMapLayer::_scenes_update() {
_scenes_clear_cell(kv.value);
}
} else {
if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_scenes_update_cell(kv.value);
@ -1268,7 +1265,7 @@ void TileMapLayer::_scenes_clear_cell(CellData &r_cell_data) {
void TileMapLayer::_scenes_update_cell(CellData &r_cell_data) {
TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
// Clear the scene in any case.
_scenes_clear_cell(r_cell_data);
@ -1305,7 +1302,7 @@ void TileMapLayer::_scenes_update_cell(CellData &r_cell_data) {
#ifdef DEBUG_ENABLED
void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND(!tile_set.is_valid());
if (!Engine::get_singleton()->is_editor_hint()) {
@ -1356,13 +1353,13 @@ void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vecto
void TileMapLayer::_build_runtime_update_tile_data() {
const TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
// Check if we should cleanup everything.
bool forced_cleanup = in_destructor || !enabled || !tile_set.is_valid() || !is_visible_in_tree();
if (!forced_cleanup) {
if (tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) {
if (_runtime_update_tile_data_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) {
if (_runtime_update_tile_data_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET]) {
for (KeyValue<Vector2i, CellData> &E : tile_map) {
_build_runtime_update_tile_data_for_cell(E.value);
}
@ -1386,7 +1383,7 @@ void TileMapLayer::_build_runtime_update_tile_data() {
void TileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_auto_add_to_dirty_list) {
TileMap *tile_map_node = _fetch_tilemap();
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
TileMapCell &c = r_cell_data.cell;
TileSetSource *source;
@ -1428,8 +1425,8 @@ void TileMapLayer::_clear_runtime_update_tile_data() {
}
}
TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) {
const Ref<TileSet> &tile_set = _fetch_tileset();
TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const {
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (!tile_set.is_valid()) {
return TileSet::TerrainsPattern();
}
@ -1490,7 +1487,7 @@ TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints
}
RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (!tile_set.is_valid()) {
return RBSet<TerrainConstraint>();
}
@ -1511,7 +1508,7 @@ RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_patte
}
RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (!tile_set.is_valid()) {
return RBSet<TerrainConstraint>();
}
@ -1599,8 +1596,7 @@ RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cel
}
void TileMapLayer::_renamed() {
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
emit_signal(CoreStringNames::get_singleton()->changed);
}
void TileMapLayer::_update_notify_local_transform() {
@ -1614,6 +1610,66 @@ void TileMapLayer::_update_notify_local_transform() {
set_notify_local_transform(notify);
}
void TileMapLayer::_queue_internal_update() {
if (pending_update) {
return;
}
pending_update = true;
callable_mp(this, &TileMapLayer::_deferred_internal_update).call_deferred();
}
void TileMapLayer::_deferred_internal_update() {
// Other updates.
if (!pending_update) {
return;
}
// Update dirty quadrants on layers.
_internal_update();
pending_update = false;
}
void TileMapLayer::_internal_update() {
// Find TileData that need a runtime modification.
// This may add cells to the dirty list is a runtime modification has been notified.
_build_runtime_update_tile_data();
// Update all subsystems.
_rendering_update();
_physics_update();
_navigation_update();
_scenes_update();
#ifdef DEBUG_ENABLED
_debug_update();
#endif // DEBUG_ENABLED
_clear_runtime_update_tile_data();
// Clear the "what is dirty" flags.
for (int i = 0; i < DIRTY_FLAGS_MAX; i++) {
dirty.flags[i] = false;
}
// List the cells to delete definitely.
Vector<Vector2i> to_delete;
for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) {
CellData &cell_data = *cell_data_list_element->self();
// Select the the cell from tile_map if it is invalid.
if (cell_data.cell.source_id == TileSet::INVALID_SOURCE) {
to_delete.push_back(cell_data.coords);
}
}
// Remove cells that are empty after the cleanup.
for (const Vector2i &coords : to_delete) {
tile_map.erase(coords);
}
// Clear the dirty cells list.
dirty.cell_list.clear();
}
void TileMapLayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
@ -1623,32 +1679,27 @@ void TileMapLayer::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_notify_local_transform();
dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
_queue_internal_update();
} break;
case NOTIFICATION_EXIT_TREE: {
dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
_queue_internal_update();
} break;
case TileMap::NOTIFICATION_ENTER_CANVAS: {
dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
_queue_internal_update();
} break;
case TileMap::NOTIFICATION_EXIT_CANVAS: {
dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
_queue_internal_update();
} break;
case TileMap::NOTIFICATION_VISIBILITY_CHANGED: {
dirty.flags[DIRTY_FLAGS_LAYER_VISIBILITY] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
_queue_internal_update();
} break;
}
@ -1657,18 +1708,23 @@ void TileMapLayer::_notification(int p_what) {
_navigation_notification(p_what);
}
void TileMapLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapLayer::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(0));
ADD_SIGNAL(MethodInfo(CoreStringNames::get_singleton()->changed));
}
void TileMapLayer::set_layer_index_in_tile_map_node(int p_index) {
if (p_index == layer_index_in_tile_map_node) {
return;
}
TileMap *tile_map_node = _fetch_tilemap();
layer_index_in_tile_map_node = p_index;
dirty.flags[DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE] = true;
tile_map_node->queue_internal_update();
_queue_internal_update();
}
Rect2 TileMapLayer::get_rect(bool &r_changed) const {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (tile_set.is_null()) {
r_changed = rect_cache != Rect2();
return Rect2();
@ -1702,8 +1758,8 @@ Rect2 TileMapLayer::get_rect(bool &r_changed) const {
return rect_cache;
}
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) {
const Ref<TileSet> &tile_set = _fetch_tileset();
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) const {
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (!tile_set.is_valid()) {
return HashMap<Vector2i, TileSet::TerrainsPattern>();
}
@ -1750,9 +1806,9 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constrain
return output;
}
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) const {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
@ -1856,9 +1912,9 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(c
return output;
}
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) const {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
@ -1930,9 +1986,9 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(cons
return output;
}
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) const {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
@ -1988,7 +2044,7 @@ TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies)
return TileMapCell();
} else {
TileMapCell c = tile_map.find(p_coords)->value.cell;
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile);
c.source_id = proxyed[0];
@ -2064,7 +2120,7 @@ void TileMapLayer::set_tile_data(TileMapDataFormat p_format, const Vector<int> &
coord_y = decode_uint16(&local[10]);
}
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (tile_set.is_valid()) {
Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose);
if (a.size() == 3) {
@ -2105,49 +2161,19 @@ Vector<int> TileMapLayer::get_tile_data() const {
}
void TileMapLayer::notify_tile_map_change(DirtyFlags p_what) {
TileMap *tile_map_node = _fetch_tilemap();
if (p_what == DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS ||
p_what == DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED ||
p_what == DIRTY_FLAGS_LAYER_GROUP_TILE_SET) {
emit_signal(CoreStringNames::get_singleton()->changed);
}
dirty.flags[p_what] = true;
tile_map_node->queue_internal_update();
_queue_internal_update();
}
void TileMapLayer::internal_update() {
// Find TileData that need a runtime modification.
// This may add cells to the dirty list is a runtime modification has been notified.
_build_runtime_update_tile_data();
// Update all subsystems.
_rendering_update();
_physics_update();
_navigation_update();
_scenes_update();
#ifdef DEBUG_ENABLED
_debug_update();
#endif // DEBUG_ENABLED
_clear_runtime_update_tile_data();
// Clear the "what is dirty" flags.
for (int i = 0; i < DIRTY_FLAGS_MAX; i++) {
dirty.flags[i] = false;
}
// List the cells to delete definitely.
Vector<Vector2i> to_delete;
for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) {
CellData &cell_data = *cell_data_list_element->self();
// Select the the cell from tile_map if it is invalid.
if (cell_data.cell.source_id == TileSet::INVALID_SOURCE) {
to_delete.push_back(cell_data.coords);
}
}
// Remove cells that are empty after the cleanup.
for (const Vector2i &coords : to_delete) {
tile_map.erase(coords);
}
// Clear the dirty cells list.
dirty.cell_list.clear();
void TileMapLayer::update_internals() {
pending_update = true;
_deferred_internal_update();
}
void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
@ -2190,10 +2216,7 @@ void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vec
if (!E->value.dirty_list_element.in_list()) {
dirty.cell_list.add(&(E->value.dirty_list_element));
}
TileMap *tile_map_node = _fetch_tilemap();
if (tile_map_node) { // Needed to avoid crashes in destructor.
tile_map_node->queue_internal_update();
}
_queue_internal_update();
used_rect_cache_dirty = true;
}
@ -2210,7 +2233,7 @@ int TileMapLayer::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxie
return TileSet::INVALID_SOURCE;
}
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile);
return proxyed[0];
@ -2227,7 +2250,7 @@ Vector2i TileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords, bool p_us
return TileSetSource::INVALID_ATLAS_COORDS;
}
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile);
return proxyed[1];
@ -2244,7 +2267,7 @@ int TileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use
return TileSetSource::INVALID_TILE_ALTERNATIVE;
}
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile);
return proxyed[2];
@ -2259,7 +2282,7 @@ TileData *TileMapLayer::get_cell_tile_data(const Vector2i &p_coords, bool p_use_
return nullptr;
}
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
Ref<TileSetAtlasSource> source = tile_set->get_source(source_id);
if (source.is_valid()) {
return source->get_tile_data(get_cell_atlas_coords(p_coords, p_use_proxies), get_cell_alternative_tile(p_coords, p_use_proxies));
@ -2277,7 +2300,7 @@ void TileMapLayer::clear() {
}
Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr);
Ref<TileMapPattern> output;
@ -2331,7 +2354,7 @@ Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_arra
}
void TileMapLayer::set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND(tile_set.is_null());
ERR_FAIL_COND(p_pattern.is_null());
@ -2343,7 +2366,7 @@ void TileMapLayer::set_pattern(const Vector2i &p_position, const Ref<TileMapPatt
}
void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND(!tile_set.is_valid());
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
@ -2383,7 +2406,7 @@ void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p
}
void TileMapLayer::set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
const Ref<TileSet> &tile_set = _fetch_tileset();
const Ref<TileSet> &tile_set = get_effective_tile_set();
ERR_FAIL_COND(!tile_set.is_valid());
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
@ -2490,10 +2513,10 @@ void TileMapLayer::set_enabled(bool p_enabled) {
}
enabled = p_enabled;
dirty.flags[DIRTY_FLAGS_LAYER_ENABLED] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
_queue_internal_update();
emit_signal(CoreStringNames::get_singleton()->changed);
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->update_configuration_warnings();
}
@ -2507,9 +2530,8 @@ void TileMapLayer::set_self_modulate(const Color &p_self_modulate) {
}
CanvasItem::set_self_modulate(p_self_modulate);
dirty.flags[DIRTY_FLAGS_LAYER_SELF_MODULATE] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
_queue_internal_update();
emit_signal(CoreStringNames::get_singleton()->changed);
}
void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) {
@ -2518,10 +2540,10 @@ void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) {
}
CanvasItem::set_y_sort_enabled(p_y_sort_enabled);
dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
_queue_internal_update();
emit_signal(CoreStringNames::get_singleton()->changed);
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->update_configuration_warnings();
_update_notify_local_transform();
}
@ -2532,9 +2554,8 @@ void TileMapLayer::set_y_sort_origin(int p_y_sort_origin) {
}
y_sort_origin = p_y_sort_origin;
dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
_queue_internal_update();
emit_signal(CoreStringNames::get_singleton()->changed);
}
int TileMapLayer::get_y_sort_origin() const {
@ -2547,19 +2568,18 @@ void TileMapLayer::set_z_index(int p_z_index) {
}
CanvasItem::set_z_index(p_z_index);
dirty.flags[DIRTY_FLAGS_LAYER_Z_INDEX] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
_queue_internal_update();
emit_signal(CoreStringNames::get_singleton()->changed);
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->update_configuration_warnings();
}
void TileMapLayer::set_use_kinematic_bodies(bool p_use_kinematic_bodies) {
use_kinematic_bodies = p_use_kinematic_bodies;
dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] = p_use_kinematic_bodies;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
_queue_internal_update();
emit_signal(CoreStringNames::get_singleton()->changed);
}
bool TileMapLayer::is_using_kinematic_bodies() const {
@ -2572,9 +2592,8 @@ void TileMapLayer::set_navigation_enabled(bool p_enabled) {
}
navigation_enabled = p_enabled;
dirty.flags[DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED] = true;
TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
_queue_internal_update();
emit_signal(CoreStringNames::get_singleton()->changed);
}
bool TileMapLayer::is_navigation_enabled() const {
@ -2595,7 +2614,7 @@ RID TileMapLayer::get_navigation_map() const {
}
void TileMapLayer::fix_invalid_tiles() {
Ref<TileSet> tileset = _fetch_tileset();
Ref<TileSet> tileset = get_effective_tile_set();
ERR_FAIL_COND_MSG(tileset.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet.");
RBSet<Vector2i> coords;
@ -2618,6 +2637,15 @@ Vector2i TileMapLayer::get_coords_for_body_rid(RID p_physics_body) const {
return bodies_coords[p_physics_body];
}
Ref<TileSet> TileMapLayer::get_effective_tile_set() const {
TileMapLayerGroup *tile_map_layer_group = Object::cast_to<TileMapLayerGroup>(get_parent());
if (tile_map_layer_group) {
return tile_map_layer_group->get_tileset();
} else {
return Ref<TileSet>();
}
}
TileMapLayer::TileMapLayer() {
set_notify_transform(true);
}
@ -2625,7 +2653,7 @@ TileMapLayer::TileMapLayer() {
TileMapLayer::~TileMapLayer() {
in_destructor = true;
clear();
internal_update();
_internal_update();
}
HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coords_and_peering_bits() const {

View file

@ -232,13 +232,15 @@ public:
DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED,
DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE,
DIRTY_FLAGS_TILE_MAP_SELECTED_LAYER,
DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS,
DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED,
DIRTY_FLAGS_LAYER_GROUP_TILE_SET,
DIRTY_FLAGS_TILE_MAP_LIGHT_MASK,
DIRTY_FLAGS_TILE_MAP_MATERIAL,
DIRTY_FLAGS_TILE_MAP_USE_PARENT_MATERIAL,
DIRTY_FLAGS_TILE_MAP_TEXTURE_FILTER,
DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT,
DIRTY_FLAGS_TILE_MAP_TILE_SET,
DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE,
DIRTY_FLAGS_TILE_MAP_COLLISION_VISIBILITY_MODE,
DIRTY_FLAGS_TILE_MAP_NAVIGATION_VISIBILITY_MODE,
@ -259,6 +261,7 @@ private:
// Internal.
int layer_index_in_tile_map_node = -1;
HashMap<Vector2i, CellData> tile_map;
bool pending_update = false;
// Dirty flag. Allows knowing what was modified since the last update.
struct {
@ -275,7 +278,6 @@ private:
// Method to fetch the TileSet to use
TileMap *_fetch_tilemap() const;
Ref<TileSet> _fetch_tileset() const;
// Runtime tile data.
bool _runtime_update_tile_data_was_cleaned_up = false;
@ -331,15 +333,21 @@ private:
#endif // DEBUG_ENABLED
// Terrains.
TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern);
TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const;
RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const;
void _renamed();
void _update_notify_local_transform();
// Internal updates.
void _queue_internal_update();
void _deferred_internal_update();
void _internal_update();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
// TileMap node.
@ -349,10 +357,10 @@ public:
Rect2 get_rect(bool &r_changed) const;
// Terrains.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints); // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) const; // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true) const; // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true) const; // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true) const; // Not exposed.
// Not exposed to users.
TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const;
@ -361,10 +369,10 @@ public:
void set_tile_data(TileMapDataFormat p_format, const Vector<int> &p_data);
Vector<int> get_tile_data() const;
void notify_tile_map_change(DirtyFlags p_what);
void internal_update();
void update_internals();
// --- Exposed in TileMap ---
// Cells manipulation.
void set_cell(const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
void erase_cell(const Vector2i &p_coords);
@ -410,6 +418,11 @@ public:
bool has_body_rid(RID p_physics_body) const;
Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision.
// Helper.
Ref<TileSet> get_effective_tile_set() const;
// ---
TileMapLayer();
~TileMapLayer();
};

View file

@ -0,0 +1,148 @@
/**************************************************************************/
/* tile_map_layer_group.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_map_layer_group.h"
#include "core/core_string_names.h"
#include "scene/2d/tile_map_layer.h"
#include "scene/resources/tile_set.h"
#ifdef TOOLS_ENABLED
void TileMapLayerGroup::_cleanup_selected_layers() {
for (int i = 0; i < selected_layers.size(); i++) {
const String name = selected_layers[i];
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_node_or_null(name));
if (!layer) {
selected_layers.remove_at(i);
i--;
}
}
}
#endif // TOOLS_ENABLED
void TileMapLayerGroup::_tile_set_changed() {
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET);
}
}
update_configuration_warnings();
}
#ifdef TOOLS_ENABLED
void TileMapLayerGroup::set_selected_layers(Vector<StringName> p_layer_names) {
selected_layers = p_layer_names;
_cleanup_selected_layers();
// Update the layers modulation.
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS);
}
}
}
Vector<StringName> TileMapLayerGroup::get_selected_layers() const {
return selected_layers;
}
void TileMapLayerGroup::set_highlight_selected_layer(bool p_highlight_selected_layer) {
if (highlight_selected_layer == p_highlight_selected_layer) {
return;
}
highlight_selected_layer = p_highlight_selected_layer;
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED);
}
}
}
bool TileMapLayerGroup::is_highlighting_selected_layer() const {
return highlight_selected_layer;
}
#endif // TOOLS_ENABLED
void TileMapLayerGroup::remove_child_notify(Node *p_child) {
#ifdef TOOLS_ENABLED
_cleanup_selected_layers();
#endif // TOOLS_ENABLED
}
void TileMapLayerGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMapLayerGroup::set_tileset);
ClassDB::bind_method(D_METHOD("get_tileset"), &TileMapLayerGroup::get_tileset);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
}
void TileMapLayerGroup::set_tileset(const Ref<TileSet> &p_tileset) {
if (p_tileset == tile_set) {
return;
}
// Set the tileset, registering to its changes.
if (tile_set.is_valid()) {
tile_set->disconnect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
}
tile_set = p_tileset;
if (tile_set.is_valid()) {
tile_set->connect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
}
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET);
}
}
}
Ref<TileSet> TileMapLayerGroup::get_tileset() const {
return tile_set;
}
TileMapLayerGroup::~TileMapLayerGroup() {
if (tile_set.is_valid()) {
tile_set->disconnect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
}
}

View file

@ -0,0 +1,73 @@
/**************************************************************************/
/* tile_map_layer_group.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_MAP_LAYER_GROUP_H
#define TILE_MAP_LAYER_GROUP_H
#include "scene/2d/node_2d.h"
class TileSet;
class TileMapLayerGroup : public Node2D {
GDCLASS(TileMapLayerGroup, Node2D);
private:
mutable Vector<StringName> selected_layers;
bool highlight_selected_layer = true;
#ifdef TOOLS_ENABLED
void _cleanup_selected_layers();
#endif
void _tile_set_changed();
protected:
Ref<TileSet> tile_set;
virtual void remove_child_notify(Node *p_child) override;
static void _bind_methods();
public:
#ifdef TOOLS_ENABLED
// For editor use.
void set_selected_layers(Vector<StringName> p_layer_names);
Vector<StringName> get_selected_layers() const;
void set_highlight_selected_layer(bool p_highlight_selected_layer);
bool is_highlighting_selected_layer() const;
#endif
// Accessors.
void set_tileset(const Ref<TileSet> &p_tileset);
Ref<TileSet> get_tileset() const;
~TileMapLayerGroup();
};
#endif // TILE_MAP_LAYER_GROUP_H

View file

@ -783,6 +783,7 @@ void register_scene_types() {
GDREGISTER_CLASS(TileMapPattern);
GDREGISTER_CLASS(TileData);
GDREGISTER_CLASS(TileMap);
GDREGISTER_ABSTRACT_CLASS(TileMapLayerGroup);
GDREGISTER_CLASS(ParallaxBackground);
GDREGISTER_CLASS(ParallaxLayer);
GDREGISTER_CLASS(TouchScreenButton);

View file

@ -1478,7 +1478,7 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti
ERR_FAIL_V(TileMapCell());
}
Vector<Vector2> TileSet::get_tile_shape_polygon() {
Vector<Vector2> TileSet::get_tile_shape_polygon() const {
Vector<Vector2> points;
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
points.push_back(Vector2(-0.5, -0.5));
@ -1519,7 +1519,7 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() {
return points;
}
void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) {
void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) const {
if (tile_meshes_dirty) {
Vector<Vector2> shape = get_tile_shape_polygon();
Vector<Vector2> uvs;
@ -2165,7 +2165,40 @@ Vector2i TileSet::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh
}
}
Vector2i TileSet::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) {
TypedArray<Vector2i> TileSet::get_surrounding_cells(const Vector2i &p_coords) const {
TypedArray<Vector2i> around;
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_SIDE));
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
} else {
if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
} else {
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_SIDE));
around.push_back(get_neighbor_cell(p_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
}
}
return around;
}
Vector2i TileSet::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) const {
ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i());
ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i());
@ -2189,6 +2222,49 @@ Vector2i TileSet::map_pattern(const Vector2i &p_position_in_tilemap, const Vecto
return output;
}
void TileSet::draw_cells_outline(CanvasItem *p_canvas_item, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform) const {
Vector<Vector2> polygon = get_tile_shape_polygon();
for (const Vector2i &E : p_cells) {
Vector2 center = map_to_local(E);
#define DRAW_SIDE_IF_NEEDED(side, polygon_index_from, polygon_index_to) \
if (!p_cells.has(get_neighbor_cell(E, side))) { \
Vector2 from = p_transform.xform(center + polygon[polygon_index_from] * tile_size); \
Vector2 to = p_transform.xform(center + polygon[polygon_index_to] * tile_size); \
p_canvas_item->draw_line(from, to, p_color); \
}
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, 2, 3);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_LEFT_SIDE, 3, 0);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_SIDE, 0, 1);
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 2, 3);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 3, 0);
} else {
if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 2, 3);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_LEFT_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 5, 0);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, 4, 5);
} else {
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, 4, 5);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 5, 0);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_SIDE, 1, 2);
DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 2, 3);
}
}
}
#undef DRAW_SIDE_IF_NEEDED
}
Vector<Point2> TileSet::get_terrain_polygon(int p_terrain_set) {
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
return _get_square_terrain_polygon(tile_size);
@ -3185,6 +3261,115 @@ void TileSet::reset_state() {
tile_size = Size2i(16, 16);
}
Vector2i TileSet::transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) {
// Transform to stacked layout.
Vector2i output = p_coords;
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
SWAP(output.x, output.y);
}
switch (p_from_layout) {
case TileSet::TILE_LAYOUT_STACKED:
break;
case TileSet::TILE_LAYOUT_STACKED_OFFSET:
if (output.y % 2) {
output.x -= 1;
}
break;
case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
case TileSet::TILE_LAYOUT_STAIRS_DOWN:
if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if (output.y < 0 && bool(output.y % 2)) {
output = Vector2i(output.x + output.y / 2 - 1, output.y);
} else {
output = Vector2i(output.x + output.y / 2, output.y);
}
} else {
if (output.x < 0 && bool(output.x % 2)) {
output = Vector2i(output.x / 2 - 1, output.x + output.y * 2);
} else {
output = Vector2i(output.x / 2, output.x + output.y * 2);
}
}
break;
case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if ((output.x + output.y) < 0 && (output.x - output.y) % 2) {
output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x);
} else {
output = Vector2i((output.x + output.y) / 2, -output.x + output.y);
}
} else {
if ((output.x - output.y) < 0 && (output.x + output.y) % 2) {
output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y);
} else {
output = Vector2i((output.x - output.y) / 2, output.x + output.y);
}
}
break;
}
switch (p_to_layout) {
case TileSet::TILE_LAYOUT_STACKED:
break;
case TileSet::TILE_LAYOUT_STACKED_OFFSET:
if (output.y % 2) {
output.x += 1;
}
break;
case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
case TileSet::TILE_LAYOUT_STAIRS_DOWN:
if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if (output.y < 0 && (output.y % 2)) {
output = Vector2i(output.x - output.y / 2 + 1, output.y);
} else {
output = Vector2i(output.x - output.y / 2, output.y);
}
} else {
if (output.y % 2) {
if (output.y < 0) {
output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1);
} else {
output = Vector2i(2 * output.x + 1, -output.x + output.y / 2);
}
} else {
output = Vector2i(2 * output.x, -output.x + output.y / 2);
}
}
break;
case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
if (output.y % 2) {
if (output.y > 0) {
output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1);
} else {
output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2);
}
} else {
output = Vector2i(output.x - output.y / 2, output.x + output.y / 2);
}
} else {
if (output.y % 2) {
if (output.y < 0) {
output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1);
} else {
output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2);
}
} else {
output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2);
}
}
break;
}
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
SWAP(output.x, output.y);
}
return output;
}
const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1);
const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1;

View file

@ -319,7 +319,7 @@ private:
Ref<ArrayMesh> tile_lines_mesh;
Ref<ArrayMesh> tile_filled_mesh;
bool tile_meshes_dirty = true;
mutable bool tile_meshes_dirty = true;
// Physics
struct PhysicsLayer {
@ -527,15 +527,17 @@ public:
TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
// Helpers
Vector<Vector2> get_tile_shape_polygon();
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
Vector<Vector2> get_tile_shape_polygon() const;
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()) const;
// Used by TileMap/TileMapLayer
Vector2 map_to_local(const Vector2i &p_pos) const;
Vector2i local_to_map(const Vector2 &p_pos) const;
bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &p_coords) const;
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) const;
void draw_cells_outline(CanvasItem *p_canvas_item, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D()) const;
Vector<Point2> get_terrain_polygon(int p_terrain_set);
Vector<Point2> get_terrain_peering_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
@ -545,6 +547,9 @@ public:
// Resource management
virtual void reset_state() override;
// Helpers.
static Vector2i transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout);
TileSet();
~TileSet();
};