Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I already have:

mySpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); 

and take care to draw all my sprites in order.

I now want to draw some textures tiled. Do I have to create another SpriteBatch and call:

mySpriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.Opaque, SamplerState.LinearWrap, DepthStencilState.Default, RasterizerState.CullNone); 

To draw my tiled textures? But won't these tiled sprites be drawn on top the ones already drawn with the previous SpriteBatch?

What I would really like is to be able to do is draw some sprites normally and some tiled using the same SpriteBatch then my ordering wont be messed up. Is this possible? I tried:

GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap; GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;

In my first and only SpriteBatch but got the InvalidOperationException:

Cannot change read-only SamplerState. State objects become read-only the first time they are bound to a GraphicsDevice. To change property values, create a new SamplerState instance.

share|improve this question

1 Answer 1

up vote 9 down vote accepted

Here's the problems you are having:

  1. Sorting for sprite batches only applies within that batch. At End, everything in the batch gets drawn to screen and becomes pixels. The default depth state for SpriteBatch does not read the depth buffer.
  2. State objects (eg: SamplerState) are read-only once they are used. In this case, you're accessing the state that the graphics device is currently using. You can create a new state of your own, if you like - or use different existing ones.

Here's what you need to do:

Fortunately you don't have to create a custom SamplerState, as you can just use the built-in SamplerState.LinearWrap. Pass it into SpriteBatch.Begin like so:

spriteBatch.Begin(SpriteSortMode.Deferred,
                  null, SamplerState.LinearWrap, null, null);

(Note the use of Deferred - this is the default. You should only use Immediate if you actually need that behaviour, as it is much, much slower. Passing null in for state objects causes SpriteBatch to use the default state object for that state.)

Then it's a simple matter of tiling your sprites. The trick to do this with SpriteBatch.Draw is the source rectangle parameter. You can specify a source rectangle that extends beyond the edge of your texture. If, for example, you wanted to draw your sprite tiled 3 times horizontally and 2 times vertically, here's how:

spriteBatch.Draw(myTexture, myPosition,
                 new Rectangle(0, 0, myTexture.Width * 3, myTexture.Height * 2),
                 Color.White);

Then, to draw a non-tiled sprite, simply don't specify a source rectangle.


It is worth noting the reason for the existence of TextureAddressMode.Clamp: If you draw a tile with Wrap, and then position or scale your sprite it so that a pixel in the sprite's texture does not perfectly line up with a pixel on the screen, you will find that your sprite gets a fringe where blending is happening between two pixels on the opposite sides of the texture. Clamp prevents blending from doing this by extending the edge pixels of the texture out to infinity. To avoid this problem you must either have pixel-perfect sprite positioning, or ensure that your art assets look reasonable if it happens.

It's also worth noting that this wrapping technique precludes the use of texture atlases. So, if you have many different tile types, it can be better for performance to just draw the tile multiple times. (Details)

share|improve this answer
    
+1 Really nice answer! I didn't know the part about sorting is only in that batch, never thought of it, but it really makes sense. –  Mikael Högström Aug 12 '12 at 16:28
    
great answer, and thanks for the info on immediate - it was on my todo list to check out other options. –  markmnl Aug 14 '12 at 0:24
    
however I am relying on the TextureAddressMode.Clamp at the moment - I use it to strech my sky texture across the screen. so my question becomes: can I create a custom SamplerState and changed the SamplerState after by SpriteBatch has begun? –  markmnl Aug 14 '12 at 0:28
    
@Fëanor No, you need to end the batch and start a fresh one (you can use the same SpriteBatch object) with the desired settings. SpriteBatch basically makes a list of sprites internally and then draws the list when you call End(), using the states you specified to Begin, and batching together as many sprites as possible (details). The exception to this is Immediate mode, which draws immediately on Draw() instead of filling a list. This allows you to set states on the graphics device - but you don't get batching (slow). –  Andrew Russell Aug 14 '12 at 7:47
    
(It's worth pointing out that you can still do stretching with Wrap specified. Just specify a scaling factor or a larger destination rectangle, and leave the source rectangle sized "normally". Unless you're actually deliberately rendering outside the clamped region to stretch only the edges of the texture?) –  Andrew Russell Aug 14 '12 at 7:49

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.