mapeditor / tiled

Flexible level editor
https://www.mapeditor.org/
Other
11.18k stars 1.76k forks source link

Unique Properties based on tile location on map? #1419

Open slidingboar opened 7 years ago

slidingboar commented 7 years ago

If I have a tiled map that is 100x100, and I want some YxY grids to have a different property, how can I set this?

For example, say the entire map has a default property elevation=1. But I want some spots to have an elevation of 2, 5, or 10.

I can't use custom properties, because that seems tied to the tile itself and not where it's location is on the map. ie, if I create a custom property for a grass tile to be elevation=1, I can't set that same grass tile later to elevation=5 without affecting all the other grass tiles already placed on the map.

Using object layers seems like a messy alternative. I'd have to highlight only parts that are default, then painstakingly highlight all the other parts that have different elevations, making sure no two object layers overlap in a grid. And since it's pixel based, its makes calculation in my application a bit messier and potentially buggy since I could make a mistake and overlap object layers.

It would be great if we could just highlight parts of a finished map, and then set some property for that group of tiles, unique for that location. Using the elevation example, the ability would be for me to highlight the entire map, set elevation=1. Then select a 2x2 grid somewhere, and change elevation=5... etc.

Then in my application code I could do something like:

if currentTile.get('elevation') > 10 {...}

Is there already a way to do this that I'm not aware?

kheftel-old commented 7 years ago

Why not use another tile layer for this? It sounds perfect for a heightmap/elevation. Have a separate tile for each needed elevation in a tileset used specifically for elevation. It even gives you a handy visual representation of elevation inside Tiled itself.

slidingboar commented 7 years ago

@kheftel To create a new tileset for each elevation is too cumbersome. It would be easier to just define a unique property for that tile, then I can dynamically swap shaders in my application code based on this property value.

I just found this thread which explains my issue exactly (from 2011), and it seems like the author is not willing to have unique properties per cell: https://github.com/bjorn/tiled/issues/31

That is unfortunate. Apparently object layer can snap to grid, but I still have to do coordinate math which is not ideal. Also, it still makes everything too cumbersome.

Imagine:

1000x1000 grid with elevation=1, and I want to define 2x2 grid in the center to have elevation=2. That means in order to define one unique property, I need to draw 4 objects to define elevation=1, then one object to define elevation=2.

Now imagine that I want multiple grids that have different elevations. I'd have to draw X number of objects just for a default elevation of 1, and then a bunch others to define other elevations.

Now imagine that I not only want just elevation, but say Y number of extra unique properties. Then I have to create a new object layer for each property, and then do all this work of drawing objects again...

It's just overall way too much work when this can be solved by having unique properties within the cell itself.

Having global shared properties based on a tile from a tileset makes sense if you want them all to stay static. ie, "I want all mountain tiles to have an elevation = 1". The problem comes when you say "I also want many mountains to have elevation = X,Y,Z". It's not scalable.

kheftel-old commented 7 years ago

I must be misunderstanding something, because it doesn't seem that complicated to me. Please let me know what I'm missing.

You don't have to create a new tileset for each elevation value, but rather one tileset for all elevations. You need as many tiles as you have elevation values. So to represent elevation 0-31, you'd need 32 tiles in that tileset.

To take your example, for a 1000x1000 grid with elevation=1, couldn't you flood-fill a tile that represents elevation=1? And then to have a 2x2 grid in the center with elevation=2, draw 4 tiles of the tile that represents elevation=2 in the middle. In general, for a NxN grid of elevation=x, draw the tile that corresponds to elevation=x N x N times (using selection, floodfill, etc to speed up painting if necessary/desired).

A tile layer is by definition a property that is unique per tile on the map. You would have to have special logic in your game code to not draw the elevation tile layer, but you'd read it at runtime to look up the elevation of a particular tile, and then dynamically swap shaders in your application code based on this property value as you said.

slidingboar commented 7 years ago

@kheftel I think I see what you're saying. It's an interesting way to do it if I understand correctly. But I have some gripes:

Let's say I have 256 types of terrain: grass, dirt, cobblestone, etc... and it is stitched together onto one tilesheet (so a png file that holds 16x16 tiles).

Correct me if I"m wrong, but I think what you're suggesting is I take each terrain, and create a new tilesheet with 32 grids so that I can have 32 different elevations of that particular terrain?

That would mean I would need a png file of 256 * 32 tiles... just so I can have one unique property. Extend this to multiple unique properties and I'll have an even bigger file. It doesn't seem to scale well, and seems like a hackish workaround when it could be solved by having unique properties per cell.

kheftel-old commented 7 years ago

no, that's not what I'm suggesting at all. I'm suggesting not using the elevation layer for graphics at ALL - its only use is as a property that varies per-tile and can be easily modified (drawn).

Even with 256 types of terrain, you'd only have ONE elevation tileset and layer. You could honestly just use solid colors as a sort of heatmap. Then you have your terrain layer where you draw your grass, dirt, etc, using your regular tilsets and an elevation layer where you draw your elevation using your elevation tileset.

In your game code, when you go to render, say you have a grass tile. You lookup the elevation value in the elevation layer at that position and say it's 1. So you draw whatever grass looks like at elevation 1, but you don't draw the elevation layer at all.. Then the next tile is grass but elevation 3. So you draw whatever your grass looks like at elevation 3. Then say the next tile is stone at elevation 5.

I don't know how your terrain tilesets are laid out or how they vary visually based on elevation. That's up to your game code to figure out. All the elevation layer does is provide an integer value per tile position (the tile ID), which you can translate in your game code into an elevation value, and then pass to your rendering system to do whatever it needs to to render it.

Does that help? If you need more per-tile properties, you'd need one more layer and (possibly) one more tileset, depending on whether the new property uses the same set of values as the first one.

It's a bit like defining collision detection per tile - instead of setting a "collidable" flag on each tile or somesuch, you have a collision tile layer, and draw a dummy tile if it's collidable and draw no tile if it's not collidable. Then in the game code, you don't draw the collision layer, but use it as a look-up table to figure out whether the current tile is collidable or not.

Hope that helps, feel free to keep asking questions if I've been unclear.