How can I use auto-tiling to define several separate regions which all share the exact same tile textures?

Godot Version

4.4.1

Question

How can I achieve something like the following with auto tiling and tile sets?


The red lines have been added to indicate the boundaries of the tiles

The image shows two regions which use the exact same tile imagery but are two distinct regions and should not be merged. I tried using terrains but found I could only assign one terrain to a given tile in the tile set. In my game I might have say 10-20 different types of region or “terrain” but they all look the same. Is there a way to achieve this without duplicating all the tile artwork 20 times in order to assign each a unique terrain?

Thanks,

Gareth

Ok, I discovered I could use multiple tile sources and map a different terrain to each tile source, but crucially, all the tile sources can just point to the same texture atlas. I am therefore able to solve this by selecting different tile sources to paint with and I think that’ll probably work for my use case.

Me again, the problem with this new approach is that I have to duplicate all the navigation mesh configuration etc. for each tile source so this approach isn’t scaling very well.

Another thrilling update - I wrote a script to clone the tile source and make all the changes I need for each terrain so that I only have to manually edit a single source but can have as many sources as I want.

#if TOOLS
using Godot;
using System;
using System.Linq;

[Tool]
public partial class TileSetBuilder : Node
{
    [ExportToolButton("Generate TileSet")]
    public Callable GenerateAction => Callable.From(Run);

    [Export]
    public TileSet SourceTileSet;
    [Export]
    public Texture2D SlotTileTexture;
    [Export]
    public Texture2D PartTileTexture;
    [Export(PropertyHint.File)]
    public string ResourcePath;

    public void Run()
    {
        GD.Print("Generating TileSet");
        var result = SourceTileSet.Duplicate(true) as TileSet;
        var sourceId = result.GetSourceId(0);

        // Start from one as 0 will always be the empty slot TileSetSource
        for (var terrain = 1; terrain < result.GetTerrainsCount(0); terrain++)
        {
            var source = result.GetSource(sourceId).Duplicate(true) as TileSetAtlasSource;
            source.Texture = PartTileTexture;
            // https://docs.godotengine.org/en/stable/classes/class_tilesetsource.html
            for (var coordIndex = 0; coordIndex < source.GetTilesCount(); coordIndex++)
            {
                var tileId = source.GetTileId(coordIndex);
                for (var i = 0; i < source.GetAlternativeTilesCount(tileId); i++)
                {
                    var altId = source.GetAlternativeTileId(tileId, i);
                    var tileData = source.GetTileData(tileId, altId);

                    tileData.Terrain = terrain;
                    foreach (var neighbour in Enum.GetValues(typeof(TileSet.CellNeighbor)).Cast<TileSet.CellNeighbor>())
                    {
                        tileData.SetTerrainPeeringBit(neighbour, terrain);
                    }
                }
            }
            result.AddSource(source);
        }

        GD.Print("Saving");
        ResourceSaver.Save(result, ResourcePath);

        GD.Print($"TileSet Generated {ResourcePath}");
    }
}
#endif