Related
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.
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.
So I'm a beginner programmer with Python I have been playing with pyglet and I've discovered a problem if you run this code:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
image =pyglet.resource.image("logo.png")
intt = None
def pixel(x1,y1):
color = []
size = []
for x in range(x1):
for y in range(y1):
size.append(x)
size.append(y)
color.append(0)
color.append(255)
color.append(0)
vertex_list = pyglet.graphics.vertex_list(x1 * y1,('v2i', size ),
('c3B', color ))
return vertex_list
#window.event
def on_draw():
window.clear()
#vertex_list = pixel(600,500)
#vertex_list.draw(GL_POINTS)
color = []
size = []
for x in range(100):
for y in range(500):
size.append(x+504)
size.append(y)
color.append(0)
color.append(255)
color.append(0)
vertex_list = pyglet.graphics.vertex_list(100 * 500, ('v2i', size),
('c3B', color))
vertex_list.draw(GL_POINTS)
pyglet.app.run()
You will find that the area shown has black lines in it. I've tried to fix it but it does not work and because pyglet is not pretty famous there aren't videos that describe that problem. I hope you know how to fix it because you are my last hope.
If you want to draw a filled area then draw a rectangular GL_POLYGON primitive instead of multiple GL_POINT primitives. That will be much faster and ensures that the area is completely filled:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
def pixel(x1, y1, x2, y2):
size = [x1, y1, x2, y1, x2, y2, x1, y2]
color = []
for _ in range(4):
color += [0, 255, 0]
vertex_list = pyglet.graphics.vertex_list(4, ('v2i', size ), ('c3B', color ))
return vertex_list
#window.event
def on_draw():
window.clear()
vertex_list = pixel(500, 0, 600, 500)
vertex_list.draw(GL_POLYGON)
pyglet.app.run()
If you want to draw an pyglet.image, then generate a texture and a pyglet.graphics.Batch:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
def quadTexture(x, y, w, h, texture):
vertex = [x, y, x+w, y, x+w, y+h, x, y+h]
tex = [0, 0, 1, 0, 1, 1, 0, 1]
batch = pyglet.graphics.Batch()
batch.add(4, GL_QUADS, texture, ('v2i', vertex ), ('t2f', tex ))
return batch
#window.event
def on_draw():
window.clear()
batch.draw()
file = "logo.png"
image = pyglet.image.load(file)
tex = pyglet.graphics.TextureGroup(image.get_texture())
batch = quadTexture(20, 20, image.width, image.height, tex)
pyglet.app.run()
Much easier is to use a pyglet.sprite. e.g:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
#window.event
def on_draw():
window.clear()
sprite.draw()
file = "logo.png"
image = pyglet.image.load(file)
sprite = pyglet.sprite.Sprite(image, x=20, y=20)
pyglet.app.run()
I am using Pyglet to create a main menu for my python game. I want to draw text on top of a box that would act as its container. Whenever I render the text, instead of it having a transparent background, it draws whatever the glClearColor is set to. This also happens whenever I try and draw an image that has no background.
I am currently using these two lines for my textbox and the text.
self.text_box = pyglet.sprite.Sprite(pyglet.image.load('../Resources/Textures/Menu/text_box.png'),640-150,360-25, batch=self.menuBatch,group=boxGroup)
self.play_text = pyglet.text.Label("Play", font_name="Arial", font_size=32, x=640, y=360, anchor_x='center', anchor_y='center', color=(255,255,255,255), batch=self.menuBatch,group=textGroup)
Then I just call self.menuBatch.draw(). A picture of the problem I am having is:
For transparency effects to work, 'blend' should be enabled in OpenGL.
To enable blend:
glEnable(GL_BLEND) # transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # transparency
This complete snippet is working for me:
import pyglet
from pyglet.gl import *
# Pyglet Window stuff ---------------------------------------------------------
batch = pyglet.graphics.Batch() # holds all graphics
config = Config(sample_buffers=1, samples=4,depth_size=16, double_buffer=True, mouse_visible=False)
window = pyglet.window.Window(fullscreen=False, config=config)
glClearColor( 0, 100, 0, 255) # background color
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE)
glEnable(GL_BLEND) # transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # transparency
play_text = pyglet.text.Label("Play", font_name="Arial", font_size=32, x=240, y=140, anchor_x='center', anchor_y='center', color=(255,0,255,255), batch=batch,group=None)
text_box = pyglet.sprite.Sprite(pyglet.image.load('text_box.png'),240-150,160-25, batch=batch,group=None)
#window.event
def draw(dt):
window.clear()
batch.draw()
if __name__ == "__main__":
pyglet.clock.schedule_interval(draw, 1.0/60)
pyglet.app.run()
I have done something very similar to this before when making the GUI for one of my 3D programs. To make things very simple, I firstly turned off GL_DEPTH_TEST before the draw() call and then I re-enabled GL_DEPTH_TEST after the draw() call. I believe that the reason why the previous answer was working for them is because they didn't have GL_DEPTH_TEST enabled in the first place. I made a button class that you could maybe use as well (It's important to note that the argument "pos" is for the (x, y) coordinate of the lower left corner of the button and the argument "dim" is for the (width, height) dimensions of the button. The "texture_coords" argument refers to the texture coordinates of the background image of the button. If you want the full image, just leave it at the default. The rest is pretty self explanatory). So here's the code:
class Button:
def __init__(self, btn_img_file, text = "Button", pos = (0, 0), dim = (10, 10), text_color = (255, 255, 255), texture_coords = (0, 0, 1, 0, 1, 1, 0, 1)):
self.x, self.y = pos
self.w, self.h = dim
self.text = text
self.img = btn_img_file
self.tex = get_tex(self.img)
self.coords = texture_coords
self.color = text_color
self.batch = pyglet.graphics.Batch()
def on_click(self, mouse_x, mouse_y, function, *args, **kwargs):
x, y = self.x, self.y
X, Y = x + self.w, y + self.h
if mouse_x > x and mouse_x < X and mouse_y > y and mouse_y < Y:
function(*args, **kwargs)
def draw(self):
x, y = self.x, self.y
X, Y = x + self.w, y + self.h
font_size = (self.w // len(self.text)) - 5
label = pyglet.text.Label(self.text,
font_name = 'Times New Roman',
font_size = font_size,
x = x + (self.w // 2), y = y + (self.h // 2),
anchor_x = 'center', anchor_y = 'center',
color = (*self.color, 255))
self.batch.add(4, GL_QUADS, self.tex, ('v2f', (x, y, X, y, X, Y, x, Y)), ('t2f', self.coords))
glDisable(GL_DEPTH_TEST)
self.batch.draw()
label.draw()
glEnable(GL_DEPTH_TEST)
I hope this was helpful!
~ Rick Bowen
I am trying to have a circle that, when clicked, moves somewhere else on the screen. However, when I click the circle, nothing happens.
#IMPORT STUFF
import pyglet as pg
from random import randint
mouse = pg.window.mouse
#VARS
window = pg.window.Window(width = 640, height = 480)
score = 0
circleImg = pg.image.load("circle.png")
circle = pg.sprite.Sprite(circleImg, randint(1, window.width), randint(1, window.height))
text = pg.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
#DETECT MOUSE PRESS ON CIRCLE
#window.event
def on_mouse_press(x, y, button, modifiers):
if x == circle.x and y == circle.y:
circle.x = randint(1, window.width)
circle.y = randint(1, window.height)
#window.event
def on_draw():
window.clear()
text.draw()
circle.draw()
pg.app.run()
import pyglet
from pyglet.gl import *
from random import randint
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
class Circle(pyglet.sprite.Sprite):
def __init__(self, radiance=5, x=0, y=0):
self.texture = pyglet.image.load('circle.png')
super(Circle, self).__init__(self.texture)
def click(self, x, y):
if x >= self.x and y >= self.y:
if x <= self.x + self.texture.width and y <= self.y + self.texture.height:
return self
mouse = pyglet.window.mouse
#VARS
window = pyglet.window.Window(width = 640, height = 480)
score = 0
#circleImg = pyglet.image.load("circle.png")
#circle = pyglet.sprite.Sprite(circleImg, randint(1, window.width), randint(1, window.height))
circle = Circle(x=50, y=50)
text = pyglet.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
#DETECT MOUSE PRESS ON CIRCLE
#window.event
def on_mouse_press(x, y, button, modifiers):
if circle.click(x, y):
print('Clicked in circle')
circle.x = randint(0, window.width - 10)
circle.y = randint(0, window.height - 10)
#window.event
def on_draw():
window.clear()
text.draw()
circle.draw()
pyglet.app.run()
A short description of what this does is it creates a custom class called Circle that inherits the Sprite class. It loads the circle.png as a texture with a alpha channel that gets blended by the GL library.
We add a custom function called click that checks if the lowest x,y coordinates are higher than the circles lowest x,y, then we check if the cursor is below x+width and same for y of the image region.
If that's the case, we return the circle sprite class as a True value in case we want to use the sprite.
Future enhancements:
You should draw the circle using gl functions, hence why I've defined radiance in the class definitions. However radiance here is never used, it's a placeholder for the future.
This is so you can use math to defined if you actually clicked within the circle, but this is beyond my scope of quick answers.. I would have to do a lot of debugging myself in order to get the math to add up (it's not my strong side).
What makes it work now is that we use the image width, height, x and y data to crudely check if we're within the image, aka "the circle".
trying to draw over sprite or change picture pyglet
As a bonus, I'll add this answer to the list of enhancements because it contains some stuff that might be useful. One would be to replace 90% of your code with a custom pyglet.window.Window class to replace global variables and decorators and stuff.
And it would look something like this:
import pyglet
from pyglet.gl import *
from random import randint
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
key = pyglet.window.key
class Circle(pyglet.sprite.Sprite):
def __init__(self, radiance=5, x=0, y=0):
self.texture = pyglet.image.load('circle.png')
super(Circle, self).__init__(self.texture)
def click(self, x, y):
if x >= self.x and y >= self.y:
if x <= self.x + self.texture.width and y <= self.y + self.texture.height:
return self
class MainScreen(pyglet.window.Window):
def __init__ (self):
super(MainScreen, self).__init__(800, 600, fullscreen = False)
self.x, self.y = 0, 0
self.bg = pyglet.sprite.Sprite(pyglet.image.load('background.jpg'))
self.sprites = {}
self.sprites['circle'] = Circle(x=50, y=50)
self.sprites['label'] = pyglet.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_press(self, x, y, button, modifiers):
if self.sprites['circle'].click(x, y):
print('Clicked in circle')
self.sprites['circle'].x = randint(0, self.width - 10)
self.sprites['circle'].y = randint(0, self.height - 10)
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.bg.draw()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
x = MainScreen()
x.run()
I'm not familiar with pyglet, but I'm guessing the problem is that you're checking whether x == circle.x etc, which means it only moves when you click the single pixel at the exact centre of the circle. Try some kind of maximum distance from the centre (e.g. a hypotenuse math.sqrt( (x-circle.x)**2 + (y-circle.y)**2) < circle.radius