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'm trying to scroll my background forever and so I'm using the background image twice. When the first one goes off screen, it should relocate to the end of the second one. However, there seems to be a black bar inbetween the two images and so it seems that the image is not being moved into place quick enough. What would be the algorithm to do such a thing to give the illusion of a continuous background? I've got this at the moment (which loops, but with a black bar in between the background image)

 GeneralTransform gt = bg1Canvas.TransformToVisual(Application.Current.RootVisual as UIElement);
        Point bg1Offset = gt.Transform(new Point(0, 0));


 GeneralTransform gt2 = bg2Canvas.TransformToVisual(Application.Current.RootVisual as UIElement);
        Point bg2Offset = gt2.Transform(new Point(0, 0));


 bg1.TranslateX -=  playerVel.X; //bg1 and bg2 are the composite transforms for the canvas
 bg2.TranslateX -=  playerVel.X;

 if (bg1Offset.X < -bg_width)
     bg1.TranslateX -= bg1Offset.X - bg_width;
 if (bg2Offset.X < -bg_width)
     bg2.TranslateX -= bg2Offset.X - bg_width;

Any ideas?

Edit - Updated code Edit 2: New code with three images (all same width). Works for the first few scrolls, then suddenly stops working and no background is displayed.

if (bg1Offset.X < -bg_width )
                bg1.TranslateX = bg3Offset.X+bg_width;

            if (bg2Offset.X < -bg_width )
                bg2.TranslateX = bg1Offset.X + bg_width;

            if (bg3Offset.X < -bg_width)
                bg3.TranslateX = bg2Offset.X + bg_width;
share|improve this question
    
playerVel versus playerVek -- is this a typo, or are you using the velocity vector in one instance the position vector in another? Beyond that I don't think you've provided enough context (for example, there are gates on bg1Offset, but you never update it that snippet, and it's not clear what bg1Offset represents that differs from bg1.X, et cetera...). –  Josh Petrie Apr 4 '11 at 15:38
    
Sorry, I had my own custom classes and such and tried to extract the main points into one example. I've edited the code to show what it would look like without the custom player class. The playerVek was a typo :) –  XSL Apr 4 '11 at 15:50
add comment

4 Answers 4

up vote 2 down vote accepted

Try making you background image bigger than your screen.

This can be done by duplicating the image in paint or something.

Start at the beginning of the image and scroll along and when you get near the end of the image jump back to displaying the beginning of the image.

Try to jump before you reach the end of the image.

This will save memory and run faster.

Sorry I cant give an example but I don't recognise the language your using.

share|improve this answer
add comment

I've written an article about a general purpose solution that lets you scroll an image infinitely in every direction even if your camera is transformed. But it's done in XNA with HLSL and relies on the texture addressing mode being set to wrap, so I'm not sure if it could be translated to Silverlight. Here's the link:

http://www.david-gouveia.com/scrolling-textures-with-zoom-and-rotation/

For a less general purpose solution, that doesn't use a vertex shader you could try the following link where I describe two other ways to solve it (the first should work anywhere since you only need the ability to choose a source rectangle when drawing):

http://www.david-gouveia.com/scrolling-textures-in-xna/

EDIT:

To give a general description of the techniques linked above...

Basically, your way of scrolling the background by drawing it twice and moving these two pieces around when the player moves outside a certain range, is the way I first implemented texture scrolling.

But soon I figured there were several limitations to that approach, and throughout the years found about a few other ways to do this that are much more flexible and general.

For instance, first I wanted to have the background scroll diagonally instead of horizontally, and so the original technique broke. Later on I found a way to achieve the same thing in a much easier way so I changed to that. Eventually I wanted to be able to scale and rotate the camera as well so had to go even further.

Here's a summary of all three that I described on my blog (code samples are XNA/HLSL). If possible, I really recommend the first one, but of course it depends on your platform's limitations.


Technique 1

  1. Create a fullscreen textured quad (a SpriteBatch.Draw with destination rectangle set to the entire screen will do just fine) and map the texture you want to scroll to it.
  2. Set the texture addressing mode to wrap so that it automatically wraps whenever the texture coordinates leave the normalized [0,1] range.
  3. Use a vertex shader to generate new texture coordinates for your quad's vertices based on your camera's transform. This transform is pretty much the inverse of your view matrix, but with translations being done in texture space (i.e. dividing the translation amount by the texture size).

I use this vertex shader (where 99% of it is boilerplate needed for SpriteBatch to work - only the last line is new):

sampler TextureSampler : register(s0);
float2   ViewportSize;
float4x4 ScrollMatrix;

void SpriteVertexShader(inout float4 color : COLOR0, inout float2 texCoord : TEXCOORD0, inout float4 position : POSITION0)
{
  // Half pixel offset for correct texel centering.
   position.xy -= 0.5;

   // Viewport adjustment.
   position.xy = position.xy / ViewportSize;
   position.xy *= float2(2, -2);
   position.xy -= float2(1, -1);

    // Transform our texture coordinates to account for camera
    texCoord = mul(float4(texCoord.xy, 0, 1), ScrollMatrix).xy;
}

And pass it this matrix, which is calculated using the properties of my camera:

public Matrix GetScrollMatrix(Vector2 textureSize)
{
    return Matrix.CreateTranslation(new Vector3(-Origin / textureSize, 0.0f)) *
             Matrix.CreateScale(1f / Zoom) *
             Matrix.CreateRotationZ(Rotation) *
             Matrix.CreateTranslation(new Vector3(Origin / textureSize, 0.0f)) *
             Matrix.CreateTranslation(new Vector3(Position / textureSize, 0.0f));
}
  • Pros: Single quad being drawn to cover the entire screen. Works perfectly with a zoomed and rotated camera too.
  • Cons: Needs vertex shader and texture address set to wrap.

Technique 2

  1. Same as Technique 1 Step 1
  2. Same as Technique 1 Step 2
  3. Play around with SpriteBatch.Draw source rectangle by setting the rectangle's X/Y to be the camera's position and the rectangle's Width/Height to be the screen's size.

Example:

spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.LinearWrap, null, null);
spriteBatch.Draw(texture, position, new Rectangle(-scrollX, -scrollY, texture.Width, texture.Height), Color.White);
spriteBatch.End();
  • Pros: Simplest method. Doable in 2/3 lines of code. No need for vertex shader either.
  • Cons: Still needs texture wrap addressing mode. You can still move the camera around in every direction but you can't rotate it. You can however zoom the camera with just a little trick: just scale the source rectangle's size along inversely to the camera's zoom (i.e. the larger the camera zoom, the smaller the rectangle - just divide the original rectangle size by the zoom factor).

Technique 3

This method doesn't need anything other than being able to specify a source rectangle on drawing.

  1. It's a variation of your idea of drawing the background twice depending on the player's position, but enlarged to all four directions (so that it also repeats up down), and instead of moving the pieces around, you move their source rectangles (so the pieces stay still but the content you see changes).
  2. Basically draw the texture four times like in the image below, and depending on the camera's position you draw more, or less of each of them to give the impression that the camera is moving.

Four Backgrounds

Something like:

Rectangle source1 = new Rectangle(0, 0, texture.Width - scrollX, texture.Height - scrollY);
Rectangle source2 = new Rectangle(texture.Width - scrollX, 0, scrollX, texture.Height - scrollY);
Rectangle source3 = new Rectangle(0, texture.Height - scrollY, texture.Width - scrollX, scrollY);
Rectangle source4 = new Rectangle(texture.Width - scrollX, texture.Height - scrollY, scrollX, scrollY);

Vector2 position1 = new Vector2(position.X + scrollX, position.Y + scrollY);
Vector2 position2 = new Vector2(position.X, position.Y + scrollY);
Vector2 position3 = new Vector2(position.X + scrollX, position.Y);
Vector2 position4 = new Vector2(position.X, position.Y);

spriteBatch.Begin();
spriteBatch.Draw(texture, position1, source1, Color.White);
spriteBatch.Draw(texture, position2, source2, Color.White);
spriteBatch.Draw(texture, position3, source3, Color.White);
spriteBatch.Draw(texture, position4, source4, Color.White);
spriteBatch.End();

And you also need to be careful and wrap the scrollX and scrollY values around so that they don't leave the texture.

  • Pros: Doesn't need many features to implement.
  • Cons: Can only move camera around, no other transformations.

share|improve this answer
add comment

This may be for XNA not silverlight (still C# though), but this is a good tutorial to achieve what you want.

share|improve this answer
    
Thanks, will take a look. –  XSL Apr 4 '11 at 15:51
    
The method seems to work for the first few 'scrolls' but then the image never comes back after that. I've updated the original post to show the new one with the method from your link. –  XSL Apr 4 '11 at 17:43
add comment

I think you need to draw the image three times: Imagine your background is \/

1.      [ /\ ] \/ \/
2.    \ [ /\ ] / \/
3.    \/[ \/ ] \/ 
4. \/ \ [ /\ ] /    
5.      [ /\ ] \/ \/

(assuming Right ==> Left scrolling)

If you're seeing a black bar (I presuem you are clearing to Color.Black) you probably have an error in your positioning code.

share|improve this answer
    
I've added another image, but it doesn't work. I'll update my OP to to show my new code. –  XSL Apr 4 '11 at 17:43
add comment

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.