Implement animated tiles

This commit is contained in:
Gilles Roudière 2021-09-10 16:23:36 +02:00
parent 8c8feb3ebd
commit f9e6329496
9 changed files with 677 additions and 219 deletions

View file

@ -15,16 +15,6 @@
<tutorials>
</tutorials>
<methods>
<method name="can_move_tile_in_atlas" qualifiers="const">
<return type="bool" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="new_atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" />
<argument index="2" name="new_size" type="Vector2i" default="Vector2i(-1, -1)" />
<description>
Returns true if the tile at the [code]atlas_coords[/code] coordinates can be moved to the [code]new_atlas_coords[/code] coordinates with the [code]new_size[/code] size. This functions returns false if a tile is already present in the given area, or if this area is outside the atlas boundaries.
If [code]new_atlas_coords[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's coordinates. If [code]new_size[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's size.
</description>
</method>
<method name="clear_tiles_outside_texture">
<return type="void" />
<description>
@ -61,6 +51,49 @@
Returns the alternative ID a following call to [method create_alternative_tile] would return.
</description>
</method>
<method name="get_tile_animation_columns" qualifiers="const">
<return type="int" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<description>
Returns how many columns the tile at [code]atlas_coords[/code] has in its animation layout.
</description>
</method>
<method name="get_tile_animation_frame_duration" qualifiers="const">
<return type="float" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="frame_index" type="int" />
<description>
Returns the animation frame duration of frame [code]frame_index[/code] for the tile at coordinates [code]atlas_coords[/code].
</description>
</method>
<method name="get_tile_animation_frames_count" qualifiers="const">
<return type="int" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<description>
Returns how many animation frames has the tile at coordinates [code]atlas_coords[/code].
</description>
</method>
<method name="get_tile_animation_separation" qualifiers="const">
<return type="Vector2i" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<description>
Returns the separation (as in the atlas grid) between each frame of an animated tile at coordinates [code]atlas_coords[/code].
</description>
</method>
<method name="get_tile_animation_speed" qualifiers="const">
<return type="float" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<description>
Returns the animation speed of the tile at coordinates [code]atlas_coords[/code].
</description>
</method>
<method name="get_tile_animation_total_duration" qualifiers="const">
<return type="float" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<description>
Returns the sum of the sum of the frame durations of the tile at coordinates [code]atlas_coords[/code]. This value needs to be divided by the animation speed to get the actual animation loop duration.
</description>
</method>
<method name="get_tile_at_coords" qualifiers="const">
<return type="Vector2i" />
<argument index="0" name="atlas_coords" type="Vector2i" />
@ -86,8 +119,21 @@
<method name="get_tile_texture_region" qualifiers="const">
<return type="Rect2i" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="frame" type="int" default="0" />
<description>
Returns a tile's texture region in the atlas texture.
Returns a tile's texture region in the atlas texture. For animated tiles, a [code]frame[/code] argument might be provided for the different frames of the animation.
</description>
</method>
<method name="has_room_for_tile" qualifiers="const">
<return type="bool" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="size" type="Vector2i" />
<argument index="2" name="animation_columns" type="int" />
<argument index="3" name="animation_separation" type="Vector2i" />
<argument index="4" name="frames_count" type="int" />
<argument index="5" name="ignored_tile" type="Vector2i" default="Vector2i(-1, -1)" />
<description>
Returns whether there is enough room in an atlas to create/modify a tile with the given properties. If [code]ignored_tile[/code] is provided, act as is the given tile was not present in the atlas. This may be used when you want to modify a tile's properties.
</description>
</method>
<method name="has_tiles_outside_texture">
@ -104,7 +150,7 @@
<description>
Move the tile and its alternatives at the [code]atlas_coords[/code] coordinates to the [code]new_atlas_coords[/code] coordinates with the [code]new_size[/code] size. This functions will fail if a tile is already present in the given area.
If [code]new_atlas_coords[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's coordinates. If [code]new_size[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's size.
To avoid an error, first check if a move is possible using [method can_move_tile_in_atlas].
To avoid an error, first check if a move is possible using [method has_room_for_tile].
</description>
</method>
<method name="remove_alternative_tile">
@ -133,6 +179,47 @@
Calling this function with [code]alternative_id[/code] equals to 0 will fail, as the base tile alternative cannot be moved.
</description>
</method>
<method name="set_tile_animation_columns">
<return type="void" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="frame_columns" type="int" />
<description>
Sets the number of columns in the animation layout of the tile at coordinates [code]atlas_coords[/code]. If set to 0, then the different frames of the animation are laid out as a single horizontal line in the atlas.
</description>
</method>
<method name="set_tile_animation_frame_duration">
<return type="void" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="frame_index" type="int" />
<argument index="2" name="duration" type="float" />
<description>
Sets the animation frame duration of frame [code]frame_index[/code] for the tile at coordinates [code]atlas_coords[/code].
</description>
</method>
<method name="set_tile_animation_frames_count">
<return type="void" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="frames_count" type="int" />
<description>
Sets how many animation frames the tile at coordinates [code]atlas_coords[/code] has.
</description>
</method>
<method name="set_tile_animation_separation">
<return type="void" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="separation" type="Vector2i" />
<description>
Sets the margin (in grid tiles) between each tile in the animation layout of the tile at coordinates [code]atlas_coords[/code] has.
</description>
</method>
<method name="set_tile_animation_speed">
<return type="void" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="speed" type="float" />
<description>
Sets the animation speed of the tile at coordinates [code]atlas_coords[/code] has.
</description>
</method>
</methods>
<members>
<member name="margins" type="Vector2i" setter="set_margins" getter="get_margins" default="Vector2i(0, 0)">

View file

@ -94,12 +94,14 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
}
// Copy the texture.
Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id);
Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size);
if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size);
if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
}
output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
// Compute the atlas offset.

View file

@ -256,11 +256,15 @@ void TileAtlasView::_draw_base_tiles() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
// Update the y to max value.
Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
int animation_columns = tile_set_atlas_source->get_tile_animation_columns(atlas_coords);
Vector2i frame_coords = atlas_coords + (tile_set_atlas_source->get_tile_size_in_atlas(atlas_coords) + tile_set_atlas_source->get_tile_animation_separation(atlas_coords)) * ((animation_columns > 0) ? Vector2i(frame % animation_columns, frame / animation_columns) : Vector2i(frame, 0));
Vector2i offset_pos = (margins + (frame_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
// Draw the tile.
TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
// Draw the tile.
TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame);
}
}
}
}
@ -326,13 +330,18 @@ void TileAtlasView::_draw_base_tiles_shape_grid() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_effective_texture_offset(tile_id, 0);
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
// Draw only if the tile shape fits in the texture region
Transform2D tile_xform;
tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset);
tile_xform.set_scale(tile_shape_size);
tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, grid_color);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Color color = grid_color;
if (frame > 0) {
color.a *= 0.3;
}
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
Transform2D tile_xform;
tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset);
tile_xform.set_scale(tile_shape_size);
tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);
}
}
}
@ -360,7 +369,7 @@ void TileAtlasView::_draw_alternatives() {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
current_pos.x = 0;
int y_increment = 0;
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(atlas_coords);
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
@ -370,18 +379,18 @@ void TileAtlasView::_draw_alternatives() {
// Update the y to max value.
Vector2i offset_pos = current_pos;
if (transposed) {
offset_pos = (current_pos + Vector2(texture_region.size.y, texture_region.size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
y_increment = MAX(y_increment, texture_region.size.x);
offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
y_increment = MAX(y_increment, texture_region_size.x);
} else {
offset_pos = (current_pos + texture_region.size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
y_increment = MAX(y_increment, texture_region.size.y);
offset_pos = (current_pos + texture_region_size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
y_increment = MAX(y_increment, texture_region_size.y);
}
// Draw the tile.
TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
current_pos.x += transposed ? texture_region.size.y : texture_region.size.x;
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
}
if (alternatives_count > 1) {
current_pos.y += y_increment;

View file

@ -1456,13 +1456,25 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
if (E->get().source_id == source_id && E->get().alternative_tile == 0) {
tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), selection_color, false);
for (int frame = 0; frame < atlas->get_tile_animation_frames_count(E->get().get_atlas_coords()); frame++) {
Color color = selection_color;
if (frame > 0) {
color.a *= 0.3;
}
tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords(), frame), color, false);
}
}
}
// Draw the hovered tile.
if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords()), Color(1.0, 1.0, 1.0), false);
for (int frame = 0; frame < atlas->get_tile_animation_frames_count(hovered_tile.get_atlas_coords()); frame++) {
Color color = Color(1.0, 1.0, 1.0);
if (frame > 0) {
color.a *= 0.3;
}
tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords(), frame), color, false);
}
}
// Draw the selection rect.

View file

@ -135,45 +135,81 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
const Vector2i &coords = tiles.front()->get().tile;
const int &alternative = tiles.front()->get().alternative;
if (alternative == 0 && p_name == "atlas_coords") {
Vector2i as_vector2i = Vector2i(p_value);
ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, as_vector2i), false);
if (alternative == 0) {
Vector<String> components = String(p_name).split("/", true, 2);
if (p_name == "atlas_coords") {
Vector2i as_vector2i = Vector2i(p_value);
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(as_vector2i, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
ERR_FAIL_COND_V(!has_room_for_tile, false);
if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
tiles_set_atlas_source_editor->selection.clear();
tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 });
tiles_set_atlas_source_editor->_update_tile_id_label();
if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
tiles_set_atlas_source_editor->selection.clear();
tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 });
tiles_set_atlas_source_editor->_update_tile_id_label();
}
tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i);
tiles.clear();
tiles.insert({ as_vector2i, 0 });
emit_signal(SNAME("changed"), "atlas_coords");
return true;
} else if (p_name == "size_in_atlas") {
Vector2i as_vector2i = Vector2i(p_value);
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, as_vector2i, tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
ERR_FAIL_COND_V(!has_room_for_tile, false);
tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
emit_signal(SNAME("changed"), "size_in_atlas");
return true;
} else if (p_name == "animation_columns") {
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), p_value, tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
ERR_FAIL_COND_V(!has_room_for_tile, false);
tile_set_atlas_source->set_tile_animation_columns(coords, p_value);
emit_signal(SNAME("changed"), "animation_columns");
return true;
} else if (p_name == "animation_separation") {
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), p_value, tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
ERR_FAIL_COND_V(!has_room_for_tile, false);
tile_set_atlas_source->set_tile_animation_separation(coords, p_value);
emit_signal(SNAME("changed"), "animation_separation");
return true;
} else if (p_name == "animation_speed") {
tile_set_atlas_source->set_tile_animation_speed(coords, p_value);
emit_signal(SNAME("changed"), "animation_speed");
return true;
} else if (p_name == "animation_frames_count") {
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), p_value, coords);
ERR_FAIL_COND_V(!has_room_for_tile, false);
tile_set_atlas_source->set_tile_animation_frames_count(coords, p_value);
notify_property_list_changed();
emit_signal(SNAME("changed"), "animation_separation");
return true;
} else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) {
int frame = components[0].trim_prefix("animation_frame_").to_int();
ERR_FAIL_INDEX_V(frame, tile_set_atlas_source->get_tile_animation_frames_count(coords), false);
if (components[1] == "duration") {
tile_set_atlas_source->set_tile_animation_frame_duration(coords, frame, p_value);
return true;
}
}
} else if (alternative > 0) {
if (p_name == "alternative_id") {
int as_int = int(p_value);
ERR_FAIL_COND_V(as_int < 0, false);
ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords));
tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i);
tiles.clear();
tiles.insert({ as_vector2i, 0 });
emit_signal(SNAME("changed"), "atlas_coords");
return true;
} else if (alternative == 0 && p_name == "size_in_atlas") {
Vector2i as_vector2i = Vector2i(p_value);
ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i), false);
if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) {
tiles_set_atlas_source_editor->selection.clear();
tiles_set_atlas_source_editor->selection.insert({ coords, as_int });
}
tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
emit_signal(SNAME("changed"), "size_in_atlas");
return true;
} else if (alternative > 0 && p_name == "alternative_id") {
int as_int = int(p_value);
ERR_FAIL_COND_V(as_int < 0, false);
ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords));
int previous_alternative_tile = alternative;
tiles.clear();
tiles.insert({ coords, as_int }); // tiles must be updated before.
tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int);
if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) {
tiles_set_atlas_source_editor->selection.clear();
tiles_set_atlas_source_editor->selection.insert({ coords, as_int });
emit_signal(SNAME("changed"), "alternative_id");
return true;
}
int previous_alternative_tile = alternative;
tiles.clear();
tiles.insert({ coords, as_int }); // tiles must be updated before.
tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int);
emit_signal(SNAME("changed"), "alternative_id");
return true;
}
}
@ -206,15 +242,41 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na
const Vector2i &coords = tiles.front()->get().tile;
const int &alternative = tiles.front()->get().alternative;
if (alternative == 0 && p_name == "atlas_coords") {
r_ret = coords;
return true;
} else if (alternative == 0 && p_name == "size_in_atlas") {
r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords);
return true;
} else if (alternative > 0 && p_name == "alternative_id") {
r_ret = alternative;
return true;
if (alternative == 0) {
Vector<String> components = String(p_name).split("/", true, 2);
if (p_name == "atlas_coords") {
r_ret = coords;
return true;
} else if (p_name == "size_in_atlas") {
r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords);
return true;
} else if (p_name == "animation_columns") {
r_ret = tile_set_atlas_source->get_tile_animation_columns(coords);
return true;
} else if (p_name == "animation_separation") {
r_ret = tile_set_atlas_source->get_tile_animation_separation(coords);
return true;
} else if (p_name == "animation_speed") {
r_ret = tile_set_atlas_source->get_tile_animation_speed(coords);
return true;
} else if (p_name == "animation_frames_count") {
r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords);
return true;
} else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) {
int frame = components[0].trim_prefix("animation_frame_").to_int();
if (components[1] == "duration") {
if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(coords)) {
return false;
}
r_ret = tile_set_atlas_source->get_tile_animation_frame_duration(coords, frame);
return true;
}
}
} else if (alternative > 0) {
if (p_name == "alternative_id") {
r_ret = alternative;
return true;
}
}
}
@ -246,6 +308,20 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
if (tiles.front()->get().alternative == 0) {
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "atlas_coords", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, ""));
// Animation.
p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP));
p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_"));
if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) {
p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY));
} else {
for (int i = 0; i < tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile); i++) {
p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, ""));
}
}
} else {
p_list->push_back(PropertyInfo(Variant::INT, "alternative_id", PROPERTY_HINT_NONE, ""));
}
@ -846,7 +922,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
CursorShape cursor_shape = CURSOR_ARROW;
bool can_grow[4];
for (int i = 0; i < 4; i++) {
can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
for (int i = 0; i < 4; i++) {
@ -869,7 +945,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
new_rect.size += Vector2i(1, 1);
// Check if the new tile can fit in the new rect.
if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
// Move and resize the tile.
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
drag_current_tile = new_rect.position;
@ -908,7 +984,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset);
coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) {
if (drag_current_tile != coords && tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile), tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords);
selection.clear();
selection.insert({ coords, 0 });
@ -948,7 +1024,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1)));
}
if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
selection.clear();
selection.insert({ new_rect.position, 0 });
@ -1056,7 +1132,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
CursorShape cursor_shape = CURSOR_ARROW;
bool can_grow[4];
for (int i = 0; i < 4; i++) {
can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
for (int i = 0; i < 4; i++) {
@ -1511,37 +1587,45 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
TileSelection selected = E->get();
if (selected.alternative == 0) {
// Draw the rect.
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
tile_atlas_control->draw_rect(region, selection_color, false);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(selected.tile); frame++) {
Color color = selection_color;
if (frame > 0) {
color.a *= 0.3;
}
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile, frame);
tile_atlas_control->draw_rect(region, color, false);
}
}
}
if (selection.size() == 1) {
// Draw the resize handles (only when it's possible to expand).
TileSelection selected = selection.front()->get();
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
bool can_grow[4];
for (int i = 0; i < 4; i++) {
can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
for (int i = 0; i < 4; i++) {
Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
if (can_grow[i] && can_grow[(i + 3) % 4]) {
tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false);
} else {
tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false);
if (selected.alternative == 0) {
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
bool can_grow[4];
for (int i = 0; i < 4; i++) {
can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
if (can_grow[i]) {
tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
} else {
tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
for (int i = 0; i < 4; i++) {
Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
if (can_grow[i] && can_grow[(i + 3) % 4]) {
tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false);
} else {
tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false);
}
Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
if (can_grow[i]) {
tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
} else {
tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
}
}
}
}
@ -1550,7 +1634,9 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
if (drag_type == DRAG_TYPE_REMOVE_TILES) {
// Draw the tiles to be removed.
for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) {
tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get()), Color(0.0, 0.0, 0.0), false);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(E->get()); frame++) {
tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get(), frame), Color(0.0, 0.0, 0.0), false);
}
}
} else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) {
// Draw tiles to be removed.
@ -1617,7 +1703,13 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i hovered_tile = tile_set_atlas_source->get_tile_at_coords(hovered_base_tile_coords);
if (hovered_tile != TileSetSource::INVALID_ATLAS_COORDS) {
// Draw existing hovered tile.
tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(hovered_tile); frame++) {
Color color = Color(1.0, 1.0, 1.0);
if (frame > 0) {
color.a *= 0.3;
}
tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile, frame), color, false);
}
} else {
// Draw empty tile, only in add/remove tiles mode.
if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) {

View file

@ -891,7 +891,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
modulate.a *= 0.3;
}
}
draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, modulate);
draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate);
// --- Occluders ---
for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) {
@ -1008,15 +1008,19 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
}
}
void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) {
void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Check for the frame.
if (p_frame >= 0) {
ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords));
}
// Get the texture.
Ref<Texture2D> tex = atlas_source->get_texture();
if (!tex.is_valid()) {
@ -1032,13 +1036,16 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
// Get tile data.
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
// Compute the offset
Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords);
// Get the tile modulation.
Color modulate = tile_data->get_modulate();
modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a);
// Compute the offset.
Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile);
// Compute the destination rectangle in the CanvasItem.
// Get destination rect.
Rect2 dest_rect;
dest_rect.size = source_rect.size;
dest_rect.size = atlas_source->get_tile_texture_region(p_atlas_coords).size;
dest_rect.size.x += FP_ADJUST;
dest_rect.size.y += FP_ADJUST;
@ -1057,12 +1064,28 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
dest_rect.size.y = -dest_rect.size.y;
}
// Get the tile modulation.
Color modulate = tile_data->get_modulate();
modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a);
// Draw the tile.
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
if (p_frame >= 0) {
Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, p_frame);
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
} else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) {
Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, 0);
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
} else {
real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords);
real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed;
real_t time = 0.0;
for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) {
real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed;
RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, 0.0);
Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, frame);
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
time += frame_duration;
}
RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0);
}
}
}

View file

@ -305,7 +305,7 @@ public:
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0));
static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0));
// Layers management.
int get_layers_count() const;

View file

@ -3119,7 +3119,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
}
bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
Vector<String> components = String(p_name).split("/", true, 3);
// Compute the vector2i if we have coordinates.
Vector<String> coords_split = components[0].split(":");
@ -3138,8 +3138,32 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value)
// Properties.
if (components[1] == "size_in_atlas") {
move_tile_in_atlas(coords, coords, p_value);
return true;
} else if (components[1] == "next_alternative_id") {
tiles[coords].next_alternative_id = p_value;
return true;
} else if (components[1] == "animation_columns") {
set_tile_animation_columns(coords, p_value);
return true;
} else if (components[1] == "animation_separation") {
set_tile_animation_separation(coords, p_value);
return true;
} else if (components[1] == "animation_speed") {
set_tile_animation_speed(coords, p_value);
return true;
} else if (components[1] == "animation_frames_count") {
set_tile_animation_frames_count(coords, p_value);
return true;
} else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) {
int frame = components[1].trim_prefix("animation_frame_").to_int();
if (components[2] == "duration") {
if (frame >= get_tile_animation_frames_count(coords)) {
set_tile_animation_frames_count(coords, frame + 1);
}
set_tile_animation_frame_duration(coords, frame, p_value);
return true;
}
return false;
} else if (components[1].is_valid_int()) {
int alternative_id = components[1].to_int();
if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
@ -3185,6 +3209,28 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components[1] == "next_alternative_id") {
r_ret = tiles[coords].next_alternative_id;
return true;
} else if (components[1] == "animation_columns") {
r_ret = get_tile_animation_columns(coords);
return true;
} else if (components[1] == "animation_separation") {
r_ret = get_tile_animation_separation(coords);
return true;
} else if (components[1] == "animation_speed") {
r_ret = get_tile_animation_speed(coords);
return true;
} else if (components[1] == "animation_frames_count") {
r_ret = get_tile_animation_frames_count(coords);
return true;
} else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) {
int frame = components[1].trim_prefix("animation_frame_").to_int();
if (frame < 0 || frame >= get_tile_animation_frames_count(coords)) {
return false;
}
if (components[2] == "duration") {
r_ret = get_tile_animation_frame_duration(coords, frame);
return true;
}
return false;
} else if (components[1].is_valid_int()) {
int alternative_id = components[1].to_int();
if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) {
@ -3226,6 +3272,40 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
}
tile_property_list.push_back(property_info);
// animation_columns.
property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
if (E_tile->get().animation_columns == 0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_separation.
property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
if (E_tile->get().animation_separation == Vector2i()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_speed.
property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
if (E_tile->get().animation_speed == 1.0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_frames_count.
tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK));
// animation_frame_*.
bool store_durations = tiles[E_tile->key()].animation_frames_durations.size() >= 2;
for (int i = 0; i < (int)tiles[E_tile->key()].animation_frames_durations.size(); i++) {
property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
if (!store_durations) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
}
for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
// Add a dummy property to show the alternative exists.
tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
@ -3234,11 +3314,10 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> alternative_property_list;
E_alternative->get()->get_property_list(&alternative_property_list);
for (PropertyInfo &alternative_property_info : alternative_property_list) {
bool valid;
Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid);
Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name);
Variant value = E_alternative->get()->get(alternative_property_info.name);
if (valid && value == default_value) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name);
tile_property_list.push_back(alternative_property_info);
@ -3257,33 +3336,27 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
// Create a tile if it does not exists.
ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0);
ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
for (int x = 0; x < p_size.x; x++) {
for (int y = 0; y < p_size.y; y++) {
Vector2i coords = p_atlas_coords + Vector2i(x, y);
ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords));
}
}
bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1);
ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover.");
// Initialize the tile data.
TileAlternativesData tad;
tad.size_in_atlas = p_size;
tad.animation_frames_durations.push_back(1.0);
tad.alternatives[0] = memnew(TileData);
tad.alternatives[0]->set_tile_set(tile_set);
tad.alternatives[0]->set_allow_transform(false);
tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed));
tad.alternatives[0]->notify_property_list_changed();
tad.alternatives_ids.append(0);
// Create and resize the tile.
tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData());
tiles.insert(p_atlas_coords, tad);
tiles_ids.append(p_atlas_coords);
tiles_ids.sort();
tiles[p_atlas_coords].size_in_atlas = p_size;
tiles[p_atlas_coords].alternatives[0] = memnew(TileData);
tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set);
tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false);
tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed));
tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed();
tiles[p_atlas_coords].alternatives_ids.append(0);
// Add all covered positions to the mapping cache
for (int x = 0; x < p_size.x; x++) {
for (int y = 0; y < p_size.y; y++) {
Vector2i coords = p_atlas_coords + Vector2i(x, y);
_coords_mapping_cache[coords] = p_atlas_coords;
}
}
_create_coords_mapping_cache(p_atlas_coords);
emit_signal(SNAME("changed"));
}
@ -3292,14 +3365,7 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
// Remove all covered positions from the mapping cache
Size2i size = tiles[p_atlas_coords].size_in_atlas;
for (int x = 0; x < size.x; x++) {
for (int y = 0; y < size.y; y++) {
Vector2i coords = p_atlas_coords + Vector2i(x, y);
_coords_mapping_cache.erase(coords);
}
}
_clear_coords_mapping_cache(p_atlas_coords);
// Free tile data.
for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
@ -3326,6 +3392,118 @@ Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const {
return _coords_mapping_cache[p_atlas_coords];
}
void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_COND(p_frame_columns < 0);
TileAlternativesData &tad = tiles[p_atlas_coords];
bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, p_frame_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords);
ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover.");
_clear_coords_mapping_cache(p_atlas_coords);
tiles[p_atlas_coords].animation_columns = p_frame_columns;
_create_coords_mapping_cache(p_atlas_coords);
emit_signal(SNAME("changed"));
}
int TileSetAtlasSource::get_tile_animation_columns(const Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
return tiles[p_atlas_coords].animation_columns;
}
void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_COND(p_separation.x < 0 || p_separation.y < 0);
TileAlternativesData &tad = tiles[p_atlas_coords];
bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, p_separation, tad.animation_frames_durations.size(), p_atlas_coords);
ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover.");
_clear_coords_mapping_cache(p_atlas_coords);
tiles[p_atlas_coords].animation_separation = p_separation;
_create_coords_mapping_cache(p_atlas_coords);
emit_signal(SNAME("changed"));
}
Vector2i TileSetAtlasSource::get_tile_animation_separation(const Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
return tiles[p_atlas_coords].animation_separation;
}
void TileSetAtlasSource::set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_COND(p_speed <= 0);
tiles[p_atlas_coords].animation_speed = p_speed;
emit_signal(SNAME("changed"));
}
real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1.0, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
return tiles[p_atlas_coords].animation_speed;
}
void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_COND(p_frames_count < 1);
TileAlternativesData &tad = tiles[p_atlas_coords];
bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords);
ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover.");
_clear_coords_mapping_cache(p_atlas_coords);
int old_size = tiles[p_atlas_coords].animation_frames_durations.size();
tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count);
for (int i = old_size; i < p_frames_count; i++) {
tiles[p_atlas_coords].animation_frames_durations[i] = 1.0;
}
_create_coords_mapping_cache(p_atlas_coords);
notify_property_list_changed();
emit_signal(SNAME("changed"));
}
int TileSetAtlasSource::get_tile_animation_frames_count(const Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
return tiles[p_atlas_coords].animation_frames_durations.size();
}
void TileSetAtlasSource::set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_INDEX(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size());
ERR_FAIL_COND(p_duration <= 0.0);
tiles[p_atlas_coords].animation_frames_durations[p_frame_index] = p_duration;
emit_signal(SNAME("changed"));
}
real_t TileSetAtlasSource::get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_INDEX_V(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size(), 0.0);
return tiles[p_atlas_coords].animation_frames_durations[p_frame_index];
}
real_t TileSetAtlasSource::get_tile_animation_total_duration(const Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
real_t sum = 0.0;
for (int frame = 0; frame < (int)tiles[p_atlas_coords].animation_frames_durations.size(); frame++) {
sum += tiles[p_atlas_coords].animation_frames_durations[frame];
}
return sum;
}
Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
@ -3341,16 +3519,34 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const {
return tiles_ids[p_index];
}
Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const {
for (int frame = 0; frame < p_frames_count; frame++) {
Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0));
for (int x = 0; x < p_size.x; x++) {
for (int y = 0; y < p_size.y; y++) {
Vector2i coords = frame_coords + Vector2i(x, y);
if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) {
return false;
}
}
}
}
return true;
}
Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas;
Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i());
const TileAlternativesData &tad = tiles[p_atlas_coords];
Vector2i size_in_atlas = tad.size_in_atlas;
Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation));
Vector2i frame_coords = p_atlas_coords + (size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0));
Vector2 origin = margins + (frame_coords * (texture_region_size + separation));
return Rect2(origin, region_size);
;
}
Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const {
@ -3369,56 +3565,23 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
return effective_texture_offset;
}
bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) {
return false;
}
Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false);
Size2i grid_size = get_atlas_grid_size();
if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) {
return false;
}
Rect2i new_rect = Rect2i(new_atlas_coords, size);
// Check if the new tile can fit in the new rect.
for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) {
for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) {
Vector2i coords = get_tile_at_coords(Vector2i(x, y));
if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) {
return false;
}
}
}
return true;
}
void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) {
bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size);
ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size));
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
TileAlternativesData &tad = tiles[p_atlas_coords];
// Compute the actual new rect from arguments.
Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
Vector2i new_size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tad.size_in_atlas;
if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) {
if (new_atlas_coords == p_atlas_coords && new_size == tad.size_in_atlas) {
return;
}
// Remove all covered positions from the mapping cache.
Size2i old_size = tiles[p_atlas_coords].size_in_atlas;
for (int x = 0; x < old_size.x; x++) {
for (int y = 0; y < old_size.y; y++) {
Vector2i coords = p_atlas_coords + Vector2i(x, y);
_coords_mapping_cache.erase(coords);
}
}
bool room_for_tile = has_room_for_tile(new_atlas_coords, new_size, tad.animation_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords);
ERR_FAIL_COND_MSG(!room_for_tile, vformat("Cannot move tile at position %s with size %s. Tile already present.", new_atlas_coords, new_size));
_clear_coords_mapping_cache(p_atlas_coords);
// Move the tile and update its size.
if (new_atlas_coords != p_atlas_coords) {
@ -3429,15 +3592,9 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
tiles_ids.append(new_atlas_coords);
tiles_ids.sort();
}
tiles[new_atlas_coords].size_in_atlas = size;
tiles[new_atlas_coords].size_in_atlas = new_size;
// Add all covered positions to the mapping cache again.
for (int x = 0; x < size.x; x++) {
for (int y = 0; y < size.y; y++) {
Vector2i coords = new_atlas_coords + Vector2i(x, y);
_coords_mapping_cache[coords] = new_atlas_coords;
}
}
_create_coords_mapping_cache(new_atlas_coords);
emit_signal(SNAME("changed"));
}
@ -3566,12 +3723,25 @@ void TileSetAtlasSource::_bind_methods() {
// Base tiles
ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1)));
ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS));
ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns);
ClassDB::bind_method(D_METHOD("get_tile_animation_columns", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_columns);
ClassDB::bind_method(D_METHOD("set_tile_animation_separation", "atlas_coords", "separation"), &TileSetAtlasSource::set_tile_animation_separation);
ClassDB::bind_method(D_METHOD("get_tile_animation_separation", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_separation);
ClassDB::bind_method(D_METHOD("set_tile_animation_speed", "atlas_coords", "speed"), &TileSetAtlasSource::set_tile_animation_speed);
ClassDB::bind_method(D_METHOD("get_tile_animation_speed", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_speed);
ClassDB::bind_method(D_METHOD("set_tile_animation_frames_count", "atlas_coords", "frames_count"), &TileSetAtlasSource::set_tile_animation_frames_count);
ClassDB::bind_method(D_METHOD("get_tile_animation_frames_count", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_frames_count);
ClassDB::bind_method(D_METHOD("set_tile_animation_frame_duration", "atlas_coords", "frame_index", "duration"), &TileSetAtlasSource::set_tile_animation_frame_duration);
ClassDB::bind_method(D_METHOD("get_tile_animation_frame_duration", "atlas_coords", "frame_index"), &TileSetAtlasSource::get_tile_animation_frame_duration);
ClassDB::bind_method(D_METHOD("get_tile_animation_total_duration", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_total_duration);
// Alternative tiles
ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile);
@ -3584,7 +3754,7 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region);
ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
}
TileSetAtlasSource::~TileSetAtlasSource() {
@ -3618,6 +3788,45 @@ void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coo
};
}
void TileSetAtlasSource::_clear_coords_mapping_cache(Vector2i p_atlas_coords) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
TileAlternativesData &tad = tiles[p_atlas_coords];
for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) {
Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0));
for (int x = 0; x < tad.size_in_atlas.x; x++) {
for (int y = 0; y < tad.size_in_atlas.y; y++) {
Vector2i coords = frame_coords + Vector2i(x, y);
if (!_coords_mapping_cache.has(coords)) {
WARN_PRINT(vformat("TileSetAtlasSource has no cached tile at position %s, the position cache might be corrupted.", coords));
} else {
if (_coords_mapping_cache[coords] != p_atlas_coords) {
WARN_PRINT(vformat("The position cache at position %s is pointing to a wrong tile, the position cache might be corrupted.", coords));
}
_coords_mapping_cache.erase(coords);
}
}
}
}
}
void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
TileAlternativesData &tad = tiles[p_atlas_coords];
for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) {
Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0));
for (int x = 0; x < tad.size_in_atlas.x; x++) {
for (int y = 0; y < tad.size_in_atlas.y; y++) {
Vector2i coords = frame_coords + Vector2i(x, y);
if (_coords_mapping_cache.has(coords)) {
WARN_PRINT(vformat("The cache already has a tile for position %s, the position cache might be corrupted.", coords));
}
_coords_mapping_cache[coords] = p_atlas_coords;
}
}
}
}
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
void TileSetScenesCollectionSource::_compute_next_alternative_id() {

View file

@ -447,16 +447,23 @@ public:
class TileSetAtlasSource : public TileSetSource {
GDCLASS(TileSetAtlasSource, TileSetSource);
public:
private:
struct TileAlternativesData {
Vector2i size_in_atlas = Vector2i(1, 1);
Vector2i texture_offset;
// Animation
int animation_columns = 0;
Vector2i animation_separation;
real_t animation_speed = 1.0;
LocalVector<real_t> animation_frames_durations;
// Alternatives
Map<int, TileData *> alternatives;
Vector<int> alternatives_ids;
int next_alternative_id = 1;
};
private:
Ref<Texture2D> texture;
Vector2i margins;
Vector2i separation;
@ -471,6 +478,9 @@ private:
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
void _create_coords_mapping_cache(Vector2i p_atlas_coords);
void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@ -513,18 +523,32 @@ public:
Vector2i get_texture_region_size() const;
// Base tiles.
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does.
void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1));
void remove_tile(Vector2i p_atlas_coords);
virtual bool has_tile(Vector2i p_atlas_coords) const override;
bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const;
void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1));
Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const;
virtual int get_tiles_count() const override;
virtual Vector2i get_tile_id(int p_index) const override;
bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const;
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
// Animation.
void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns);
int get_tile_animation_columns(const Vector2i p_atlas_coords) const;
void set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation);
Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const;
void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed);
real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const;
void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count);
int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const;
void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration);
real_t get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const;
real_t get_tile_animation_total_duration(const Vector2i p_atlas_coords) const;
// Alternative tiles.
int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1);
void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile);
@ -542,7 +566,7 @@ public:
Vector2i get_atlas_grid_size() const;
bool has_tiles_outside_texture();
void clear_tiles_outside_texture();
Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const;
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
~TileSetAtlasSource();