godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.35k stars 21.06k forks source link

Missing properties after saving or duplicating TileSetAtlasSource #74356

Open SaronTetra opened 1 year ago

SaronTetra commented 1 year ago

Godot version

4.0-stable

System information

Windows 10

Issue description

Using ResourceSaver.save() or duplicate() on TileSetAtlasResource does not duplicate all properties. Resulting resources after saving are missing Physics Layer and Terrain Layer information

Resource Action Result
TileSet save correct
TileSet save + duplicate() correct
TileSet save + duplicate(true) missing info
TileSetAtlasResource save missing info
TileSetAtlasResource save + duplicate() missing info
TileSetAtlasResource save + duplicate(true) missing info

Part of saved TileSet without duplicate:

[gd_resource type="TileSet" load_steps=3 format=3]

[ext_resource type="Texture2D" path="res://tiles.png" id="1_7xgb5"]

[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_fsxqu"]
texture = ExtResource("1_7xgb5")
texture_region_size = Vector2i(12, 12)
0:0/0 = 0
0:0/0/terrain_set = 0
0:0/0/terrain = 0
0:0/0/physics_layer_0/linear_velocity = Vector2(0, 0)
0:0/0/physics_layer_0/angular_velocity = 0.0
0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-6, -6, 6, -6, 6, 6, -6, 6)

Part of saved TileSetAtlasResource without duplicate:

[gd_resource type="TileSetAtlasSource" load_steps=2 format=3]

[ext_resource type="Texture2D" path="res://tiles.png" id="1_mf5ni"]

[resource]
texture = ExtResource("1_mf5ni")
texture_region_size = Vector2i(12, 12)
0:0/0 = 0
0:0/0/terrain_set = 0
0:0/0/terrain = 0

Steps to reproduce

For minimal project:

From scratch:

func _ready(): var new_tileset = preload("res://tile_set.tres") var new_atlas = new_tileset.get_source(0)

ResourceSaver.save(new_tileset, "out/tile_set_saved.tres")
ResourceSaver.save(new_tileset.duplicate(), "out/tile_set_duplicated.tres")
ResourceSaver.save(new_tileset.duplicate(true), "out/tile_set_duplicated_true.tres")

ResourceSaver.save(new_atlas, "out/atlas_saved.tres")
ResourceSaver.save(new_atlas.duplicate(), "out/atlas_duplicated.tres")
ResourceSaver.save(new_atlas.duplicate(true), "out/atlas_duplicated_true.tres")

- Run the script

### Minimal reproduction project

[TileSetAtlasSource-Duplicate.zip](https://github.com/godotengine/godot/files/10888197/TileSetAtlasSource-Duplicate.zip)
Loufe commented 1 year ago

I'm experiencing the same bug, thanks for sending such a great write-up.

akien-mga commented 1 year ago

Can you still reproduce this in 4.0.3 and 4.1-beta3 or later?

Rindbee commented 1 year ago

The property lists returned by get_property_list() of new_atlas and new_atlas.duplicate() are different.

Rindbee commented 1 year ago

TileSetAtlasSource can only be assigned to one TileSet. If it is assigned to a new TileSet during duplicate(), this will cause the TileSetAtlasSource to be removed from the previous TileSet. The information loss when using duplicate(true) to copy TileSet is because the original TileSet (new_tileset) has lost TileSetAtlasSource when duplicate().

https://github.com/godotengine/godot/blob/46424488edc341b65467ee7fd3ac423e4d49ad34/scene/resources/tile_set.cpp#L3159-L3167

https://github.com/godotengine/godot/blob/46424488edc341b65467ee7fd3ac423e4d49ad34/scene/resources/tile_set.cpp#L482-L487

The missing information in TileSetAtlasSource is saved via TileData, which is also TileSet specific, and if the tile_set is missing, the value cannot be read.

https://github.com/godotengine/godot/blob/46424488edc341b65467ee7fd3ac423e4d49ad34/scene/resources/tile_set.cpp#L4036

https://github.com/godotengine/godot/blob/46424488edc341b65467ee7fd3ac423e4d49ad34/scene/resources/tile_set.cpp#L5637