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 have a sprite that I use as a sprite sheet, where regions of the image represent various UI elements (window corner, border, background, etc.).

I want to use a certain region of that sprite as a texture that I can tile over a larger destination rectangle.

Setting the SamplerState to LinearWrap and then calling SpriteBatch.Draw with destination rect and source rect ends up stretching that region of the image, not tiling it.

All the examples I've seen that implement tiling use the entire sprite - I only want to use a region of it, and tile that region.

Is there any way to do this?

Edit for clarity:

Here's an example spritesheet

enter image description here

where I want that colored region. I have a rectangle that represents a window border, that is larger than the region in the source image.

Here's what I want to end up with

enter image description here

Here's the code I've been using.

spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.LinearWrap, null, null);
spriteBatch.Draw(sourceTexture, windowTopBorderRect, sourceTextureRegionRect, Color.White, 0, Vector2.Zero, SpriteEffects.None, 0);

And what I get is the image stretched across the window border rectangle, instead of tiling the source region within that rectangle.

enter image description here

share|improve this question

2 Answers 2

up vote 2 down vote accepted

I don't think using LinearWrap will work in this case, the reason being that LinearWrap takes place automatically when the texture coordinates fall beyond the edges of the texture, but you're using a source rectangle that falls entirely within the texture, so it's not even doing anything.

I can think of three solutions (in the order I'd probably choose):

  1. Create a new Texture2D and copy only that portion of the texture into the new one, or do this offline in your image editor. Then draw it with a source rectangle that has the same size as the destination rectangle. Since this source rectangle will be larger than the texture, wrapping will take place. And since they both have the same size, there won't be any stretching.

    To be more specific what I mean is something like:

    Rectangle windowTopBorderRect = /* wherever you want it to draw */
    Rectangle sourceTextureRegionRect = new Rectangle(0, 0, windowTopBorderRect.Width, windowTopBorderRect.Height);
    

    You could also use the position parameter instead of the destinationRectangle and let the sourceRectangle determine the size.

  2. Alternatively do the tiling manually with multiple SpriteBatch.Draw calls. Basically calculate how many times you need to draw the texture to fill that space, and draw them in the loop. In the case of an horizontal strip like your example, the last draw call will probably need a source rectangle, to crop only the remaining portion that fits that space.

  3. You could also probably draw using a special pixel shader effect that calculates a new texture coordinate that wraps around the original source rectangle without stretching, but this might be tricky.

share|improve this answer
    
Thank you. I think your first option is likely what I'll go with. I'd prefer a single spritesheet for organization reasons, but if it's just the UI layer it's not a huge deal. It doesn't seem like what I'm asking for is all that arcane; I guess the team just didn't think of it. –  Superstringcheese Jul 29 '12 at 0:21
1  
@Superstringcheese Note that the primary reason for using sprite sheets is performance (avoiding texture changes). If you're just going to pull the data into separate textures again, then there are advantages to simply using multiple textures in the first place. –  Andrew Russell Jul 29 '12 at 7:27

Pass the SpriteBatch a rectangle instead of null for the sourceRectangle parameter. It is defined as a nullable type in the method signature so it is optional and defaults to the whole texture.

Edit: You will also need to change your destination rect with each draw call. Sorry I left that out.

share|improve this answer
    
I said in my OP that I'm passing both the source and destination rectangle. I added an example with the method signature I'm using. –  Superstringcheese Jul 28 '12 at 15:55
    
Sorry about that I just reread my answer and fixed it. It is the destination rect that is the problem. –  Sinthia V Jul 28 '12 at 15:58
    
Could you possibly work this up locally and verify the outcome, then post your code here? –  Superstringcheese Jul 28 '12 at 16:00
    
No time, sorry. You'll have to do the math yourself. You have to alter the width and position of your destination rect to reflect the width of your source. Modify the position so you don't actually draw it on top of itself. –  Sinthia V Jul 28 '12 at 16:08
    
Thanks, but I don't think this is a math problem. It's becoming clear that the Draw method just doesn't support this scenario. –  Superstringcheese Jul 29 '12 at 0:22

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.