Tile Based Map Parallax Scrolling

I’m in the process trying to add parallax scrolling to my game engine, however I cant quite get it working right, I’ve had results that seem to be close but there’s the problem with either the tiles drifting off screen or the tiles don’t offset properly.

I don’t set the Z of the layers manually the layers movement speed should be based on how wide the layer is relative to the actual stage width, I’ve read several forum posts on the subject and several articles on Parallax scrolling and this is what i have so far (with some messy code as its still in the process of being figured out properly). I’ve left in some commented out code that i experimented with

the Stage class chooses the layers to be rendered, the Scene class tells the screen class which sprite and position to render it at offset by the viewport camera.

I’ve been focusing on making it work along the X axis first so the Y axis code is very preliminary.

I hope my code is descriptive enough

Also some of the code that was originally in the Stage Class has been moved to Scene so if you’re wondering why there’s some duplicate code, that’s why

Also my code is inspired by the methods The Cherno and VanZeben (on youtube) use

sprites are currently 32x32

and any shifts by 5 is reflective of that size. (aka * or / 32)

and shiftX and shiftY are int == 5

also i forgot to mention

width and height = amount of tiles x and y
tilemap.getWidth and getHeight are also amount of tiles;

viewport width and height are in pixels

Edit:

I’m cleaning up my code and i’ll repost once done, i’m also re-looking over this reference material which is the formula for parallax i’m using

http://books.google.co.uk/books?id=7S4ZpTR0DGwC&lpg=PA234&ots=_YrKHjkv3Q&dq=efficient%20parallax%20scrolling%20algorithms%20java&pg=PA233#v=onepage&q=efficient%20parallax%20scrolling%20algorithms%20java&f=false

The Stage Class Render Code:

  public final void render(Screen screen) {
    final int camX = viewport.getCamX();
    final int camY = viewport.getCamY();

    final int x0 = camX >> scene.getShiftX();
    final int x1 = (camX + viewport.getWidth()) >> scene.getShiftX();
    final int y0 = camY >> scene.getShiftY();
    final int y1 = (camY + viewport.getHeight()) >> scene.getShiftY();
    
    final int xRange = x1+2;
    final int yRange = y1+2;
    
    int x, y;
    
   scene.renderBackground(screen, player);//, x0, y0, xRange, yRange);
   
   renderEntities(screen);

    for (y = y0; y < yRange; y++) {
      for (x = x0; x < xRange; x++) {
        scene.renderCollisionForegroundTile(screen, x, y);
        scene.renderForegroundTile(screen, x, y);
      }  
    }

    eventChangedRender(screen);
  }

The Scene Class Render Code:

  void renderBackground(Screen screen, Player player) {//, int y0, int x0, int xRange, int yRange) {
    final int camX = viewport.getCamX();
    final int camY = viewport.getCamY();
    
    final int x0 = camX >> shiftX;
    final int x1 = (camX + viewport.getWidth()) >> shiftX;
    final int y0 = camY >> shiftY;
    final int y1 = (camY + viewport.getHeight()) >> shiftY;
    
    final int xRange = x1+2;
    final int yRange = y1+2;
    
    int x, y;
    for (y = y0; y < yRange; y++) {
      for (x = x0; x < xRange; x++) {
        renderBackgroundTile(screen, player, x, y);
        renderCollisionTile(screen, x, y);
      }
    }
  }

  public final void renderBackgroundTile(Screen screen, Player player, int x, int y) {
    int i, spriteIndex;
    StageMap mapLayer;
    
    for (i = 0; i < backgroundLayers; i++) {
      mapLayer = mapLayers[i];
      
      double displacementX = viewport.getWidth() - pixelWidth-32;
      double displacementY = viewport.getHeight() - pixelHeight-32;
      
      double mapDisplacementX = viewport.getWidth() - (mapLayer.getWidth() << shiftX);
      double mapDisplacementY = viewport.getHeight() - (mapLayer.getHeight() << shiftY);
      
      int xOffset = (int)(viewport.getCamX() * (mapDisplacementX / displacementX));//(int)(player.getX() * (mapLayer.getDisplacementX() / displacementX));
      int yOffset = (int)(viewport.getCamY() * (mapDisplacementY / displacementY));
      
     // x = viewport.getCamX() * (viewport.getWidth() - mapLayers[i].getWidth()) / (viewport.getWidth() - pixelWidth);
      //xa = (xOffset >> shiftX);// ((x << shiftX) - (viewport.getCamX())) * (viewport.getWidth() - (mapLayers[i].getWidth()+64)) / (viewport.getWidth() - (pixelWidth+64)) >> shiftX;
      //ya = ((y >> shiftY) * (viewport.getHeight() - mapLayers[i].getHeight()) / (viewport.getHeight() - pixelHeight)) >> shiftY;
      
      spriteIndex = mapLayer.getSpriteID((xOffset-64 >> 5) + x, y);
      if (spriteIndex != voidSpriteIndex || i == 0) {
        
        //mapLayers[i].render(screen, viewport, sprites[spriteIndex], pixelWidth, pixelHeight, x << shiftY, y << shiftY, xOffset, 0);
        screen.renderSceneTile(viewport, sprites[spriteIndex], x << shiftY, y << shiftY, xOffset, yOffset);
        //if (!sprites[spriteIndex].hasTransparency()) break;
      }
    }
  }

Stage Map Render Code (Currently unused and rendering directly to the screen as I was previously before trying to implement Parallax)

  void render(Screen screen, Viewport viewport, Sprite sprite, int stageWidth, int stageHeight, int xPos, int yPos, int xOffset, int yOffset) {
     screen.renderSceneTile(viewport, sprite, xPos, yPos, xOffset, yOffset);
  }

Screen Render Code:

  public final void renderSceneTile(Viewport viewport, Sprite sprite, int xPos, int yPos, int xOffset, int yOffset) {
    // Viewport bounds can never be lower than 0 or beyond Screen Width or Height
    final int viewportLeft = (viewport.getLeft() < 0 ? 0 : viewport.getLeft());
    final int viewportTop = (viewport.getTop() < 0 ? 0 : viewport.getTop());
    final int viewportRight = (viewport.getRight() > width ? width : viewport.getRight());
    final int viewportBottom = (viewport.getBottom() > height ? height : viewport.getBottom());

    // xPos Starts at the viewport boundry
    xPos += viewportLeft;
    yPos += viewportTop;
    
    // Offset by Camera
    xPos -= viewport.getCamX(); //(viewport.getWidth()>>1) - (viewport.getCamX() * xOffset);
    yPos -= viewport.getCamY(); 
    
    xPos += xOffset; //(viewport.getWidth()>>1) - (viewport.getCamX() * xOffset);
    //yPos += yOffset; 
    
    // If Position + Width and Height of the Sprite will go outside Viewport then crop it by the difference
    final int spriteWidth = (xPos + sprite.getWidth() > viewportRight ? sprite.getWidth() - ((xPos + sprite.getWidth()) - viewportRight) : sprite.getWidth());
    final int spriteHeight = (yPos + sprite.getHeight() > viewportBottom ? sprite.getHeight() - ((yPos + sprite.getHeight()) - viewportBottom) : sprite.getHeight());
    
    // If Position starts outside of viewport then set a Starting position of the difference
    final int spriteStartPixelX = (xPos < viewportLeft ?  viewportLeft - xPos : 0);
    final int spriteStartPixelY = (yPos < viewportTop ?  viewportTop - yPos : 0);
    
    final int xPixelStart = spriteStartPixelX + xPos;
    final int yPixelStart = spriteStartPixelY + yPos;
    
    // x & y Correspond to Sprite Pixel, xPixel & yPixel Correspont do Screen Pixel
    int x, y, xPixel, yPixel;//, pixel, index;
    
    for (y = spriteStartPixelY, yPixel = yPixelStart; y < spriteHeight; y++, yPixel++) {
      for (x = spriteStartPixelX, xPixel = xPixelStart; x < spriteWidth; x++, xPixel++) {
         pixels[xPixel + yPixel * width] = sprite.getPixel(x, y);
      }
    }
  }