What would the best way to use shadergraph to add an outline to the entirety of a tilemap (not just to each individual tile)?
I’ve spent a while searching on this, but I couldn’t find anything useful. I tried to ask ChatGPT (I’m not proud of this), but nothing came out of it.
Thoughts?
What’s with the shadergraph need?
I would just slap a tiled sprite (could just be solid white, color-tinted to whatever you like) around the edges of it, make it as thick as you want.
With a tiny bit of code that knows the size of the tilemap you can produce the border easily at runtime, injecting it rather than authoring it.
EDIT: here’s the border I slapped around the full maze in my Royal Mazes game… it’s not a tilemap, but it is a grid of tiles.
Code was basically:
{
var sr = MakeSprite(v3i.zero, sprEntireFloor);
sr.color = colorEntireFloor;
sr.sortingOrder = sortOrderEntireFloor;
sr.drawMode = SpriteDrawMode.Tiled;
sr.size = new Vector2(maze.xSize, maze.ySize) * CellSize2D;
sr.name = "EntireFloor" + (floor + 1);
sr.transform.position = positionCenter;
const float floorBorderThickness = 0.2f;
// now the four floor borders for this floor
{
SpriteRenderer b = null;
float xLateral = maze.xSize * CellSize2D / 2 + floorBorderThickness / 2;
float yVertical = maze.ySize * CellSize2D / 2 + floorBorderThickness / 2;
// left border
b = Instantiate<SpriteRenderer>(sr, sr.transform.parent);
b.sprite = sprBorder;
b.color = colorFloorBorder;
b.sortingOrder = sortOrderFloorBorder;
b.transform.position += Vector3.left * xLateral;
b.size = new Vector2(floorBorderThickness,
maze.ySize * CellSize2D + floorBorderThickness * 2);
// right wall
b = Instantiate<SpriteRenderer>(b, b.transform.parent);
b.transform.position += Vector3.right * xLateral * 2;
// top wall
b = Instantiate<SpriteRenderer>(b, b.transform.parent);
b.transform.position = sr.transform.position +
Vector3.up * yVertical;
b.size = new Vector2(maze.xSize * CellSize2D, floorBorderThickness);
// bottom wall
b = Instantiate<SpriteRenderer>(b, b.transform.parent);
b.transform.position += Vector3.down * yVertical * 2;
}
}
And the MakeSprite() stuff is just as trivially-simple as you could imagine:
Transform VizParent;
SpriteRenderer MakeSprite( Vector2 position, Sprite sprite)
{
var go = new GameObject(sprite.name + "-" + position);
go.transform.SetParent(VizParent);
go.transform.position = position;
var sr = go.AddComponent<SpriteRenderer>();
sr.sprite = sprite;
return sr;
}
SpriteRenderer MakeSprite(v3i v, Sprite sprite)
{
Vector3 position = POS2D(v);
return MakeSprite(position, sprite);
}
OP could even just simply use a rule tile and setup the border tiles to have the correct outline sprites via the inspector.
I really want it to be a shader because I’m working with 2d world gen, and I don’t want to make different border tiles for every single tile. And I want to make the thickness different for different layers of the terrain-- a shader can easily do this, right?
There seems to be no shortage of tutorials on this: https://www.youtube.com/watch?v=FvQFhkS90nI
Potentially something exists on the asset store.
Yeah, I tried using a normal sprite outline, but it affected every tile rather than the entire tilemap.
Right, probably because every tile is sampled separately.
You can the probably have a single sprite for the background, using pixels assigned based on the tile map. Basically transparent for no tile, opaque for a tile. This big single sprite is positioned behind your tilemap, with a material/shader that draws the outline.
1 Like
Would this be performant?
Depends on the size of your grid and your tiles, and probably other factors.
In a current project I use a sprite spread over an a procedurally generated landscape to generate the tile-based lighting:
These are fairly small (for now) landscapes usually around 4 x 4 chunks made up of 16 x 16 tiles. But even with a flood-fill lighting algorithm of my design (that’s not using Jobs, Burst or anything yet), it only takes 4ms to do the calculations, make the texture, assign the pixels, etc etc.
So I imagine going over a tilemap to assign 0 or 1 to a blob of data, and then using that to generate a texture, is probably going to be fine.
Either way, test small in isolation to see if it works. Then scale those tests up. No doubt some optimisations will be needed but the profiler can help with that.
Thanks! I will definitely try!
1 Like