Related
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((476,800))
clock = pygame.time.Clock()
win = pygame.display.set_mode((500, 600))
pygame.display.set_caption("DOGGO GAME")
dogimg = pygame.image.load('dog.png')
*doglist will contain the image rects so that multiple images/entities will fill the screen
doglist = []
dogx = []
dogy = []
blot triggers the creation of element(dog image rect = dogimg)
BLOT = pygame.USEREVENT
pygame.time.set_timer(BLOT, 500)
drawdog blits the images in the doglist
def drawdog(doglist):
for dog in doglist:
screen.blit(dogimg, new_dog)
run = True
while run:
#pygame.time.delay(50)
win.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == BLOT:
dogimg = pygame.image.load('dog.png')
dogranx = random.randint(50,200)
dograny = random.randint(50,450)
new_dog = dogimg.get_rect(midtop=(dogranx, dograny))
doglist.append(new_dog)
print(doglist)
drawdog(doglist)
pygame.display.update()
clock.tick(1)
pygame.quit()
I was able to correct it by replacing:
for dog in doglist
with
for new_dog in doglist
In the former, I was using extend. I changed it to append and used the latter code, and it worked.
I am currently developing a game using Python and PyGame. I have made image sprites for the main option buttons, but I cannot seem to figure out how to make the images clickable which will take me to a different screen. Source code:
import os
import sys
import pygame
import time
class Colors:
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
dark_red = (102, 0, 0)
grey = (128, 128, 128)
dark_grey = (51, 51, 51)
class Variables:
screen_w = 1000
screen_h = 600
battleButton = pygame.image.load("Images/battle_button.png")
shopButton = pygame.image.load("Images/shop_button.png")
saveButton = pygame.image.load("Images/save_button.png")
equippedItem = pygame.image.load("Images/equipped.png")
creditsButton = pygame.image.load("Images/credits_button.png")
baseballBat = pygame.image.load("Images/baseball_bat.png")
baseballBat = pygame.transform.scale(baseballBat, (250, 250))
playerHealthMax = 100
playerHealthMin = 0
playerHealth = playerHealthMax
playerCash = 0
pygame.font.init()
gameFont = pygame.font.SysFont("Tweaky", 70)
title = gameFont.render("Causatum", 1, (Colors.black))
mainFont = pygame.font.SysFont("Arial Rounded MT Bold", 50)
equippedWeap = mainFont.render("Equipped", 1, (Colors.black))
cash = mainFont.render("Cash: ${}".format(playerCash), 1,
(Colors.black))
health = mainFont.render("Health: {}".format(playerHealth), 1,
(Colors.black))
gameVersion = mainFont.render("v1.0.0", 1, (Colors.black))
screen = pygame.display.set_mode((Variables.screen_w, Variables.screen_h))
pygame.display.set_caption("Causatum")
screen.fill(Colors.black)
pygame.display.flip()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(Colors.grey)
screen.blit(Variables.battleButton, (0, 170))
screen.blit(Variables.shopButton, (0, 240))
screen.blit(Variables.saveButton, (0, 310))
screen.blit(Variables.title, (330, 0))
screen.blit(Variables.equippedItem, (645, 250))
screen.blit(Variables.baseballBat, (735, 230))
screen.blit(Variables.equippedWeap, (723, 215))
screen.blit(Variables.creditsButton, (0, 380))
screen.blit(Variables.cash, (0, 50))
screen.blit(Variables.health, (0, 0))
screen.blit(Variables.gameVersion, (905, 0))
pygame.display.flip()
Any kind of help is appreciated. Thanks.
Try something like this:
#in 'Variables' add this line
battleButton_rect = battleButton.get_rect(centerx = 0, centery = 170)
#new function, outside any classes
def collide(mouseX, mouseY, rect): #new function for checking the collision of the mouse with a button
return (mouseX >= rect.x-rect.width/2 and mouseX <= rect.x+rect.width/2 and mouseY >= rect.y-rect.height/2 and mouseY <= rect.y+rect.height/2)
# in 'while running'
if pygame.mouse.get_pressed()[0]: #0 for left button, 1 for right, 2 for middle
mouse_pos = pygame.mouse.get_pos()
if collide(mouse_pos[0], mouse_pos[1], Variables.battleButton_rect):
#code for battle button clicked here
#replace
screen.blit(Variables.battleButton, (0, 170))
#with
screen.blit(Variables.battleButton, Variables.battleButton_rect)
This, of course, is only for the battle button. You need to add a Rect for every button you use. Also, I would suggest making a button class with the collision function inside and having an array of those buttons that you can iterate through without convoluting your code. But the above is a quick and dirty solution.
Hope this helps!
I have made a list of bullets and a list of sprites using the classes below. How do I detect if a bullet collides with a sprite and then delete that sprite and the bullet?
#Define the sprite class
class Sprite:
def __init__(self,x,y, name):
self.x=x
self.y=y
self.image = pygame.image.load(name)
self.rect = self.image.get_rect()
def render(self):
window.blit(self.image, (self.x,self.y))
# Define the bullet class to create bullets
class Bullet:
def __init__(self,x,y):
self.x = x + 23
self.y = y
self.bullet = pygame.image.load("user_bullet.BMP")
self.rect = self.bullet.get_rect()
def render(self):
window.blit(self.bullet, (self.x, self.y))
In PyGame, collision detection is done using pygame.Rect objects. The Rect object offers various methods for detecting collisions between objects. Even the collision between a rectangular and circular object such as a paddle and a ball can be detected by a collision between two rectangular objects, the paddle and the bounding rectangle of the ball.
Some examples:
pygame.Rect.collidepoint:
Test if a point is inside a rectangle
repl.it/#Rabbid76/PyGame-collidepoint
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(100, 100)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
point = pygame.mouse.get_pos()
collide = rect.collidepoint(point)
color = (255, 0, 0) if collide else (255, 255, 255)
window.fill(0)
pygame.draw.rect(window, color, rect)
pygame.display.flip()
pygame.quit()
exit()
pygame.Rect.colliderect
Test if two rectangles overlap
See also How to detect collisions between two rectangular objects or images in pygame
repl.it/#Rabbid76/PyGame-colliderect
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
rect1 = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
rect2 = pygame.Rect(0, 0, 75, 75)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
rect2.center = pygame.mouse.get_pos()
collide = rect1.colliderect(rect2)
color = (255, 0, 0) if collide else (255, 255, 255)
window.fill(0)
pygame.draw.rect(window, color, rect1)
pygame.draw.rect(window, (0, 255, 0), rect2, 6, 1)
pygame.display.flip()
pygame.quit()
exit()
Furthermore, pygame.Rect.collidelist and pygame.Rect.collidelistall can be used for the collision test between a rectangle and a list of rectangles. pygame.Rect.collidedict and pygame.Rect.collidedictall can be used for the collision test between a rectangle and a dictionary of rectangles.
The collision of pygame.sprite.Sprite and pygame.sprite.Group objects, can be detected by pygame.sprite.spritecollide(), pygame.sprite.groupcollide() or pygame.sprite.spritecollideany(). When using these methods, the collision detection algorithm can be specified by the collided argument:
The collided argument is a callback function used to calculate if two sprites are colliding.
Possible collided callables are collide_rect, collide_rect_ratio, collide_circle, collide_circle_ratio, collide_mask
Some examples:
pygame.sprite.spritecollide()
repl.it/#Rabbid76/PyGame-spritecollide
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
sprite1 = pygame.sprite.Sprite()
sprite1.image = pygame.Surface((75, 75))
sprite1.image.fill((255, 0, 0))
sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
sprite2 = pygame.sprite.Sprite()
sprite2.image = pygame.Surface((75, 75))
sprite2.image.fill((0, 255, 0))
sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
all_group = pygame.sprite.Group([sprite2, sprite1])
test_group = pygame.sprite.Group(sprite2)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sprite1.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.spritecollide(sprite1, test_group, False)
window.fill(0)
all_group.draw(window)
for s in collide:
pygame.draw.rect(window, (255, 255, 255), s.rect, 5, 1)
pygame.display.flip()
pygame.quit()
exit()
For a collision with masks, see How can I make a collision mask? or Pygame mask collision
See also Collision and Intersection
pygame.sprite.spritecollide() / collide_circle
repl.it/#Rabbid76/PyGame-spritecollidecollidecircle
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
sprite1 = pygame.sprite.Sprite()
sprite1.image = pygame.Surface((80, 80), pygame.SRCALPHA)
pygame.draw.circle(sprite1.image, (255, 0, 0), (40, 40), 40)
sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80)
sprite1.radius = 40
sprite2 = pygame.sprite.Sprite()
sprite2.image = pygame.Surface((80, 89), pygame.SRCALPHA)
pygame.draw.circle(sprite2.image, (0, 255, 0), (40, 40), 40)
sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80)
sprite2.radius = 40
all_group = pygame.sprite.Group([sprite2, sprite1])
test_group = pygame.sprite.Group(sprite2)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sprite1.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.spritecollide(sprite1, test_group, False, pygame.sprite.collide_circle)
window.fill(0)
all_group.draw(window)
for s in collide:
pygame.draw.circle(window, (255, 255, 255), s.rect.center, s.rect.width // 2, 5)
pygame.display.flip()
pygame.quit()
exit()
What does this all mean for your code?
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. The position of the rectangle can be specified by a keyword argument. For example, the centre of the rectangle can be specified with the keyword argument center. These keyword arguments are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a list of the keyword arguments).
See *Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
You do not need the x and y attributes of Sprite and Bullet at all. Use the position of the rect attribute instead:
#Define the sprite class
class Sprite:
def __init__(self, x, y, name):
self.image = pygame.image.load(name)
self.rect = self.image.get_rect(topleft = (x, y))
def render(self):
window.blit(self.image, self.rect)
# Define the bullet class to create bullets
class Bullet:
def __init__(self, x, y):
self.bullet = pygame.image.load("user_bullet.BMP")
self.rect = self.bullet.get_rect(topleft = (x + 23, y))
def render(self):
window.blit(self.bullet, self.rect)
Use pygame.Rect.colliderect() to detect collisions between instances of Sprite and Bullet.
See How to detect collisions between two rectangular objects or images in pygame:
my_sprite = Sprite(sx, sy, name)
my_bullet = Bullet(by, by)
while True:
# [...]
if my_sprite.rect.colliderect(my_bullet.rect):
printe("hit")
From what I understand of pygame you just need to check if the two rectangles overlap using the colliderect method. One way to do it is to have a method in your Bullet class that checks for collisions:
def is_collided_with(self, sprite):
return self.rect.colliderect(sprite.rect)
Then you can call it like:
sprite = Sprite(10, 10, 'my_sprite')
bullet = Bullet(20, 10)
if bullet.is_collided_with(sprite):
print('collision!')
bullet.kill()
sprite.kill()
There is a very simple method for what you are trying to do using built in methods.
here is an example.
import pygame
import sys
class Sprite(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20, 20])
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.center = pos
def main():
pygame.init()
clock = pygame.time.Clock()
fps = 50
bg = [255, 255, 255]
size =[200, 200]
screen = pygame.display.set_mode(size)
player = Sprite([40, 50])
player.move = [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN]
player.vx = 5
player.vy = 5
wall = Sprite([100, 60])
wall_group = pygame.sprite.Group()
wall_group.add(wall)
player_group = pygame.sprite.Group()
player_group.add(player)
# I added loop for a better exit from the game
loop = 1
while loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = 0
key = pygame.key.get_pressed()
for i in range(2):
if key[player.move[i]]:
player.rect.x += player.vx * [-1, 1][i]
for i in range(2):
if key[player.move[2:4][i]]:
player.rect.y += player.vy * [-1, 1][i]
screen.fill(bg)
# first parameter takes a single sprite
# second parameter takes sprite groups
# third parameter is a do kill command if true
# all group objects colliding with the first parameter object will be
# destroyed. The first parameter could be bullets and the second one
# targets although the bullet is not destroyed but can be done with
# simple trick bellow
hit = pygame.sprite.spritecollide(player, wall_group, True)
if hit:
# if collision is detected call a function in your case destroy
# bullet
player.image.fill((255, 255, 255))
player_group.draw(screen)
wall_group.draw(screen)
pygame.display.update()
clock.tick(fps)
pygame.quit()
# sys.exit
if __name__ == '__main__':
main()
Make a group for the bullets, and then add the bullets to the group.
What I would do is this:
In the class for the player:
def collideWithBullet(self):
if pygame.sprite.spritecollideany(self, 'groupName'):
print("CollideWithBullet!!")
return True
And in the main loop somewhere:
def run(self):
if self.player.collideWithBullet():
print("Game Over")
Hopefully that works for you!!!
Inside the Sprite class, try adding a self.mask attribute with
self.mask = pygame.mask.from_surface(self.image)
and a collide_mask function inside of the Sprite class with this code:
def collide_mask(self, mask):
collided = False
mask_outline = mask.outline()
self.mask_outline = self.mask.outline()
for point in range(len(mask_outline)):
mask_outline[point] = list(mask_outline[point])
mask_outline[point][0] += bullet.x
mask_outline[point][1] += bullet.y
for point in range(len(self.mask_outline)):
self.mask_outline[point] = list(mask_outline[point])
self.mask_outline[point][0] += self.x
self.mask_outline[point][1] += self.y
for point in mask_outline:
for self_mask_point in self.mask_outline:
if point = self_mask_point:
collided = True
return collided
From the documentation:
"Assigning to size, width or height changes the dimensions of the rectangle".
I have the following circle, assign a new attribute but no changes can be seen in circle. What am i doing wrong?
import pygame
from pygame.locals import *
pygame.init()
TV=pygame.display.set_mode((400,400))
pygame.display.set_caption("Rect")
c=pygame.draw.circle(TV,(0,100,0),(150,150),100,1)
pygame.draw.rect(TV,(100,0,0),c,1)
print c.size
c.size=(100,100)
print c.size
pygame.display.flip()
while True:
for e in pygame.event.get():
if e.type==QUIT:
pygame.quit()
You draw the circle on the screen surface. Every drawing function then returns a Rect, but:
The functions return a rectangle representing the bounding area of changed pixels.
To actually change the circle on the screen, you have to erase it first (draw something above it), then draw a new circle in the size you want.
Here's a simple example:
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption("Rect")
TV = pygame.display.set_mode((400,400))
c = pygame.draw.circle(TV, (0, 100, 0), TV.get_rect().center, 100, 1)
clock = pygame.time.Clock()
pygame.time.set_timer(USEREVENT, 250)
value = 10
run = True
while run:
for e in pygame.event.get():
if e.type == QUIT:
run = False
if e.type == USEREVENT:
c.inflate_ip(value, value)
if c.width > 260 or c.width < 200:
value *= -1
TV.fill(pygame.color.Color('Black'))
pygame.draw.circle(TV, (0, 100, 0), c.center, c.width/2, 1)
pygame.display.flip()
clock.tick(60)
pygame.quit()
Ive been trying to make a recursive rectangles and I wanted to make the rectangles move in the forward direction like each time it recursed, so that it gives a motion as one is going into an endless rectangles. Ive tried making the size bigger each time it recursed but failed as it wont recurse or nothing would show up. Any tips or how to do this would be appreciated. This sample I implemented from pygamearcade. I want to get the feeling as one is going into the rectangles and that can be implemented as the rectangles get bigger each time it goes through recurion. So any tips or how to do it is fine. Thank you
import pygame
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
def recursive_draw(x, y, width, height):
""" Recursive rectangle function. """
pygame.draw.rect(screen, WHITE,
[x, y, width, height],
1)
speed = [10,0]
# Is the rectangle wide enough to draw again?
while (width > 14):
# Scale down
x += width * .1
y += height * .1
width *= .8
height *= .8
# Recursively draw again
recursive_draw(x, y, width, height)
pygame.init()
#rectanglelist = [big()]
# Set the height and width of the screen
size = [700, 500]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Set the screen background
screen.fill(BLACK)
# ALL CODE TO DRAW SHOULD GO BELOW THIS COMMENT
recursive_draw(0, 0, 700, 500)
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
# Be IDLE friendly. If you forget this line, the program will 'hang'
# on exit.
pygame.quit()
The problem is that your recursive_draw() function is not really a recursive function, because a recursive function is a function that conditionally calls itself:
Every properly designed recursive function must have at least one base case [A] and must redefine the problem into sub problems that work towards a base case [B].
def countdown(n):
if n < 1:
print "Lift Off" #[A]
else:
print n
countdown(n - 1) #[B]
What you could do for your code:
First fix the indent of your code.
Second, replace your while loop by an ´if´ statement.
This is the important part or recursion: The function calls itself unless a certain condition is not true anymore (in your case width > 14). For more information see How can I build a recursive function in python? or Recursion function in python on SO.
The updated function (code from http://www.balloonbuilding.com/ by Paul Vincent Craven):
def recursive_draw(x, y, width, height):
""" Recursive rectangle function. """
pygame.draw.rect(screen, BLACK, (x, y, width, height), 1)
# Is the rectangle wide enough to draw again?
if(width > 14):
# Scale down
x += width * .1
y += height * .1
width *= .8
height *= .8
# Recursively draw again
recursive_draw(x, y, width, height)
I hope this helps :)
EDIT:
The updated program:
import pygame
# Colors
BLUE = (55, 155, 255)
WHITE = (255, 255, 255)
def recursive_draw(x, y, width, height):
""" Recursive rectangle function. """
pygame.draw.rect(screen, WHITE, (x, y, width, height), 2)
# Is the rectangle wide enough to draw again?
if(width > 14):
# Scale down
x += width * .1
y += height * .1
width *= .8
height *= .8
# Recursively draw again
recursive_draw(x, y, width, height)
speed = [10,0]
pygame.init()
#rectanglelist = [big()]
# Set the height and width of the screen
size = [700, 500]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Set the screen background
screen.fill(BLUE)
# ALL CODE TO DRAW SHOULD GO BELOW THIS COMMENT
recursive_draw(0, 0, 700, 500)
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
# Be IDLE friendly. If you forget this line, the program will 'hang'
# on exit.
pygame.quit()
Screenshot: