Python Forum

Full Version: warnings of sentdex pygame tutorials
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
This is not meant to be bullying against sentdex. However people use his tutorials and come back to us to get information on what they learned from him. I thought i would share of information that i came across while viewing his tutorials. Disclaimer: I did not watch every minute tick by through all of the videos. 

#8 issue is by far the worst one that teaches improper coding to new users. The code snippets below are pertaining to this issue.

Feel free to add to if you have any other info.


1) all video tutorials eventually get outdated. 
https://www.youtube.com/watch?v=ujOTNg17LjI
For example. when downloading pygame here at 5:02
The video shows exe's but are now wheels. Granted he does have text to describe this, the process is different than what is portrayed in the video. It provides confusion to a newcomer.

2) his downloads tutorial does not include the most up to date pygames, which is bitbucket
https://bitbucket.org/pygame/pygame/downloads
Quote:UPDATE:
Actually now they switched to github
https://github.com/pygame/pygame
Also you can just download pygame with pip now pip install pygame

3) He does not describe fix-it's for possible errors when testing if pygame works or not

4) He leads users to use IDLE which is considered by the python community as a horrible IDE. This can lead to more errors and problems when trying pygame or tkinter.

5) His initial example portrays to people to not use OOP. This example gets added on over and over throughout his tutorials. I understand to keep it simple to new users. But this leads people to make spaghetti code. Then they have to learn OOP (if they dont know), or they have to figure out how to convert their code to use OOP. He could of at least made a tutorial to convert his "tutorial code" to use OOP. 

6) He does not utilize pygame rects or other pygame utilities.

7) He floods his namespace with star imports.

8) uses nested while loops to implement game states and that he loops states using recursion rather than a proper game loop

9) He uses different alias in youtube making confusion among pygame/python users. Bucky (thenewboston) is a good instructor, whereas sendex (thenewboston) is not.
Quote:here sendex is using newboston youtube channel. This is just Sendex.
https://www.youtube.com/watch?v=K5F-aGDIYaM

where this is "Bucky" what i call the real newboston.
https://www.youtube.com/watch?v=4Mf0h3Hp...H2TePGDpnZ

and then his other pygame tutorial channel under sendex
https://www.youtube.com/watch?v=3RJx34kGRGk
This is his tactic to gain the fame of google queries containing pygame or newboston or sendex. Its a tactic that gets a lot of views in which returns bad habits based off of a decent instructor.

Now lets say you have no idea what i just mentioned. Your like "Whatever i just want to learn python, ill learn it the proper way after". Here below are the real details in what is above. This is pertaining to #8 issue and the meat of why his code is such a bad habit.

The first code box is pure sentdex code. 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).