diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml index c72b638fc39..7daabcbfeeb 100644 --- a/doc/classes/TileSetAtlasSource.xml +++ b/doc/classes/TileSetAtlasSource.xml @@ -80,6 +80,13 @@ Returns how many animation frames has the tile at coordinates [param atlas_coords]. + + + + + Returns the [enum TileAnimationMode] of the tile at [param atlas_coords]. See also [method set_tile_animation_mode]. + + @@ -215,6 +222,14 @@ Sets how many animation frames the tile at coordinates [param atlas_coords] has. + + + + + + Sets the [enum TileAnimationMode] of the tile at [param atlas_coords] to [param mode]. See also [method get_tile_animation_mode]. + + @@ -250,4 +265,15 @@ Disabling this setting might lead a small performance improvement, as generating the internal texture requires both memory and processing time when the TileSetAtlasSource resource is modified. + + + Tile animations start at same time, looking identical. + + + Tile animations start at random times, looking varied. + + + Represents the size of the [enum TileAnimationMode] enum. + + diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index ff21e8a709f..4e8c28e9978 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -246,6 +246,12 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na } emit_signal(SNAME("changed"), "animation_speed"); return true; + } else if (p_name == "animation_mode") { + for (TileSelection tile : tiles) { + tile_set_atlas_source->set_tile_animation_mode(tile.tile, VariantCaster::cast(p_value)); + } + emit_signal(SNAME("changed"), "animation_mode"); + return true; } else if (p_name == "animation_frames_count") { for (TileSelection tile : tiles) { int frame_count = p_value; @@ -349,6 +355,9 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na } else if (p_name == "animation_speed") { r_ret = tile_set_atlas_source->get_tile_animation_speed(coords); return true; + } else if (p_name == "animation_mode") { + r_ret = tile_set_atlas_source->get_tile_animation_mode(coords); + return true; } else if (p_name == "animation_frames_count") { r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords); return true; @@ -417,6 +426,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(Listpush_back(PropertyInfo(Variant::INT, PNAME("animation_columns"))); p_list->push_back(PropertyInfo(Variant::VECTOR2I, PNAME("animation_separation"))); p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("animation_speed"))); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("animation_mode"), PROPERTY_HINT_ENUM, "Default,Random Start Times")); p_list->push_back(PropertyInfo(Variant::INT, PNAME("animation_frames_count"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_")); // Not optimal, but returns value for the first tile. This is similar to what MultiNodeEdit does. if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 5ea751f089a..c5ce45091e8 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1317,8 +1317,21 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList::List ci = prev_ci; } + Vector2 p_position = E_cell.key - tile_position; + Vector2 p_atlas_coords = c.get_atlas_coords(); + + // Random animation offset. + real_t p_random_animation_offset = 0.0; + if (atlas_source->get_tile_animation_mode(p_atlas_coords) != TileSetAtlasSource::TILE_ANIMATION_MODE_DEFAULT) { + Array to_hash; + to_hash.push_back(p_position); + to_hash.push_back(q.layer); + to_hash.push_back(Variant(this)); + p_random_animation_offset = RandomPCG(to_hash.hash()).randf(); + } + // Drawing the tile in the canvas item. - draw_tile(ci, E_cell.key - tile_position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, get_self_modulate(), tile_data); + draw_tile(ci, p_position, tile_set, c.source_id, p_atlas_coords, c.alternative_tile, -1, get_self_modulate(), tile_data, p_random_animation_offset); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { @@ -1436,7 +1449,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } } -void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override) { +void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_animation_offset) { 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)); @@ -1504,7 +1517,7 @@ void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref< 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); + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, p_animation_offset); Rect2i source_rect = atlas_source->get_runtime_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()); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 3c135d1317a..13c0eb4a95c 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -313,7 +313,7 @@ public: void set_quadrant_size(int p_size); int get_quadrant_size() const; - static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr); + static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_animation_offset = 0.0); // Layers management. int get_layers_count() const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index d74809a5127..3c0fb6b9fab 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -3948,6 +3948,9 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) } else if (components[1] == "animation_speed") { set_tile_animation_speed(coords, p_value); return true; + } else if (components[1] == "animation_mode") { + set_tile_animation_mode(coords, VariantCaster::cast(p_value)); + return true; } else if (components[1] == "animation_frames_count") { set_tile_animation_frames_count(coords, p_value); return true; @@ -4015,6 +4018,9 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "animation_speed") { r_ret = get_tile_animation_speed(coords); return true; + } else if (components[1] == "animation_mode") { + r_ret = get_tile_animation_mode(coords); + return true; } else if (components[1] == "animation_frames_count") { r_ret = get_tile_animation_frames_count(coords); return true; @@ -4090,6 +4096,13 @@ void TileSetAtlasSource::_get_property_list(List *p_list) const { } tile_property_list.push_back(property_info); + // animation_mode. + property_info = PropertyInfo(Variant::INT, "animation_mode", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); + if (E_tile.value.animation_mode == TILE_ANIMATION_MODE_DEFAULT) { + 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_NONE)); @@ -4252,6 +4265,20 @@ real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coord return tiles[p_atlas_coords].animation_speed; } +void TileSetAtlasSource::set_tile_animation_mode(const Vector2i p_atlas_coords, TileSetAtlasSource::TileAnimationMode p_mode) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + tiles[p_atlas_coords].animation_mode = p_mode; + + emit_signal(SNAME("changed")); +} + +TileSetAtlasSource::TileAnimationMode TileSetAtlasSource::get_tile_animation_mode(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TILE_ANIMATION_MODE_DEFAULT, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + return tiles[p_atlas_coords].animation_mode; +} + 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); @@ -4577,6 +4604,8 @@ void TileSetAtlasSource::_bind_methods() { 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_mode", "atlas_coords", "mode"), &TileSetAtlasSource::set_tile_animation_mode); + ClassDB::bind_method(D_METHOD("get_tile_animation_mode", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_mode); 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); @@ -4599,6 +4628,10 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_padded_texture"), &TileSetAtlasSource::_update_padded_texture); ClassDB::bind_method(D_METHOD("get_runtime_texture"), &TileSetAtlasSource::get_runtime_texture); ClassDB::bind_method(D_METHOD("get_runtime_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_runtime_tile_texture_region); + + BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_DEFAULT) + BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_RANDOM_START_TIMES) + BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_MAX) } TileSetAtlasSource::~TileSetAtlasSource() { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 337a945e7bb..70646ca5d87 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -592,6 +592,13 @@ public: class TileSetAtlasSource : public TileSetSource { GDCLASS(TileSetAtlasSource, TileSetSource); +public: + enum TileAnimationMode { + TILE_ANIMATION_MODE_DEFAULT, + TILE_ANIMATION_MODE_RANDOM_START_TIMES, + TILE_ANIMATION_MODE_MAX, + }; + private: struct TileAlternativesData { Vector2i size_in_atlas = Vector2i(1, 1); @@ -601,6 +608,7 @@ private: int animation_columns = 0; Vector2i animation_separation; real_t animation_speed = 1.0; + TileSetAtlasSource::TileAnimationMode animation_mode = TILE_ANIMATION_MODE_DEFAULT; LocalVector animation_frames_durations; // Alternatives @@ -701,6 +709,8 @@ public: 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_mode(const Vector2i p_atlas_coords, const TileSetAtlasSource::TileAnimationMode p_mode); + TileSetAtlasSource::TileAnimationMode get_tile_animation_mode(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); @@ -932,4 +942,6 @@ VARIANT_ENUM_CAST(TileSet::TileShape); VARIANT_ENUM_CAST(TileSet::TileLayout); VARIANT_ENUM_CAST(TileSet::TileOffsetAxis); +VARIANT_ENUM_CAST(TileSetAtlasSource::TileAnimationMode); + #endif // TILE_SET_H