godotengine / godot

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

TileMap terrain system gives different results depending the order you place the tiles #73903

Open azagaya opened 1 year ago

azagaya commented 1 year ago

Godot version

4.0 rc5

System information

Ubuntu 22.04, Vulkan renderer, gforce 1650ti

Issue description

Im making use of the terrain system for a side-scroller. I set the terrain and when i start painting, i find that the results are not always the same depending in the order i paint the tiles (and many of the results should not be valid).

Im ussing "match corners and sides". This video shows the issue:

https://user-images.githubusercontent.com/46932830/221331754-11542a33-375d-40ca-8e34-3def33020043.mp4

Steps to reproduce

1- Set up the terrain system. 2- Paint 2x2 squares in different order.

Minimal reproduction project

TilemapTest.zip

naturally-intelligent commented 1 year ago

Just a tip, you can fix any mistakes with pencil after

Calinou commented 1 year ago

I can confirm this on 4.0.rc adbabfbaa (Linux).

groud commented 1 year ago

I think I kind of know what is happening.

When you draw a set of tiles like that, it's a bit like if you drew the tiles one by one. The problem arises when the first tile is added on the map, as there's no tile (in your tileset) set up to have the center bit set but nothing around it, it randomly selects a tile that fits the situation the most. Then, when drawing the remaining of the square, it tries to keep what is already in place, as much as possible (as other users complained the algorithm was doing that).

The only solution I can think of is executing the drawing at the end of the drag (like the rectangle tool does), instead of doing it at every frame. That means that you'll have to release the mouse to see the drawn area then, otherwise I fear it would destroy performances.

azagaya commented 1 year ago

The problem arises when the first tile is added on the map, as there's no tile (in your tileset) set up to have the center bit set but nothing around it, it randomly selects a tile that fits the situation the most.

I'm not sure that's the issue, because i indeed have a tile that has only the center bit. If you look at the video, it first selects a single tile platform, and when i paint the one below, it converts it to a column right away (without needing to release) correctly. The problem is when i paint the one on the side.

Also note that if i do the operation in another order, it does update the tiles correctly, which also led me to think the problem is other. This seems to be consistent BTW.

Also, when you say "it tries to keep what is already in place as much as possible", this is even if what's in place is not valid?

naturally-intelligent commented 1 year ago

I also have this issue, but I consider it minor/non-urgent since you can correct the odd mistake with pencil tool.

Remember the Terrain tool just functions as a brush like the pencil. It doesn't add persistent logic onto your tile grid.

azagaya commented 1 year ago

I also have this issue, but I consider it minor/non-urgent since you can correct the odd mistake with pencil tool.

Remember the Terrain tool just functions as a brush like the pencil. It doesn't add persistent logic onto your tile grid.

I understand for your use case it may be not important. But if you are adding/changing tiles by code, you cannot really fix them with the pencil.

Also, if you are doing a lot of big levels, it can soon start to be annoying to need to fix invalid combinations every time.

naturally-intelligent commented 1 year ago

That's true I hadn't thought of using the Terrain brush programatically/procedurally. In that case it would need to be 100%

azagaya commented 1 year ago

Then, when drawing the remaining of the square, it tries to keep what is already in place..

Another observation that may help to find the solution: I don't think the problem is trying to keep what is already in place, because as you can see, it is actually changing the tile in the last step. Only it is changing it for one that should not be valid. The last tile has a bit in the top-side, and there is nothing in the top. It shouldn't pick that tile, but one that has no bit in the top (only center, bot, right, and bot-right corner)

azagaya commented 1 year ago

I found something else, having in mind what @groud said.

As i didn't have a tile that has only center and bot bits, when doing a 2 tiles column, it picked one that had also top bit (best option). Then when updating the tiles, it tried to choose a tile that had that top bit also, even if it would be better in this case to chose another tile when completing the 4 tiles square, because there is nothing in the top.

If i set a tile to have only bot and center, when doing the 2 tiles it choose that one, and then updates correctly to one that does not have top bit.

I still think it should update it to something valid given there is something valid to choose, because you not always have complete tilesets. But i guess is less dramatic now, as it not as unpredictable as i thought.

securas commented 1 year ago

I understand the point of changing a minimum number of tiles. But the autotile system should be systematic non-causal to support a consistent UI and programatic control.

dandeliondino commented 1 year ago

I just tested this MRP to attempt to reproduce the issue, and found that the tile at atlas coords (6,1) had the top side bit set to "walls". By erasing that bit (setting it to empty), the tileset seems to paint as expected in 4.0.3. The overall point that the terrain system sometimes fails to find correct matches is valid (I've been able to reproduce #64300 and #70218, for example), but it seems to be choosing tiles correctly here.

Edited: I just re-read the prior comments and I'm unclear if this had already been caught. I apologize if this unnecessarily brought up an old post. I was just trying to prevent someone else from trying to troubleshoot it.

azagaya commented 1 year ago

Edited: I just re-read the prior comments and I'm unclear if this had already been caught. I apologize if this unnecessarily brought up an old post. I was just trying to prevent someone else from trying to troubleshoot it.

Yes, exactly, that's what i tried to explain in my last post. The tile at coords (6,1) should never be used in that 4 tiles arrangement. But if i dont have a tile that has only bot and center, it doesnt work properly, even if it has the 4 correct tiles to pick.

And i not always use a tilesets with all the possible combinations. It should imho always pick tiles that matches correctly.

You can test by removing completely the tile at (6,1) and see how it still fails.

dandeliondino commented 1 year ago

You can test by removing completely the tile at (6,1) and see how it still fails.

Ah thank you for clarifying! Here's a gif with Godot 4.0.3 vs a plugin when that tile is removed.

73903

Is the plugin's behavior the expected one here? (The last 3 results are identical as it doesn't update-as-you-go when drawing.) I am planning on turning the plugin into a feature proposal for updating the terrain algorithm if it is working well enough, so I've been testing it on various open issues.

azagaya commented 1 year ago

Is the plugin's behavior the expected one here? (The last 3 results are identical as it doesn't update-as-you-go when drawing.) I am planning on turning the plugin into a feature proposal for updating the terrain algorithm if it is working well enough, so I've been testing it on various open issues.

Yes! The plugin is working as expected it seems. I think it would be nice to have the preview of what are you drawing though, like it used to work on 3.x

dandeliondino commented 1 year ago

It's interesting you mention that because the real-time updates are (one of) the causes of this problem. As you can see, it chooses all the right tiles if you paint with the rectangle.

Here is a video using Terrain Debugger that shows what's happening at the very beginning, where the root of the problem lies. It starts out choosing a different tile (I think it's the correct one, it's hard to see) but it calls many updates when dragging to the second cell (the screenshot below shows this -- every printed "tile is painted" meant that a full update occurred), and one of them causes the cell to change to an incorrect one. Then when the bottom tile is painted, the top tile is stuck in that configuration, since the bits that aren't immediately adjacent to the newly painted tile aren't allowed to be updated. (This is also the reason that painting single tiles in corners mode causes so many failures -- it can't fully update the neighbors).

The updates are happening too quickly, so I don't know what made it switch to the wrong tile in the first place, but it's more than just the frequent updates that are causing this. The reason the plugin succeeds in this case is partly due to having a more deterministic algorithm (though it's impossible to be fully deterministic with a system this complex), and it fully updates all the adjacent cells in Connect mode.

https://github.com/godotengine/godot/assets/68911895/ae4f8427-bcc6-494b-afb8-a39c792299a1

updates

azagaya commented 1 year ago

While understsand the complications, I dont think it couldn't be done. Other softwares like tiled, LDTk, which also have complex algorithms, work fine and update as you paint. I know is not of much use comparing to other tools, but anyways I think this should be taken into account, also because in 3.x the autotile worked flawlessly and updated in real time as you paint.

Also, even if this method is not deterministic, a deterministic method like the one provided in 3.x MUST be provided imho. It would be frustrating to generate levels procedurally in runtime if algorighm is not deterministic.

Having said that, in this specific case we have four tiles that match to form a square. It when you place the last tile, it should fix the others to matching ones. I understand it first chose a bad tile because it didnt have anyone matching, but it makes no sense to leave a not matching set of tiles imo.

dandeliondino commented 1 year ago

Yeah, I didn't mean to imply that real-time updates can't be accurate. Just that if the editor hadn't been updating in real-time in draw mode, it would have chosen the correct tiles in this case. The combination of real-time updates and not fully updating surrounding tiles (as well as whatever caused it to switch to the wrong tile above) are what caused this. An algorithm that didn't have the other limitations should be able to handle real time updates. Real-time updates just expose the limitations here.

Also, I do want to argue the point that it's ok for it to have chosen a bad tile. In this entire example, the algorithm has always had options with fully matching peering bits, so there's no reason it should have ever chosen non-matching tiles, even temporarily.

azagaya commented 1 year ago

Ah ok, sorry i misunderstood your previous message.

lufog commented 1 year ago

In my experiments, I also had strange terrain placement results. Terrain, instead of a completely suitable tile, chooses a completely unsuitable tile, which changes the surrounding tiles.

https://github.com/godotengine/godot/assets/10379487/f23c7b1d-71c6-4d90-a3c8-989346700725

The incorrect tiles displayed in the 'Terrains Panel' are due to alternate tiles (bug #70825). In my understanding, everything is set up correctly.

snake_terrain_bits

TileMapTerrains_Test.zip