Pygame scrolling background relative to character position very buggy

Viewed 41

I am very new to both programming and pygame, this is my first attempt of making a platformer game. I followed a tutorial on youtube for how to make a scrolling background and tried to implement it to my own code. But it barely runs, most of the time the charcter gets stuck randomely and I have no idea how and why.

Here is a video of the bug: https://youtu.be/sQK-Yeil4GE

This is all of the code

import pygame, os
#from pygame import Vector2

#Images
BG = pygame.image.load(os.path.join("assets\Background.png"))
BG2 = pygame.image.load(os.path.join("assets\F38196.jpg"))
idle_mario = pygame.image.load(os.path.join('assets\player_animations\Bigidle\Bigidle_0.png'))
mario_run_0 = pygame.image.load(os.path.join('assets\player_animations\Bigrun\Bigrun_0.png'))
mario_run_1 = pygame.image.load(os.path.join('assets\player_animations\Bigrun\Bigrun_1.png'))
mario_run_2 = pygame.image.load(os.path.join('assets\player_animations\Bigrun\Bigrun_2.png'))
mario_jump = pygame.image.load(os.path.join('assets\player_animations\Bigjump\Bigjump_0.png'))
bgWidth, bgHeight = BG2.get_rect().size

#Classes
class Character:
    def __init__(self):
        self.x = 200
        self.x2 = 960
        self.y = 810
        self.ground = 810
        self.vel = 0
        #Idle image
        self.mario_idle = pygame.transform.scale(idle_mario, (70,140))
        #Running animation:
        self.mario_run_0 = pygame.transform.scale(mario_run_0, (70,140))
        self.mario_run_1 = pygame.transform.scale(mario_run_1, (70, 140))
        self.mario_run_2 = pygame.transform.scale(mario_run_2, (70, 140))
        self.mario_run_set = [self.mario_run_0, self.mario_run_0, self.mario_run_1, self.mario_run_1, self.mario_run_2,self.mario_run_2]
        self.i = 0
        #Jumping image:
        self.mario_jump = pygame.transform.scale(mario_jump, (70,140))

    def draw_character(self, idle, moving_right, moving_left, jumping):
        if jumping and moving_left is False:
            screen.blit(self.mario_jump, (self.x2, self.y))
        elif jumping and moving_left:
            screen.blit(pygame.transform.flip(self.mario_jump, True, False), (self.x2,self.y))
        elif idle:
            screen.blit(self.mario_idle, (self.x2,self.y))
        elif moving_right:
            screen.blit(self.mario_run_set[self.i], (self.x2,self.y))
            self.i += 1
            if self.i == 6:
                self.i = 0
        elif moving_left:
            screen.blit(pygame.transform.flip(self.mario_run_set[self.i], True, False), (self.x2,self.y))
            self.i += 1
            if self.i == 6:
                self.i = 0

class Stage:
    def __init__(self):
        self.width = bgWidth
        self.x = 0
        self.start_scroll = W / 2
        self.stop_scroll = self.width - self.start_scroll

class Main:
    def __init__(self):
        self.player = Character()
        self.stage = Stage()
        self.background = BG
        self.background2 = BG2
        self.bg_width = self.background2.get_rect().width
        self.ground = pygame.Rect(0,990,1920,20)
        self.move_right = False
        self.move_left = False
        self.idle = True
        self.is_jumping = False
        self.i = 1

    def draw(self):
        #pygame.draw.rect(screen, BROWN, self.ground)
        self.player.draw_character(self.idle, self.move_right, self.move_left, self.is_jumping)

#Config
W = 1920
H = 1020
pygame.init()
screen = pygame.display.set_mode((W,H))
pygame.display.set_caption("Lidl Adventures")
clock = pygame.time.Clock()

#Constants
BLACK = (0,0,0)
WHITE = (255,255,255)
BROWN = (137,94,39)
FPS = 60

#Functions

def main():
    true = True
    main = Main()
    while true:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                true = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_d:
                    main.move_right = True
                    main.move_left = False
                    main.idle = False
                    main.player.vel = 12
                if event.key == pygame.K_a:
                    main.move_left = True
                    main.move_right = False
                    main.idle = False
                    main.player.vel = -12
                if event.key == pygame.K_w or event.key == pygame.K_SPACE: #https://stackoverflow.com/questions/20940852/calculate-gravity-jump-in-pygame
                    main.is_jumping = True
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_d:
                    main.move_right = False
                    main.player.vel = 0
                if event.key == pygame.K_a:
                    main.move_left = False
                    main.player.vel = 0
                if main.move_left is False and main.move_right is False:
                    main.idle = True

        if main.is_jumping:
            v = 40-4*main.i
            main.player.y -= v
            main.i += 1
            if main.player.y == main.player.ground: #or main.player.y = main.player.object:
                main.is_jumping = False
                main.i = 1

        main.player.x += main.player.vel
        print(main.player.x)
        if main.player.x > main.stage.width - 70: main.player.x = main.stage.width - 70
        if main.player.x < 10: main.player.x = 10
        if main.player.x < main.stage.start_scroll: main.player.x2 = main.player.x
        elif main.player.x > main.stage.stop_scroll: main.stage.x2 = main.player.x - main.stage.width + W
        else:
            main.player.x2 = main.stage.start_scroll
            main.stage.x += -main.player.vel

        rel_x = main.stage.x % main.bg_width
        screen.blit(main.background2, (rel_x - main.bg_width, 0))
        if rel_x < W:
            screen.blit(main.background2, (rel_x, 0))

        main.draw()
        pygame.display.update()
        clock.tick(FPS)

if __name__ == "__main__":
    main()


This is the part for scrolling background

        if main.player.x > main.stage.width - 70: main.player.x = main.stage.width - 70
        if main.player.x < 10: main.player.x = 10
        if main.player.x < main.stage.start_scroll: main.player.x2 = main.player.x
        elif main.player.x > main.stage.stop_scroll: main.stage.x2 = main.player.x - main.stage.width + W
        else:
            main.player.x2 = main.stage.start_scroll
            main.stage.x += -main.player.vel

        rel_x = main.stage.x % main.bg_width
        screen.blit(main.background2, (rel_x - main.bg_width, 0))
        if rel_x < W:
            screen.blit(main.background2, (rel_x, 0))

My question: What might be causing the charcter to get randomly stuck? And also to claify: When the charcter gets stuck, it is usually temporarily.

Thanks in advance!

1 Answers

I figured it out myself: I had put main.stage.x2 instead of main.player.x2 in the elif statement, as well as some missing if statements in the movement handling section.

This works as the main function with the previous code:

def main():
    true = True
    main = Main()
    while true:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                true = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_d:
                    main.move_right = True
                    main.move_left = False
                    main.idle = False
                    main.player.vel = 12
                if event.key == pygame.K_a:
                    main.move_left = True
                    main.move_right = False
                    main.idle = False
                    main.player.vel = -12
                if event.key == pygame.K_w or event.key == pygame.K_SPACE:  # https://stackoverflow.com/questions/20940852/calculate-gravity-jump-in-pygame
                    main.is_jumping = True
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_d:
                    main.move_right = False
                    if main.move_left:
                        pass
                    else:
                        main.player.vel = 0
                if event.key == pygame.K_a:
                    main.move_left = False
                    if main.move_right:
                        pass
                    else:
                        main.player.vel = 0
        if main.move_left is False and main.move_right is False:
            main.idle = True
            main.player.vel = 0

        if main.is_jumping:
            v = 40 - 4 * main.i
            main.player.y -= v
            main.i += 1
            print(v)
            if main.player.y == main.player.ground:  # or main.player.y = main.player.object:
                main.is_jumping = False
                main.i = 1

        if main.move_right or main.move_left:
            main.player.x += main.player.vel
            print(main.player.x)

        if main.player.x > main.stage.width - 70: main.player.x = main.stage.width - 70
        if main.player.x < 10: main.player.x = 10
        if main.player.x < main.stage.start_scroll: main.player.x2 = main.player.x
        elif main.player.x > main.stage.stop_scroll: main.player.x2 = main.player.x - main.stage.width + W
        else:
            main.player.x2 = main.stage.start_scroll
            main.stage.x += -main.player.vel

        rel_x = main.stage.x % main.bg_width
        screen.blit(main.background, (rel_x - main.bg_width, 0))
        if rel_x < W:
            screen.blit(main.background, (rel_x, 0))

        main.draw()
        pygame.display.update()
        clock.tick(FPS)


if __name__ == "__main__":
    main()
Related