This question already has answers here:
Pygame window not responding after a few seconds
(3 answers)
Pygame unresponsive display
(1 answer)
Closed 1 year ago.
I am trying to make a simple snake game off a tutorial and the game is incomplete but I am supposed to be able to get the pygame gameboard or surface to open and show the basic grid structure but it shows the pygame window as not responding. I've already installed pygame on the anaconda prompt with pip install pygame. I am using Spyder 5, and python 3.8.10 and pygame 2.0.1.
import math
import random
import pygame
import tkinter as tk
from tkinter import messagebox
class cube(object):
rows = 0
w = 0
def _init_(self, start,dirnx=1, dirny=0, color=(255, 0, 0)):
pass
def move(self, dirnx, dirny):
pass
def draw(self, surface, eyes=False):
pass
class snake(object):
def _init_(self, color, pos):
pass
def move(self):
pass
def reset(self, pos):
pass
def addCube(self):
pass
def draw(self, surface):
pass
def drawGrid(w, rows, surface):
sizeBtwn = w // rows
x = 0
y = 0
for l in range(rows):
x = x + sizeBtwn
y = y + sizeBtwn
pygame.draw.line(surface, (255, 255, 255), (x, 0), (x, w))
pygame.draw.line(surface, (255, 255, 255), (0, y), (w, y))
def drawWindow (surface):
global rows, width
surface.fill((0, 0, 0))
drawGrid(width, rows, surface)
pygame.display.update()
def randomSnack(rows, items):
pass
def message_box(subject, content):
pass
def main() :
global width, rows
width = 500
height = 500
rows = 20
win = pygame.display.set_mode((width, width))
s = snake((255, 0, 0), (10, 10))
flag = True
clock = pygame.time.Clock()
while flag:
pygame.time.delay(50)
clock.tick(10)
redrawWindow(win)
pass
main()
You have to handle the events in the application loop. See pygame.event.get() respectively pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
def main() :
global width, rows
width = 500
height = 500
rows = 20
win = pygame.display.set_mode((width, width))
s = snake((255, 0, 0), (10, 10))
flag = True
clock = pygame.time.Clock()
while flag:
for event in pygame.event.get():
if event.type == pygame.QUIT:
flag = False
clock.tick(10)
redrawWindow(win)
I don't know if it is a typo, but I think the
redrawWindow(win)
in the while loop should actually be
drawWindow(win)
as you named the function.
And def __init__ should consist of two underscores before and after.
Related
I am trying to draw squares in random positions and random rgb values and I want 1000 of them to be created. The problem I'm facing is that everytime the loop for drawing occurs, it randomizes it all again, is there any way to make this not happen
import pygame
import sys
import random
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Simulation")
def safeZone():
#Draws a top rectangle
pygame.draw.rect(win, (50,205,50), (0, 0, 800, 100))
def dot():
width = 10
height = 10
spawnX = random.randrange(1, 801)
spawnY = random.randrange(1, 601)
r = random.randrange(1, 256)
g = random.randrange(1, 256)
b = random.randrange(1, 256)
pygame.draw.rect(win, (r, g, b), (spawnX, spawnY, width, height))
def population(size):
for x in range(size):
dot()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill((255, 255, 255))
safeZone() # Always draw dots after safe zone
population(1000)
pygame.display.update()
pygame.quit()
Create a dot collection, then just draw that dot collection. Now you can update the dot positions separately, and they will redraw in the new positions. Here, I'm having each dot move a random amount in every loop.
import pygame
import sys
import random
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Simulation")
class Dot:
def __init__(self):
self.spawnX = random.randrange(0, 800)
self.spawnY = random.randrange(0, 600)
self.r = random.randrange(0, 256)
self.g = random.randrange(0, 256)
self.b = random.randrange(0, 256)
def safeZone():
#Draws a top rectangle
pygame.draw.rect(win, (50,205,50), (0, 0, 800, 100))
def drawdot(dot):
width = 10
height = 10
pygame.draw.rect(win, (dot.r, dot.g, dot.b), (dot.spawnX, dot.spawnY, width, height))
def population(dots):
for dot in dots:
dot.spawnX += random.randrange(-3,4)
dot.spawnY += random.randrange(-3,4)
drawdot(dot)
alldots = [Dot() for _ in range(1000)]
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill((255, 255, 255))
safeZone() # Always draw dots after safe zone
population(alldots)
pygame.display.update()
A worthwhile modification is to store the whole rectangle in the object:
...
class Dot:
def __init__(self):
self.location = [
random.randrange(0, 800),
random.randrange(0, 600),
10, 10
]
self.color = (
random.randrange(0, 256),
random.randrange(0, 256),
random.randrange(0, 256)
)
def move(self, dx, dy ):
self.location[0] += dx
self.location[1] += dy
def drawdot(dot):
pygame.draw.rect(win, dot.color, dot.location)
def population(dots):
for dot in dots:
dot.move( random.randrange(-3,4), random.randrange(-3,4) )
drawdot(dot)
...
You call a function dot() in which you have assigned randomization. You should introduce a piece of code that randomizes the values outside of the dot() function, and store them in a separate array, and then call the function.
Your description sounds like you aren't necessarily trying to store the result so much as you want the process to be the same every time, but still sort of random? You could just use a hard-coded seed?
import random
random.seed(10)
print(random.random())
See this link for more detail: Random Seed
This question already has answers here:
How can I make a sprite move when key is held down
(6 answers)
Closed 1 year ago.
Hi am new fairly new to programming so I tried to learn by making my hands dirty. I tried making a simple box game program all was working fine until I made my player Class which inherit from the base class of Box_User for some reasons my box(player class object) is no longer moving I tried to see what it prints and it seems like none of the keys work when I press them
Can anyone explain what happened?
import pygame
pygame.init()
# Classes
class Window():
def __init__(self, width, height):
self.width = width
self.height = height
self.window_init() # this functions is to get the window up
def window_init(self):
self.window = pygame.display.set_mode((self.width, self.height)) # this is the main window
self.background = pygame.Surface((self.window.get_size())) # this one is named background but should be use like the main window
self.background.fill((255, 255, 255))
#staticmethod
def draw_objects_to_Screen():
win.window.blit(win.background, (0,0))
win.window.blit(box.box, (box.pos_x - scroll[0], box.pos_y + scroll[1])) # the scroll is used to make it look like its moving
win.window.blit(box2.box, (box2.pos_x - scroll[0], box2.pos_y + scroll[1]))
pygame.display.update()
class Box_User():
jump = 10
jump_status = False
def __init__(self, x, y, height, width):
self.pos_x = x
self.pos_y = y
self.box_height = height
self.box_width = width
self.box = pygame.Surface((self.box_height, self.box_width))
self.color = (0, 20, 0)
self.draw_box()
def draw_box(self):
pygame.draw.rect(self.box, self.color, pygame.Rect(self.pos_x, self.pos_y, self.box_height, self.box_width))
#staticmethod
def _jump():
if Box_User.jump >= -10:
box.pos_y -= (Box_User.jump * abs(Box_User.jump)) * 0.3
scroll[1] += (Box_User.jump * abs(Box_User.jump)) * 0.3
Box_User.jump -= 1
else:
Box_User.jump = 10
Box_User.jump_status = False
class Player(Box_User):
key_pressed = pygame.key.get_pressed()
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
# self.pos_x = x
#self.pos_y = y
def movements(self):
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
if self.key_pressed[pygame.K_d]:
self.pos_x += 5
# place here things that you dont want to move while the box is jumping
if not self.jump_status:
if self.key_pressed[pygame.K_w]:
self.jump_status = True
else:
self._jump() # the box jumps here
class Auto_Box(Box_User):
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
pass
# Variables
# window
win = Window(700, 500)
clock = pygame.time.Clock()
FPS = 60
# boxes
box = Player(30, 200, 64, 64)
box2 = Box_User(300, 200, 100, 120)
# The Scroll which controls the things when the box is moving
scroll = [0, 0]
# Functions
# Main Loop
def main():
run = True
while run:
clock.tick(FPS)
# value of the scroll is updated here
scroll[0] += (box.pos_x - scroll[0]-250)
#print("coordinate are")
#print(scroll)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
box.movements()
print(box.pos_x, box.pos_y)
Window.draw_objects_to_Screen()
if __name__ == "__main__":
main()
pygame.key.get_pressed() returns a list of booleans that represent the state of the keyboard when you call the function.
If you continually check that list, no keys will ever change state. It's just a list of Trues and Falses.
You need to reset the values of key_pressed in the loop, updating it with fresh values from pygame.key.get_pressed().
This should do the trick:
def movements(self):
self.key_pressed = pygame.key.get_pressed()
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
...
The key_pressed class variables is only initialized once. It is never changed. Therefore the pressed keys are not detected.
pygame.key.get_pressed() returns a iterable with the current state of all keyboard buttons. You must get the states of the keys in every frame:
class Player(Box_User):
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
def movements(self):
# get the current states of the keys
self.key_pressed = pygame.key.get_pressed()
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
if self.key_pressed[pygame.K_d]:
self.pos_x += 5
# place here things that you dont want to move while the box is jumping
if not self.jump_status:
if self.key_pressed[pygame.K_w]:
self.jump_status = True
else:
self._jump() # the box jumps here
I am making an agario clone for a project and I was wondering what the quickest way to draw many dots in pygame.
from pygame import *
import random as rd
x = rd.randint(100, 700)
y = rd.randint(100, 500)
# I would like to draw about 50 dots of this type.
dot = draw.circle(screen, (0, 0, 0), (x, y), 5)
Here's some basic code to draw circles using pygame:
import pygame as pg
import random as rd
pg.init() # initialize pygame
screen = pg.display.set_mode((500,500)) # create main screen
for ctr in range(25): # 25 circles
x = rd.randint(50, 450)
y = rd.randint(50, 450)
# I would like to draw about 50 dots of this type.
dot = pg.draw.circle(screen, (100, 200, 100), (x, y), 15)
pg.display.update() # update screen
while True: # main pygame loop, always include this
for event in pg.event.get(): # required for OS events
if event.type == pg.QUIT: # user closed window
pg.quit()
Output
Any easy way to do this to use a simple python "for" loop with a "range":
As the loop iterates, it executes the content of the loop body, with the variable i incrementing from 0 to N-1 (i.e. 49). The variable i could be any valid variable name, but for simple numbered loops, i, j & k are commonly used.
from pygame import *
import random as rd
# Draw 50 dots
for i in range( 0, 50 ):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dot = draw.circle(screen, (0, 0, 0), (x, y), 5)
I made a class for dots:
class Dot():
SIZE = 5
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, self.color, (self.x, self.y), Dot.SIZE)
Then I made an array and generate NUMBER_OF_DOTS like this:
dots = []
for i in range(NUMBER_OF_DOTS):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dots.append(Dot(x,y))
and in the while loop and redraw after filling the whole scene by white color:
while True:
screen.fill((255, 255, 255))
...
for dot in dots:
dot.draw()
The whole source:
from pygame import *
import random as rd
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
NUMBER_OF_DOTS = 300
class Dot():
SIZE = 5
def __init__(self, x, y):
self.x = x
self.y = y
self.color = random_color()
def draw(self):
draw.circle(screen, self.color, (self.x, self.y), Dot.SIZE)
def random_color():
r = rd.randint(0, 255)
g = rd.randint(0, 255)
b = rd.randint(0, 255)
return (r, g, b)
init()
screen = display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
dots = []
# generate random dots all over the screen
for i in range(NUMBER_OF_DOTS):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dots.append(Dot(x,y))
# main while loop
while True:
screen.fill((255, 255, 255))
for dot in dots:
dot.draw()
display.update()
time.delay(1) # Speed down
If you want to draw the same circles continuously in the main application loop, then you've to generate a list of random positions:
import pygame as pg
import random as rd
pg.init()
screen = pg.display.set_mode((800,600))
cpts = []
for i in range(25):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
cpts.append((x, y))
run = True
while run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
for cpt in cpts:
pg.draw.circle(screen, (255, 255, 255), cpt, 15)
pg.display.update()
See the answer to Pygame unresponsive display for a minimum pygame application.
This question already has answers here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 2 years ago.
I am trying to make a canvas for pixel art.
class Canvas:
def __init__(self):
self.__blocks = []
self.__positions = []
for i in range(1830):
self.__blocks.append(pygame.Surface((20, 20)).convert())
for y in range(30):
y *= 20
for x in range(61):
x = x* 20
self.__positions.append([x, y])
self.__color = False
def draw(self, window):
for i in range(1830):
self.__color = not self.__color
if self.__color:
self.__blocks[i].fill((200, 200, 200))
else:
self.__blocks[i].fill((50, 50, 50))
window.blit(self.__blocks[i], (self.__positions[i][0]
, self.__positions[i][1]))
Here I am trying to generate and draw 1830 unique surfaces and this works. I then tried implementing collision detection between each block and the mouse and failed.
def collided(self, pos):
for i in range(1380):
block = self.__blocks[i].get_rect()
if block.collidepoint(pos[0], pos[1]):
print(block.x, block.y)
Then I did different tests on why it might be failing. Here is one of them. I will change a single block's color, in our case the 10th block self.__blocks[10].fill((255, 0, 0)) to red so we know which box to click on. Then we will try to check for collision for that particular block.
def testBlock(self, pos):
block = self.__blocks[10].get_rect()
if block.collidepoint(pos[0], pos[1]):
print(block.x)
And it doesn't work, but the weird thing is it works for the first block(in the 0th index) and only the first block no matter which surface I test. Any idea on how to fix this would be appreciated. The following is copy and paste code.
import pygame
pygame.init()
win = pygame.display
D = win.set_mode((1220, 600))
class Canvas:
def __init__(self):
self.__blocks = []
self.__positions = []
for i in range(1830):
self.__blocks.append(pygame.Surface((20, 20)).convert())
for y in range(30):
y *= 20
for x in range(61):
x = x* 20
self.__positions.append([x, y])
self.__color = False
self.testBlock = 10
def draw(self, window):
for i in range(1830):
self.__color = not self.__color
if self.__color:
self.__blocks[i].fill((200, 200, 200))
else:
self.__blocks[i].fill((50, 50, 50))
self.__blocks[self.testBlock].fill((255, 0, 0)) # Changing the color for testing
window.blit(self.__blocks[i], (self.__positions[i][0]
, self.__positions[i][1]))
def test(self, pos):
block = self.__blocks[self.testBlock].get_rect()
if block.collidepoint(pos[0], pos[1]):
print(block.x, block.y)
canvas = Canvas()
while True:
D.fill((0, 0, 0))
pygame.event.get()
mousepos = pygame.mouse.get_pos()
canvas.draw(D)
canvas.test(mousepos)
win.flip()
When you call .get_rect() on a Surface, it does not know its current position, because that is not Surface information. So you need to assign the location to the Rect before collision detection.
With your current code layout, you could do this during the construction. With the Canvass blocks position now held in the __rects list, the __positions list becomes superfluous.
class Canvass:
def __init__(self):
self.__blocks = []
self.__rects = []
for y in range( 30 ):
for x in range( 61 ):
self.__blocks.append(pygame.Surface((20, 20)).convert())
self.__rects.append( self.__blocks[-1].get_rect() )
self.__rects[-1].topleft = ( x, y )
self.__color = False
self.testBlock = 10
This gives you a simple test:
def collided(self, pos):
hit = False
for i in range( len( self.__rects ) ):
if ( self.__rects[i].collidepoint( pos[0], pos[1] ) ):
print( "Click on block %d" % ( i ) )
hit = True
break
return hit, i
.get_rect() gives rect with block's size but with position (0, 0)
you have real position in __positions and you would need
.get_rect(topleft=self.__positions[self.testBlock])
def test(self, pos):
block = self.__blocks[self.testBlock].get_rect(topleft=self.__positions[self.testBlock])
if block.collidepoint(pos[0], pos[1]):
print(block.x, block.y)
But it would be better to get rect and set its position at start and later not use get_rect().
You could also create class Pixel similar to class Sprite with self.image to keep surface and self.rect to keep its size and position. And then you could use Group to check collision with all pixels.
EDIT:
Example which uses class pygame.sprite.Sprite to create class Pixel and it keeps all pixels in pygame.sprite.Group
It also handle events (MOUSEBUTTONDOWN) to change color in any pixel when it is clicked.
import pygame
# --- classes ---
class Pixel(pygame.sprite.Sprite):
def __init__(self, x, y, color, width=20, height=20):
super().__init__()
self.color_original = color
self.color = color
self.image = pygame.Surface((20, 20)).convert()
self.image.fill(self.color)
self.rect = pygame.Rect(x, y, width, height)
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
if self.color != self.color_original:
self.color = self.color_original
else:
self.color = (255,0,0)
self.image.fill(self.color)
# event handled
return True
# event not handled
return False
class Canvas:
def __init__(self):
# create group for sprites
self.__blocks = pygame.sprite.Group()
# create sprites
self.__color = False
for y in range(30):
y *= 20
for x in range(61):
x *= 20
self.__color = not self.__color
if self.__color:
color = (200, 200, 200)
else:
color = (50, 50, 50)
self.__blocks.add(Pixel(x, y, color))
# changing the color for testing
self.testBlock = 10
all_sprites = self.__blocks.sprites()
block = all_sprites[self.testBlock]
block.image.fill((255, 0, 0))
def draw(self, window):
# draw all sprites in group
self.__blocks.draw(window)
def test(self, pos):
# test collision with one sprite
all_sprites = self.__blocks.sprites()
block = all_sprites[self.testBlock]
if block.rect.collidepoint(pos):
print(block.rect.x, block.rect.y)
def handle_event(self, event):
for item in self.__blocks:
if item.handle_event(event):
# don't check other pixels if event already handled
return True
# --- main ---
pygame.init()
win = pygame.display
D = win.set_mode((1220, 600))
canvas = Canvas()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
canvas.handle_event(event)
#mousepos = pygame.mouse.get_pos()
#canvas.test(mousepos)
# draws (without updates, etc)
#D.fill((0, 0, 0)) # no need clean screen if it will draw all elements again
canvas.draw(D)
win.flip()
I'm fairly new to pygame and was wondering how I can choose the position of where this text box would be displayed, because right now it is always displaying the textbox in the same position.
Ideally, I'd call the function like so: "function("Hi, how are you", (x,y)), though I'm not sure how to implement this.
(I've looked around and I cant find any answers on stackoverflow or reddit and found nothing).
def textBox(Question, Position):
#screen.fill((255,255,255))# fill the screen w/ white
question = eztext.Input(maxlength=100, color=black, prompt=Question)#Create an input with max length 45.
clock = pygame.time.Clock() # create the pygame clock
run = True
while run == True:
clock.tick(30) # make sure the program is running at 30 fps
events = pygame.event.get() # events for txtbx
for event in events:
if event.type == QUIT: # close it if x button is pressed
return
if event.type == KEYDOWN:
if event.key == K_RETURN:
textBox.userResponse = question.value
run = False
screen.fill((255,255,255)) # clear the screen
question.update(events) # update txtbx
question.draw(screen) # blit txtbx on the sceen
pygame.display.flip() # refresh the display
Here is a very simple method of creating a text render function :
def RenderText(Text, Font, Target, X, Y, R, G, B): # The target is your screen
""""Text , font, target surface, X, Y,
and color (RGB)"""
RenderedText = Font.render(Text, True, (R, G, B))
Target.blit(RenderedText, (X, Y))
To use it :
Load font :
default_font = pygame.font.SysFont(None, 30)
See the wiki for font customization
Apply in your loop :
... # Loop Stuff
# I assume that your screen is named `screen` X Y Color in RGB
RenderText('Hi, how are you?', default_font, screen, 50, 50, 255, 255, 255)
# Experiment with all the above args
I'm not using your eztext class, but try this method, it's quite simple.
Get source code of eztext, open in editor and you will see what you can do:
question = eztext.Input(x=10, y=20, ...)
or
question.set_pos(10, 20)
Source code:
class Input:
""" A text input for pygame apps """
def __init__(self, **options):
""" Options: x, y, font, color, restricted, maxlength, prompt """
self.options = Config(options, ['x', '0'], ['y', '0'], ['font', 'pygame.font.Font(None, 32)'],
['color', '(0,0,0)'], ['restricted', '\'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\\\'()*+,-./:;<=>?#[\]^_`{|}~\''],
['maxlength', '-1'], ['prompt', '\'\''])
self.x = self.options.x; self.y = self.options.y
self.font = self.options.font
self.color = self.options.color
self.restricted = self.options.restricted
self.maxlength = self.options.maxlength
self.prompt = self.options.prompt; self.value = ''
self.shifted = False
def set_pos(self, x, y):
""" Set the position to x, y """
self.x = x
self.y = y
To render text
choose font and size
font = pygame.font.Font(None, 40)
render text as pygame.surface.Surface()
text = font.render("Hello World", 1, (255,0,0))
display in position (10,20)
surface.blit(text, (10, 20))
You can use pygame.Rect() to keep position
create rect with size of text
text_rect = text.get_rect()
change position
text_rect.x = 10
text_rect.y = 20
or
text_rect.topleft = (10, 20)
or center on screen (or on other surface like button)
text_rect.cetner = screen.get_rect().center
display using rect
surface.blit(text, text_rect)