Browse Source

Merge pull request #77257 from rakkarage/tile_animation_sync

Add `TileSetAtlasSource::TileAnimationMode` options and allow to shuffle tile animations
Yuri Sizov 2 years ago
parent
commit
a927b22f65

+ 26 - 0
doc/classes/TileSetAtlasSource.xml

@@ -80,6 +80,13 @@
 				Returns how many animation frames has the tile at coordinates [param atlas_coords].
 			</description>
 		</method>
+		<method name="get_tile_animation_mode" qualifiers="const">
+			<return type="int" enum="TileSetAtlasSource.TileAnimationMode" />
+			<param index="0" name="atlas_coords" type="Vector2i" />
+			<description>
+				Returns the [enum TileAnimationMode] of the tile at [param atlas_coords]. See also [method set_tile_animation_mode].
+			</description>
+		</method>
 		<method name="get_tile_animation_separation" qualifiers="const">
 			<return type="Vector2i" />
 			<param index="0" name="atlas_coords" type="Vector2i" />
@@ -215,6 +222,14 @@
 				Sets how many animation frames the tile at coordinates [param atlas_coords] has.
 			</description>
 		</method>
+		<method name="set_tile_animation_mode">
+			<return type="void" />
+			<param index="0" name="atlas_coords" type="Vector2i" />
+			<param index="1" name="mode" type="int" enum="TileSetAtlasSource.TileAnimationMode" />
+			<description>
+				Sets the [enum TileAnimationMode] of the tile at [param atlas_coords] to [param mode]. See also [method get_tile_animation_mode].
+			</description>
+		</method>
 		<method name="set_tile_animation_separation">
 			<return type="void" />
 			<param index="0" name="atlas_coords" type="Vector2i" />
@@ -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.
 		</member>
 	</members>
+	<constants>
+		<constant name="TILE_ANIMATION_MODE_DEFAULT" value="0" enum="TileAnimationMode">
+			Tile animations start at same time, looking identical.
+		</constant>
+		<constant name="TILE_ANIMATION_MODE_RANDOM_START_TIMES" value="1" enum="TileAnimationMode">
+			Tile animations start at random times, looking varied.
+		</constant>
+		<constant name="TILE_ANIMATION_MODE_MAX" value="2" enum="TileAnimationMode">
+			Represents the size of the [enum TileAnimationMode] enum.
+		</constant>
+	</constants>
 </class>

+ 10 - 0
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<TileSetAtlasSource::TileAnimationMode>::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(List<Pro
 		p_list->push_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) {

+ 16 - 3
scene/2d/tile_map.cpp

@@ -1317,8 +1317,21 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::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<TileSet> 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<TileSet> 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());

+ 1 - 1
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<TileSet> 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<TileSet> 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;

+ 33 - 0
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<TileSetAtlasSource::TileAnimationMode>::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<PropertyInfo> *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() {

+ 12 - 0
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<real_t> 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