2

My pygame is running way too slow. Without using class oop it was running perfectly but now using oop its very slow.

I have tested putting that separate class file in main file also but the result was same.

import pygame
from snake import Snake

pygame.init()
surf_width = 800
surf_height = 600
clock = pygame.time.Clock()

dis_surf = pygame.display.set_mode((surf_width, surf_height))
pygame.display.set_caption("snake game")
run = True
def game_loop():
    x = 255
    y = 255
    x_change = 0
    y_change = 0
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
        dis_surf.fill((255, 255, 255))
        game = Snake(dis_surf, x, y, x_change, y_change)
        x = game.x
        y = game.y

another file: import pygame

class Snake():
    def __init__(self, dis_surf, x, y, x_change, y_change):
        self.dis_surf = dis_surf
        self.x = x
        self.y = y
        self.width = 20
        self.height = 20
        self.x_change = x_change
        self.y_change = y_change
        self.vel = 5
        self.draw()

    def draw(self):
        pygame.draw.rect(self.dis_surf, (0, 255, 0), (self.x, self.y, self.width, self.height))
        self.run()
        pygame.display.update()

    def run(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    self.x_change = self.vel
                    self.y_change = 0
                elif event.key == pygame.K_LEFT:
                    self.x_change = -self.vel
                    self.y_change = 0
                elif event.key == pygame.K_UP:
                    self.y_change = -self.vel
                    self.x_change = 0
                elif event.key == pygame.K_DOWN:
                    self.y_change = self.vel
                    self.x_change = 0
            print(event)
        self.x += self.x_change
        self.y += self.y_change


        x_change = game.x_change
        y_change = game.y_change
        pygame.display.update()
        clock.tick(60)

game_loop()
Dale K
  • 25,246
  • 15
  • 42
  • 71
  • 1
    Dang... Relax on that language a little bit please. From what I can see, you have your `pygame.display.update` inside the `draw` function. You can delete that :D – GeeTransit Jan 04 '19 at 07:29
  • Did you notice that you actually have two event processing loops? Also you are creating a new Snake on every iteration of the outer loop. – Klaus D. Jan 04 '19 at 07:34

2 Answers2

2

A few things are wrong.

1) You are instantiating a new Snake class every game loop when you do game = Snake() inside of the while loop. This in combination with number 2 is your main problem. I moved this line outside of the while loop for you.

2) You are calling run() inside of __init__. This is something you should never do in a constructor, constructors generally should only be used for setting initial data. This also contributed to problem number 1 significantly because this was happening every game loop. I removed the call self.run() inside __init__ for you.

3) pygame.display.update() was being called twice. Not the cause of your problem, but still unnecessary.

Made some small corrections for you.

import pygame

pygame.init()
surf_width = 800
surf_height = 600
clock = pygame.time.Clock()

dis_surf = pygame.display.set_mode((surf_width, surf_height))
pygame.display.set_caption("snake game")
run = True

def game_loop():
    x = 255
    y = 255
    x_change = 0
    y_change = 0
    game = Snake(dis_surf, x, y, x_change, y_change)
    while run:
        dis_surf.fill((255, 255, 255))
        game.draw()

class Snake():
    def __init__(self, dis_surf, x, y, x_change, y_change):
        self.dis_surf = dis_surf
        self.x = x
        self.y = y
        self.width = 20
        self.height = 20
        self.x_change = x_change
        self.y_change = y_change
        self.vel = 5

    def draw(self):
        pygame.draw.rect(self.dis_surf, (0, 255, 0), (self.x, self.y, self.width, self.height))
        self.run()

    def run(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    self.x_change = self.vel
                    self.y_change = 0
                elif event.key == pygame.K_LEFT:
                    self.x_change = -self.vel
                    self.y_change = 0
                elif event.key == pygame.K_UP:
                    self.y_change = -self.vel
                    self.x_change = 0
                elif event.key == pygame.K_DOWN:
                    self.y_change = self.vel
                    self.x_change = 0
        self.x += self.x_change
        self.y += self.y_change
        pygame.display.update()
        clock.tick(60)

game_loop()
Kevin Welch
  • 1,488
  • 1
  • 9
  • 18
1

If you want to use OOP in pygame, use pygame's Sprite class. It's made exactly for this purpose.

Your code should look like this (I tried to change not too much):

import pygame

pygame.init()
surf_width = 800
surf_height = 600
clock = pygame.time.Clock()

screen = pygame.display.set_mode((surf_width, surf_height))
pygame.display.set_caption("snake game")


class Snake(pygame.sprite.Sprite):
    def __init__(self, pos):
        super().__init__()
        self.image = pygame.Surface((20, 20))
        self.image.fill(pygame.Color('orange'))
        self.rect = self.image.get_rect(topleft=pos)
        self.x_change = 0
        self.y_change = 0
        self.vel = 5

    def update(self, events):
        for event in events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    self.x_change = self.vel
                    self.y_change = 0
                elif event.key == pygame.K_LEFT:
                    self.x_change = -self.vel
                    self.y_change = 0
                elif event.key == pygame.K_UP:
                    self.y_change = -self.vel
                    self.x_change = 0
                elif event.key == pygame.K_DOWN:
                    self.y_change = self.vel
                    self.x_change = 0

        self.rect.move_ip(self.x_change, self.y_change)

def main():
    snake = Snake((0, 0))
    snakes = pygame.sprite.Group(snake)
    while True:
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                return

        snakes.update(events)
        screen.fill((30, 30, 30))
        snakes.draw(screen)
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()        

Ensure to only call pygame.display.flip() and pygame.event.get() once every frame.

If you want to handle events in other parts of your code, just store the current frame's events in a variable and pass them around. Using a Group makes this easy.

See how we cleanly seperated the logic of the game:

The main loop does only the three things it's supposed to do. Handling events, updating the game state, and drawing to the screen. It does so without "knowing what actually happens" in the game.

The Snake sprite only reacts when told so by the main loop (when its update method is called), and it does not care where the events come from and how and where it is actually displayed.

sloth
  • 99,095
  • 21
  • 171
  • 219