Blending Tiles ?

Hey,

I’m doing a 2d tile map system.
I generate a simple mesh and i’m using UV to show the terrain type (corresponding to a sprite, I mean I map the UVs to the correct part of the Texture), this way I have 1 texture, 1 material, et 1 object for the entire map.

    public void UpdateUV(Vector2 t, int verticeIndex) {
        this.meshData.UV [verticeIndex] = new Vector2 (
            this.ux*t.x+this.px,
            this.uy*t.y+this.px
        );
        this.meshData.UV [verticeIndex+1] = new Vector2 (
            this.ux*t.x+this.px,
            this.uy*t.y+(this.uy-this.py)
        );
        this.meshData.UV [verticeIndex+2] = new Vector2 (
            this.ux*t.x+(this.ux-this.px),
            this.uy*t.y+(this.uy-this.py)
        );
        this.meshData.UV [verticeIndex+3] = new Vector2 (
            this.ux*t.x+(this.ux-this.px),
            this.uy*t.y+this.py
        );
    }

    public static Vector2 GetTextureByName(string name) {
        if (name == "WaterDeep") {
            return new Vector2 (2, 0);
        } else if (name == "Water") {
            return new Vector2 (3, 1);
        } else if (name == "Sand") {
            return new Vector2 (1, 1);
        } else if (name == "Dirt") {
            return new Vector2 (4, 0);
        } else if (name == "DirtGrass") {
            return new Vector2 (0, 1);
        } else if (name == "DirtForest") {
            return new Vector2 (3, 0);
        } else if (name == "Rocks") {
            return new Vector2 (5, 0);
        } else if (name == "HardRocks") {
            return new Vector2 (6, 0);
        }

        return Vector2.zero;
    }

    public void UpdateLayer() {
        int vertexIndex = 0;
        for (int x = 0; x < this.map.Width; x++) {
            for (int y = 0; y < this.map.Height; y++) {
                this.UpdateUV (MapPlaneController.GetTextureByName (this.map.GetTileAt (x, y).Ground.Name), verticeIndex);
                vertexIndex += 4;
            }
        }

        this.mesh.uv = this.meshData.UV;
    }

I posted the code so you can see how I generate my map, it’s standard. I would like to know if there is an easy way to blend tiles textures (using a shadder?) If you need more infos don’t hestitate.

Thanks in advance for your help.

Well I found a trick using vertex colors.
If someone is Interested, instead of settings the uv. I set the Color of the vertex to correspond to a texture.

    public Color GetColorAt(int x, int y) {
    private Color[] verticeColors = new Color[] {
        new Color(1, 0, 0, 0),
        new Color(0, 1, 0, 0),
        new Color(0, 0, 1, 0),
        new Color(0, 0, 0, 1)
    };

...

    public Color GetColorAt(int x, int y) {
        Tile t = this.map.GetTileAt (x, y);

        if (t.Ground.Name == this.Grounds [0].Name)
            return this.verticeColors [0];
        if (t.Ground.Name == this.Grounds [1].Name)
            return this.verticeColors [1];
        if (t.Ground.Name == this.Grounds [2].Name)
            return this.verticeColors [2];
        if (t.Ground.Name == this.Grounds [3].Name)
            return this.verticeColors [3];
     
        return this.verticeColors [0];
    }


...

            this.meshData.Colors [this.verticeIndex] = this.GetColorAt (x, y);
            this.meshData.Colors [this.verticeIndex + 1] = this.GetColorAt (x, y + 1);
            this.meshData.Colors [this.verticeIndex + 2] = this.GetColorAt (x + 1, y + 1);
            this.meshData.Colors [this.verticeIndex + 3] = this.GetColorAt (x + 1, y);

Red = Water, Green = Grass, Blue = Dirt, Alpha = Rocks. I use a shader to replace the color by the texture and blend the textures:

                fixed4 l1 = tex2D (_Texture1, i.uv);
                fixed4 l2 = tex2D (_Texture2, i.uv);
                fixed4 l3 = tex2D (_Texture3, i.uv);
                fixed4 l4 = tex2D (_Texture4, i.uv);

                fixed4 c = (l1.rgba * i.color.r) + (l2.rgba * i.color.g) + (l3.rgba * i.color.b) + (l4.rgba * i.color.a);
                c.a = 1;

It’s working quite well but it’s limited to 4 textures par map (r/g/b). I’m working on using my NoiseMap (Float[,] with the z of each tiles) to work around this issue. Advices are welcome!

Now it’s working with unlimitet* textures. If someone is interested on how to do this, please let me know.

Hello,
I’m new to programming and meeting the same problem here, could you share the final function? Thanks a lot!

You could use a texture atlas and set texture offsets for each vertex of your mesh with the uv coordinates. (Basically just combine more than 4 textures in one and then tell the shader to render different parts of this big texture over different parts of your mesh)

Here is the shader I am currently using:

Shader "Unlit/TileFloorShader"
{
    Properties
    {
        _MainTex1("Texture", 2D) = "white" {}
    }

    SubShader
       {
        Tags
        {
            "RenderType" = "Opaque"
            "DisableBatching" = "True"
        }
        LOD 100

        Pass
        {
            CGPROGRAM
#pragma vertex vert
#pragma fragment frag            
#include "UnityCG.cginc"

            struct v2f
            {
                float4 color : COLOR;
                float4 vertex : SV_POSITION;

                float2 offsets_1: TEXCOORD0;
                float2 offsets_2: TEXCOORD1;
                float2 offsets_3: TEXCOORD2;
                float2 offsets_4: TEXCOORD3;
            };

            sampler2D _MainTex1;
            float4 _MainTex1_ST;

            v2f vert(appdata_full v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);

                o.offsets_1 = TRANSFORM_TEX(v.vertex * _MainTex1_ST + v.texcoord.xy, _MainTex1);
                o.offsets_2 = TRANSFORM_TEX(v.vertex * _MainTex1_ST + v.texcoord1.xy, _MainTex1);
                o.offsets_3 = TRANSFORM_TEX(v.vertex * _MainTex1_ST + v.texcoord2.xy, _MainTex1);
                o.offsets_4 = TRANSFORM_TEX(v.vertex * _MainTex1_ST + v.texcoord3.xy, _MainTex1);

                o.color.xyz = v.color;
                o.color.w = v.color.w;

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 l1 = tex2D(_MainTex1, i.offsets_1.xy);
                fixed4 l2 = tex2D(_MainTex1, i.offsets_2.xy);
                fixed4 l3 = tex2D(_MainTex1, i.offsets_3.xy);
                fixed4 l4 = tex2D(_MainTex1, i.offsets_4.xy);
 
                fixed4 c = (l1.rgba * i.color.r) + (l2.rgba * i.color.g) + (l3.rgba * i.color.b) + (l4.rgba * i.color.a);
                c.a = 1;
     
                return c;
            }
            ENDCG
        }
    }
}

So when a new tiletype is set you have to find and change the according offsets (use uv for R, uv2 for G, uv3 for B and uv4 for A) to render a new part of your atlas per color on the changed part of your mesh.

Here is how the uv’s are set:

// tempVecs2 is a Vector2[]

// Get the uv data from the mesh
tempVecs2 = mesh.uv;

// Set the new changed uv's
mesh.uv = tempVecs2;

// Same for uv2, uv3 and uv4

I am also new to shaders and most of the time I have no idea what I’m doing, so if someone has an improvement or a better method - please let me know!

I made a tutorial about this: Loading...

I made a html version too (Loading...).

1 Like