I'm trying to write a shader in unity that will take tiles from a sprite sheet and draw them onto a tile map.
I'm new to shaders so this might be totally wrong, I think it might just be a math error on my part, but maybe I'm misunderstanding how shaders work.
Here's my Shader:
Shader "Custom/TileMapShader" {
Properties {
_TileSheet ("Tile_Sheet", 2D) = "white" {}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _TileSheet;
uniform float2 _MapSize;
struct vertexInput {
float4 vertex : POSITION;
float2 texcoord: TEXCOORD0;
float4 tilecoord : TEXCOORD1;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float2 tex: TEXCOORD0;
float2 tileIndex : VECTOR;
};
vertexOutput vert (vertexInput v)
{
vertexOutput output;
output.pos = mul (UNITY_MATRIX_MVP, v.vertex);
output.tex = v.texcoord;
output.tileIndex = v.tilecoord.xy; // <-- Using UV2 might be a bit hacky
// to get the tile index but it
// seemed better than creating a
// texture to do it...
return output;
}
float4 frag (vertexOutput i) : SV_Target
{
int xTile = (i.tileIndex.x * 16);
int yTile = (i.tileIndex.y * 16);
float2 uv = float2(xTile, yTile + 1) / 16;
float xoffset = frac( i.tex.x * _MapSize.x ) / 16;
float yoffset = frac( i.tex.y * _MapSize.y ) / 16;
uv += float2( xoffset, -yoffset );
uv.y = 1.0 - uv.y;
return tex2D(_TileSheet, uv);
}
ENDCG
}
}
}
I stumbled across this http://connorhollis.com/code/fast-tilemap-shader/ and modified it to get what I have above.
this is how I create my Mesh: (I don't think this is the issue but who knows)
void BuildMesh()
{
int numTiles = (int) (mapSize.x * mapSize.y);
int numTris = numTiles * 2;
int vsize_x = (int)mapSize.x + 1;
int vsize_z = (int)mapSize.y + 1;
int numVerts = vsize_x * vsize_z;
// Generate the mesh data
Vector3[] vertices = new Vector3[numVerts];
Vector3[] normals = new Vector3[numVerts];
Vector2[] uv = new Vector2[numVerts];
//Tiles
_tileMap = new Vector2[numVerts];
int[] triangles = new int[numTris * 3];
int x, z;
for (z = 0; z < vsize_z; z++)
{
for (x = 0; x < vsize_x; x++)
{
vertices[z * vsize_x + x] = new Vector3(x * TileSize, 0, -z * TileSize);
normals[z * vsize_x + x] = Vector3.up;
uv[z * vsize_x + x] = new Vector2((float)x / (int)mapSize.x, 1f - (float)z / (int)mapSize.y);
//Tiles TEMP
switch (Random.Range(0, 2))
{ // Tile Selection
case 0: // X Y
_tileMap[z * vsize_x + x] = new Vector2((1 * 0.0625f), (0 * 0.0625f));
break;
case 1:
_tileMap[z * vsize_x + x] = new Vector2((2 * 0.0625f), (0 * 0.0625f));
break;
}
}
}
for (z = 0; z < (int)mapSize.y; z++)
{
for (x = 0; x < (int)mapSize.x; x++)
{
int squareIndex = z * (int)mapSize.x + x;
int triOffset = squareIndex * 6;
triangles[triOffset + 0] = z * vsize_x + x + 0;
triangles[triOffset + 2] = z * vsize_x + x + vsize_x + 0;
triangles[triOffset + 1] = z * vsize_x + x + vsize_x + 1;
triangles[triOffset + 3] = z * vsize_x + x + 0;
triangles[triOffset + 5] = z * vsize_x + x + vsize_x + 1;
triangles[triOffset + 4] = z * vsize_x + x + 1;
}
}
//Populate mesh with the data
_mesh.vertices = vertices;
_mesh.triangles = triangles;
_mesh.normals = normals;
_mesh.uv = uv;
_mesh.uv2 = _tileMap;
_material.SetVector("_MapSize", new Vector4(mapSize.x, mapSize.y));
}
And this is how it all ends up looking
and this is the tile sheet I'm using to test. There are 16x16 tiles, and each tile is 16x16 pixels
https://i.stack.imgur.com/zzQpJ.png
Thank you for the Help, I've been stuck on this for a few days now, I tried to keep things short and to the point.