This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Collision between masks in pygame
(1 answer)
Closed 1 year ago.
So, I'm making an app/game in python using pygame module, and my problem is that, I cannot find a way to check if a sprite is entirely touching another sprite or not.
What I mean is
if (sprite is touching anything else that some white rectangle):
do some code
Here is my messy code :
main.py :
import pygame, Classes, Groups, Images, random, time
pygame.init()
pygame.font.init()
# App Variables
run_bool = False
# Fonts
ARAL_20_ITALIC = pygame.font.SysFont('Arial' , 20, italic=True)
# Colors
BG = (30, 30, 30)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# Window
size = (800, 600)
scr = pygame.display.set_mode(size)
pygame.display.set_caption('Simulation by Cold Fire (0.1)')
# App Loop
while True:
time.sleep(0.01)
# Graphics
scr.fill(BG)
"""TERRAIN"""
terrain_hitbox = pygame.draw.rect(scr, WHITE, (300, 100, size[0] - 350, size[1] - 150))
pygame.draw.rect(scr, BLACK, (300, 100, size[0] - 350, size[1] - 150), width=10)
"""SUBJECTS"""
for subject in Groups.G_Subject:
if run_bool == True:
subject.update__()
scr.blit(subject.img, subject.rect)
"""GUI"""
pygame.draw.line(scr, WHITE, (200, 0), (200, size[1]), width=3)
scr.blit(ARAL_20_ITALIC.render('Subjects' ,False, WHITE), (30, 10))
add_hitbox = scr.blit(Images.add_img.convert_alpha(), (30, 50))
remove_hitbox = scr.blit(Images.remove_img.convert_alpha(), (30, 90))
scr.blit(ARAL_20_ITALIC.render(f'Subjects: {len(Groups.G_Subject)}' , False, WHITE), (30, 130))
if run_bool == False:
run_hitbox = scr.blit(Images.run_img.convert_alpha(), (210, size[1] - 40))
else:
run_hitbox = scr.blit(Images.stop_img.convert_alpha(), (210, size[1] - 40))
# Updating Screen
pygame.display.flip()
# Events
for event in pygame.event.get():
# Quitting App
if event.type == pygame.QUIT:
pygame.quit()
quit()
# Clicking
if event.type == pygame.MOUSEBUTTONDOWN:
mouse = pygame.mouse.get_pos()
if add_hitbox.collidepoint(mouse[0], mouse[1]):
rand_x = random.randint(terrain_hitbox[0], terrain_hitbox[0] + terrain_hitbox[2])
rand_y = random.randint(terrain_hitbox[1], terrain_hitbox[1] + terrain_hitbox[3])
Classes.Subject(rand_x, rand_y, Images.subject0_img.convert_alpha())
if remove_hitbox.collidepoint(mouse[0], mouse[1]) and not 1 > len(Groups.G_Subject):
Groups.G_Subject.remove(random.choice(Groups.G_Subject.sprites()))
if run_hitbox.collidepoint(mouse[0], mouse[1]):
if run_bool == True:
run_bool = False
else:
run_bool = True
Classes.py :
import pygame, Groups, random, math
class Subject(pygame.sprite.Sprite):
def __init__(self, x, y, img=pygame.image.load('assets/img/subject0.png').convert_alpha(pygame.display.set_mode((800, 600))), speed=5):
super().__init__()
self.img = img
self.rect = self.img.get_rect()
self.rect.x = x
self.rect.y = y
self.oldx = self.rect.x
self.oldy = self.rect.y
self.speed = speed
Groups.G_Subject.add(self)
def calculat_new_xy(self, old_xy, speed, angle_in_radians):
new_x = old_xy.x + (speed * math.cos(angle_in_radians))
new_y = old_xy.y + (speed * math.sin(angle_in_radians))
return new_x, new_y
def update__(self):
self.oldx, self.oldy = self.rect.x, self.rect.y
self.rect.x, self.rect.y = self.calculat_new_xy(self.rect, self.speed, random.randint(1, 360))
Images.py :
import pygame
add_img = pygame.transform.scale(pygame.image.load('assets/img/gui/add.png'), (90, 30))
remove_img = pygame.transform.scale(pygame.image.load('assets/img/gui/remove.png'), (90, 30))
subject0_img = pygame.image.load('assets/img/subject0.png')
run_img = pygame.transform.scale(pygame.image.load('assets/img/gui/run.png'), (90, 30))
stop_img = pygame.transform.scale(pygame.image.load('assets/img/gui/stop.png'), (90, 30))
Groups.py :
import pygame
G_Subject = pygame.sprite.Group()
I do know my code is a mess, so if you don't wanna help, it's ok ! Thx in advance :D
Related
Why do bodies in pymunk keep colliding without actually touching each other even after the position and body shape is correctly set?
You will see from my code below that the bird keeps colliding with the ball without actually touching the ball.
import pygame, pymunk
pygame.init()
WIDTH, HEIGHT = 900, 500
win = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Flappy OBJ")
color = (255,255,255)
win.fill(color)
fps = 120
fb_img = pygame.image.load("images/flappy-bird.png")#.convert()
fb_img = pygame.transform.rotate(pygame.transform.scale(fb_img, (30,20)), 0)
ball_img = pygame.image.load("images/ball.png")#.convert()
ball_img = pygame.transform.rotate(pygame.transform.scale(ball_img, (60,60)), 0)
def bird_body():
body = pymunk.Body(1, 100, body_type=pymunk.Body.DYNAMIC)
body.position = (50, 0)
shape = pymunk.Poly.create_box(body, (30,20))
space.add(body, shape)
return shape
def ball_body():
body = pymunk.Body(1, 100, body_type = pymunk.Body.KINEMATIC)
body.position = (500, 300)
body.velocity = (-25,0)
shape = pymunk.Circle(body, 60)
shape.color = (0,255,255, 128)
space.add(body, shape)
return shape
space = pymunk.Space()
space.gravity = (0, 20)
fb_body = bird_body()
b_body = ball_body()
def draw_window():
global scroll
win.fill(color)
space.step(0.03)
win.blit(ball_img, (b_body.body.position.x, b_body.body.position.y))
win.blit(fb_img, (fb_body.body.position.x, fb_body.body.position.y))
pygame.display.update()
def main():
w_was_down = True
clock = pygame.time.Clock()
run = True
while run:
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_w]:
if not w_was_down:
fb_body.body.apply_impulse_at_local_point((0,-50))
w_was_down = True
else:
w_was_down = False
draw_window()
pygame.quit()
main()
Tap w to move the bird up. some more details some more details some more details
The 2nd argument of pymunk.Circle is the radius, but not the diameter:
shape = pymunk.Circle(body, 60)
shape = pymunk.Circle(body, 30)
And the position of the pymuk object is the center of the object:
win.blit(ball_img, (b_body.body.position.x, b_body.body.position.y))
win.blit(fb_img, (fb_body.body.position.x, fb_body.body.position.y))
win.blit(ball_img, (b_body.body.position.x-30, b_body.body.position.y-30))
win.blit(fb_img, (fb_body.body.position.x-15, fb_body.body.position.y-10))
In this program when the user types more text I want the rectangle to automatically get longer when the user types to keep the letters inside of the rectangle. However, it doesn't update the rectangle when the text gets longer. How do I fix this?
from pygame import *
init()
screen = display.set_mode((800, 600))
name_font = font.Font(None, 32)
name_text = ''
class Rectangle:
def __init__(self, x, y):
self.x = x
self.y = y
self.input_rect = Rect(x, y, 140, 32)
self.text_surface = name_font.render(name_text, True, (255, 255, 255))
color = Color('lightskyblue3')
draw.rect(screen, color, self.input_rect, 2)
self.input_rect.w = self.text_surface.get_width() + 10
screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))
def naming():
global name_text
if events.type == KEYDOWN:
if keys[K_BACKSPACE]:
name_text = name_text[:-1]
screen.fill((0, 0, 0))
rect_1 = Rectangle(200, 200)
else:
name_text += events.unicode
while True:
rect_1 = Rectangle(200, 200)
for events in event.get():
keys = key.get_pressed()
naming()
if events.type == QUIT:
quit()
display.update()
time.delay(1)
The Rectangle.text_surface is a PyGame Surface. So you can easily get the precise width of the bounding box by simply calling self.text_surface.get_width().
But you start the size of the border-rect at 140, so this size has to be the maximum of 140 or whatever the new (longer) width is. Another problem is that when the rectangle re-sizes, the old rectangle is left behind. So whenever we now re-draw the rectangle, it erases the background to black.
This is all pretty easily encapsulated into the exiting __init__():
def __init__(self, x, y):
self.x = x
self.y = y
self.text_surface = name_font.render(name_text, True, (255, 255, 255))
rect_width = max( 140, 10 + self.text_surface.get_width() ) # Adjust the width
color = Color('lightskyblue3')
self.input_rect = Rect(x, y, rect_width, 32) # Use new width (if any)
draw.rect(screen, (0,0,0) , self.input_rect, 0) # Erase any existing rect
draw.rect(screen, color, self.input_rect, 2)
self.input_rect.w = self.text_surface.get_width() + 10
screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))
Here's the code, i'm working on Atom. I've been using pygame for a lot of time, and that rarely happens. I never knew what the problem was, but i never needed to, until now.
import pygame
from random import randint as rnd
from colorama import Cursor
import math
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (600,90)
pygame.init()
xsize, ysize = 700, 700
screen = pygame.display.set_mode((xsize, ysize))
screen.fill((0, 0, 0))
def dis(p1, a1):
return math.sqrt((p1[0] - a1[0])**2 + (p1[1] - a1[1])**2)
inner = 0
tot = 0
pygame.draw.circle(screen, (255, 255, 255), (350, 350), 350, 2)
while True:
pos = (rnd(0, xsize), rnd(0, ysize))
if dis((350, 350), pos) < 350:
color = (50, 255, 50)
inner += 1
else:
color = (50, 50, 250)
tot += 1
pygame.draw.circle(screen, color, pos, 2)
print(" pi = " + str(4 * inner / tot) + "\nnÂș dots: " + str(tot), Cursor.UP(2))
pygame.display.flip()
The window freeze, because you do not handle the events. You have to handle the events by either pygame.event.pump() or pygame.event.get(), to keep the window responding.
Add an event loop, for instance:
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# [...]
This question already has answers here:
Why my pygame game with a tiled map is lagging?
(1 answer)
How could I optimise this simple python pygame code
(1 answer)
Closed 2 years ago.
I am trying to make a game, but when I try to blit a few flowers onto the screen it slows down from 60 FPS to 12 FPS.
At first, I tried to .convert() all my images, but then they turned pink. So I used .convert_alpha() instead. using .convert_alpha() made the images not pink, but it only made the FPS go up a teeny bit.
the relevant parts of my code are
### Creates a random assortment of Flora ###
def createFlora(screenSize, cellSize):
flower1 = pygame.transform.scale(pygame.image.load("Flower1.PNG"), (64, 64))
flower1 = flower1.convert_alpha()
flower2 = pygame.transform.scale(pygame.image.load("Flower2.PNG"), (64, 64))
flower2 = flower2.convert_alpha()
flowers = [flower1, flower2]
flower1ds = pygame.transform.scale(pygame.image.load("Flower1DropShadow.PNG"), (64, 64))
flower1ds = flower1ds.convert_alpha()
flower2ds = pygame.transform.scale(pygame.image.load("Flower2DropShadow.PNG"), (64, 64))
flower2ds = flower2ds.convert_alpha()
flowersds = [flower1ds, flower2ds]
flora = []
for i in range(screenSize[0] // cellSize + 1):
floraRow = []
for j in range(screenSize[1] // cellSize + 1):
if randint(0, len(flowers) * 4) < len(flowers):
choice = randint(0, len(flowers) - 1)
floraRow.append((flowers[choice],
(i * cellSize, j * cellSize),
randint(0, 3) * 90, choice))
else: floraRow.append(False)
flora.append(floraRow)
floraFinal = []
for i in range(len(flora)):
for j in range(len(flora[0])):
if flora[i][j] != False:
if flora[i][j][3] == 0:
if flora[i][j][2] == 0: #depending on the rotation of parent plant, the shadow will be changed.
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), flora[i][j][1]))
elif flora[i][j][2] == 90:
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), (flora[i][j][1][0], flora[i][j][1][1] + 8)))
elif flora[i][j][2] == 180:
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), (flora[i][j][1][0] + 8, flora[i][j][1][1] + 8)))
elif flora[i][j][2] == 270:
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), (flora[i][j][1][0] + 8, flora[i][j][1][1])))
elif flora[i][j][3] == 1:
if flora[i][j][2] == 0:
floraFinal.append((flowersds[flora[i][j][3]], flora[i][j][1]))
elif flora[i][j][2] == 90:
floraFinal.append((flowersds[flora[i][j][3]], (flora[i][j][1][0], flora[i][j][1][1] + 4)))
elif flora[i][j][2] == 180:
floraFinal.append((flowersds[flora[i][j][3]], (flora[i][j][1][0] + 4, flora[i][j][1][1] + 4)))
elif flora[i][j][2] == 270:
floraFinal.append((flowersds[flora[i][j][3]], (flora[i][j][1][0] + 4, flora[i][j][1][1])))
floraFinal.append((pygame.transform.rotate(flora[i][j][0], flora[i][j][2]), flora[i][j][1]))
return floraFinal
def renderFlora(flora, screen):
for i in range(len(flora)):
screen.blit(flora[i][0], flora[i][1])
def main():
os.environ['SDL_VIDEO_CENTERED'] = '1' #center the screen
pygame.init()
screenSize = (1856, 960) #24 x 14 cells
onTop(pygame.display.get_wm_info()['window']) #move pygame window to the front
screen = pygame.display.set_mode(screenSize)
screen.fill((25, 25, 25))
""" VARS """
cellSize = 64
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 30)
path = createPath()
flora = createFlora(screenSize, cellSize)
tileMap = createTileMap(screenSize, cellSize)
tileGridOn = False
currentButton = 0
buttons = []
gridButtonImg = pygame.transform.scale(pygame.image.load("GridButton.PNG").convert_alpha(), (48, 48))
#Grid Button Toggles a grid so you can see where Tiles are easier
buttons.append(Button((20, screenSize[1] - 64), False, gridButtonImg, screen, 48))
pathSds = pygame.transform.scale(pygame.image.load("StraightPathDropShadow.PNG").convert_alpha(), (64, 64))
pathCds = pygame.transform.scale(pygame.image.load("CornerPathDropShadow.PNG").convert_alpha(), (64, 64))
#==# MAIN LOOP #==#
clock = pygame.time.Clock()
while True:
screen.fill((25, 25, 25))
for evt in pygame.event.get():
if evt.type == pygame.QUIT:
pygame.quit()
quit()
#Draw Background Tiles
renderBackground(tileMap, screen)
#Draw Midground Items/Tiles
renderFlora(flora, screen)
#Draw Foreground Items/Tiles
renderPath(path, screen, pathSds, pathCds)
textsurface = myfont.render(str(round(clock.get_fps() * 10) / 10), False, (0, 0, 0))
screen.blit(textsurface,(15, 15))
### Buttons ###
for i in range(len(buttons)):
buttons[i].update(screen)
if buttons[i].getCTF():
currentButton = i
#Toggle Grid
tileGridOn = buttons[0].getValue()
if tileGridOn: drawGrid(screenSize, cellSize, screen)
pygame.display.flip()
clock.tick(60)
main()
The flora is created before the main loop, so I'm not creating images every cycle, I'm just passing a list through the render function, which only blits the images onto the screen.
If I comment out the renderFlora() method it goes to 60 FPS, but if I leave it in it drops significantly, I don't know why, because I already blit a lot more images for the tiled background. method.
Improve performance by pre-treating the flora. Render on a surface before the application loop and blit the surface in the application loop:
def main():
# [...]
flora_surf = pygame.Surface(screen.get_size())
renderFlora(flora, flora_surf)
while True:
# [...]
#Draw Midground Items/Tiles
# renderFlora(flora, screen) <-- DELETE
screen.blit(flora_surf, (0, 0))
# [...]
I'm playing with pygame and I'm making a memory game.
In my memory game I need to have a small Menu on the middle-bottom of the screen and the player list on top of the screen. The game manager will output the message in the upper left corner of the screen.
The playable board where the cards will be situated should be in the center of the screen with a small margin on the right and the left and about 200px from the top and the bottom.(e.g. if the screen width and height are 1200 x 800, the playable board should be from 2 to 1998 width and 200 to 600 height.
I've made a screenshot of the idea I have:
I've put up the idea of the scrollable board with the cards, although it's hardcoded for now, I mostly need the idea not the code since I just have a hard time figuring out how it should work.
The playable board (game board on the screenshot), should be moved through the keyboard(so up, right, down, left arrows). The thing I want to do is to allow more cards than 4 in each column and 12 in each row, in a way that when there are more cards than the 12 x 4, the gameboard could be moved, but without exiting the red square marked on the screenshot(so it's a scrollable surface). I want to limit the scrollable area to a width of 1200 and height of 400 px's, but in a way that the area is scrollable only inside the red area.
If that can't work or is planned otherwise, is there any way to register clicks differently? Let's say when I move the keys in a way that the Menu area is above the game board and the menu is clicked, I don't want the card under the Menu to be clicked as well. Is there any way to lock the content under the Menu?
Adding the code sample below:
from pygame import *
import sys
init()
screen_width = 1200
screen_height = 800
screen = display.set_mode((screen_width, screen_height))
board_width = 2000
board_height = 1000
playable_board = Surface((board_width, board_height))
playable_board = playable_board.convert()
screen.fill((255, 255, 255))
#draw.rect(playable_board, (125, 125, 125), (2, 200, board_width, board_height))
for x in range(0, 50):
draw.rect(playable_board, (100, 100, 100), (x * 100 + 5, 200, 90, 90))
draw.rect(playable_board, (100, 100, 100), (x * 100 + 5, 310, 90, 90))
draw.rect(playable_board, (100, 100, 100), (x * 100 + 5, 420, 90, 90))
draw.rect(playable_board, (100, 100, 100), (x * 100 + 5, 530, 90, 90))
# draw.rect(playable_board, (100, 100, 100), (x * 100 + 5, 640, 90, 90))
point_x = 0
point_y = 0
point_change = -30
animationTimer = time.Clock()
endProgram = False
while not endProgram:
for e in event.get():
pass
key_pressed = key.get_pressed()
if e.type == QUIT:
endProgram = True
if e.type == MOUSEBUTTONUP:
mouse_x, mouse_y = e.pos
display.set_caption(('Coordinates: ' + str(mouse_x + abs(point_x)) + 'x' + str(mouse_y + abs(point_y))))
draw.rect(playable_board, (50, 200, 50), (mouse_x + abs(point_x), mouse_y + abs(point_y), 10, 10))
if key_pressed[K_LEFT]:
point_x -= point_change
if key_pressed[K_RIGHT]:
point_x += point_change
if key_pressed[K_UP]:
point_y -= point_change
if key_pressed[K_DOWN]:
point_y += point_change
if point_x > 0:
point_x = 0
if point_x < -(board_width - screen_width):
point_x = -(board_width - screen_width)
if point_y > 0:
point_y = 0
if point_y < -(board_height - screen_height):
point_y = -(board_height - screen_height)
screen.blit(playable_board, (point_x, point_y, screen_width, screen_height))
draw.rect(screen, (100, 255, 100), (500, 650, 200, 150))
draw.rect(screen, (100, 255, 100), (100, 0, 1000, 50))
draw.rect(screen, (100, 255, 100), (0, 70, 250, 100))
animationTimer.tick(60)
display.update()
I'd create a subsurface of the playable_board and then blit it at the desired coordinates. You need to define a rect (called area here) with the desired size of the subsurface and scroll it by incrementing the x and y attributes. To keep the area rect inside of the board_rect (to prevent ValueErrors), you can call the pygame.Rect.clamp or clamp_ip methods.
import pygame as pg
pg.init()
screen = pg.display.set_mode((1200, 800))
board = pg.Surface((2000, 1000))
board.fill((20, 70, 110))
board_rect = board.get_rect()
# Append the rects to a list if you want to use
# them for collision detection.
rects = []
for y in range(11):
for x in range(22):
rect = pg.draw.rect(board, (100, 100, 100), (x*95, y*95, 90, 90))
rects.append(rect)
# The blit position of the subsurface.
pos = (20, 200)
point_change = -10
# This rect is needed to create a subsurface with the size (1160, 400).
area = pg.Rect(0, 0, 1160, 400)
# Use the area rect to create a subsurface of the board.
board_subsurface = board.subsurface(area)
clock = pg.time.Clock()
endProgram = False
while not endProgram:
for e in pg.event.get():
if e.type == pg.QUIT:
endProgram = True
key_pressed = pg.key.get_pressed()
# Scroll by changing the x and y coordinates of the area rect.
if key_pressed[pg.K_LEFT]:
area.x += point_change
elif key_pressed[pg.K_RIGHT]:
area.x -= point_change
if key_pressed[pg.K_UP]:
area.y += point_change
elif key_pressed[pg.K_DOWN]:
area.y -= point_change
# Check if one of the movement keys was pressed.
if any(key_pressed[key] for key in (pg.K_LEFT, pg.K_RIGHT, pg.K_UP, pg.K_DOWN)):
# Clamp the area rect to the board_rect, otherwise calling
# `board.subsurface` could raise a ValueError.
area.clamp_ip(board_rect)
# Create a new subsurface with the size of the area rect.
board_subsurface = board.subsurface(area)
screen.fill((30, 30, 30))
screen.blit(board_subsurface, pos)
pg.draw.rect(screen, (100, 255, 100), (500, 650, 200, 150))
pg.draw.rect(screen, (100, 255, 100), (100, 0, 1000, 50))
pg.draw.rect(screen, (100, 255, 100), (0, 70, 250, 100))
clock.tick(60)
pg.display.update()