diff --git a/README.md b/README.md index 2d34812..1f1729e 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,30 @@ If you prefer watching check the video in YouTube: [How to export from Tiled To Godot 3.2](https://youtu.be/4jSFAXIa_Lo) +The exporter needs to know how to set resource paths correctly for Godot before exporting. There are several ways to do this: + +- If your Tiled files exist in your Godot project structure alongside the exported files, the exporter can automatically determine the resource paths. There is also the option to use the `projectRoot` custom property if needed. +- If your Tiled files exist outside your Godot project structure, you will need to set the `relativePath` custom property so the exporter can determine resource paths. + +**_!!! Pay attention to the "/" instead of standard windows "\\". Single Backslash "\\" is used for escaping special symbols._** + +### Setting relativePath + +The custom property `relativePath : string` contains the relative path to the exported files from the Godot project root. +The value of `relativePath` is transformed to a resource path which Godot uses. + +* When placed on a Tiled TileMap (.tmx) object, it will define where the Tileset objects (.tres) are located in the exported TileMap (.tscn). +* When placed on a Tiled TileSet (.tsx) object, it will define where the Image is located in the exported TileMap (.tres). + +Example: +If the location of an exported tileset is: `C:/project/maps/level1/tileset.tres` +Then the `relativePath` custom property would be: `/maps/level1` + ### Setting projectRoot `res://` The exporter needs to know where `res://` is so when you export to a subfolder in your Godot project all the relative paths for the resources `(res://)` are set correctly and relative to the project root. `res://` is equivalent to the location of `project.godot` in your Godot project. -**By default, the exporter expects the Tiled files to be saved in a subfolder of your Godot project and will determine the project root automatically. Setting `projectRoot` is no longer required.** +**By default, the exporter expects the Tiled files to be saved in a subfolder of your Godot project next to the exported files and will determine the project root automatically. Setting `projectRoot` is no longer required in this scenario.** If needed, you can override this path with a custom property `projectRoot : string` that is either relative to the file you are exporting (starting with a `.`) or an absolute path. Either way, the value of `projectRoot` is transformed to the `res://` resource path which Godot uses and should be equivalent to the location of `project.godot`. @@ -69,8 +88,11 @@ Either way, the value of `projectRoot` is transformed to the `res://` resource p * When placed on a Tiled TileMap (.tmx) object it will be used as the root to determine the relative resource path to the Tileset objects (.tres) used in the exported TileMap (.tscn). * When placed on a Tiled TileSet (.tsx) object it will be used as the root to determine the relative resource path to the Image used in the exported TileMap (.tres). -**_!!! Pay attention to the "/" instead of standard windows "\\". -Single Backslash "\\" is used for escaping special symbols._** +Example: +If the location of an exported tileset is: `C:/project/maps/level1/tileset.tres` +Then the `projectRoot` custom property would be: `C:/project` + +### Exporting to Godot If everything is fine when you go to _File -> Export As_, a new option should exist: diff --git a/export_to_godot_tilemap.js b/export_to_godot_tilemap.js index 3dc7eda..be5a7bf 100644 --- a/export_to_godot_tilemap.js +++ b/export_to_godot_tilemap.js @@ -5,8 +5,6 @@ class GodotTilemapExporter { constructor(map, fileName) { this.map = map; this.fileName = fileName; - // noinspection JSUnresolvedFunction - this.projectRoot = getResPath(this.map.property("projectRoot"), fileName); this.tileOffset = 65536; this.tileMapsString = ""; this.tilesetsString = ""; @@ -76,8 +74,8 @@ class GodotTilemapExporter { const tileset = this.map.tilesets[index]; this.extResourceId = index + 1; this.tilesetsIndex.set(tileset.name, this.extResourceId); - // noinspection JSUnresolvedVariable - let tilesetPath = tileset.asset.fileName.replace(this.projectRoot, "").replace('.tsx', '.tres'); + // noinspection JSUnresolvedFunction + let tilesetPath = getResPath(this.map.property("projectRoot"), this.map.property("relativePath"), tileset.asset.fileName.replace('.tsx', '.tres')); this.tilesetsString += this.getTilesetResourceTemplate(this.extResourceId, tilesetPath, "TileSet"); } @@ -132,7 +130,8 @@ class GodotTilemapExporter { this.extResourceId = this.extResourceId + 1; textureResourceId = this.extResourceId; this.tilesetsIndex.set(tilesetsIndexKey, this.extResourceId); - let tilesetPath = object.tile.tileset.image.replace(this.projectRoot, ""); + // noinspection JSUnresolvedFunction + let tilesetPath = getResPath(this.map.property("projectRoot"), this.map.property("relativePath"), object.tile.tileset.image); this.tilesetsString += this.getTilesetResourceTemplate(this.extResourceId, tilesetPath, "Texture"); } else { textureResourceId = this.tilesetsIndex.get(tilesetsIndexKey); diff --git a/export_to_godot_tileset.js b/export_to_godot_tileset.js index b483f34..69bb2aa 100644 --- a/export_to_godot_tileset.js +++ b/export_to_godot_tileset.js @@ -6,11 +6,7 @@ class GodotTilesetExporter { this.tileset = tileset; this.fileName = fileName; // noinspection JSUnresolvedFunction - this.projectRoot = getResPath(this.tileset.property("projectRoot"), fileName); - this.spriteImagePath = this.tileset.image.replace(this.projectRoot, ""); - - // Strip leading slashes to prevent invalid triple slashes in Godot res:// path: - this.spriteImagePath = this.spriteImagePath.replace(/^\/+/, ''); + this.spriteImagePath = getResPath(this.tileset.property("projectRoot"), this.tileset.property("relativePath"), this.tileset.image); this.shapesResources = ""; this.shapes = ""; this.navpolyMap = []; diff --git a/utils.js b/utils.js index 5d14b3b..121ae44 100644 --- a/utils.js +++ b/utils.js @@ -11,47 +11,64 @@ function logk(data) { } /** - * Determines the project root of a Godot project, which is the equivalent of 'res://' within Godot. - * There are three possible inputs for projectRoot: - * - absolute: This defines the projectRoot. Ex: getResPath('C:/project', 'C:/project/map/tileset.tres') => 'C:/project' - * - relative: This defines the projectRoot relative to the outputPath. Ex: getResPath('./..', 'C:/project/map/tileset.tres') => 'C:/project' - * - undefined: Attempts to automatically determine the projectRoot. Ex: getResPath(undefined, 'C:/project/map/tileset.tres') => 'C:/project' + * Returns a full res path to a file. + * + * If relativePath is defined, uses relativePath to determine the res path. + * If relativePath is undefined, uses projectRoot to determine the res path. + * If relativePath is undefined and projectRoot is undefined, automatically determines the res path. + * * Information on file paths in Godot: https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html * - * @param {string} projectRoot desired project root path, which can be an absolute or relative path - * @param {string} outputPath full path and name of destination file - * @return {string} project root path, which is the equivalent of 'res://' + * @param {string} projectRoot desired project root path, which can be an absolute or relative path to the outputPath. Ex: 'C:/project' or './../..' + * @param {string} relativePath relative path to file. Ex: '/maps/level1' + * @param {string} outputPath full path and name of destination file. Ex: 'C:/project/maps/level1/tileset.tres' + * @returns {string} full relative path to file to be included in a 'res://' path. Ex: 'maps/level1/tileset.tres' */ -function getResPath(projectRoot, outputPath) { - const p = outputPath.split('/').slice(0, -1) +function getResPath(projectRoot, relativePath, outputPath) { + let fullResPath = '' + if (relativePath) { + // Replace all backslashes with forward slashes + relativePath = relativePath.replace(/\\/g, '/'); - // If projectRoot is not set, attempt to automatically determine projectRoot by searching for godot project file - if (!projectRoot) { - const out = p - outputPath.split('/').every(_ => { - var godotProjectFile = FileInfo.joinPaths(out.join('/'), 'project.godot'); - if (!File.exists(godotProjectFile)) { - out.pop() - return true; - } - return false; - }) - projectRoot = out.join('/') - } - projectRoot = projectRoot.replace(/\\/g, '/') + fullResPath = FileInfo.joinPaths(relativePath, FileInfo.fileName(outputPath)); + } else { + const p = outputPath.split('/').slice(0, -1) + + // If projectRoot is not set, attempt to automatically determine projectRoot by searching for godot project file + if (!projectRoot) { + const out = p + outputPath.split('/').every(_ => { + let godotProjectFile = FileInfo.joinPaths(out.join('/'), 'project.godot'); + if (!File.exists(godotProjectFile)) { + out.pop() + return true; + } + return false; + }) + projectRoot = out.join('/') + } - // Use projectRoot as absolute if it doesn't start with ".", relative if it does - if (projectRoot[0] === '.') { - const out = p - projectRoot.split('/').forEach((segment) => { - if (segment === '..') { - out.pop() - } - }) - projectRoot = out.join('/') + // Replace all backslashes with forward slashes + projectRoot = projectRoot.replace(/\\/g, '/'); + + // Use projectRoot as absolute if it doesn't start with ".", relative if it does + if (projectRoot[0] === '.') { + const out = p + projectRoot.split('/').forEach((segment) => { + if (segment === '..') { + out.pop() + } + }) + projectRoot = out.join('/') + } + + fullResPath = outputPath.replace(projectRoot, ""); } - return projectRoot + // Strip leading slashes to prevent invalid triple slashes in Godot res:// path + fullResPath = fullResPath.replace(/^\/+/, ''); + + return fullResPath } /** @@ -88,7 +105,7 @@ function splitCommaSeparated(str) { * @param {object} nodeProperties pair key/values for the "node" properties * @param {object} contentProperties pair key/values for the content properties * @param {object} metaProperties pair key/values for the meta properties - * @return {string} TSCN scene node like so : + * @returns {string} TSCN scene node like so : * ``` * [node key="value"] * content_key = AnyValue