Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Why use classes?
#1
This is a tutorial that uses an example of non class code and converts it to use classes. This is not meant to be an in depth conversation about classes and instructs everything about classes. You will want one of our advanced tutorials on classes about that. This is for the new user who is asking "why use classes?", "Classes makes the code longer", "How would i convert my code to use classes", "Whatever i just want to learn python, ill learn it the proper habits after". This is in hopes that someone reads this and writes code like this and learns to use classes because of this. It will save you a long headache later.

The first code box is pure sentdex code. This is an actual tutorial someone made that makes me cringe when i see it. The second is my modification i made to his code. The first is what is shown and given in one of his youtube videos. The second is his same example I converted to do the same exact thing his does, but use OOP and get rid of the horrible recursive elements and use a proper real game loop. The only modification to do something different was I made his code replace his image line with a pygame surface to make both code snippets runnable without resources. This is just so you can run these without worrying about image problems.

sentdex code
import pygame
import time
import random
 
pygame.init()
 
display_width = 800
display_height = 600
 
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
 
car_width = 55
 
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('A bit Racey')
clock = pygame.time.Clock()
 
#carImg = pygame.image.load('Car.png')
carImg = pygame.Surface((car_width,50)).convert()
carImg.fill((255,0,0))
 
def thing(thingx, thingy, thingw, thingh, color):
    pygame.draw.rect(gameDisplay, color, [thingx, thingy, thingw, thingh])
 
 
def car(x,y):
    gameDisplay.blit(carImg, (x,y))
 
def text_objects(text, font):
    textSurface = font.render(text, True, black)
    return textSurface, textSurface.get_rect()
 
def message_display(text):
    largeText = pygame.font.Font('freesansbold.ttf',115)
    TextSurf, TextRect = text_objects(text, largeText)
    TextRect.center = ((display_width/2), (display_height/2))
    gameDisplay.blit(TextSurf, TextRect)
 
    pygame.display.update()
 
    time.sleep(2)
 
    game_loop()
 
def crash():
    message_display("You Crashed!")
 
def game_loop():
    x = (display_width * 0.5)
    y = (display_height * 0.8)
 
    x_change = 0
 
    thing_startx = random.randrange(0, display_width)
    thing_starty = -600
    thing_speed = 7
    thing_width = 100
    thing_height = 100
 
    gameExit = False

    while not gameExit:

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

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_a:
                    x_change += -5
                if event.key == pygame.K_d:
                    x_change += 5

            if event.type == pygame.KEYUP:
                if event.key == pygame.K_a or event.key == pygame.K_d:
                    x_change = 0

        x += x_change
        gameDisplay.fill(white)

        #thingx, thingy, thingw, thingh, color
        thing(thing_startx, thing_starty, thing_width, thing_height, black)
        thing_starty += thing_speed
        car(x,y)

        if x > display_width - car_width or x < 0:
            crash()

        if thing_starty > display_height:
            thing_starty = 0 - thing_height
            thing_startx = random.randrange(0,display_width)

        if y < thing_starty + thing_height:
            print('y crossover')

            if x > thing_startx and x < thing_startx + thing_width or x + car_width > thing_startx and x + car_width < thing_startx + thing_width:
                print('x crossover')
                crash()
        pygame.display.update()
        clock.tick(60)





game_loop()
pygame.quit()
quit()
sentdex code modified with comments. Read the --> comments on each code line for more info provided. Where #straight code comments are sendex code replaced
import pygame
import time
import random
 
pygame.init()
display_width = 800
display_height = 600
 
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
 
gameDisplay = pygame.display.set_mode((display_width,display_height))
gameDisplay_rect = gameDisplay.get_rect()
pygame.display.set_caption('A bit Racey')
clock = pygame.time.Clock()

class Car:
    def __init__(self, screen_rect):
        self.screen_rect = screen_rect
        width = 100
        height = 100
        self.set_start()
        self.starty = -100
        self.speed = 7
        self.image = pygame.Surface((width,height)).convert()
        self.image.fill((0,0,0))
        self.set_rect()
        
    def set_rect(self):
        self.rect = self.image.get_rect(center=(self.startx,self.starty))
        
    def set_start(self):
        self.startx = random.randrange(0, self.screen_rect.width)
        
    def reset(self):
        self.set_start()
        self.set_rect()
        
    def update(self):
        self.rect.y += self.speed
        if self.rect.top > self.screen_rect.bottom:
            self.reset()
        #if thing_starty > display_height:
        #    thing_starty = 0 - thing_height
        #    thing_startx = random.randrange(0,display_width)
        
    def draw(self,surface):
        surface.blit(self.image, self.rect)

class Player:
    def __init__(self, screen_rect):
        self.screen_rect = screen_rect
        car_width = 55
        #carImg = pygame.image.load('Car.png') #-->no image to use
        self.image = pygame.Surface((car_width,50)).convert()
        self.image.fill((255,0,0))
        self.rect = self.image.get_rect(center=(screen_rect.centerx, screen_rect.centery+200))
        self.speed = 5
            
    def check_collision(self, car_rect):
        if self.rect.colliderect(car_rect):
            return True #switch pause state
        
    def update(self, keys):
        if keys[pygame.K_a]:
            self.rect.x -= self.speed
        if keys[pygame.K_d]:
            self.rect.x += self.speed
        self.rect.clamp_ip(self.screen_rect) #keep player in screen
        
    def draw(self, surface):
        surface.blit(self.image, self.rect)
        #def car(x,y):
        #    gameDisplay.blit(carImg, (x,y))

#-->no need for this, Car.image is the rectange, and Car.draw draws it to the screen
#def thing(thingx, thingy, thingw, thingh, color):
#    pygame.draw.rect(gameDisplay, color, [thingx, thingy, thingw, thingh])
     
#-->having this split into two functions is redundant, moved to message_display()
#def text_objects(text, font):
#    textSurface = font.render(text, True, black)
#    return textSurface, textSurface.get_rect()

 
def message_display(text, screen_rect):
    largeText = pygame.font.Font('freesansbold.ttf',115)
    textSurface = largeText.render(text, True, black)
    TextRect = textSurface.get_rect()
    #TextRect.center = ((display_width/2), (display_height/2)) #-->no need for math when you use pygame rects
    TextRect.center = screen_rect.center
    return textSurface, TextRect
    #gameDisplay.blit(TextSurf, TextRect) #-->no need to blit here
    #pygame.display.update() #-->you should only ever see one of these in your entire game
    #time.sleep(2) #-->NEVER use time.sleep in a GUI program
    #game_loop() #-->gameloop only runs once and is ever called once. ACtually you dont even need the content in a function at all
 
def game_loop():
    gameExit = False
    player = Player(gameDisplay_rect)
    car = Car(gameDisplay_rect)
    msg, msg_rect = message_display('You Crashed!', gameDisplay_rect)
    pause = False
    pause_timer = 0.0
    pause_delay = 3000

    while not gameExit:
        now = pygame.time.get_ticks()
        keys = pygame.key.get_pressed()
        gameDisplay.fill(white)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                gameExit = True
                #-->this is already called at the end of the script, should always use the variable to break
                #pygame.quit()
                #quit()
        if not pause:
            car.update()
            car.draw(gameDisplay)
            player.update(keys)
            player.draw(gameDisplay)
        else:
            gameDisplay.blit(msg, msg_rect)
            
        #pause if collision
        if player.check_collision(car.rect):
            pause = True
        
        #reset pause
        if now-pause_timer > pause_delay:
            pause_timer = now
            if pause:
                pause = False
                car.reset()
            
        pygame.display.update()
        clock.tick(60)
        
        
        #-->its better to use pygame.key.get_pressed() for constant key press, located in Player.update()
        #if event.type == pygame.KEYDOWN:
        #    if event.key == pygame.K_a:
        #        x_change += -5
        #    if event.key == pygame.K_d:
        #        x_change += 5
        #if event.type == pygame.KEYUP:
        #    if event.key == pygame.K_a or event.key == pygame.K_d:
        #        x_change = 0
        #x += x_change

        #-->This is all handled by Player.check_collision()
        #thingx, thingy, thingw, thingh, color
        #thing(thing_startx, thing_starty, thing_width, thing_height, black)
        #thing_starty += thing_speed
        #car(x,y)
        #if x > display_width - car_width or x < 0:
        #    crash()
        #if y < thing_starty + thing_height:
        #    print('y crossover')
        #    if x > thing_startx and x < thing_startx + thing_width or x + car_width > thing_startx and x + car_width < thing_startx + thing_width:
        #        print('x crossover')
        #        crash()

game_loop()
pygame.quit()
quit()
and still there can be modifications to better suit this code, but i tried to make it to compare to sentdex, not remake the whole thing. You might be thinking that there is more lines of code in my modification. However you have to consider i have included his code as comments in mine as well. Classes sometimes also takes a little bit more lines, but is more readable and able to be modified more easily. Everything has its place. And you can find it there.

It is one thing to learn pygame utilities (funtions and methods) and another thing to learn technique using a library such as pygame. These bad habits can be in any language, not just python/pygame. I hope that when you read this you leave here thinking more about the technique of programming. That it is not just about the end of the race (completed game) but how you get there too (structure and technique).
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020