Pygame not letting me change display after .update() - python

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()

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

Random Brick Color -> TypeError: 'pygame.Color' object is not callable

so this is my first time as a registered user here, so please bear with me if I was searching wrong,
but with the tags [python] or [pygame] I could not find an answer to this question of a random color.
So my problem is the following:
The Game
In this game I want the bricks to be random colors. To do so I made a function to generate a random color:
def brick_color():
color_var = random.randint(0,3)
if color_var == 0: #red
brick_color1 = pygame.Color(198,44,58)
elif color_var == 1:#blue
brick_color1 = pygame.Color(1,128,181)
elif color_var == 2:#yellow
brick_color1 = pygame.Color(255,211,92)
elif color_var == 3:#green
brick_color1 = pygame.Color(0,157,103)
return brick_color1
and implemented the function into this lines:
# brick init
brick = pygame.Surface([brick_width, brick_height]),brick_color # surface for a single brick
pygame.draw.rect(brick[0], brick[1], [0, 0, brick_width, brick_height])
bricks = [] # list of *coordinates* of the bricks
# initialize coordinates of bricks
for y in range(num_brick_rows):
brickY = (y * brick_row_height) + brick_offset_y
for x in range(num_bricks_in_row):
brickX = (x * brick_column_width) + brick_offset_x
color_of_brick = brick_color()
bricks.append((brickX, brickY),color_of_brick) # coordinates are in fact tuples (x,y)
But i keep getting this error:
File "C:\_____\unbenannt0.py", line 146, in <module>
color_of_brick = brick_color()
TypeError: 'pygame.Color' object is not callable
And this is the full code.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 2 09:33:50 2020
#author:
"""
# example from the Sloan Kelly book
# very slightly modified
# imports (or makes usable) external packages that may be 3rd party (such as pygame)
import pygame, sys, random
# enable "short-hand" notation for imported stuff
from pygame.locals import QUIT, MOUSEBUTTONUP, MOUSEMOTION, KEYDOWN, K_ESCAPE
from pygame import display
#Functions
def draw_bricks():
for b in bricks:
windowSurfaceObj.blit(brick[0], b) # makes a copy of brick image acording to coordinates, where stuff is drawn!
def draw_bat():
windowSurfaceObj.blit(bat, batRect)
def draw_ball():
windowSurfaceObj.blit(ball, ballRect)
def brick_color():
color_var = random.randint(0,3)
if color_var == 0: #red
brick_color = pygame.Color(198,44,58)
elif color_var == 1:#blue
brick_color = pygame.Color(1,128,181)
elif color_var == 2:#yellow
brick_color = pygame.Color(255,211,92)
elif color_var == 3:#green
brick_color = pygame.Color(0,157,103)
return brick_color
pygame.init()
fpsClock = pygame.time.Clock()
# new and improved:
# create surface object aka. the main window
inf = display.Info()
screen_width = inf.current_w - 200 # make window sizea little bit smaller than actual screen
screen_height = inf.current_h - 200
# windowSurfaceObj = display.set_mode(size=(screen_width, screen_height), flags=pygame.FULLSCREEN) # initialize window
windowSurfaceObj = display.set_mode(size=(screen_width, screen_height)) # initialize window
display.set_caption('New and improved Bricks'); # set window title
# brick layout
brick_width = 50
brick_height = 20
num_bricks_in_row = 7
num_brick_rows = 5
brick_row_height = 2 * brick_height
brick_offset_y = 100
brick_column_width = 2 * brick_width
brick_offset_x = int(screen_width/2 - brick_column_width*num_bricks_in_row/2) # place it in the middle of the screen
brick_color = brick_color()
# ball related stuff
ball_radius = int(screen_height/200)
# more game constants!
fps = 60 # desired frames per second
background_colour = pygame.Color(0, 0, 0) # background is black
# used variables for bat dimensions
bat_width = 100
bat_height = 10
# ball related stuff
ball_start_x = 24 # somehwere near the left of the window
ball_start_y = 200 # initial ball position when new ball is released
ball_speed = int(fps*0.15) # speed of ball in pixel per frame! use fps var. here to make real ball speed independent of frame rate
# bat init
# replace bat with crude hand drawn one
batcolor = pygame.Color(0, 0, 255) # bat color: blue!
bat = pygame.Surface([bat_width, bat_height]) # this Surface is for drawing the bat upon
pygame.draw.rect(bat, batcolor, [0, 0, bat_width, bat_height]) # draw bat. It's a simple rectangle.
bat = bat.convert_alpha() # deal with transparency
# place the bat somewhere near the bottom of the screen/window
player_start_x = 0 # initial position is on left
player_start_y = screen_height - 6 * bat_height # this is used as Y coordinate for bat, near the bottom of the screen
batRect = bat.get_rect() # rectangle around bat, used to move it around later
mousex = player_start_x
mousey = player_start_y # mousex and mousey later used for moving the bat around, not actual mouse coordinates at this point
# ball init
ball_color = pygame.Color(255, 255, 255) # white
ball = pygame.Surface([ball_radius*2, ball_radius*2]) # Surface for drawing the ball upon
pygame.draw.circle(ball, ball_color, (ball_radius, ball_radius), ball_radius) # draw circle on ball surface
ballRect = ball.get_rect() # rectangle around ball, use to move it around later
ballServed = False
bx = ball_start_x # bx is actual ball postion
by = ball_start_y # by is actual (current) ball position
sx = ball_speed # current ball speed in horizontal direction
sy = ball_speed # current ball speed vertical
ballRect.topleft = (bx, by) # move ball rectangle to initial position
# brick init
brick = pygame.Surface([brick_width, brick_height]),brick_color # surface for a single brick
pygame.draw.rect(brick[0], brick[1], [0, 0, brick_width, brick_height])
bricks = [] # list of *coordinates* of the bricks
# initialize coordinates of bricks
for y in range(num_brick_rows):
brickY = (y * brick_row_height) + brick_offset_y
for x in range(num_bricks_in_row):
brickX = (x * brick_column_width) + brick_offset_x
color_of_brick = brick_color()
bricks.append((brickX, brickY),color_of_brick) # coordinates are in fact tuples (x,y)
while True: # main loop, run once per frame (i.e. fps times per second)
windowSurfaceObj.fill(background_colour) # clear the screen
# brick draw
# for b in bricks: # remember: bricks is a list of brick coordinates, not surfaces
# windowSurfaceObj.blit(brick, b) # make copy of brick image and place it on screen, b = brick coordinates
draw_bricks()
# bat and ball draw, rectangles around bat and ball are used for positioning
# windowSurfaceObj.blit(bat, batRect) # copy surface with image of bat to screen
# windowSurfaceObj.blit(ball, ballRect) # same for ball
draw_bat()
draw_ball()
# main event loop
# process user interaction
for event in pygame.event.get():
# quit the game if window is closed or escape key is pressed
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONUP and not ballServed:
ballServed = True # start game, when mouse is clicked
elif event.type == MOUSEMOTION: # mouse has moved
mousex, mousey = event.pos # set mouse coordinate variables to actual mouse coordinates
if mousex < screen_width - bat_width:
if mousex < 0: # may occur in full screen / large window mode
batRect.topleft = (0, player_start_y)
else:
batRect.topleft = (mousex, player_start_y)
else:
batRect.topleft = (screen_width - bat_width, player_start_y)
# main game logic
if ballServed: # if game is in progress
ballRect.topleft = (bx, by) # position the ball using its rectangle
bx += sx # sx = speed of the ball in X direction
by += sy # sy = speed of the ball in Y direction
if (by >= screen_height): # ball below bottom of screen
ballServed = False # game not in progess, ball lost!
bx, by = (ball_start_x, ball_start_y) # ball is reset to start position
ballRect.topleft = (bx, by) # move the rectangle around ball to correct position
if by <= 0: # ball hits top
by = 0
sy *= -1 # reflect
if bx <= 0: # ball hits left side of window
bx = 0
sx *= -1 # reflect
if bx >= screen_width - ball_radius*2: # ball hits right side of window
bx = screen_width - ball_radius*2
sx *= -1 # reflection
# collision detection
brickForRemoval = None
for b in bricks: # remember: bricks is list of coordinates of bricks; iterating all bricks and check each one for collision
briX, briY = b # tuple unwrapping: x and y coordinates of top left of brick
if bx + ball_radius*2 >= briX and bx <= briX + brick_width: # is x coordinate of ball inside brick (or touching brick)
if (by + ball_radius*2 >= briY and by <= briY + brick_height): # same for y coordinate
brickForRemoval = b # brick was hit and is scheduled for removal
if bx <= briX + ball_radius*2: # ball hit brick from left
sx *= -1 # reflect
elif bx >= briX + brick_width - ball_radius*2: # ball hit brick from right
sx *= -1 # reflect
if by <= briY + ball_radius * 2: # ball hit brick from top
sy *= -1 # reflect
elif by >= briY + brick_height - ball_radius*2: # ball hit brick from below
sy *= -1 # reflect
break # ball hit a brick and cannot hit another one at the same time
if brickForRemoval != None: # if a brick is scheduled for removal…
bricks.remove(brickForRemoval) # … remove it!
# collision check: does bat hit ball?
if (bx >= mousex and bx <= mousex + bat_width): # using bat_width variable
if(by >= player_start_y - bat_height and by <= player_start_y):
sy *= -1 # reflect
pygame.display.update() # show updated screen
fpsClock.tick(fps) # limit fps
Any help is highly appreciated!
As per #jasonharper's comment, when you assign a value to a variable name that is the same as the function name, that name becomes "re-assigned", in this case - making the function no longer accessible.
Using any other variable name inside your function would fix it:
def brick_color():
color_var = random.randint(0,3)
if color_var == 0: #red
new_color = pygame.Color(198,44,58)
elif color_var == 1:#blue
new_color = pygame.Color(1,128,181)
elif color_var == 2:#yellow
new_color = pygame.Color(255,211,92)
elif color_var == 3:#green
new_color = pygame.Color(0,157,103)
return new_color
Your function could also use a python list to be a bit more flexible and compact:
def getRandomColor():
# red blue yellow green
color_list = [ (198,44,58), (1,128,181), (255,211,92), (0,157,103) ]
random_choice = random.randrange( len( color_list ) )
return color_list[ random_choice ]
This form is more flexible because you can easily add more colours by appending them to the list (no more to do).

I am making a Whack a Mole game where user gets a point when he clicks the mole image. I am unable to figure out how to make that happen [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
So i am making this whack a mole game. I am able to make mole appear in random locations in the game however i cant understand how to award the player a point when he clicks on the mole. As the mole appears randomly it is possible that it appears on the same location twice. How would i stop this from happening. Here is the code:
import pygame
from pygame.locals import MOUSEBUTTONDOWN
import random
import time
pygame.init()
# constants
width = 300
height = 300
z = 2
Radius = 21
# loading mole image
mole_image = pygame.image.load("mole image2.png")
modified_image = pygame.transform.scale(mole_image, (40, 40))
# burrow and mole positions
burrow_x = -50
burrow_y = 50
count = int(0)
burrow_positions_list = []
mole_positions_list = []
while count != 9:
count += 1
burrow_x += 100
if burrow_x == 350:
burrow_x -= 300
burrow_y += 100
tuple1 = (burrow_x, burrow_y)
tuple2 = (burrow_x - 20, burrow_y - 20)
burrow_positions_list.append(tuple1)
mole_positions_list.append(tuple2)
# colours
white = (255, 255, 255)
blue = (0, 0, 255)
black = (0, 0, 0)
# setting up the display
display = pygame.display.set_mode((width, height))
pygame.display.set_caption("Whack A Mole")
# creating burrows for the moles
def Burrows():
circle_count = int(-1)
while circle_count != len(burrow_positions_list) - 1:
circle_count += 1
pygame.draw.circle(display, black, burrow_positions_list[circle_count], 30)
def Moles():
display.blit(modified_image, random.choice(mole_positions_list))
time.sleep(z)
# running pygame until quit
run = True
while run:
# speeding up mole blitting
z -= 0.05
if z < 0.4:
z += 0.05
display.fill(white)
Burrows()
Moles()
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
pygame.quit()
You already have the list of burrow positions:
burrow_positions_list[]
At each of which you draw a circle, radius 30. First let's convert that 30 into a constant.
BURROW_RADIUS = WINDOW_WIDTH // 10 # scales with window size, default 30
You already know the position when the mouse is clicked:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos() # <-- HERE
So now we need to marry the two together. We know where the burrow centre is, so if the mouse-click is within BURROW_RADIUS pixels of this point, it's a hit.
There's a formula for calculating the line distance between points known as the Euclidian Distance. For 2 points it's pretty easy:
def twoPointDistance( point_a, point_b ):
x1, y1 = point_a
x2, y2 = point_b
x_squared = (x2 - x1) * (x2 - x1)
y_squared = (y2 - y1) * (y2 - y1)
length = math.sqrt( x_squared + y_squared )
return length
So now we have two points - burrow_point and mouse_click_point, and a way of determining the distance. So when a click happens, we just need to see if it was close enough.
elif event.type == MOUSEBUTTONDOWN:
mouse_click_point = pygame.mouse.get_pos() # Mouse was clicked
# Loop through every burrow point, checking the distance
for i, burrow_point in enumerate( burrow_positions_list ):
if ( twoPointDistance( mouse_click_point, burrow_point ) < BURROW_RADIUS ):
# Burrow was clicked
print( "Burrow %d was clicked" % ( i ) )
And that's it.
However...
It would be much less CPU-intensive to pre-calculate a bounding square around each burrow, and only check if the point was inside this (which is only a few simple < / > checks) rather than the complicated maths of square-roots. There's even a pre-existing function for it: pygame.Rect.collidepoint(). But this is left as an exercise for the reader!

My screen glitches out when I try to run my subprogram code

I'm working on a subprogram code that will make this happy face bounce around the screen and turn different colours. For some reason, the screen turns into that black glitchy screen and when I press exit at the top the face shows for a quick second before the program shuts down. I can't figure out why this is, here is my code and I've included a picture of what happens at first when I run it:
""" Program to show a very basic function
Most of the program is exactly the same as other programs we have done
The main difference is the grouping of code into a function called
drawHappy() to draw a few shapes together
In the main loop we "call" this function whenever we want to draw this
group of shapes
"""
# import the necessary modules
import pygame
import sys
import math
import random
from random import randint
# initialize pygame
pygame.init()
# set the size for the surface (screen)
# note this screen is resizable by the user
screen = pygame.display.set_mode((800, 600), pygame.RESIZABLE)
# set the caption for the screen
pygame.display.set_caption("Happy Face")
#screen width and height
screenW = screen.get_width()
screenH = screen.get_height()
# define colours you will be using
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
# funtion to draw a the "happy face"
# it has 4 parameters passed to it xPos, yPos, radius, and colour
# notice all the shapes are drawn "relative" to the xPos and yPos and the radius
def drawHappy(xPos,yPos,r,colour):
pygame.draw.circle(screen,colour,(xPos,yPos),r,1)
eyeRadius = int(1/6*r)
eyeX = int(xPos-1/3*r)
eyeY = int(yPos- 1/3*r)
pygame.draw.circle(screen,colour,(eyeX,eyeY),eyeRadius,1)
eyeX = int(xPos + 1/3*r)
pygame.draw.circle(screen,colour,(eyeX,eyeY),eyeRadius,1)
wMouth = 1.5*r
xMouth = xPos - 3/4*r
yMouth = yPos - 3/4*r
pygame.draw.arc(screen,colour,(xMouth,yMouth,wMouth,wMouth),math.pi,2*math.pi,1)
randomR = randint(1,300)
r = randomR
randomX = randint(r, 800-r)
randomY = randint(r, 600-r)
dx = 0
dy = 0
x = 100
y = 100
speed = 3
x2 = randomX
y2 = randomY
dx2 = speed
dy2 = -speed
colour_list = [YELLOW, BLACK, BLUE, RED, GREEN]
randomcolour = random.choice(colour_list)
colour = RED
# set up clock to control frames per second
clock = pygame.time.Clock()
FPS = 120
# set main loop to True so it will run
main = True
# main loop
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type == pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
clock.tick(FPS)
screen.fill(WHITE)
oldx = x
oldy = y
x += dx
y += dy
if x >= 800-r or x <= 0+r:
x = oldx
if y >= 600-r or y <= 0+r:
y = oldy
x2 += dx2
y2 += dy2
if x >= 800-r or x <= 0+r:
dx2 = -dx2
randomcolour = random.choice(colour_list)
colour = randomcolour
if y2 >= 600-r or y2 <= 0+r:
dy2 = -dy2
randomcolour = random.choice(colour_list)
colour = randomcolour
# "call" the function "drawHappy()" to draw the happy face
# this is where we would normally do a pygame.draw or a screen.blit()
# we are "passing" the function 4 values to use(x,y,radius, colour)
# it will use these to know where to draw the happy face
drawHappy(x2,y2,r,colour)
pygame.display.flip()
# quit pygame and exit the program (i.e. close everything down)
pygame.quit()
sys.exit()
First of all, you need to call your draw function inside the loop. Your current code shows only a glimpse of "drawing" because it gets executed once you exit the main loop.
So, put your drawHappy() inside of main loop:
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type == pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
drawHappy(x2,y2,r,colour)
pygame.display.update()
clock.tick(FPS)
screen.fill(WHITE)
Now you will get a random size "smiley" on the screen, But now it will move on exit only, for the same reason it wouldn't display earlier. Next thing is to make it bounce (move). For this you'll need some kind of update of the coordinates, just like you did in the last part of your code, except they also need to be updated during the loop, not after it.
I suggest making a Class because then it will be easier to manipulate the object.
Also, I found it easier to separate draw and update_coordinates code into separate functions and them call them from main loop for example.
Hope this helps, and if you need more help, ask.
Here, I made a quick solution using parts of your code, there is plenty room for improvement especially for update_smiley_position() method where you can control how "smiley" moves.
Also, if you need multiple objects, a list should be passed instead of single object.
import pygame as pg
import math
import random
pg.init()
clock = pg.time.Clock()
window = pg.display.set_mode((800, 600), pg.RESIZABLE)
pg.display.set_caption("Happy Face")
SCREEN_W = window.get_width()
SCREEN_H = window.get_height()
class Smiley:
def __init__(self, x, y, r, color):
self.x = x
self.y = y
self.r = r
self.color = color
self.create_smiley()
def create_smiley(self):
self.eye_radius = int(1/6 * self.r)
self.eye_x1 = int(self.x - 1/3 * self.r)
self.eye_x2 = int(self.x + 1/3 *self.r)
self.eye_y = int(self.y - 1/3 *self.r)
self.mouth_width = 1.5 * self.r
self.mouth_x = self.x - self.r * 0.75
self.mouth_y = self.y - self.r * 0.75
def draw_smiley(self, win):
pg.draw.circle(win, self.color, (self.x, self.y), self.r, 1)
pg.draw.circle(win, self.color, (self.eye_x1, self.eye_y), self.eye_radius, 1)
pg.draw.circle(win, self.color, (self.eye_x2, self.eye_y), self.eye_radius, 1)
pg.draw.arc(win, self.color, (self.mouth_x, self.mouth_y, self.mouth_width, self.mouth_width), math.pi, 2*math.pi, 1)
def update_smiley_position(self):
if self.x >= SCREEN_H - self.r or self.x <= 0 + self.r:
self.x = random.randint(100, 400)
else:
self.x += 5
if self.y >= SCREEN_W - self.r or self.y <= 0 + self.r:
self.y = random.randint(100, 400)
else:
self.y -= 5
self.create_smiley()
def draw(win, smiley):
win.fill(pg.Color("white"))
smiley.draw_smiley(win)
smiley.update_smiley_position()
pg.display.update()
def main_loop(win, smiley):
clock.tick(30)
for event in pg.event.get():
if event.type == pg.QUIT:
return False
draw(win, smiley)
return True
r = random.randint(1, 300)
x = random.randint(r, SCREEN_W - r)
y = random.randint(r, SCREEN_H - r)
smiley = Smiley(x, y, r, pg.Color("red"))
while main_loop(window, smiley):
pass
pg.quit()

Bouncing Ball doesn't come back pygame

import pygame
pygame.init()
width = 400
hight = 600
screen = pygame.display.set_mode((width, hight))
pygame.display.set_caption("Engine")
dot = pygame.image.load("KreisSchwarz.png")
clock = pygame.time.Clock()
running = True
WHITE = (255, 255, 255)
# Set (x, y) for Dot
def updateDot(x, y):
screen.blit(dot, (x, y))
# Display Dot at (x, y)
def update(fps=30):
screen.fill(WHITE)
updateDot(x, y)
pygame.display.flip()
return clock.tick(fps)
# Quit if User closes the window
def evHandler():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
yKoords = []
(x, y) = (300, 200)
t = 1 # time variable
a = 2 # acceleration constant
tol = 40 # tolerance
i = 0 # just some iterator
# MAIN LOOP
while running:
evHandler()
update()
y += a * (t ^ 2)
t += 1
yKoords.append(int(y))
i += 1
if (y < (hight + tol)) and (y > (hight - tol)):
y = 580
yKoords.reverse()
update()
for q in range(i):
evHandler()
y = yKoords[q]
update()
if q == i - 1: # Because i didn't write the Part for the Dot coming back down
running = False
This is my Code for a Ball accelerating down and then jumping back up.
My Problem is, that the code works fine until the if statement. There the Programm just displays the Ball at the last position in yKoords and waits until the for loop finishes. If i remove the for loop the Ball gets displayed at y=580 and stops but thats fine.
Please help i have no idea whats wrong about this.
Don't do a separate process loop in the main loop.
It is sufficient to invert the direction, when the ball bounce on the ground (abs(y - hight)) or the ball reaches the top (t == 0).
direction = 1
while running:
evHandler()
update()
y += (a * (t ^ 2)) * direction
t += direction
if abs(y - hight) < tol:
y = 580
t -= 1
direction *= -1
elif t == 0:
direction *= -1

Pygame not responding to variable updates

I'm trying to build a program that will change each individual pixel within a Pygame surface to a random colour.
For a fixed and constant surface size (eg. 1300 x 700) this works and the whole surface is filled with random colours, however I'm trying to resize the surface with the pygame.RESIZABLE feature of Pygame on the fly (by updating the computed X and Y values that the program works from), every time the surface is resized, however, the Pygame surface only outputs randomly coloured pixels for pixels within the programs initial surface width and height (in this case 1300 x 700) and the rest of the surface is left black (the defult background colour I set), even though when I print the variables that respond to the screen height and width (and are being used to iterate through all the pixel) to the program log, they update as expected.
I don't understand how the surface is not responding to thees updated variables and I don't know how to fix this issue.
Any help is much appreciated and any questions about my code are welcome, thanks!
import pygame
import random
pygame.init() #initiates pygame
Comp_X = 1300
Comp_Y = 700
Window = pygame.display.set_mode((Comp_X, Comp_Y), pygame.RESIZABLE) #defines the window size (the varible doesnt have to be called window)
pygame.display.set_caption("Random Colours") #sets the title of the window
clock = pygame.time.Clock() #sets the refresh rate of the program
def GameLoop():
global Comp_X #computed pixles for the X axis
global Comp_Y #computed pixles for the Y axis
Comp_Tot = Comp_X * Comp_Y #total pixles on the screen
print("initial computed total pixles = " + str(Comp_Tot))
#setting x and y position
x = 0
y = 0
GameExit = False #sets the defult value of this varible to true
while not GameExit: #this is effectivly a "while true" loop, unless the varible "crashed" is set to false
for event in pygame.event.get(): #this for loop will listen for events
print(event.type)
print("X = " + str(Comp_X))
print("Y = " + str(Comp_Y))
if event.type == pygame.QUIT: # this if statement checks to see if the X in the top right of the window was pressed
GameExit = True # this will break the while loop if the condition is met
print("X = " + str(Comp_X))
print("Y = " + str(Comp_Y))
if event.type == 16: #event type 16 is the window resizing event
Comp_X = event.dict['size'][0]
Comp_Y = event.dict['size'][1]
Comp_Tot = Comp_X * Comp_Y
print("current computed total pixles = " + str(Comp_Tot))
#Creating the colours
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
for pixle in range (0, Comp_Tot):
if x == Comp_X:
print("Computed X = " + str(Comp_X))
print("Computed Y = " + str(Comp_Y))
x = 0
y += 1
pygame.draw.rect(Window, (random.randint(0,255), random.randint(0,255), random.randint(0,255)), [x, y, 2, 2])# draws a red square
x += 1
#Drawing the frame
pygame.display.update() #This updates the frame
clock.tick(60) # this defines the FPS
GameLoop()
pygame.quit() # this will close the window if the "while GameLoop" loop stops running
quit()
Using defult height and width of 1300 x 700
Resizing surface to 1457 x 992 - not all of the surface is filled!
There are actually two problems with the code that cause the error. The first is not having the
Window = pygame.display.set_mode((Comp_X, Comp_Y), pygame.RESIZABLE)
line inside of the if event.type == 16: as already mentioned.
The other problem is very minor but causes it to not work properly if resized after filling the screen once already and that is that you are never resetting the values of x or y back to 0 after you fill the screen.
Fixed section of code:
if event.type == pygame.VIDEORESIZE:
Comp_X = event.w
Comp_Y = event.h
Comp_Tot = Comp_X * Comp_Y
print("current computed total pixles = " + str(Comp_Tot))
Window = pygame.display.set_mode((Comp_X, Comp_Y), pygame.RESIZABLE)
#Creating the colours
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
for pixle in range (0, Comp_Tot):
if x == Comp_X:
#print("Computed X = " + str(Comp_X))
#print("Computed Y = " + str(Comp_Y))
x = 0
y += 1
pygame.draw.rect(Window, (random.randint(0,255), random.randint(0,255), random.randint(0,255)), [x, y, 2, 2])# draws a red square
x += 1
x = 0
y = 0
I also changed event.type == 16 to event.type == pygame.VIDEORESIZEas it's more readable.
When the window resize event fires, you need to call pygame.display.set_mode((w, h)) again to allocate a new surface of that size. Otherwise you're still writing to the original 1300x700 Surface instance.

Categories