Rectangle collision in pygame? (Bumping rectangles into each other) - python

I decided to move that Squarey game to pygame, and now I have 2 rectangles that can move around and bump into the walls. However, the rectangles can move right through each other. How would I make them bump into each other and stop?
My code:
import pygame
pygame.init()
screen = pygame.display.set_mode((1000, 800))
pygame.display.set_caption("Squarey")
done = False
is_red = True
x = 30
y = 30
x2 = 100
y2 = 30
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_red = not is_red
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]: y -= 3
if pressed[pygame.K_DOWN]: y += 3
if pressed[pygame.K_LEFT]: x -= 3
if pressed[pygame.K_RIGHT]: x += 3
if pressed[pygame.K_w]: y2 -= 3
if pressed[pygame.K_s]: y2 += 3
if pressed[pygame.K_a]: x2 -= 3
if pressed[pygame.K_d]: x2 += 3
if y < 0:
y += 3
if x > 943:
x -= 3
if y > 743:
y -= 3
if x < 0:
x += 3
if y2 < 0:
y2 += 3
if x2 > 943:
x2 -= 3
if y2 > 743:
y2 -= 3
if x2 < 0:
x2 += 3
screen.fill((0, 0, 0))
if is_red: color = (252, 117, 80)
else: color = (168, 3, 253)
if is_red: color2 = (0, 175, 0)
else: color2 = (255, 255, 0)
rect1 = pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))
rect2 = pygame.draw.rect(screen, color2, pygame.Rect(x2, y2, 60, 60))
pygame.display.flip()
clock.tick(60)
pygame.quit()

Use pygame.Rect.colliderect
if rect1.colliderect(rect2):
print("Collision !!")
BTW: you can create rect1 (and rect2) only once - before main loop - and then you can use rect1.x and rect1.y instead of x, y. And you can use pygame.draw.rect(screen, color, rect1) without creating new Rect all the time.
Rect is usefull
# create
rect1 = pygame.Rect(30, 30, 60, 60)
# move
rect1.x += 3
# check colision with bottom of the screen
if rect1.bottom > screen.get_rect().bottom:
# center on the screen
rect1.center = screen.get_rect().center

To check for collisions, try something like this:
def doRectsOverlap(rect1, rect2):
for a, b in [(rect1, rect2), (rect2, rect1)]:
# Check if a's corners are inside b
if ((isPointInsideRect(a.left, a.top, b)) or
(isPointInsideRect(a.left, a.bottom, b)) or
(isPointInsideRect(a.right, a.top, b)) or
(isPointInsideRect(a.right, a.bottom, b))):
return True
return False
def isPointInsideRect(x, y, rect):
if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y < rect.bottom):
return True
else:
return False
Then, while moving them, you can call
if doRectsOverlap(rect1, rect2):
x -= 3
y -= 3
rect1 = pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))
Or something like that.

Related

Limiting the amount of circles that spawn in

im fairly new to coding and python, i was messing around with pygame and i was wondering if theres a way i could limit the amount of circles that spawn in this game im making? when i run the code, it just spawns in circles all over the place really fast. i tried doing the time.sleep thing, but all it does is slow down the entire game.
import pygame
import random
import time
pygame.init()
y = 0
x = 0
point = 0
is_blue = True
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode([500, 500])
def food():
pygame.draw.circle(screen, WHITE, (random.randint(1, 400), random.randint(1, 400)), 5)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pygame.display.set_caption("Collect the balls to win!")
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 3
if pressed[pygame.K_DOWN]:
y += 3
if pressed[pygame.K_LEFT]:
x -= 3
if pressed[pygame.K_RIGHT]:
x += 3
if x <= -1:
x = 0
if x >= 441:
x = 440
if y <= -1:
y = 0
if y >= 441:
y = 440
screen.fill((0, 0, 0))
if is_blue:
color = (0, 128, 255)
else:
color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 30, 30))
food()
pygame.display.flip()
clock.tick(144)
You have to use a list. Create a list for the food positions:
food_list = []
Fill the list in a loop:
while len(food_list) < 10:
food_list.append((random.randint(1, 400), random.randint(1, 400)))
Draw the foods in the list in a loop:
for food_pos in food_list:
pygame.draw.circle(screen, WHITE, food_pos, 5)
You can also spawn the food with an time interval. Use pygame.time.get_ticks() to measure the time. Define a time interval after which a new object should appear. Create an object when the point in time is reached and calculate the point in time for the next object:
food_list = []
time_interval = 1000 # 1000 milliseconds == 1 seconds
next_food_time = pygame.time.get_ticks()
done = False
while not done:
# [...]
current_time = pygame.time.get_ticks()
if current_time > next_food_time and len(food_list) < 10:
next_food_time += time_interval
food_list.append((random.randint(1, 400), random.randint(1, 400)))
See also Spawning multiple instances of the same object concurrently in python
Complete example:
import pygame
import random
import time
pygame.init()
y = 0
x = 0
point = 0
is_blue = True
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode([500, 500])
food_list = []
time_interval = 1000 # 1000 milliseconds == 1 seconds
next_food_time = pygame.time.get_ticks()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pygame.display.set_caption("Collect the balls to win!")
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 3
if pressed[pygame.K_DOWN]:
y += 3
if pressed[pygame.K_LEFT]:
x -= 3
if pressed[pygame.K_RIGHT]:
x += 3
if x <= -1:
x = 0
if x >= 441:
x = 440
if y <= -1:
y = 0
if y >= 441:
y = 440
current_time = pygame.time.get_ticks()
if current_time > next_food_time and len(food_list) < 10:
next_food_time += time_interval
food_list.append((random.randint(1, 400), random.randint(1, 400)))
screen.fill((0, 0, 0))
if is_blue:
color = (0, 128, 255)
else:
color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 30, 30))
for food_pos in food_list:
pygame.draw.circle(screen, WHITE, food_pos, 5)
pygame.display.flip()
clock.tick(144)

Pygame surface suddenly inverting variables [duplicate]

This question already has an answer 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)
Closed 2 years ago.
I was trying to make a game where you chop down some trees and sell it, but in doing so I managed to find a mind-breaking bug - the first tree would invert all the other trees! I know it doesn't make sense, but what i mean is that I have sorted the trees into a class and I have created a variable instance called self.tree_property which determines if the tree has been cut down or not. When you chop down the first tree all the other tree_property get set to True again. When you chop down any other tree, the first tree's tree_property gets set to True again Can anyone tell me how to fix it, and why it is happening?
The code is below:
import pygame
import time
pygame.init()
print('Loading game...')
icon = pygame.image.load('C:\Program Files\Foraging Simulator\icon.png')
market = pygame.image.load('C:\Program Files\Foraging Simulator\market.png')
house = pygame.image.load('C:\Program Files\Foraging Simulator\house.png')
pygame.display.set_icon(icon)
market = pygame.transform.scale(market, (100, 100))
house = pygame.transform.scale(house, (100, 100))
root = pygame.display.set_mode((603, 573))
pygame.display.set_caption("Foraging Simulator")
window_is_open = True
white = (255, 255, 255)
black = (0, 0, 0)
width = 10
leaves_width = 30
height = 20
leaves_height = 10
x = 0
tree_trunk_x = 10
y = 0
tree_trunk_y = 10
vel = 5
brown = (150, 75, 0)
green = (58, 95, 11)
grass = (124, 252, 0)
score = 0
chop_wood = 'C:\Program Files\Foraging Simulator\chopping.mp3'
clock = pygame.time.Clock()
time = 0
happiness = 10
def test(string):
print(string)
def reset_trees():
for tree in trees:
tree.tree_property = True
def open_house():
reset_trees()
score = 0
def open_market():
happiness = score / 2
class Tree: # The class for the trees
def __init__(self, tree_x, tree_y):
self.tree_x = tree_x
self.tree_y = tree_y
self.tree_property = True # Creating instance tree_property
self.trunk = None
self.leaves = None
def destroy(self):
self.tree_property = False
def create_tree(self):
if self.tree_property:
trunk_x = self.tree_x + 10
trunk_y = self.tree_y + 10
self.trunk = pygame.draw.rect(root, brown, (trunk_x, trunk_y, width, height))
self.leaves = pygame.draw.rect(root, green, (self.tree_x, self.tree_y, leaves_width, leaves_height))
def redraw(self):
self.create_tree()
trees = []
for x in range(5):
for y in range (5):
trees.append(Tree(x*50, y*50))
root.fill(grass)
destroy_tree = None
countdown = 3
clock.tick(60)
say = True
print('Loading and attributes finsihed! Using mainloop...')
while window_is_open:
if say:
print('Mainloop loaded! Ready to go.')
say = False
time = pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
if market.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
mouse_x, mouse_y = event.pos
if house.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees: # Clicking detection
mouse_x, mouse_y = pygame.mouse.get_pos()
if tree.trunk.collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
x += vel
if keys[pygame.K_LEFT]:
x -= vel
if keys[pygame.K_UP]:
y -= vel
if keys[pygame.K_DOWN]:
y += vel
if destroy_tree != None:
if countdown == 3:
pygame.time.delay(950)
countdown = countdown - 1
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
elif countdown > 0:
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
pygame.time.delay(950)
countdown = countdown - 1
else:
destroy_tree.destroy()
destroy_tree = None
countdown = 3
score = score + 1
font = pygame.font.SysFont('Tahoma', 18, True, False)
count = font.render(str(countdown), True, (0, 0, 0))
screen_score = font.render("Score: " + str(score), True, (0, 0, 0))
rendered_happiness = font.render("Happines: " + str(happiness), True, (0, 0, 0))
root.blit(rendered_happiness, (410, 40))
root.blit(count, (410, 0))
root.blit(screen_score, (410, 20))
rectangle = pygame.draw.rect(root, (0, 0, 0), (x, y, width, 10))
for tree in trees:
tree.redraw()
root.blit(market, (400, 300))
seconds = clock.tick()
pre = time + seconds / 1000
time = int(pre)
root.blit(house, (400, 100))
pygame.display.update()
root.fill(grass)
pygame.quit()
Thanks!
A pygame.Surface object has no location. The position of the rectangle which is returned by get_rect() is always (0, 0).
Note, you specify a location when the surface is blit:
root.blit(house, (400, 100))
You have to set the same location, when you retrieve the rectangle for the collision. You can pass keyword argument values to this function, which are set to the pygame.Rect object:
house.get_rect(topleft = (400, 100))
For instance:
while window_is_open:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
dx = mouse_x - x
dy = mouse_y - y
if market.get_rect(topleft = (400, 300)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
if house.get_rect(topleft = (400, 100)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees:
if tree.trunk.collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
# [...]
Furthermore in you have to use the global statement to treat happiness as a variable in global namespace in the function open_market:
def open_market():
global happiness
happiness = score / 2

Why is the box flashing colors in pygame?

so after the black box shrinks and then expands, I want to have these 2 colorful boxes expand out of the black background (sounds a bit confusing but you'll see what I mean). Is there a reason that these colorful boxes I'm trying to make are flashing instead of staying?
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" %(250, 10)
import pygame
import random
pygame.init()
SIZE = (650, 650)
screen = pygame.display.set_mode(SIZE)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
x = 650
y = 650
count = 0
secx = 1
secy = 1
firstsx = 325
firstsy = 325
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if x >= 1:
x -= 1
y -= 1
count += 1
randcolor = (random.randint(0,255)), (random.randint(0,255)),(random.randint(0,255))
pygame.draw.rect(screen, randcolor, (1,1, x, y))
pygame.draw.rect(screen, BLACK, (0,0, x, y))
pygame.display.flip()
pygame.time.wait(3)
if count > 649:
count += 1
secx = count-650
secy = count-650
pygame.draw.rect(screen, BLACK, (0,0, secx, secy))
pygame.display.flip()
pygame.time.wait(3)
if count > 1399:
count += 1
firstsx -= 1
firstsy -= 1
randcolor = (random.randint(0,255)), (random.randint(0,255)),(random.randint(0,255))
pygame.draw.rect(screen, randcolor, (325,0, firstsx, firstsy))
pygame.draw.rect(screen, randcolor, (0,325, firstsx, firstsy))
pygame.display.flip()
pygame.time.wait(3)
What you actually do is to update the display and wait after drawing the 1st box. At this point the 2nd box is not drawn at all.
Just do 1 pygame.display.flip() and pygame.time.wait(3) at the end of the application loop.
Furthermore you have to define the random colors before the main application loop. The rapidly changing colors makes the box flashing:
randcolor = (random.randint(0,255)), (random.randint(0,255)),(random.randint(0,255))
randcolor1 = (random.randint(0,255)), (random.randint(0,255)),(random.randint(0,255))
randcolor2 = (random.randint(0,255)), (random.randint(0,255)),(random.randint(0,255))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if x >= 1:
x -= 1
y -= 1
count += 1
pygame.draw.rect(screen, randcolor, (1,1, x, y))
pygame.draw.rect(screen, BLACK, (0,0, x, y))
if count > 649:
count += 1
secx = count-650
secy = count-650
pygame.draw.rect(screen, BLACK, (0,0, secx, secy))
if count > 1399:
count += 1
firstsx -= 1
firstsy -= 1
pygame.draw.rect(screen, randcolor1, (325,0, firstsx, firstsy))
pygame.draw.rect(screen, randcolor2, (0,325, firstsx, firstsy))
if firstsx == 0:
x, y = 650, 650
count = 0
firstsx, firstsy = 325, 325
pygame.display.flip()
pygame.time.wait(3)

Trying to Make Slime Bombs and clear screen with spacebar

import pygame
pygame.init()
screen = pygame.display.set_mode((500,500))
clock = pygame.time.Clock()
isRunning = True
r = 255
g = 255
b = 255
color = (r, g, b)
gridDraw = 0
mouse_pressed = []
def grid():
x = 50
y = 50
for a in range(10):
for c in range(10):
pygame.draw.circle(screen, color, (x, y), 10)
x += 45
y += 45
x = 50
def slime(mPos):
x = 50
y = 50
green = (0,255,0)
for a in range(10):
for c in range(10):
if (mPos[0] <= x+10 and mPos[0] >= x-10) and\
(mPos[1] <= y+10 and mPos[1] >= y-10):
pygame.draw.circle(screen, green, (x, y), 10)
if x + 45 <= 455:
pygame.draw.circle(screen, green, (x+45, y), 10)
if x - 45 >= 45:
pygame.draw.circle(screen, green, (x-45, y), 10)
if y + 45 <= 455:
pygame.draw.circle(screen, green, (x, y+45), 10)
if y - 45 >= 45:
pygame.draw.circle(screen, green, (x, y-45), 10)
x += 45
y += 45
x = 50
def bomb(mouse):
for a in mouse:
slime(a)
pygame.draw.circle(screen, (0,200,150), a, 8)
while isRunning:
event = pygame.event.get()
for e in event:
if e.type == pygame.QUIT:
isRunning = False
elif e.type == pygame.KEYDOWN:
if e.type == pygame.K_ESCAPE:
isRunning = False
if e.type == pygame.MOUSEBUTTONDOWN:
mpos = pygame.mouse.get_pos()
mouse_pressed.append(mpos)
gridDraw = 0
if gridDraw == 0:
grid()
gridDraw += 1
bomb(mouse_pressed)
pygame.display.update()
pygame.quit()
So I have most of the first part of the assignment done except I can still press in the black spaces and I cant figure out how to not, I thought I had gotten the constraints correct but apparently not. Im thinking about making right click the medium or large bomb, would this be ideal?
This is my lab for class, I finally was able to complete the placing small bombs but I havent put in screen wipe after I press and I can still press in the black spaces.
First of all you have to draw the circles continuously in the application loop rather than the event loop.
Get the current position of the muse by pygame.mouse.get_pos and the state of the mouse buttons by pygame.mouse.get_pressed:
mpos = pygame.mouse.get_pos()
mpressed = pygame.mouse.get_pressed()
Evaluate if the left mouse button is pressed (mpressed[0]) and the distance of the mouse to the center of the circle is less than the radius of the circle (pygame.math.Vector2.distance_to):
if mpressed[0] and pygame.math.Vector2(x, y).distance_to(mpos) < 10:
# [...]
See the full example:
import pygame
screen = pygame.display.set_mode((500,500))
clock = pygame.time.Clock()
isRunning = True
x, y = 50, 50
r, g, b = 255, 255, 255
while isRunning:
#Time
deltaTime = clock.tick() / 1000
event = pygame.event.get()
#USER Events
for e in event:
if e.type == pygame.QUIT:
isRunning = False
mpos = pygame.mouse.get_pos()
mpressed = pygame.mouse.get_pressed()
#Draws a grid
for a in range(50):
x = a * 50
for c in range(50):
y = c * 50
color = (r, g, b)
if mpressed[0] and pygame.math.Vector2(x, y).distance_to(mpos) < 10:
color = (0, g, 0)
pygame.draw.circle(screen, color, (x, y), 10)
pygame.display.update()
pygame.quit()
I used the previous answer for the mouse position and added a board_state so that the Circle stays in your desired color.
import pygame
import numpy as np
import time
def set_color(board_state):
if not board_state:
return 255, 255, 255
else:
return 255, 0, 0
screen = pygame.display.set_mode((500,500))
clock = pygame.time.Clock()
isRunning = True
board_state = np.zeros((50, 50), dtype=np.bool)
pressed = True
while isRunning:
deltaTime = clock.tick() / 1000
event = pygame.event.get()
for e in event:
if e.type == pygame.QUIT:
isRunning = False
mpos = pygame.mouse.get_pos()
mpressed = pygame.mouse.get_pressed()
for a in range(board_state.shape[0]):
x = a * 50
for c in range(board_state.shape[1]):
y = c * 50
if mpressed[0] and pygame.math.Vector2(x, y).distance_to(mpos) < 10:
board_state[a, c] = not board_state[a, c]
print("Kick", a, c, board_state[a, c])
pressed = True
pygame.draw.circle(screen, set_color(board_state[a, c]), (x, y), 10)
if pressed:
pygame.display.update()
time.sleep(0.2)
pressed = False
pygame.quit()
You can change the color in the set_color function and I added a time.sleep which needs to be adjusted to the klicking length, because I had some problem with detecting a single klick with the trackpad.

Pygame display not working under If condition

I am trying to make my code display a text in the middle of the screen once a square goes past right by 500 pixels, but it does seem to be displaying with my If condition. I dont know what I am doing wrong.
import pygame
pygame.init()
displaywidth=500
displayheight=500
gameDisplay = pygame.display.set_mode((displaywidth, displayheight))
pygame.display.set_caption("First Game")
red = (0,255,0)
font=pygame.font.Font(None, 20)
#Class
def Message(msg, color):
screen_text=font.render(msg,True,color)
gameDisplay.blit(screen_text,[displaywidth/2,displayheight/2])
win = pygame.display.set_mode((displaywidth, displayheight))
x = 50
y = 50
width = 40
height = 40
vel = 5
x1 = 0
y1 = 0
width1 = 40
height1 = 40
vel2 = 100
vel3=100
x2 = 100
y2 = 100
pygame.draw.rect(win, (0, 255, 0), (x, y, width, height))
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= vel2
elif keys[pygame.K_RIGHT]:
x += vel2
elif keys[pygame.K_UP]:
y1-=vel3
elif keys[pygame.K_DOWN]:
y1+=vel3
if x > 500:
Message("meow", red)
pygame.display.update()
print("pew")
elif x < 0:
x = 50
elif y1 > 500:
y1 = 450
elif y1 < 0:
y1 = 50
print(x)
print(y)
win.fill((0, 0, 0))
meow = pygame.draw.rect(win, (0, 255, 0), (x, y1, width, height))
pygame.draw.rect(win, (160, 0, 0), (x1, y1, width1, height1))
pygame.display.update()
pygame.quit()
My print command appears to be working but I dont know why its not displaying.
The issue is that you've 2 calls to pygame.display.update() in your code, but the display is cleared immediately after the first one:
if x > 500:
Message("meow", red)
pygame.display.update() # <----
print("pew")
# [...]
win.fill((0, 0, 0)) # <----
Clear the display before anything is drawn and do a single pygame.display.update() at the end of the main application loop:
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= vel2
elif keys[pygame.K_RIGHT]:
x += vel2
elif keys[pygame.K_UP]:
y1-=vel3
elif keys[pygame.K_DOWN]:
y1+=vel3
if x < 0:
x = 50
elif y1 > 500:
y1 = 450
elif y1 < 0:
y1 = 50
# clear dispaly
win.fill((0, 0, 0))
# draw scene
if x > 500:
Message("meow", red)
print("pew")
meow = pygame.draw.rect(win, (0, 255, 0), (x, y1, width, height))
pygame.draw.rect(win, (160, 0, 0), (x1, y1, width1, height1))
# update display
pygame.display.update()

Categories

Resources