summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
authorRĂ©mi Verschelde <remi@verschelde.fr>2021-09-23 18:06:41 +0200
committerGitHub <noreply@github.com>2021-09-23 18:06:41 +0200
commit2ec1152b0fda21f2050b30e49630659697a5e68b (patch)
tree136bcdc26ea26291657ed715755ccd1857158971 /scene
parent8c8feb3ebdeeeee89133fc22516bab94801b5a09 (diff)
parentf9e6329496cafae1ee1f285d89f38abd6a2ed1bd (diff)
Merge pull request #52539 from groud/implement_animated_tiles
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/tile_map.cpp47
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/resources/tile_set.cpp391
-rw-r--r--scene/resources/tile_set.h36
4 files changed, 366 insertions, 110 deletions
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index a139a92ab4..40a3214967 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -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);
+ }
}
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 3ac50fc7cc..ca38baa550 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -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;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 918fc3fe9c..67cbd0e094 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -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 {
+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;
+}
+
+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 = tiles[p_atlas_coords].size_in_atlas;
+ 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() {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index ba7207241a..46cb1fb36d 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -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();