Home > Back-end >  Pygame not letting me change display after .update()
Pygame not letting me change display after .update()

Time:11-11

I've put pygame.display.update() multiple different places but for some reason it absolutely doesnt let me do the H keypress so it draws help, everything else works. This is for a school assignment that ive made much harder than it was. I'm technically done i just wanted to make a main menu, everything went smooth then the help screen never wanted to draw

import pygame 
import time
import random
import sys
import os

# ! Use print(event) for coordinate reading

# TODO: Add highscore file read and overwrite ✔️
# TODO: Add Game over message ✔️
# TODO: Show highscore and points of that run ✔️
# TODO: Fix Snake not being on same x,y as apple (Most likely wrong movement increments) ✔️
# TODO: Add GRID ✔️


# ? Find a better spawning logic ✔️
# ? Add menu instead of a simple string - NOE AV DET JÆVLIGSTE JEG HAR STARTET PÅ
# ? Fine tune resolution and size 
# ? Add a unique powerup with 3% spawn chance giving 5 points and 1 body length



# ! Pygame Initiatior 
pygame.init() # * Initiates pygame

# ! Screen resolution and title
sc_width = 400
sc_height = 400
screen = pygame.display.set_mode((sc_width, sc_height)) # * Sets pygame window 
pygame.display.set_caption('Snake') # * Sets window title to Snake

# ! Color Variables
bgcolor = (25, 51, 102) # * Dark blue for background color
yellow = (255, 255, 0) # * Yellow color for snake 
red = (255, 0, 0) # * Red color for points & game over
white = (255, 255, 255) # * White color for score

# ! FPS variable
clock = pygame.time.Clock() # * Used to set the FPS of the game using clock.tick(xyz)

# ! Font 
font = pygame.font.SysFont(None, 30) # * None = Default pygame font. 40 = size of font

# ! Game Over Draw function
def GameOver(msg, color): # * Function takes in a string and color and draws it on screen
    text = font.render(msg, True, color) # * Draws msg with font with True for anti aliasing (Smooth edges) with specified color when func is called
    screen.blit(text, [50, 100]) # * Draws text at 100x100

# ! Snake
snake_block = 10 

def Snake(snake_block, snakelist):
    for x in snakelist:
        pygame.draw.rect(screen, yellow, [x[0], x[1], snake_block, snake_block])


# ! Grid 
black = (0, 0, 0)
def Grid():
    size = 10 # * Sets grid size to 10 (same as snake)
    for x2 in range(0, sc_width, size): # * Gaps 10 in width
        for y2 in range(0, sc_height, size): # * Gaps height with 10
            drawGrid = pygame.Rect(x2, y2, size, size) # * Makes a grid
            pygame.draw.rect(screen, black, drawGrid, 1) # * Draws it onto the screen

# ! Draw Score
def drawScore(score):
    points = font.render('Score: '   str(score), True, white) 
    screen.blit(points, [0, 0]) # * Draws it top left

# ! Make highscore txt
try:
    f = open("highscore.txt", 'x') # * Creates file if it doesnt exist
    f.write('0')  # * No highscore set
    f.close()  # * Closes file to let other codes down the line rewrite the file
except FileExistsError: # * If exist continue to rest of code using it as the "highscore" file
    pass



# ! Draw Highscore
f = open('highscore.txt', 'r') # * Opens as read 
highscore = int(f.readline())
f.close()
def drawHighscore(highscore):
    hc = font.render('Score: '   str(highscore), True, white) 
    screen.blit(hc, [310, 0]) # * Draws it top right


# # ! Draw PRE GAME Screen
# def drawMenu(menu, color):
#     pregame = font.render(menu, True, white) 
#     screen.blit(pregame, [50, 100])




# ! Multiline draw function (Stackoverflow) / PRE GAME
def drawMenu(menu, x, y, fsize):
    lines = menu.splitlines()
    for i, l in enumerate(lines):
        screen.blit(font.render(l, 0, white), (x, y   fsize*i))



# ! Draw Help screen
def drawHelp(x, y, fsize):
    help = "Controls: [W][A][S][D]\nInstructions: \nUse WASD to control the snake.\nGrab as many apples as you can;\nand dont crash into yourself :)\n\n [ (B) A C K ]"
    hlp = help.splitlines()
    for i, l in enumerate(hlp):
        screen.blit(font.render(l, 0, white), (x, y   fsize*i)) 



def mainMenu():
    menu = "[ (S) T A R T ]\n [ (H) E L P ]\n [ (Q) U I T ]\n jeg er best"
    screen.fill(bgcolor)
    drawMenu(menu, 140, 120, 80)
    for event in pygame.event.get(): # * Gathers inputs
        if event.type == pygame.KEYDOWN: # * Checks key presses
            if event.key == pygame.K_q: # * If Q is pressed
                pygame.quit()
                sys.exit()
            elif event.key == pygame.K_s: # * Calls game loop again
                game()
            elif event.key == pygame.K_h:
                screen.fill(bgcolor)
                drawHelp(25, 70, 40)




# ! Game Loop
def game():
    finished = False
    done = False

    # ! Spawn location of snake
    x = sc_width / 2 # * Spawns snake at screen width / 2
    x_move = 0 # * To update x coordinates as the snake moves
    y = sc_height / 2 # * Spawns snake at screen height / 2
    y_move = 0 # * To update y coordinates as the snake moves
    score = 0


    f = open('highscore.txt', 'r') # * Opens as read 
    highscore = int(f.readline())
    f.close()

    
    snakelist = []
    snake_len = 1

    # ! Randomizing apple spawn locations
    apple_x = round(random.randrange(0, sc_width - 10) / 10.0) * 10.0
    apple_y = round(random.randrange(0, sc_height - 10) / 10.0) * 10.0


    while not finished:
        while done == True:
            screen.fill(bgcolor)
            drawScore(snake_len - 1)
            drawHighscore(highscore)
            mainMenu()


        # ! Movement input logic
        for event in pygame.event.get(): # * Gathers inputs
            if event.type == pygame.QUIT: # * If pygame.quit gets called (clicking X on window) it will set finished to True which will end game loop
                finished = True # * Ends game loop
                sys.exit()
            if event.type == pygame.KEYDOWN: # * Checks if a key is pressed down
                if event.key == pygame.K_d: # * If key 'D' is pressed increase x by 10 making it move right
                    x_move = 10 # * Speed it moves
                    y_move = 0 # * No vertical movement
                elif event.key == pygame.K_a: # * If key 'A' is pressed decrease x by -10 making it move left
                    x_move = -10 # * Speed it moves
                    y_move = 0 # * No vertical movement
                elif event.key == pygame.K_w: # * If key 'W' is pressed decrease y by -10 making it move up
                    x_move = 0 # * No horizontal movement
                    y_move = -10 # * Speed it moves
                elif event.key == pygame.K_s: # * If key 'S' is pressed increase y by 10 making it move down
                    x_move = 0 # * No horizontal movement
                    y_move = 10 # * Speed it moves

        # ! Out of bounds logic
        # * If snake is at a value higher than screen res or lower than 0; it means its outside the screen box, then the Game loop var will be True and game ends
        if x >= sc_width or x < 0 or y >= sc_height or y < 0: 
            done = True

        # ! Update movement
        x  = x_move 
        y  = y_move
        
        screen.fill(bgcolor)

        # ! Draw grid 
        Grid() # * Draws grid

        # ! Draw snake and apples
        pygame.draw.rect(screen, red, [apple_x, apple_y, 10, 10]) # * Draws apples on screen with red color and puts it at apple_x,y with size of 10
        pygame.draw.rect(screen, yellow, [x, y, 10, 10]) # * Draws snake on screen with yellow color and puts it at var x,y with a size of 10

        snakeH = [] 
        snakeH.append(x) # * Appends snake head x position
        snakeH.append(y) # * Appends snake head y position 
        snakelist.append(snakeH) # * Adds snake head to THE snake list
        if len(snakelist) > snake_len: # * If snake list is bigger than snake length
            del snakelist[0] # * Kills head

        for x1 in snakelist[:-1]:
            # ? Tried to understand this by using prints and seeing values: 
            # ? I'm guessing since it checks each block (prints show list with values with 10 seperating (size of block))
            # ? DOES NOT PRINT HEAD
            # ? If head hits one of the other boxes, it will overlap in list and head wont be skipped and it dies
            # ! print(snakelist[:-1]) (Bugtesting / code understanding)
            if x1 == snakeH:
                # ! print(snakelist[:-1]) (Bugtesting / code understanding)
                done = True

        Snake(snake_block, snakelist) # * Updates func with new values
        drawScore(snake_len - 1) # * - 1 because dont want to count head as a point
        drawHighscore(highscore)


        # ! Snake eating apple and growing
        if x == apple_x and y == apple_y: # * Checks if snake is at apple pos
            apple_x = round(random.randrange(0, sc_width - 10) / 10.0) * 10.0 # * Spawns new apple inside play area
            apple_y = round(random.randrange(0, sc_height - 10) / 10.0) * 10.0 # * Spawns new apple inside play area
            snake_len  = 1 # * Increases snake length by 1
            score  = 1 # * Increases score by 1
        
        # ! Saving highscore
        f = open("highscore.txt", 'r ') # * Opens Highscore
        highscore = int(f.readline()) # * Reads Highscore
        if highscore > score: # * If Highscore > score(that run)
            f.close() # * Close file
        else: # * But if score > highscore
            f.close # * Close it so it doesnt add to existing number ex: highscore: 5 score that run 6 = highscore: 56
            f = open("highscore.txt", 'w') # * Open as write so it writes a new number
            f.write(f'{score}') # * Write current score
            f.close() # * Close

        f = open('highscore.txt', 'r') # * Opens as read 
        highscore = int(f.readline())
        f.close() # * Close file


        # ! Setting FPS 
        clock.tick(30) # * Sets FPS to 30
         
        pygame.display.update()
    pygame.quit() # * Uninitializes pygame
    quit() # * Quits


while True:
    mainMenu()

CodePudding user response:

I have modified your drawHelp function as follows:

def drawHelp(x, y, fsize):
    help = "Controls: [W][A][S][D]\nInstructions: \nUse WASD to control the snake.\nGrab as many apples as you can;\nand dont crash into yourself :)\n\n [ (B) A C K ]"
    hlp = help.splitlines()
    for i, l in enumerate(hlp):
        screen.blit(font.render(l, 0, white), (x, y   fsize*i)) 
    pygame.display.update()
    # to stay in this function until the B key is pressed.
    b_pressed = False
    while not b_pressed:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_b: 
                    b_pressed = True

Note I also had to add pygame.display.update() to the end of drawMenu() to make the main menu appear.

That said, there are several problems that make it harder for you to extend an debug your code. Typically pygame.display.update() should only be called once, also events should ideally be handled in one place. Event handlers would ideally modify the game state, e.g. change state from Main Menu to Help. Your event handlers call functions that take over event handling and drawing and updates. In order to achieve your outcome without refactoring your code, I've followed a similar practice.

It would probably help you to read up on state machines, here's an answer that may help you, or a pygame example whose use I imagine isn't strictly permitted for your homework. You've already started down this path with your done and finished variables.

  • Related