Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (522)
Games in Android Showcase (127)
games submitted by our members
Games in WIP (590)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Tile Based Map Parallax Scrolling  (Read 1545 times)
0 Members and 1 Guest are viewing this topic.
Offline CyberAxe

Junior Newbie





« Posted 2013-06-28 14:40:49 »

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:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
  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:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
  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)
1  
2  
3  
  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:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
  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);
      }
    }
  }
Offline CyberAxe

Junior Newbie





« Reply #1 - Posted 2013-06-29 12:35:05 »

I've changed the code for rendering parallax to more match the code in the book.

It works as intended with very little performance drop, I'll post the code finished code after I clean it up and optimize it for people to refer to it in future.

Offline CyberAxe

Junior Newbie





« Reply #2 - Posted 2013-06-29 16:20:52 »

Here's the finished code:

Scene variables: (absolute width and height of the stage)
1  
2  
    displacementX = viewport.getWidth() - pixelWidth;
    displacementY = viewport.getHeight() - pixelHeight;


Stage Map variables: (width and height of specific layer)
1  
2  
    displacementX = viewport.getWidth() - pixelWidth;
    displacementY = viewport.getHeight() - pixelHeight;


Stage Class (most peoples Level class)
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
  @Override
  public final void tick(long tickTimeNow) {
    this.tickTimeNow = tickTimeNow;
    eventChangedTick();
   
    //sound.tick();
   
    updateEntities();
    viewport.setCam(player.getCenterX() - (viewport.getWidth() >> 1), player.getCenterY() - (viewport.getHeight() >> 1));
   
    // Set the Parallax Offsets
    final int xOffset = (viewport.getWidth() >> 1) - Math.round(player.getX()) - scene.getTileWidth() + ((player.getWidth() >> 1) % scene.getTileWidth());
    final int yOffset = (viewport.getHeight() >> 1) - Math.round(player.getY()) - scene.getTileHeight() + ((player.getHeight() >> 1) % scene.getTileHeight());
    viewport.setScroll(xOffset, yOffset);
  }

  @Override
  public final void render(Screen screen) {
    // Render backgrounds with Parallax Scrolling
    scene.renderBackground(screen, viewport);

    final int camX = viewport.getCamX();
    final int camY = viewport.getCamY();

    // The first visible tile on the screen.
    final int xTileStart = camX >> scene.getShiftX();
    final int yTileStart = camY >> scene.getShiftY();

    // The amount of Visible tiles on the screen.
    final int xTileLast = (camX + viewport.getWidth()) >> scene.getShiftX();
    final int yTileLast = (camY + viewport.getHeight()) >> scene.getShiftY();

    // The amount of visible tiles ont he screen plus 2 to account for offsets.
    final int xRange = xTileLast + 2;
    final int yRange = yTileLast + 2;

    int x, y;    
    // Loop through the tile range and render the background Collision tiles that are visible
    for (y = yTileStart; y < yRange; y++) {
      for (x = xTileStart; x < xRange; x++) {
        scene.renderCollisionTile(screen, x, y);
      }
    }

    renderEntities(screen);

    // Loop through the tile range and render the foreground tiles that are visible
    for (y = yTileStart; y < yRange; y++) {
      for (x = xTileStart; x < xRange; x++) {
        scene.renderCollisionForegroundTile(screen, x, y);
      }
    }
   
    // Render foregrounds with Parallax Scrolling
    scene.renderForeground(screen, viewport);

    // Render Stage Name if Recently Changed
    eventChangedRender(screen);
  }


Scene Class:
1  
2  
3  
4  
5  
6  
7  
8  
9  
  public boolean constrained = false;
  public final void renderBackground(Screen screen, Viewport viewport) {
    final int xOffset = (constrained ? Math.max(Math.min(viewport.getScrollX(), 0), displacementX) : viewport.getScrollX());
    final int yOffset = (constrained ? Math.max(Math.min(viewport.getScrollY(), 0), displacementY) : viewport.getScrollY());
   
    for (int i = 0; i < backgroundLayers; i++) {
      mapLayers[i].renderParallax(screen, viewport, sprites, xOffset, yOffset, displacementX, displacementY, i == 0);
    }
  }


MapLayer Code
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
  void renderParallax(Screen screen, Viewport viewport, Sprite[] sprites, int xOffset, int yOffset, int dx, int dy, boolean renderVoid) {
    final int xOffsetParallax = xOffset * displacementX / dx;
    final int yOffsetParallax = yOffset * displacementY / dy;

    final int shiftX = sprites[voidSpriteIndex].getShiftX();
    final int shiftY = sprites[voidSpriteIndex].getShiftY();

    // The first visible tile on the screen.
    final int xTileStart = (-xOffsetParallax) >> shiftX;
    final int yTileStart = (-yOffsetParallax) >> shiftY;

    // The amount of Visible tiles on the screen.
    final int xTileLast = xTileStart + (viewport.getWidth() >> shiftX);
    final int yTileLast = yTileStart + (viewport.getHeight() >> shiftY);

    // The amount of visible tiles ont he screen plus 2 to account for offsets.
    final int xRange = xTileLast + 2;
    final int yRange = yTileLast + 2;

    int x, y, spriteIndex;
    for (y = yTileStart; y < yRange; y++) {
      for (x = xTileStart; x < xRange; x++) {
        spriteIndex = getSpriteID(x, y);
        if ((!renderVoid && spriteIndex != voidSpriteIndex) || renderVoid) screen.renderSceneParallax(viewport, sprites[spriteIndex], x << shiftX, y << shiftY, xOffsetParallax, yOffsetParallax);
      }
    }
  }


Screen Class
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
public final void renderSceneParallax(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 Layer Offset
    xPos += 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 to crop
    final int spriteStartPixelX = (xPos < viewportLeft ?  viewportLeft - xPos : 0);
    final int spriteStartPixelY = (yPos < viewportTop ?  viewportTop - yPos : 0);
   
    // Starting x and y Pixels on the Screen
    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;
   
    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);
      }
    }
  }
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

trollwarrior1 (33 views)
2014-11-22 12:13:56

xFryIx (73 views)
2014-11-13 12:34:49

digdugdiggy (52 views)
2014-11-12 21:11:50

digdugdiggy (46 views)
2014-11-12 21:10:15

digdugdiggy (40 views)
2014-11-12 21:09:33

kovacsa (65 views)
2014-11-07 19:57:14

TehJavaDev (69 views)
2014-11-03 22:04:50

BurntPizza (68 views)
2014-11-03 18:54:52

moogie (82 views)
2014-11-03 06:22:04

CopyableCougar4 (82 views)
2014-11-01 23:36:41
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!