1
\$\begingroup\$

i try to learn Xna, but now i have problem:

In my simple 2D game i have tile engine, using

    SpriteBatch.Draw(Texture2D, Rectangle, Nullable<Rectangle>, Color, Single, Vector2, SpriteEffects, Single)

to extract tiles from TileSet (1 big texture). This works nice.

But then i wanted to merge all displayed tiles into one big texture, instead of many small tiles (something like THIS). Problem is that, i cant use SpriteBatch.Draw() with 2 different overloads at same time.

So, is there any way to use GPU for (1.) extracting tiles from tileSet and (2.) merge tiles together (and render them as 1 texture) ?

thanks for ideas

\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

Are you sure you really need to store the result into a separate texture? Normally you'd just iterate over each individual visible tile in your map and draw them one at a time inside the same SpriteBatch block. Since you're using the same texture in all the calls, the SpriteBatch class will automatically group all calls into a single dynamic vertex buffer and submit it to the GPU all at once.

But if you really need to render it to a texture, you can do it using the RenderTarget2D class which looks like a regular Texture2D but lets you set up the GraphicsDevice so that it draws directly to the texture instead of drawing to the screen.

Here's an (untested) example that should give you a general idea how to use it. First create an instance of RenderTarget2D:

RenderTarget2D renderTarget2D = new RenderTarget2D(GraphicsDevice, textureWidth, textureHeight);

Then call something like the following code once in order to render your merged tiles into the texture. Note: The way I draw my tiles is just an example, use whatever you're already using. The important part is setting and unsetting the render target:

// Set render target
GraphicsDevice.SetRenderTarget(renderTarget2D);

// Render your tiles as usual, this is just an example
spriteBatch.Begin();
for(int x=0; x<width; ++x)
    for(int y=0; y<height; ++y)
       spriteBatch.Draw(tileset, new Vector2(x*tileWidth, y*tileHeight), CalculateRectangleForTile(map[x,y]), Color.White);
spriteBatch.End();

// Unset render target
GraphicsDevice.SetRenderTarget(null);

And finally on your Draw method use the render target as a normal texture:

// On draw you can use your render target as any other texture
spriteBatch.Begin();
spriteBatch.Draw(renderTarget2D, Vector2.Zero, Color.White);
spriteBatch.End();
\$\endgroup\$
2
  • \$\begingroup\$ No, i propably dont need to do this "merging", but i draw 2040 visible tiles (depending on resolution) and even my GPU and CPU are bored (almost no load), i simply want try it.. Btw. RenderTarget what u mentioned here, looks great and easy to understand. I will try, thanks for this. \$\endgroup\$
    – ns27
    Mar 9, 2012 at 18:27
  • \$\begingroup\$ This is exactly what i needed, thank you sir and obviously thank you google :) \$\endgroup\$ Jul 6, 2012 at 21:17
0
\$\begingroup\$

That tutorial works well for tiling an image across the viewport but is incomparable with an image map. What the author is doing is scrolling the sampling location on the texture and when the source rectangle extends past the borders of the Texture2D its wrapping around to select from the other side.

To render a tile map you are going to have to make as many draw calls as there are visible tiles.

Assuming that your tiles are in an 2 dimensional array this is easy

The left most visible tile is

var minX = Math.Max((int)Math.Floor((float)worldArea.Left / TileWidth), 0);

The right most visible tile is

var maxX = Math.Min((int)Math.Ceiling((float)worldArea.Right / TileWidth), Width);

The top most visible tile is

var minY = Math.Max((int)Math.Floor((float)worldArea.Top / TileHeight), 0);

The bottom most visible tile is

var maxY = Math.Min((int)Math.Ceiling((float)worldArea.Bottom / TileHeight), Height);

One you know this all you need to do is loop though the visible tiles and render them. Below is an example of how I have done this.

public void Draw(SpriteBatch spriteBatch, Rectangle worldArea) {

    //Init the holder
    _holder = new Rectangle(0, 0, TileWidth, TileHeight);

    //Figure out the min and max tile indices to draw
    var minX = Math.Max((int)Math.Floor((float)worldArea.Left / TileWidth), 0);
    var maxX = Math.Min((int)Math.Ceiling((float)worldArea.Right / TileWidth), Width);

    var minY = Math.Max((int)Math.Floor((float)worldArea.Top / TileHeight), 0);
    var maxY = Math.Min((int)Math.Ceiling((float)worldArea.Bottom / TileHeight), Height);

    for (var y = minY; y < maxY; y++) {
        for (var x = minX; x < maxX; x++) {

            _holder.X = x * TileWidth;
            _holder.Y = y * TileHeight;

            var t = tileLayer[y * Width + x];
            spriteBatch.Draw(
                t.Texture,
                _holder, 
                t.SourceRectangle,
                Color.White, 
                0,
                Vector2.Zero,
                t.SpriteEffects, 
                0);
        }
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ Hmm, propably i was not clear in my question (or my english isnt good enought), but i was asking about "tiles extracted from tileSet" (using spriteBatch.Draw) and "merge these tiles to 1 texture" (at same time), then render.. You talk about "rendering only displayed tiles". This already do my "camera", and code looks quite same.. \$\endgroup\$
    – ns27
    Mar 9, 2012 at 18:15
  • \$\begingroup\$ So prerender the entire map save it to a texture and use that texture to actually draw it? \$\endgroup\$ Mar 9, 2012 at 18:22
  • \$\begingroup\$ yes. Simply instead of drawing many tiles, first put them into 1 texture and then render. (iam curious what it will do with performance..) \$\endgroup\$
    – ns27
    Mar 9, 2012 at 18:33
  • \$\begingroup\$ It might help fps but it will kill your memory. The max size of a texture 2D on HI-DEF config of the PC is 4096x4096 which is a pretty small map, but will consume 64mbs of memory. \$\endgroup\$ Mar 9, 2012 at 19:52
  • \$\begingroup\$ oh, my bad... not entire map, only visible tiles during game (no pre-rendering). Resulting texture is same size as user sets resolution (1920x1080 or whatever). \$\endgroup\$
    – ns27
    Mar 9, 2012 at 20:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .