Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug / Feature - allow instancing scenes from Tiled template files #17

Closed
dogezen opened this issue Aug 21, 2023 · 4 comments
Closed

Bug / Feature - allow instancing scenes from Tiled template files #17

dogezen opened this issue Aug 21, 2023 · 4 comments

Comments

@dogezen
Copy link

dogezen commented Aug 21, 2023

Describe the bug
In Tiled I can define a Tiled object that is of class instance and has a res_path that is of type File and points to a .tscn. Tiled will save the file path as a relative path from where the Tiled object is defined. If I save this Tiled object as a template .tx file and place the template in a different folder to the tilemap, then the res_path relative path will be pointing to the .tscn file from within the template directory .tx. This means that the instancing of the scene will fail because the importer expects the scene res_path to be relative to the map .tmx file.

Standard Example:

/maps/map1.tmx
/scenes/object.tscn

In map1.tmx I add an object layer with a point object of class instance and res_path = "../scenes/object.tscn"

If I then save that point object as a template:

/maps/templates/objtemplate.tx

then tiled makes res_path = "../../scenes/object.tscn and YATI will try to load this res-path from the /maps directory which is where the map1.tmx is being created. This will cause a scene import error because res_path is going up 2 directories now.

To Reproduce
Steps to reproduce the behavior:

  1. create a tilemap tmx file
  2. add a point Object in an object layer, setting class=instance and setting custom File property res_path pointing to a godot .tscn file
  3. right click on the object in Tiled and choose "Save as template"
  4. Save the template in a subfolder such as templates/
  5. Reload the godot project -> YATI will complain that it could not find the .tscn file.

Expected behavior

The issue is the res_path being used to load the .tscn:

# approx - line 676 in TilemapCreator.gd
var scene = load_resource_from_file(res_path)

YATI should adjust the res_path to be relative to the template.tx folder rather than the tilemap.tmx file when the object being instantiated is defined from within a template.

Examle map (zipped)
A zip file containing a small but complete example to reproduce the issue.
This could accelerate resolving it.

Working fix / hack

This is inside TilemapCreator.gd at about line 647 onwards. See the added # comments with the proposed fix.

	if obj.has("template"):
		var template_path = _base_path.path_join(obj["template"])
		var template_dict = preload("DictionaryBuilder.gd").new().get_dictionary(template_path)
		var template_tileset = null
		
		if template_dict.has("tilesets"):
			var tilesets = template_dict["tilesets"]
			var tileset_creator = preload("TilesetCreator.gd").new()
			tileset_creator.set_base_path(template_path)
			tileset_creator.set_map_parameters(Vector2i(_map_tile_width, _map_tile_height))
			if _map_wangset_to_terrain:
				tileset_creator.map_wangset_to_terrain()
			template_tileset = tileset_creator.create_from_dictionary_array(tilesets)

		if template_dict.has("objects"):
			for template_obj in template_dict["objects"]:
                                # FIX begin
				# since we are loading this object from a template file, save the template file's
				# directory path as additional data on the template_obj so that if the template
				# makes use of custom properties of type File, then the file paths can be computed correclty
                                # since they would be relative to the folder containing the template .tx file
				template_obj["template_dir_path"] = template_path.get_base_dir()
				handle_object(template_obj, layer_node, template_tileset, Vector2(obj_x, obj_y))

	# v1.2: New class 'instance'
	if godot_type == _godot_type.INSTANCE and not obj.has("template") and not obj.has("text"):
		var res_path = get_property(obj, "res_path", "file")
		if res_path == "":
			printerr("Object of class 'instance': Mandatory file property 'res_path' not found or invalid. -> Skipped")
			_error_count += 1
		else:
			if obj.has("template_dir_path"):
	                        # FIX Continued...
                                # when the INSTANCE is from a template, then res_path will be the relative path to the .tscn file
                                # from the folder containing the template.tx file 
				# hence, res_path should be template_dir_path + res_path
				res_path = obj.template_dir_path.path_join(res_path)
			var scene = load_resource_from_file(res_path)

Will try and create a minimal project to test this

@Kiamo2
Copy link
Owner

Kiamo2 commented Aug 21, 2023

Oh, yes.
As soon as you confirm it works I'll adapt your hack (as it's quite an elegant one).

@dogezen
Copy link
Author

dogezen commented Aug 21, 2023

Alright,

I have created a minimal example showcasing the issue. The attached zip has the fix code written, but commented out, thus showcasing the import error (note: you may need to open the tiled project, drag the map object and save in tiled to trigger a reload with the error).

yati_example.zip

You can then search for # TEMPLATE PATH FIX to find where to uncomment the code for the fix. After updating, you may need to restart godot, and ensure YATI reloads the map.

Without the fix you should see this error:
Screenshot 2023-08-21 at 17 12 53

@dogezen
Copy link
Author

dogezen commented Aug 21, 2023

I'm unsure how the fix would need to look like for C#

@Kiamo2
Copy link
Owner

Kiamo2 commented Aug 22, 2023

Fixed it in release 1.5.2.
Thanks for the solution!

@Kiamo2 Kiamo2 closed this as completed Aug 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants