Search code examples
libgdxparallax

Parallax background tiled imperfectly


I have some problems with my parallax background images. I embedded a GIF below that shows the problems: (1) there's a black flickering line at the edge of the texture and (2) the movement is not smooth (and this isn't due to the GIF's small frame rate). I also added the short code for my ScrollableImage class, which is at the heart of how I implemented parallax. I suspected the problem might be due to the modulo operation inside the setScrollOffset method, but that wasn't it and now I'm out of ideas. What should I try to fix this problem?

public class ScrollableImage extends Widget {

   private TextureRegion region;
   private float scrollOffset = 0.0f;

   public ScrollableImage(TextureRegion region) {
      this.region = region;
   }

   @Override
   public void draw(Batch batch, float parentAlpha) {
      super.draw(batch, parentAlpha);

      float w = region.getRegionWidth();
      float h = region.getRegionHeight();

      float scale = getHeight() / h;
      float scaledWidth = w * scale;
      float scaledHeight = h * scale;
      float scaledOffset = scrollOffset * scale;

      Color color = getColor();
      batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);

      for (float x = getX() - scaledOffset; x < getX() +  getWidth(); x += scaledWidth) {
         batch.draw(region, x, getY(), scaledWidth, scaledHeight);
      }
   }

   public float getScrollOffset() {
      return scrollOffset;
   }

   public void setScrollOffset(float value) {
      scrollOffset = Math.max(0, value % (float)region.getRegionWidth());
   }
}

Solution

  • I managed to fix the problem. In case someone runs into similar problems in the future, here's what I did. With the solution that gave me problems before, I was tiling the background from software (I had a for loop the was drawing the texture multiple times); what I did now and which works great is the use OpenGL's GL_REPEAT, which will use the hardware (the GPU) to repeat the texture. Here's my code:

    public class ParallaxWidget extends Widget implements Disposable {
    
       private static final int LAYER_COUNT = 2;
       private Texture[] textures = new Texture[LAYER_COUNT];   
       private float[] scrollFactors = new float[LAYER_COUNT];
       private float[] scrollAmounts = new float[LAYER_COUNT];
    
    
       public ParallaxWidget(String path0, float factor0, String path1, float factor1) {
          scrollFactors[0] = factor0;
          scrollFactors[1] = factor1;
    
          scrollAmounts[0] = 0.0f;
          scrollAmounts[1] = 0.0f;
    
          textures[0] = new Texture(Gdx.files.internal(path0));
          textures[1] = new Texture(Gdx.files.internal(path1));
    
          textures[0].setWrap(TextureWrap.Repeat, TextureWrap.ClampToEdge);
          textures[1].setWrap(TextureWrap.Repeat, TextureWrap.ClampToEdge);
       }
    
       @Override
       public void draw(Batch batch, float parentAlpha) {
          super.draw(batch, parentAlpha);
    
          Color color = getColor();
          batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
    
          for (int i = 0; i < LAYER_COUNT; ++i) {
             drawLayer(batch, i);
          }
       }
    
       @Override
       public void dispose() {
           for (Texture texture : textures) {
               texture.dispose();
           }
       } 
    
       public void updateScroll(float value) {
          for (int i = 0; i < LAYER_COUNT; ++i) {
             scrollAmounts[i] = value * scrollFactors[i];
          }
       }
    
       private void drawLayer(Batch batch, int index) {
          float x = getX();
          float y = getY();
    
          float w = getWidth();
          float h = getHeight();
    
          float th = textures[index].getHeight();
          float tw = textures[index].getWidth() * h / th;
    
          float u = scrollAmounts[index] / tw;
          float v = 1.0f;
    
          float u2 = u + (w / tw);
          float v2 = 0.0f;
    
          batch.draw(textures[index], x, y, w, h, u, v, u2, v2);
       }
    }