mapeditor/tiled-to-godot-export

[feature] Smarter path resolution & default projectRoot

konsumer opened this issue · 7 comments

This is from discussion on #5

Relative and godot-external paths would help me support different use-cases, like sharing my projects on github (where other developers won't have same file-locations.)

This is totally workable as it is. Very easy to set 1 property to my maps/tile-sets. I wonder though if a sensible default like res:// might be all it needs for it to work on github, with relative paths.

Maybe this logic would do it?

  • check for projectRoot
  • Use it as absolute, if it doesn't start with .
  • If projectRoot is not set, set it to current-files location (this would allow tiled files in the godot project root)
  • if projectRoot starts with res:// use it as-is to work out relative-to-godot-root path, like for a sub-dir

This would handle these use-cases:

  1. I put all my files in godot's project-root (just leave projectRoot blank)
  2. I put all my tiled files somewhere else, completely out of godot's root (set projectRoot to an absolute path)
  3. I put all my files in some relative dir, that is maybe outside of godot's root (set projectRoot to something like ../godot-game
  4. I put all my files in some sub-dir of godot root (set projectRoot to res://assets or whatever)

For use-case 2 & 3, I think we'd need to copy image-file, so we'd need to infer res:// from the path. We know the relative location between tile & image and map & tile, and where we are outputting godot files, so if projectRoot points to something outside of output-location we'd copy files (need to look into how to do this, or if it's even possible.)

Tasks

  • Add support for relative locations in projectRoot
  • Add "Where is res://" to README, simplify instructions (offer the default all-in-godot-root as simplest) and make sure to include specific examples

Let's write down the cases once again with example values so we are on the same page:

My Godot Project is at D:/projects/awsome-game-one/

Use-case 1: The .tmx and .tsx are in the D:/projects/awsome-game-one/ - in Tiled i don't have to add anything on export tilemap and tileset uses "res://" as a path

Use-case 2: The .tmx and .tsx are in the D:/tiled-maps/ - here I see a problem with the image used by the Tileset - the image should be at least in the Godot project folder or else it will be missing on tileset load in the Godot engine

Use-case 3: I think the same problem as 2 will occur - the exported tileset needs a path to the image which is in the res:// folder

Use-case 4: The .tmx and .tsx are in D:/projects/awsome-game-one/assets/ and in Tiled the user has to add: res://assets/ - This may work but still it's the same as the current version with the absolute path which is converted to res:// on export.

So from all of these, I think 1 and 4 can be done but I like only Use case 1 - still have to check if the current folder could be used as for example this will fail if I want to place the exported .tscn somewhere else in the Godot project and not in the same folder as the source Tiled ones.

I agree, except I am proposing an image file-copy on use-case 2 & 3. I can see this being really useful in a situation where the user keeps their map files in some other dir, like they are porting their game to multiple engines, or just in the situation where they don't want their godot root cluttered with a bunch of tiled files. I'm not sure how to do a file-copy in TIled plugin, though, and I can totally live without them.

So here is a simplified map of these:

  • check for projectRoot
  • Use it as absolute, if it doesn't start with .
  • if it is relative, strip the relative bit from the outputted paths
  • If projectRoot is not set, set it to current-files location

This would mean no confusing re-use of projectRoot functionality, it's just "where is res://?"

Using your examples it would look like this:

  1. The .tmx and .tsx are in the D:/projects/awsome-game-one/ - in Tiled i don't have to add anything on export tilemap and tileset uses "res://" as a path
  2. The .tmx and .tsx are in D:/projects/awsome-game-one/assets/ and in Tiled the user has to add .. and the res://assets/ part is what's left when it's resolved.

This would allow for a sensible default, godot-root-subdirs, and no file copying, all with relative paths for github sharing. If we wanted to support some other mapping later (like not-in-godot-root or whatever) we could add those use-cases, but again, I'm totally fine without them.

Ok, that sounds good I'll test it along with the Linux bug and gonna update it soon. If you're in a hurry you can try to fix it and do a PR.
Also, I would like to add this: "No confusing re-use of projectRoot functionality, it's just "where is res://?" to the readme.

And lastly what's going to happen if I set "/assets/something/" as a value to projectRoot
Set it to res://assets/something/ in the export ?

I am happy to help however you like. I will add a checklist to the top of this issue, so we can both work on it. I got stuck on linux bug, so if you wanna work on that, I can add this in a PR, and we'll have both working more quickly!

And lastly what's going to happen if I set "/assets/something/" as a value to projectRoot
Set it to res://assets/something/ in the export ?

For linux & mac, this would mean an absolute path. There are other things we might wanna support, too:

~/games/my-cool-game  - on my system: /home/konsumer/games/my-cool-game
$HOME/games/my-cool-game  - on my system: /home/konsumer/games/my-cool-game
%appdata%/my-cool-game - on windows C:\Users\David Konsumer\AppData

I dunno, we can avoid the whole can of worms by just supporting "where is res://?" and treat it as an absolute or relative location, with no variable subs or whatever. this is sort of how godot handles it, and to an extent, TIled (it uses relative paths between image/tile/map files.)

We just need to point out that it should be a relative-path to the Tiled file, or absolute path, and the Tiled files need to be somewhere in res://, I think those are reasonable expectations.

Maybe we even don't offer absolute path in README, but keep support (for backwards compatibility and people not quite following directions.) This brings the point home that "the Tiled files need to be somewhere in res://".

I made a function to derive res:// from projectRoot and outputPath of file:

function getResPath (projectRoot, outputPath) {
  const p = outputPath.replace(/\\/g, '/').split('/').slice(0, -1)
  // check for projectRoot
  // If projectRoot is not set, set it to current file's location
  if (!projectRoot) {
    projectRoot = p.join('/')
  }
  projectRoot = projectRoot.replace(/\\/g, '/')
  // Use it as absolute, if it doesn't start with ".", relative if it does
  if (projectRoot[0] === '.') {
    const out = p
    projectRoot.split('/').forEach((segment, i) => {
      if (segment === '..') {
        out.pop()
      }
    })
    projectRoot = out.join('/')
  }
  return projectRoot
}

console.log(getResPath(undefined, '/home/konsumer/games/tiled-example/town1.tscn'))
console.log(getResPath('././', '/home/konsumer/games/tiled-example/town1.tscn'))
console.log(getResPath('..', '/home/konsumer/games/tiled-example/assets/town1.tscn'))
console.log(getResPath('../..', '/home/konsumer/games/tiled-example/assets/maps/town1.tscn'))
console.log(getResPath('../../..', '/home/konsumer/games/tiled-example/assets/tiled/maps/town1.tscn'))
console.log(getResPath('../.././.././.', '/home/konsumer/games/tiled-example/assets/tiled/maps/town1.tscn'))
console.log(getResPath('/home/konsumer/games/tiled-example', '/home/konsumer/games/tiled-example/town1.tscn'))
console.log(getResPath('/home/konsumer/games/tiled-example', '/home/konsumer/games/tiled-example/assets/town1.tscn'))
console.log(getResPath('c:\\games\\tiled-example', 'c:\\games\\tiled-example/assets/town1.tscn'))
console.log(getResPath('..\\..', 'c:\\games\\tiled-example/assets/maps/town1.tscn'))

which gives me this:

/home/konsumer/games/tiled-example
/home/konsumer/games/tiled-example
/home/konsumer/games/tiled-example
/home/konsumer/games/tiled-example
/home/konsumer/games/tiled-example
/home/konsumer/games/tiled-example
/home/konsumer/games/tiled-example
/home/konsumer/games/tiled-example
c:/games/tiled-example
c:/games/tiled-example

This way, later I can just do this:

this.projectRoot = getResPath(this.map.property("projectRoot"), fileName);

I'm closing this since I review and tested the PR.