Unity-Technologies / 2d-extras

Fun 2D Stuff that we'd like to share!
Other
1.54k stars 342 forks source link

Why doesn't my RuleTile recognize my tile as a tile on my tilemap? #291

Open PtrStruct opened 3 years ago

PtrStruct commented 3 years ago

So I'm currently working with a Rule Tile which utilizes a Tileset that has a bunch of tiles that are 32x32.

In my scene I have an empty gameobject with a script attached to it (ProcedualGeneration.cs) as well as a "Rectangular Tilemap" which is built using a Grid and a Tilemap as it's childelement.

When I start the project, the script runs and it generates a world by first generating a 2D array which gets filled with 0's, indicating that there is no tile there.

Then it uses perlin noise to calculate where to place tiles. and this all works fine as long as it uses the tiles from the "Overworld" Rule tile.

as shown here

if (map[x, y] == 1)
{
    groundTilemap.SetTile(new Vector3Int(x, y, 0), groundTileBase);
}

Then if I add another type of tile, for instance.. If I try to add a stone tile which is a different ruletile, to the same tilemap, the tiles from "Overworld" doesnt recognize these as tiles, even though they're in the same tilemap.

else if (map[x, y] == 2)
{
    groundTilemap.SetTile(new Vector3Int(x, y, 0), stoneTileBase);
}

A more visual representation would be this.. The tile that the arrow is pointing towards is setup to only generate if there is no tile touching it from above or under, or on the sides horizontally.

Why doesn't it recognize the stone tiles as tiles? unt ovr

This is how I generate the world on start

public class ProcedualGeneration : MonoBehaviour
{
    [Header("Terrain Generation")]
    public int Width, Height;
    public float Smoothness, Seed;
    public int HeightMultiplier = 20;
    public int HeightAddition = 20;

    public float NoiseFrequency = 10;

    [Header("Tile")]
    public TileBase GroundTile;
    public TileBase StoneTile;
    public Tilemap GroundTilemap;

    public int[,] map;

    // Start is called before the first frame update
    void Start()
    {
        /* Generate a empty map */
        Generation();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Generation();
        }
    }

    void Generation()
    {
        Seed = Time.time;
        ClearMap();
        map = GenerateArray(Width, Height, true);
        map = TerrainGeneration(map);
        RenderMap(map, GroundTilemap, GroundTile, StoneTile);
    }

    void ClearMap()
    {
        GroundTilemap.ClearAllTiles();

    }

    /// <summary>
    /// Generates the world grid.
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="empty"></param>
    /// <returns></returns>
    public int[,] GenerateArray(int width, int height, bool empty)
    {
        int[,] map = new int[width, height];

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                map[x, y] = (empty) ? 0 : 1;
            }
        }

        return map;
    }

    ///Higher frequency = more hills

    /// <summary>
    /// Takes a 2D Array as an input, feeds it values, and spits the modified version back out.
    /// </summary>
    /// <param name="map">2D Array</param>
    /// <returns></returns>
    public int[,] TerrainGeneration(int[,] map)
    {
        float perlinHeight;
        for (int x = 0; x < Width; x++)
        {
            //Generate the height of each "pillar"
            perlinHeight = Mathf.PerlinNoise((x + Seed) * NoiseFrequency, 0) * HeightMultiplier + HeightAddition;

            for (int y = 0; y < perlinHeight; y++)
            {
                if (y < perlinHeight - Random.Range(5, 10))
                {

                    map[x, y] = 2;
                }
                else
                {
                    map[x, y] = 1;

                }
            }
        }

        return map;
    }

    public void RenderMap(int[,] map, Tilemap groundTilemap, TileBase groundTileBase, TileBase stoneTileBase)
    {
        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            {
                if (map[x, y] == 1)
                {
                    groundTilemap.SetTile(new Vector3Int(x, y, 0), groundTileBase);
                }
                else if (map[x, y] == 2)
                {
                    groundTilemap.SetTile(new Vector3Int(x, y, 0), stoneTileBase);
                }
                //Else if, render different tile
            }
        }
    }
}
ChuanXin-Unity commented 3 years ago

The RuleTile only recognises itself when matching Tiles around it. Other RuleTiles are treated as different and will not be matched.

This is based on the Rule matching logic found at https://github.com/Unity-Technologies/2d-extras/blob/5a3d43ee48c637bad5ffffa8501fc4fa6f2b136f/Runtime/Tiles/RuleTile/RuleTile.cs#L703 .

You could create your own RuleTile with the matching conditions that you want using Custom Rules.

An example can be found at https://github.com/Unity-Technologies/2d-techdemos/blob/master/Assets/Tilemap/Rule%20Tiles/Custom%20Rule%20Tile/Scripts/CustomTypeRuleTile.cs, where there is a new Rule which matches if the neighbouring Tile has the same Type.

Hope this helps!