Why not add a fog to reduce the number of tiles that must be drawn?
Or simply a vignette?
Just throwing in my 2 cents based on what I’ve done:
Assuming using a spanning technique: (start_x to end_x) & (start_y to end_y) relative to a player map coordinate:
It’s possible to preprocess a map to mark tile with a “next_tile”, to skip empties - and to improve further (altho it may be overkill) you could use “frame coherence” and add/remove on a render-target (with coordinate reset) as needed (ie: moving the target’s offset back 64 and adding to the edges each time the draw coords move beyond 64 pixels). This way in the initial draw - all empties are skipped (instead x++, going x=next_index). This works for static layers. [also always using sprite-sheet/batching for all world gfx]
Some of this could get tricky for layers that contain a lot of dynamic elements (in which case I’d separate dynamic from static if needed).
Personally - I’d go simple with span skips and add speed tricks as necessity arises. Disagreements & criticisms are welcome.
That’s a alternative if the map is static enough you could just render the parts of the fully drawn static map to textures and scroll thru them that would be really fast.
You could probably also instance it dunno if that would really be better.
But i suppose a mixed approach that was really refined would be the best for a particular situation were you knew exactly what was needed.
I do think in any case that this is a solid starting point to refine things further from.
Since this is up i might as well add how to manually set tiles via index.
Below i wanted to also demonstrate that the source destination and map tile indexs can be decoupled.
For example the second SetTile call below adds a map tile to be 3x as large as the other tiles so that it basically overlaps other tiles so that one tile drawn is really big on the map.
Additionally drawing a hexmap from this example instead is also straight forward and trivial.
Every odd numbered tile is displaced by 1/2 the destination tile height typically upwards and the placement position is multiplied by the destination tileWidth * by say .75 which is just some percentage of overlap
in load after the tile map is initially set up.
var source = new Rectangle(7 * mountainSourceTileWidth, 7 * mountainSourceTileHeight, mountainSourceTileWidth, mountainSourceTileHeight);
SetTile(t2d_height_mountain_mono, source, 1, 2);
var destination = new Rectangle(2 * tileDestinationWidth, 2 * tileDestinationHeight, tileDestinationWidth*3, tileDestinationHeight*3);
SetTile(t2d_height_mountain_mono, destination, source, 2, 2);
The above and below could be further automated depending on how you want them to behave and the expectations,
As well you could set them dynamically at runtime and then set the buffers again though im not sure you would want to do it that way for a dynamic layer in that case it might be better to have some layers that are in there own smaller buffer or just straight up drawn directly.
public void SetTile(Texture2D texture, Rectangle destination, Rectangle source, int mapTilePosX, int mapTilePosY)
{
int layer = -1;
for (int i = 0; i < layerTextures.Count; i++)
if (layerTextures[i] == texture)
{
layer = i;
}
if (layer < 0)
{
throw new Exception("Error This function doesn't explicitly handle adding a entirely new layer eg a new texture.");
}
int mapTilesLength = mapWidth * mapHeight;
var layerVerticeOffset = mapTilesLength * layer * VERTICES_PER_QUAD;
int startingVerticeIndex = ((mapTilePosY * mapWidth + mapTilePosX) * VERTICES_PER_QUAD) + layerVerticeOffset;
//
Vector2 tl = source.Location.ToVector2() / texture.Bounds.Size.ToVector2();
Vector2 tr = new Vector2(source.Right, source.Top) / texture.Bounds.Size.ToVector2();
Vector2 br = new Vector2(source.Right, source.Bottom) / texture.Bounds.Size.ToVector2();
Vector2 bl = new Vector2(source.Left, source.Bottom) / texture.Bounds.Size.ToVector2();
// t1
vertexList[startingVerticeIndex + 0] = new VertexPositionColorTexture(new Vector3(destination.Left, destination.Top, 0f), vertexList[startingVerticeIndex + 0].Color, tl);
vertexList[startingVerticeIndex + 1] = new VertexPositionColorTexture(new Vector3(destination.Left, destination.Bottom, 0f), vertexList[startingVerticeIndex + 1].Color, bl);
vertexList[startingVerticeIndex + 2] = new VertexPositionColorTexture(new Vector3(destination.Right, destination.Bottom, 0f), vertexList[startingVerticeIndex + 2].Color, br);
// t2
vertexList[startingVerticeIndex + 3] = new VertexPositionColorTexture(new Vector3(destination.Right, destination.Bottom, 0f), vertexList[startingVerticeIndex + 3].Color, br);
vertexList[startingVerticeIndex + 4] = new VertexPositionColorTexture(new Vector3(destination.Right, destination.Top, 0f), vertexList[startingVerticeIndex + 4].Color, tr);
vertexList[startingVerticeIndex + 5] = new VertexPositionColorTexture(new Vector3(destination.Left, destination.Top, 0f), vertexList[startingVerticeIndex + 5].Color, tl);
}
public void SetTile(Texture2D texture, Rectangle source, int mapTilePosX, int mapTilePosY)
{
int layer = -1;
for(int i =0; i < layerTextures.Count;i++)
if(layerTextures[i] == texture)
{
layer = i;
}
if(layer < 0)
{
throw new Exception("Error This function doesn't explicitly handle adding a entirely new layer eg a new texture.");
}
int mapTilesLength = mapWidth * mapHeight;
var layerVerticeOffset = mapTilesLength * layer * VERTICES_PER_QUAD;
int startingVerticeIndex = ((mapTilePosY * mapWidth + mapTilePosX) * VERTICES_PER_QUAD) + layerVerticeOffset;
//
Vector2 tl = source.Location.ToVector2() / texture.Bounds.Size.ToVector2();
Vector2 tr = new Vector2(source.Right, source.Top) / texture.Bounds.Size.ToVector2();
Vector2 br = new Vector2(source.Right, source.Bottom) / texture.Bounds.Size.ToVector2();
Vector2 bl = new Vector2(source.Left, source.Bottom) / texture.Bounds.Size.ToVector2();
// t1
vertexList[startingVerticeIndex + 0] = new VertexPositionColorTexture(vertexList[startingVerticeIndex + 0].Position, vertexList[startingVerticeIndex + 0].Color, tl);
vertexList[startingVerticeIndex + 1] = new VertexPositionColorTexture(vertexList[startingVerticeIndex + 1].Position, vertexList[startingVerticeIndex + 1].Color, bl);
vertexList[startingVerticeIndex + 2] = new VertexPositionColorTexture(vertexList[startingVerticeIndex + 2].Position, vertexList[startingVerticeIndex + 2].Color, br);
// t2
vertexList[startingVerticeIndex + 3] = new VertexPositionColorTexture(vertexList[startingVerticeIndex + 3].Position, vertexList[startingVerticeIndex + 3].Color, br);
vertexList[startingVerticeIndex + 4] = new VertexPositionColorTexture(vertexList[startingVerticeIndex + 4].Position, vertexList[startingVerticeIndex + 4].Color, tr);
vertexList[startingVerticeIndex + 5] = new VertexPositionColorTexture(vertexList[startingVerticeIndex + 5].Position, vertexList[startingVerticeIndex + 5].Color, tl);
}
I like this idea, of a “next tile”… Will think about it!