mirror of
https://github.com/godotengine/godot.git
synced 2024-11-24 21:22:48 +00:00
Implement animated tiles
This commit is contained in:
parent
8c8feb3ebd
commit
f9e6329496
@ -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)">
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user