Pygame, Collision between 2 objects in the same group - python

So, i am trying to create a game where aliens spawn from 3 specific places. Each Alien will spawn randomly in one of the 3. But there will always be at least one alien, that will spawn on top of another one. I want to delete that alien and spawn him randomly in another spawn point. If it is empty he will stay if not the process will be repeated. The thing is that i cannot find a way to detect collision of 2 objects that are in the same group.
I just started learning pygame so 1) My question may be stupid 2) My way of spawning probably is very inefficient
Here is the Alien class:
class Alien(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((80,60))
self.image.fill(GREY)
self.rect = self.image.get_rect()
spawn_point1 = x1,y1 = -30, 70
spawn_point2 = x2,y2 = -30, 150
spawn_point3 = x3,y3 = -30, 230
random_spawn = random.choice([spawn_point1,spawn_point2,spawn_point3])
self.rect.center = random_spawn
self.speedx = 10
def update(self):
spawn_point1 = x1,y1 = -30, 70
spawn_point2 = x2,y2 = -30, 150
spawn_point3 = x3,y3 = -30, 230
self.speedx = 10
random_spawn = random.choice([spawn_point1,spawn_point2,spawn_point3])
self.rect.x += self.speedx
if self.rect.x > WIDTH + 20:
self.rect.center = random_spawn
And here is the part where i detect collision(This part doesnt work)
aliens_col = pygame.sprite.groupcollide(aliens, aliens, True, False)
for i in aliens_col:
alien = Alien()
aliens.add(alien)
all_sprites.add(aliens)

Here is an implementation of the Bounding Box test.
import random
class Rectangle:
def __init__(self, height, width, x, y):
self.height = height
self.width = width
self.x = x
self.y = y
def collided_with_another_rectangle(self, rect):
""" Assumes rectangles are same size or that this rectangle is smaller than the other rectangle"""
if self.x > (rect.x + rect.width):
# Is to the right of the other rectangle
return False
elif (self.x + self.width) < rect.x:
# is to the left of the other rectangle
return False
elif (self.y + self.height) < rect.y:
# is above the other rectangle
return False
elif self.y > (rect.y + rect.height):
# is below the other rectangle
return False
else:
return True
collision_count = 0
for i in range(0, 1000):
# Here I pick random locations on a 1000X1000 screen for the first rectangle
x1 = random.randint(0, 1000)
y1 = random.randint(0, 1000)
# Here I pick random locations on a 1000X1000 screen for the second rectangle
rect1 = Rectangle(100, 100, x1, y1)
x2 = random.randint(0, 1000)
y2 = random.randint(0, 1000)
rect2 = Rectangle(100, 100, x2, y2)
"""
I use the collided with another rectangle function to test if the first rectangle is above,below,
to the right or to the left of the other rectangle. If neither of these are true then the rectangles
have collided.
"""
if rect1.collided_with_another_rectangle(rect2):
collision_count += 1
print("Rect1 X and Y:" + str(x1) + " " + str(y1))
print("Rect2 X and Y:" + str(x2) + " " + str(y2))
print("collided")
print("Collision Count:" + str(collision_count))

I'm still not absolutely sure what you want to achieve, but I think this example will be helpful to you.
When a sprite leaves the screen, I call the reset_pos method in which I iterate over the three spawn points to set the position to one spawn after the other and then I use another for loop to iterate over the sprites to check if one collides.
If a sprite collides, I continue with the next spawn point.
If no sprite collides, I just return from the method.
If no spawn is free, I remove the sprite (but you can do something else).
import random
import pygame
from pygame.math import Vector2
pygame.init()
WIDTH, HEIGHT = 640, 480
class Alien(pygame.sprite.Sprite):
def __init__(self, aliens):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((80, 60))
self.image.fill((120, random.randrange(255), random.randrange(255)))
self.rect = self.image.get_rect()
self.spawn_points = [(-30, 70), (-30, 150), (-30, 230)]
self.aliens = aliens
self.reset_pos()
self.speedx = 10
def update(self):
self.rect.x += self.speedx
if self.rect.x > WIDTH + 20:
self.reset_pos()
def reset_pos(self):
random.shuffle(self.spawn_points) # Shuffle the spawns.
for spawn in self.spawn_points:
# Set the position to one of the spawns.
self.rect.center = spawn
# Check if this sprite collides with another one.
for sprite in self.aliens:
if sprite is self: # Skip self.
continue
if self.rect.colliderect(sprite.rect):
break # Break out of the loop if the spawn is occupied.
else: # The else means no 'break' occurred in the for loop above,
# so the spawn must be free.
return # Break out of the method if the spawn is free.
# I just remove the sprite if no spawn is free. You can do something else here.
self.kill()
def main():
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
aliens = pygame.sprite.Group()
for _ in range(3):
# I pass the aliens group to the sprite because we need to
# iterate over it to see if a sprite collides.
alien = Alien(aliens)
aliens.add(alien)
all_sprites = pygame.sprite.Group(aliens)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
al = Alien(aliens)
all_sprites.add(al)
aliens.add(al)
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pygame.quit()

When using the same group in both of the group-paramaters of groupcollide it will always consider the sprite it is checking in group_a as colliding with that same sprite in group_b. This results in groupcollide always returning a collision.
To get around this I created a new function in pygame's sprite.py that ignores single collisions and only returns collisions >= 2. My only change was to add:
if len(collision) >=2:
And then the required tab for the following line(s).
The code I added to sprite.py is pasted below but the tab for the def intra_groupcollide is one too far:
def intra_groupcollide(groupa, groupb, dokilla, dokillb, collided=None):
"""detect collision between a group and itself.
This is modified from groupcollide but excludes collisions <=1
pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb):
return dict
"""
crashed = {}
# pull the collision function in as a local variable outside
# the loop as this makes the loop run faster
sprite_collide_func = spritecollide
if dokilla:
for group_a_sprite in groupa.sprites():
collision = sprite_collide_func(group_a_sprite, groupb,
dokillb, collided)
if collision:
if len(collision) >=2:
crashed[group_a_sprite] = collision
group_a_sprite.kill()
else:
for group_a_sprite in groupa:
collision = sprite_collide_func(group_a_sprite, groupb,
dokillb, collided)
if collision:
if len(collision) >=2:
crashed[group_a_sprite] = collision
#print(crashed)
return crashed
Then in my own python program, I simply replaced groupcollide with intra_groupcollide. I set both kill paramaters as 'false' because in my usage I'm bouncing them off each other. I have not tested this code with them set to 'true'.
I found sprite.py in my file system by following this answer:
Where are the python modules stored?

Related

Sprite in not updated

This code is supposed to animate a sprite on a background, but it is just showing the sprite without any movement. I spend a day trying to figure out the issue, traced the code (I am novice so I might overlooked something), compared the code with the original author code line by line with no result, btw the original code runs smoothly meaning that it is not a problem in my PC.
Could you help me please
import os
import random
import math
import pygame
from os import listdir
from os.path import isfile, join
pygame.init()
pygame.display.set_caption("Platformer") #set the caption at the top of the window
BG_COLOR = (255,255,255) #White background, dont need it anymore
WIDTH, HEIGHT = 1000, 640 #screen dimensions
FPS = 60
PLAYER_VEL = 5 # the speed of the player
window = pygame.display.set_mode((WIDTH, HEIGHT)) #set the window with sizes
def flip(sprites):
return [pygame.transform.flip(sprite, True, False) for sprite in sprites]
def load_sprite_sheets(dir1, dir2, width, height, direction=False):
path = join("assets", dir1, dir2)
images = [f for f in listdir(path) if isfile(join(path, f))] #if f is a file put in the images list
all_sprites = {}
for image in images:
sprite_sheet = pygame.image.load(join(path, image)).convert_alpha() #loaded transparent bg image
sprites = []
for i in range(sprite_sheet.get_width() // width):
surface = pygame.Surface((width, height), pygame.SRCALPHA, 32)
rect = pygame.Rect(i * width, 0, width, height)
surface.blit(sprite_sheet, (0, 0), rect)
sprites.append(pygame.transform.scale2x(surface))
if direction:
all_sprites[image.replace(".png", "") + "_right"] = sprites
all_sprites[image.replace(".png", "") + "_left"] = flip(sprites)
else:
all_sprites[image.replace(".png", "")] = sprites
return all_sprites
class Player(pygame.sprite.Sprite): #sprite is useful for perfect pixel collision
COLOR = (0,0,255)
GRAVITY = 1
SPRITES = load_sprite_sheets("MainCharacters" , "MaskDude", 32 , 32 , True)
ANIMATION_DELAY = 3
def __init__(self, x,y, width , height):
self.rect = pygame.Rect(x,y,width,height)
self.x_vel = 0
self.y_vel = 0
self.mask = None
self.direction = "left" # to record which animation to show
self.animation_count = 0 #
self.fall_count = 0
def move(self,dx,dy):
self.rect.x += dx
self.rect.y += dy #here we draw only the motion calculated in the next functions
def move_left(self, vel):
self.x_vel = -vel
if self.direction != "left":
self.direction = "left"
self.animation_count = 0
def move_right(self, vel):
self.x_vel = vel
if self.direction != "right":
self.direction = "right"
self.animation_count = 0
def loop(self , fps):
# self.y_vel += min(1 , (self.fall_count/fps) * self.GRAVITY)
self.move(self.x_vel,self.y_vel)
self.fall_count += 1
self.update_sprite()
def update_sprite(self): #is about changing the sprite shape to look walking
sprite_sheet = "idle"
if self.x_vel !=0:
sprite_sheet = "run"
sprite_sheet_name = sprite_sheet + "_" + self.direction
sprites = self.SPRITES[sprite_sheet_name]
sprite_index = (self.animation_count //
self.ANIMATION_DELAY) % len(sprites) #set new index every ANIMATION_DELAY = 5
self.sprite = sprites[sprite_index]
self.animation_count += 1
def draw(self, win):
#print(self.SPRITES)
self.sprite = self.SPRITES["idle_"+self.direction][0]
win.blit(self.sprite , (self.rect.x , self.rect.y))
def get_background(name): #name is bg image, this create the bg image position list
image = pygame.image.load(join("assets", "Background", name))
_, _, width, height = image.get_rect()
tiles = [] #list of tles I need to fil my window bg
for i in range(WIDTH//width + 1):
for j in range (HEIGHT // height+1):
pos = tuple([i * width , j*height]) # convert list into tuple
tiles.append(pos)
return tiles, image # now I now the list of positions to fill the bg and the exact file to use
def draw(window, background, bg_image, player):
for tile in background:
window.blit(bg_image, tile) # drawing the image at every position
player.draw(window)
pygame.display.update()
def handle_move(player): #check keys and collision
keys = pygame.key.get_pressed()
player.x_vel = 0; #as moveleft change the velocity we have to change it to zero so w
if keys[pygame.K_LEFT]:
player.move_left(PLAYER_VEL)
if keys[pygame.K_RIGHT]:
player.move_right(PLAYER_VEL)
def main(window):
clock = pygame.time.Clock()
background, bg_image = get_background("Blue.png")
player = Player(100,100,50,50)
run = True
while(run):
clock.tick(FPS) #fix the refresh rate to this otherwise it will be dpending on the computer power
for event in pygame.event.get():
if event.type == pygame.QUIT: #if teh program detect an event of the user Quiting the game
run = False
break
player.loop(FPS)
handle_move(player)
draw(window, background, bg_image, player)
pygame.quit()
quit() #quiting the python itself
if __name__ == "__main__":
main(window) #when we run the file go to the main function with this arg
comparing the code with the original author code, change my code
I think that the problem lies in the following line of the Player.draw function:
self.sprite = self.SPRITES["idle_"+self.direction][0]
The sprite attribute was already set in the update_sprite function to the correct sprite taking the animation count and state into an account. However, this line resets it to the first image ([0]) from the idle spritesheet ("idle_"), without taking the animation count or state into an account. This does make that only this sprite and not the correct one is being drawn.
Removing the line should resolve the problem.

How to simulate more realistic movement of an object?

I am writing a game environment, in which a person (worker) should move inside the area in a random direction, until it crosses with one of green-coloured obstacles (defined as pygame.draw.rect(screen, GREEN, [510,150,75,52]) and pygame.draw.rect(screen, GREEN, [450,250,68,40])).
Until now I can simulate a random movement of a worker, but it moves somehow irregularly and non-smoothly, jumping around the same area and slowly shifting to the right bottom corner.
How can I update the function create_randomPATH to support a more realistic smooth movement of a worker inside the screen area? I tried to increase a tick size till 70 or even more (clock.tick(70)) as it is indicated in one of threads, but it does not seem to solve the problem.
import pygame, random
import sys
WHITE = (255, 255, 255)
GREEN = (20, 255, 140)
GREY = (210, 210 ,210)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)
SCREENWIDTH=1000
SCREENHEIGHT=578
class Worker(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
def create_randomPATH(self,x,y):
randomX = random.randint(1,5)
randomY = random.randint(1,5)
if random.uniform(0,1)>0.5:
valX = x + randomX
valY = y + randomY
else:
valX = x - randomX
valY = y - randomY
return valX, valY
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
pygame.init()
size = (SCREENWIDTH, SCREENHEIGHT)
screen = pygame.display.set_mode(size)
screen_rect=screen.get_rect()
pygame.display.set_caption("TEST")
worker = Worker("worker.png", [0,0])
w_x = worker.rect.left
w_y = worker.rect.top
bg = Background("background.jpg", [0,0])
carryOn = True
clock=pygame.time.Clock()
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
pygame.display.quit()
pygame.quit()
quit()
# Draw floor layout
screen.blit(pygame.transform.scale(bg.image, (SCREENWIDTH, SCREENHEIGHT)), bg.rect)
# Draw obstacles
pygame.draw.rect(screen, GREEN, [510,150,75,52])
pygame.draw.rect(screen, GREEN, [450,250,68,40])
w_x,w_y = worker.create_randomPATH(w_x,w_y)
# worker should not go outside the screen area
worker.rect.clamp_ip(screen_rect)
screen.blit(worker.image, (w_x,w_y))
# Refresh Screen
pygame.display.flip()
clock.tick(5)
pygame.display.quit()
pygame.quit()
quit()
Take screen.blit(worker.image, (w_x,w_y)) and put it in it's own method. Then you can create a loop to loop from the oldX and oldY to the new position found by create_randomPATH You can even make the direction to move random by making them move in the x or y direction randomly each time.
Below is a rough example, but you probably have to modify it to match your code.
def UpdatePerson(image, x, y):
screen.blit(image, x, y)
while carryOn:
...
...
oldX = w_x
oldY = w_y
w_x,w_y = worker.create_randomPATH(w_x,w_y)
while(oldX < w_x || oldY < w_y):
randomDir = random.randint(1,2)
if randomDir == 1 && oldX < w_x:
UpdatePerson(worker.image, oldX, w_y)
oldX += 1
elif randomDir == 2 && oldY < w_x
UpdatePerson(worker.image, w_x, oldY)
oldY += 1
You could use an extra direction variable in your code which says in which direction the player is currently moving. One of four values: up, right, down or left. Every once in a while update that direction variable. Meanwhile move only one coordinate at a time in that direction.
def create_randomPATH(self, x, y, dir):
if random.uniform(0,1)>0.8:
# there is a 20% chance every time that direction is changed
dir = random.randInt(1,4)
if dir == 1:
return x, y+1, dir # up
if dir == 2:
return x+1, y, dir # right
if dir == 3:
return x, y-1, dir # down
if dir == 4:
return x-1, y, dir # left
In your code you would also need a global direction variable, initially it should also have a value 1, 2, 3 or 4:
w_x,w_y,w_dir = worker.create_randomPATH(w_x,w_y,w_dir)
By moving only one step on every iteration, we assure that the movement speed is always constant. In your code, it was varying from 1 to 5 steps. Also if you move a few steps in a given direction straight, it would seem more natural than changing the direction very suddenly very often. Try to play with the percentage (currently 20% chance) of how often the direction is changed and see if it makes the movement even smoother.
EDIT:
Because of the randomness of the movement, eventually the moving object will reach the borders of the game and might leave the screen. Adjust the position after each movement:
w_x,w_y,w_dir = worker.create_randomPATH(w_x,w_y,w_dir)
if (w_x + worker.rect.width > SCREENWIDTH): w_x = SCREENWIDTH - worker.rect.width
if (w_x < 0): w_x = 0
if (w_y + worker.rect.height > SCREENHEIGHT): w_y = SCREENHEIGHT - worker.rect.height
if (w_y < 0): w_y = 0
Then the object will not exit the screen and will eventually randomly move in other directions again. Because each direction is equally likely, it will move all around the screen properly.

Pygame. How to make a rect change direction on collision (boundary check)

Part of an assignment I'm working on is making a ball bounce around the screen, I can make it move, but my boundary test doesn't seem to be working: the ball simply moves in direction instead of changing direction. So to clarify, what I want to ball to do is change direction as it hits the screen edge.
import sys
import pygame
SCREEN_SIZE = 750, 550
BALL_DIAMETER = 16
BALL_RADIUS = BALL_DIAMETER // 2
MAX_BALL_X = SCREEN_SIZE[0] - BALL_DIAMETER
MAX_BALL_Y = SCREEN_SIZE[1] - BALL_DIAMETER
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
LEFT = 11
RIGHT = 12
pygame.init()
clock = pygame.time.Clock()
pygame.display.init()
font = pygame.font.SysFont("impact", 20)
pygame.display.set_caption("Breakout")
screen = pygame.display.set_mode(SCREEN_SIZE)
class Ball:
def __init__(self):
''' '''
self.ball = pygame.Rect(300, 730 -
BALL_DIAMETER,
BALL_DIAMETER, BALL_DIAMETER)
# Draw ball
def draw_ball(self):
pygame.draw.circle(screen,
WHITE, (self.ball.left
+ BALL_RADIUS, self.ball.top +
BALL_RADIUS), BALL_RADIUS)
# Updates the coordinates by adding the speed components
def move_ball(self, x, y):
self.xspeed = x
self.yspeed = y
self.ball = self.ball.move(self.xspeed, self.yspeed)
# bounds check
if self.ball.left <= 0:
self.ball.left = 0
self.xspeed = -self.xspeed
elif self.ball.left >= MAX_BALL_X:
self.ball.left = MAX_BALL_X
self.xspeed = -self.xspeed
if self.ball.top < 0:
self.ball.top = 0
self.yspeed = -self.yspeed
elif self.ball.top >= MAX_BALL_Y:
self.ball.top = MAX_BALL_Y
self.yspeed = -self.yspeed
# shows a message on screen, for testing purposes
class Text:
def show_message(self, message):
self.font = pygame.font.SysFont("impact", 20)
font = self.font.render(message,False, WHITE)
screen.blit(font, (200, 400))
class Game:
def __init__(self):
''' '''
def run(self):
b = Ball()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
# fps lock, screen fill and method call for input
clock.tick(60)
screen.fill(BLACK)
b.draw_ball()
b.move_ball(5, -5)
# used to keep track of various elements
# Text().show_message("P: " + str(p))
pygame.display.flip()
# Creates instance of the game class, and runs it
if __name__ == "__main__":
Game().run()
Your only call to move_ball uses a constant vector.
Since you never change the call parameters, the ball moves only that way.
b.move_ball(5, -5)
Yes, you change the vector components within move_ball when you hit a wall. However, on the next call, you change them back to the original values and move the ball in the original direction.
You have to initialize the vector outside move_ball, and then let the routine access the existing vector when it's called.

Problems with Sprites Appearing and Collision with Rotated Objects; Pygame object is not iterable

I'm currently trying to make pixel perfect collisions between my pong ball and my player's paddle using the mask and collide_rect functions. I made my own checkCollision function in the pong class which would check for pixel perfect collision. However, right now, I can't even get the Sprites to work or appear on the screen because my "Pong object is not iterable.
Here is my pong class with the important features: (I will post additional code if needed)
class Pong(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = screensize[0] // 2
self.centery = screensize[1] // 2
self.radius = 25
self.rect = pygame.Rect(self.centerx-self.radius,
self.centery-self.radius,
self.radius*2, self.radius*2)
self.pokeimage = pygame.image.load("pokeball.png")
self.pokeimage = pygame.transform.scale(self.pokeimage, (self.radius, self.radius))
#Create the mask
self.mask = pygame.mask.from_surface(self.pokeimage)
def checkCollision(self, player_paddle, ai_paddle):
col = pygame.sprite.collide_rect(self, player_paddle)
return col
def collisionFormula(self, player_paddle, ai_paddle):
if self.checkCollision(self, player_paddle):
def collision_checks(self, player_paddle, ai_paddle):
#if the ball hits the top or bottom of the screen, change the y direction
if self.rect.top <= 0 or self.rect.bottom >= self.screensize[1] - 1:
self.direction[1] *= -1
#if the pong hits the paddles, change how the pong ball moves
if self.rect.colliderect(player_paddle.rect) or self.rect.colliderect(ai_paddle.rect):
self.collisionFormula(player_paddle, ai_paddle)
def update(self, player_paddle, ai_paddle):
self.update_ball_position()
self.reset_ball()
self.collision_checks(player_paddle, ai_paddle)
In my PlayerPaddle class, I do the same mask initialization.
class PlayerPaddle(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = 50
self.centery = screensize[1]//2
self.height = 100
self.width = 20
self.imageMaster = pygame.image.load("naruto.png").convert_alpha()
self.imageMaster = pygame.transform.scale(self.imageMaster, (self.width, self.height))
self.image = self.imageMaster
#mask
self.mask = pygame.mask.from_surface(self.image)
def turnLeft(self):
self.dir += 45
if self.dir > 360:
self.dir = 45
def turnRight(self):
self.dir -= 45
if self.dir < 0:
self.dir = 315
def update(self):
#Rotate functions
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMaster, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
And here is my main function:
def main():
pygame.init()
screensize = (640,700)
screen = pygame.display.set_mode(screensize)
background = pygame.Surface(screen.get_size())
background.fill((0, 255, 0))
clock = pygame.time.Clock()
pong = Pong(screensize)
player_paddle = PlayerPaddle(screensize)
ai_paddle = AIPaddle(screensize)
paddleSprite = pygame.sprite.Group(player_paddle)
pongSprite = pygame.sprite.Group(pong)
while running:
running = True
#object updating phase
ai_paddle.update(pong, player_paddle)
player_paddle.update()
pong.update(player_paddle, ai_paddle)
if pygame.sprite.spritecollide(player_paddle, pong, False, pygame.sprite.collide_mask):
print("Collided")
#rendering phase
ai_paddle.render(screen)
player_paddle.render(screen)
pong.render(screen)
paddleSprite.clear(screen, background)
paddleSprite.update()
paddleSprite.draw(screen)
pongSprite.clear(screen,background)
pongSprite.update()
pongSprite.draw(screen)
pygame.display.flip()
pygame.quit()
main()
I made two "group" objects (the pong and the player_paddle) but I'm not sure why I'm even failing to run the program. Additionally, I know the collision will not work because the pong ball will hit the rectangle of the original image, but not the rotated image, but I'm not sure why that will happen if I use the built in sprite function. Thanks.
Read documentation for spritecollide
Find sprites in a group that intersect another sprite.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
Second argument has to be group (pygame.sprite.Group), not single Sprite.
It can be group event with one sprite. But you use pong which is single sprite, not group.
See documentation for collide_mask
Collision detection between two sprites, using masks.
collide_mask(SpriteLeft, SpriteRight) -> point
It checks collision between two sprites using mask.
EDIT: in your code you have problem with
spritecollide(player_paddle, pong,...)
because pong is single Sprite, not Group.
With pong you should use collide_mask
collide_mask(player_paddle, pong)
You can use spritecollidewith pongSprite which is Group
spritecollide(player_paddle, pongSprite,...)`
BTW: you could use better names ie. pong_group instead of pongSprite.
And eventually pong_sprite instead of pong (but pong is OK, too).

Software Design and Development Major: Pygame Smudge Trails

First off, i have searched online and this website for solutions and the ones i have tried are not working so i decided to post my individual question and code. This program was created using Python 3.2.2 and the corresponding compatible version of pygame. I also realize a more efficient method would be to use sprites, sprite groups and 'dirty rect' updating but i unable to convert the program and so i will continue without the added benefits of such functions.
Problem: Smudge trails where the 'asteroids' are moving are left behind.
Hypothesis: Background is blitted onto the screen however the asteroids are blitted onto the Background.
Please Reply - btw i'm a highschooler from AUS :D
import pygame
import random
import math
pygame.init()
height = 550
width = 750
screen = pygame.display.set_mode((width, height))
background = pygame.image.load("Planet.jpg")
Clock = pygame.time.Clock()
class asteroid(pygame.sprite.Sprite):
def __init__(self, x, y, size):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.size = 15
self.speed = 0.0
self.angle = 0
self.colour = (171, 130, 255)
self.thickness = 0
def display(self):
pygame.draw.circle(background, self.colour, (int(self.x),int(self.y)), self.size, self.thickness)
pygame.draw.circle(background, (255, 255, 255), (int(self.x),int(self.y)), self.size, 1)
def move(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
def boundaries(self):
if self.x > width - self.size:
self.x = 0 + self.size
elif self.x < self.size:
self.x = width - self.size
if self.y > height - self.size:
self.y = 0 + self.size
elif self.y <self.size:
self.y = height - self.size
num_target = 5
my_particles = []
num_particles = len(my_particles)
while num_particles < 5:
for n in range(num_target):
size = 20
x = random.randint(size, height - size)
y = random.randint(size, width - size)
target = asteroid(x, y, size)
target.speed = random.uniform(1.0, 1.0)
target.angle = random.uniform(0, math.pi*2)
my_particles.append(target)
num_particles = num_particles + 1
def main():
pygame.display.set_caption("Anyu's Game")
screen.blit(background, (0,0))
pygame.display.update()
score = (pygame.time.get_ticks()/1000)
print (score)
while True:
pygame.display.update()
screen.blit(background, (0,0))
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
target.display()
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit();
if __name__=='__main__':
main()
Basically, you are right! The circles are drawn directly onto the background, and everytime new circles are drawn, the old circles remain. Resulting in the smudges/trails.
You can just change background to screen in your draw method. This will fix it.
But it is really worth using the Sprite classes as intended. I've made a few changes to your code to switch it over for you. With these changes it runs without trails :)
Here are the changes and explainations:
Add this near the top:
#Create a new `pygame.Surface`, and draw a circle on it, then set transparency:
circle = pygame.Surface((30,30))
circle = circle.convert()
pygame.draw.circle(circle, (171, 130, 255), (int(15),int(15)), 15, 0)
circle.set_colorkey(circle.get_at((0, 0)), pygame.RLEACCEL)
Add this to the asteroid, __init__ method:
#Sets the asteroid image, and then the asteroids co-ords (these are in `rect`)
self.image = circle
self.rect = self.image.get_rect()
Add this to the end of def move(self):
self.rect[0] = self.x
self.rect[1] = self.y
change:
my_particles = []
to:
#This is a special pygame container class, it has a draw() method that tracks changed areas of the screen.
my_particles = pygame.sprite.RenderUpdates()
change:
my_particles.append(target)
to:
my_particles.add(target)
change:
while True:
pygame.display.update()
screen.blit(background, (0,0))
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
target.display()
pygame.display.update()
to:
#initial screen draw:
screen.blit(background, (0,0))
pygame.display.update()
while True:
#remove previous drawn sprites and replaces with background:
my_particles.clear(screen, background)
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
#draws changed sprites to the screen:
pygame.display.update(my_particles.draw(screen))
Remove the display method as it is no longer needed.
This will also run a lot faster than the your earlier code, as the time taken to draw something is proportional to the size of the drawing area, and previously it was drawing the whole background everytime - now it only draws the sprites and changes to the background!
Hope this helps :)
This already has an answer but this can be useful instead of other methods.
Make sure when you blit the images onto the screen, flip the display after blitting everything.
I would consider making a draw() function
Like this:
def draw(self):
# Blit images
self.screen.blit(image)
# Flip display
pygame.display.flip()
This will flip the display every frame and then draw the next frame without a trail.
Also quick notes, remember to do image = pygame.image.load(image).convert or .convert_alpha() else after adding more images the game will slow down.
Also, if you do import pygame as pg you don't have to type out pygame each time, instead you can just type pg.

Categories