0

I'm trying to create a parallax background effect in pygame i.e., each layer of the background will move at different speeds.

Here is the code I've written,

import pygame
pygame.init()
surface_width=576
surface_height=200
screen=pygame.display.set_mode((surface_width,surface_height))
clock=pygame.time.Clock()
dx=0
dy=0

bg=[]
for i in range(1,6):
    back=pygame.image.load(f'{i}.png').convert_alpha()
    bg.append(back)
width=bg[0].get_width()
height=surface_height-bg[0].get_height()


run=True
while run:
    clock.tick(60)
    for x in range(-3,3):
        speed=1
        for layer in bg:
            screen.blit(layer,((x*width)-dx*speed,height+dy*speed))
            speed+=0.8

    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            run=False

    keys=pygame.key.get_pressed()
    if keys[pygame.K_LEFT] == True and dx>-3*width:
        dx-=2
    if keys[pygame.K_RIGHT] == True:
        dx+=2
    if keys[pygame.K_UP] == True and dy<surface_height:
        dy+=2
    elif keys[pygame.K_DOWN] == True and dy>0:
        dy-=2

    pygame.display.update()

This works fine, when the range is in positive values i.e., the x coordinates are positive. But when they are negative as in the above code, I start facing issues when trying to move left to the negative x coordinate.

I suspected it to be an issue with the sequence of blitting and tweaked things a bit but I couldn't get any successful results.

Another thing, I've observed is that I only face this issue when the layers are moving at different speeds. If I removed the speed+=0.8 part, the code works as intended (obviously without the parallax effect)

Moving to the left with the same layer speed

Moving to the left with different layer speed

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Leon Raj
  • 36
  • 5

1 Answers1

2

Your problem is the nested loops. Iterate through the layers first and then through the tiles:

run=True
while run:
    clock.tick(60)
    
    for i, layer in enumerate(bg):
        speed = 1 + i * 0.8
        for x in range(-3,3):
            screen.blit(layer,((x*width)-dx*speed,height+dy*speed))

    # [...]

In addition, I suggest using the modulo operator (%) to calculate the beginning of the endless background and draw as many tiles as will fit on the screen:

run=True
while run:
    clock.tick(60)
    
    for i, layer in enumerate(bg):
        speed = 1 + i * 0.8
        x = ((-dx*speed) % width) - width
        while x < surface_width:
            screen.blit(layer, (x, height+dy*speed))
            x += width

    # [...]

See also How to make parallax scrolling work properly with a camera that stops at edges pygame

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    That makes perfect sense! I don't know why I couldn't think of that. I did notice that the bottom layer was blitted at the top and tried changing a few things but couldn't think of this simple thing. I guess I was too focused on "It works for the positive part and not for the negative" . Anyway, thank you so much! – Leon Raj Aug 18 '23 at 18:00