python pygame how to incorporate loops? [duplicate] - python

This question already has an answer here:
Why is my pygame application loop not working properly?
(1 answer)
Closed 2 years ago.
I made a picture of Pacman and I am trying to make it move to the right across the screen. This is my code so far. I have the Pacman drawing but it is many shapes combined together and I don't know how to move all of them at once.
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" %(20, 20)
import pygame
pygame.init()
BLACK = (0,0,0)
YELLOW = (255, 245, 59)
WHITE = (242, 242, 242)
SIZE = (500, 500)
screen = pygame.display.set_mode(SIZE)
# Fill background
pygame.draw.rect(screen, WHITE, (0,0, 500, 500))
pygame.draw.circle(screen, YELLOW, (250,250), 100,)
pygame.draw.circle(screen, BLACK, (250,250), 100, 3)
pygame.draw.circle(screen, BLACK, (260,200), 10,)
pygame.draw.polygon(screen, WHITE, ((250,250),(500,500),(500,100)))
pygame.draw.line(screen, BLACK, (250, 250), (334, 198), 3)
pygame.draw.line(screen, BLACK, (250, 250), (315, 318), 3)
pygame.display.flip()
pygame.time.wait(5000)

You have to add an application loop. The main application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (draw pacman)
update the display by either pygame.display.update() or pygame.display.flip()
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# update position
# [...]
# clear the display
screen.fill(WHITE)
# draw the scene
pacman(px, py, dir_x)
# update the display
pygame.display.flip()
Furthermore you have to draw pacman relative to a position (x, y) and a direction (dir_x). See the example:
import pygame
pygame.init()
BLACK = (0,0,0)
YELLOW = (255, 245, 59)
WHITE = (242, 242, 242)
SIZE = (500, 500)
screen = pygame.display.set_mode(SIZE)
def pacman(x, y, dir_x):
sign_x = -1 if dir_x < 0 else 1
pygame.draw.circle(screen, YELLOW, (x, y), 100,)
pygame.draw.circle(screen, BLACK, (x, y), 100, 3)
pygame.draw.circle(screen, BLACK, (x+10*sign_x, y-50), 10,)
pygame.draw.polygon(screen, WHITE, ((x, y),(x+250*sign_x, y+250),(x+250*sign_x, y-150)))
pygame.draw.line(screen, BLACK, (x, y), (x+84*sign_x, y-52), 3)
pygame.draw.line(screen, BLACK, (x, y), (x+65*sign_x, y+68), 3)
px, py, dir_x = 250, 250, 1
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
px += dir_x
if px > 300 or px < 200:
dir_x *= -1
# clear the display
screen.fill(WHITE)
# draw the scene
pacman(px, py, dir_x)
# update the display
pygame.display.flip()

Related

Surface.fill() is not clearing the display

I want to start my main game loop with surface.fill(WHITE) to repaint my display white, but every surface from the last frame still appears.
To demonstrate, the following example blits a red surface on a green surface. The blit is created by an event trigger every 1 second. However, the red surface does not disappear when it is not being triggered by the event.
import pygame as pg
pg.init()
GREEN, WHITE, RED = (60, 120, 60), (255,255,255), (120, 60, 60)
screen = pg.display.set_mode((200, 200))
box = pg.Surface((150, 150))
box.fill(GREEN)
running = True
EVENT_SECOND_TIMER = pg.event.custom_type()
pg.time.set_timer(EVENT_SECOND_TIMER, 1000)
while running:
screen.fill(WHITE)
for event in pg.event.get():
# Draw circle every second
if event.type == EVENT_SECOND_TIMER:
surf = pg.Surface((50,50))
surf.fill(RED)
box.blit(surf, (50, 50))
screen.blit(box, (25, 25))
pg.display.update()
If you want to draw something permanently, you have to draw it in the application loop. If you want to see surf instead of box for an image, you have to set a state variable and draw the scene depending on the state. Reset the state variable after drawing the scene:
import pygame as pg
pg.init()
GREEN, WHITE, RED = (60, 120, 60), (255,255,255), (120, 60, 60)
screen = pg.display.set_mode((200, 200))
clock = pg.time.Clock()
box = pg.Surface((150, 150))
box.fill(GREEN)
surf = pg.Surface((50,50))
surf.fill(RED)
EVENT_SECOND_TIMER = pg.event.custom_type()
pg.time.set_timer(EVENT_SECOND_TIMER, 1000)
running = True
while running:
clock.tick(60)
draw_surf = False
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == EVENT_SECOND_TIMER:
draw_surf = True
screen.fill(WHITE)
screen.blit(box, (25, 25))
if draw_surf:
screen.blit(surf, (50, 50))
pg.display.update()
pg.quit()

How to center text inside a shape?

I want to display a number inside a circle.
The number must be centered (x,y).
How to do it?
import pygame, sys
pygame.init()
screen= pygame.display.set_mode((0, 0))
black= (0,0,0)
white= (255,255,255)
red= (255,0,0)
number= "8"
bigfont = pygame.font.SysFont("arial", 50)
text = bigfont.render(number, True, white)
def draw():
pygame.draw.circle(screen, red, (300,300), 200)
screen.blit(text, (300,300))
pygame.display.update()
Get the bounding rectangle of the text and set the center of the rectangle by the center of the text. Use the rectangle to blit the text:
text_rect = text.get_rect()
text_rect.center = (300, 300)
screen.blit(text, text_rect)
The same with one line of code:
screen.blit(text, text.get_rect(center = (300, 300)))
Use this code in a function:
def draw_circle(x, y, text):
pygame.draw.circle(screen, red, (x, y), 200)
screen.blit(text, text.get_rect(center = (x, y)))
See pygame.Surface.blit:
[...] The dest argument can either be a pair of coordinates representing the position of the upper left corner of the blit or a Rect, where the upper left corner of the rectangle will be used as the position for the blit.
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((500, 500))
font = pygame.font.SysFont(None, 100)
clock = pygame.time.Clock()
text = font.render("Text", True, (255, 255, 0))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window_center = window.get_rect().center
window.fill(0)
pygame.draw.circle(window, (255, 0, 0), window_center, 100)
window.blit(text, text.get_rect(center = window_center))
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
To centre PyGame text around point x_pos, y_pos:
Get the dimensions of the text object: width, height = text.get_rect()[2:4]
When you render (screen.blit()) the image, shift the coordinates back by half the width and half the height: screen.blit(text, (x_pos - int(width / 2), y_pos - int(height / 2)))

how do i set the mouse position around a circle in pygame

I'm trying to create this program in pygame to study code/math, but i don't know how to lock the mouse position around a circle in pygame, any help?
import pygame
pygame.init()
x = 250
y = 250
window = pygame.display.set_mode((500, 500))
pygame.display.set_caption("around circle")
#line to be created
def Lin(xref, yref):
lin = pygame.draw.line(window, (250, 250, 0), (x, y), (xref, yref), 1)
window_open = True
while window_open:
pygame.display.update()
window.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_open = False
mousepos = pygame.mouse.get_pos()
xref = mousepos[0]
yref = mousepos[1]
# 2 circles to get only the border
cir0 = pygame.draw.circle(window, (250, 250, 250), (x, y), 100, 1)
cir1 = pygame.draw.circle(window, (250, 250, 250), (x, y), 99, 1)
Lin(xref, yref)
pygame.quit()
I would do something similar to Mike67 answer, but would take advantage of the features of pygame's Vector's (see docs here).
import pygame
pygame.init()
x = 250
y = 250
radius = 100
center = pygame.Vector2(x, y)
window = pygame.display.set_mode((500, 500))
pygame.display.set_caption("around circle")
#line to be created
def Lin(start_point, vector):
pygame.draw.line(window, (250, 250, 0), start_point, start_point+vector, 1)
window_open = True
while window_open:
pygame.display.update()
window.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_open = False
mousepos = pygame.mouse.get_pos()
line_vector = pygame.Vector2(mousepos) - center
if line_vector.length() > radius:
line_vector.scale_to_length(radius)
# 2 circles to get only the border
cir0 = pygame.draw.circle(window, (250, 250, 250), (x, y), radius, 1)
cir1 = pygame.draw.circle(window, (250, 250, 250), (x, y), radius-1, 1)
Lin(center, line_vector)
pygame.quit()
I modified the Lin() function as well.
EDIT:
From your comment you indicated that you want it to always scale to the side of the circle. In that case just skip the test if line_vector.length() > radius and always scale it. You might be tempted to only scale it if length != radius, but when comparing floating point calculated numbers they are very unlikely to actually be the same (because of the decimal places involved) and you could see if the difference is less than a threshold and call them equal, but it really isn't worth the complication.
import pygame
pygame.init()
x = 250
y = 250
radius = 100
center = pygame.Vector2(x, y)
window = pygame.display.set_mode((500, 500))
pygame.display.set_caption("around circle")
#line to be created
def Lin(start_point, vector):
pygame.draw.line(window, (250, 250, 0), start_point, start_point+vector, 1)
window_open = True
while window_open:
pygame.display.update()
window.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_open = False
mousepos = pygame.mouse.get_pos()
line_vector = pygame.Vector2(mousepos) - center
line_vector.scale_to_length(radius)
# 2 circles to get only the border
cir0 = pygame.draw.circle(window, (250, 250, 250), (x, y), radius, 1)
cir1 = pygame.draw.circle(window, (250, 250, 250), (x, y), radius-1, 1)
Lin(center, line_vector)
pygame.quit()
To keep the line in the circle, just adjust the mouse x/y coordinates relative to the mouse distance from the center.
Here is the updated code:
import pygame
import math
pygame.init()
x = 250
y = 250
window = pygame.display.set_mode((500, 500))
pygame.display.set_caption("around circle")
#line to be created
def Lin(xref, yref):
lin = pygame.draw.line(window, (250, 250, 0), (x, y), (xref, yref), 1)
window_open = True
while window_open:
pygame.display.update()
window.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_open = False
mousepos = pygame.mouse.get_pos()
xref = mousepos[0]
yref = mousepos[1]
# get mouse distance from center
dist = math.sqrt((xref-x)**2 + (yref-y)**2)
if (dist > 100): # if mouse outside circle, adjust x/y proportionally
xref = x + (xref - x) * (100/dist)
yref = y + (yref - y) * (100/dist)
# 2 circles to get only the border
cir0 = pygame.draw.circle(window, (250, 250, 250), (x, y), 100, 1)
cir1 = pygame.draw.circle(window, (250, 250, 250), (x, y), 99, 1)
Lin(xref, yref)
pygame.quit()

How to make transparent pygame.draw.circle [duplicate]

This question already has answers here:
Draw a transparent rectangles and polygons in pygame
(4 answers)
Closed 2 years ago.
How to make pygame.draw.circle transparent (add alpha level), as for "surface" "set_alpha"?
I found a solution only in changing the color of pygame.draw.circle to less bright
You've to use a pygame.Surface. Create a Surface with a per pixel alpha image format. e.g:
radius = 100
circle = pygame.Surface((radius*2, radius*2), pygame.SRCALPHA)
And draw a transparent circle on it. The color of the circle has to have an alpha channel < 255 (e.g. 128):
pygame.draw.circle(circle, (255, 0, 0, 128), (radius, radius), radius)
blit() the Surface to the window. e.g.:
window.blit(circle, (100, 100))
Example:
import pygame
pygame.init()
wndsize = (400, 400)
window = pygame.display.set_mode(wndsize)
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
pygame.draw.rect(window, (0, 0, 255), (0, 0, 200, 400))
radius = 100
circle = pygame.Surface((radius*2, radius*2), pygame.SRCALPHA)
pygame.draw.circle(circle, (255, 0, 0, 128), (radius, radius), radius)
window.blit(circle, (100, 100))
pygame.display.flip()
You can create surface with alpha channel
surface1 = screen.convert_alpha()
fill it with transparent color -
surface1.fill([0,0,0,0])
draw circle using color [R,G,B,Alpha]
pygame.draw.circle(surface1, (255, 0, 0, 128), (300, 300), 200)
and blit it on screen
screen.blit(surface1, (0,0))
But alpha always mix object color with background color so it makes it less bright.
import pygame
pygame.init()
screen = pygame.display.set_mode((800,600))#, depth=32)
surface1 = screen.convert_alpha()
surface1.fill([0,0,0,0])
pygame.draw.circle(surface1, (255, 0, 0, 128), (325, 250), 100)
surface2 = screen.convert_alpha()
surface2.fill([0,0,0,0])
pygame.draw.circle(surface2, (0, 255, 0, 128), (475, 250), 100)
surface3 = screen.convert_alpha()
surface3.fill([0,0,0,0])
pygame.draw.circle(surface3, (0, 0, 255, 128), (400, 350), 100)
screen.fill([255,255,255]) # white background
screen.blit(surface1, (0,0))
screen.blit(surface2, (0,0))
screen.blit(surface3, (0,0))
pygame.display.flip()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
pygame.quit()
Example on GitHub: furas/python-examples/pygame/transparency

Creating a rect grid in Pygame

I need to create a clickable 8 by 8 grid in pygame.
Right now I have something like this:
#!/usr/bin/python2
#-------------------------------------------------------------------------------
# Imports & Inits
import pygame, sys
from pygame.locals import *
pygame.init()
#-------------------------------------------------------------------------------
# Settings
WIDTH = 105
HEIGHT = 105
FPS = 60
#-------------------------------------------------------------------------------
# Screen Setup
WINDOW = pygame.display.set_mode([WIDTH,HEIGHT])
CAPTION = pygame.display.set_caption('Test')
SCREEN = pygame.display.get_surface()
TRANSPARENT = pygame.Surface([WIDTH,HEIGHT])
TRANSPARENT.set_alpha(255)
TRANSPARENT.fill((255,255,255))
#-------------------------------------------------------------------------------
# Misc stuff
rect1 = pygame.draw.rect(SCREEN, (255, 255, 255), (0,0, 50, 50))
rect2 = pygame.draw.rect(SCREEN, (255, 255, 255), (0,55, 50, 50))
rect3 = pygame.draw.rect(SCREEN, (255, 255, 255), (55,0, 50, 50))
rect4 = pygame.draw.rect(SCREEN, (255, 255, 255), (55,55, 50, 50))
...
#-------------------------------------------------------------------------------
# Refresh Display
pygame.display.flip()
#-------------------------------------------------------------------------------
# Main Loop
while True:
pos = pygame.mouse.get_pos()
mouse = pygame.draw.circle(TRANSPARENT, (0, 0, 0), pos , 0)
# Event Detection---------------
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
if rect1.contains(mouse):
rect1 = pygame.draw.rect(SCREEN, (155, 155, 155), (0,0, 50, 50))
pygame.display.flip()
Now, in my original code, I have far more rectangles and I need a way to do something like this:
for i in rectangles:
if i hasbeenclickedon:
change color
Obviously, my solution is far too static.
So, how could I accomplish this?
Though your solution is indeed a little cumbersome, I'd first say
rectangles = (rect1, rect2, ...)
then you can iterate over them as intended.
Try sth like
pos = pygame.mouse.get_pos()
for rect in rectangles:
if rect.collidepoint(pos):
changecolor(rect)
You'd have to implement the changecolor method, of course.
Generally, I'd recommend creating a class for you clickable fields that defines a method changecolor.
Simple 'human' colors:
Color("red")
Color(255,255,255)
Color("#fefefe")
Use:
import pygame
# This makes event handling, rect, and colors simpler.
# Now you can refer to `Sprite` or `Rect()` vs `pygame.sprite.Sprite` or `pygame.Rect()`
from pygame.locals import *
from pygame import Color, Rect, Surface
pygame.draw.rect(screen, Color("blue"), Rect(10,10,200,200), width=0)
pygame.draw.rect(screen, Color("darkred"), Rect(210,210,400,400), width=0)

Categories

Resources