How to remove image, then blit it again? - python

I am creating a game with pygame and I ran into a problem. My game is a space game: there are meteors coming at you, you have to dodge them and you can also shoot missles at them. When the missles hit the meteors, I want to make the missle dissapear and the meteor to be reset back where it started falling. I saw a line of code that showed how to make an image transparent, by doing this:
transparant = (0,0,0,0)
#Code in between
missle.image = missleImg
#I wrote some code to see if the missle was touching a meteor
missle.image.fill(transparent)
However, when I try to shoot the missle again, it is still transparent. Does anybody have a better way to unblit and the reblit an image?

Use lists or groups to organize the objects. Create one list or groups to for the meteors and another list or group for the missiles. When a missile hits a meteor, remove the objects from the container. Create new objects when you need them:
If the meteor immediately appears in a different position, it is sufficient to change the meteor's position attribute rather than destroying an object and creating a new one.
e.g. pygame.sprite.Sprite and pygame.sprite.Group:
meteor_group = pygame.sprite.Group():
missile_group = pygame.sprite.Group():
pygame.sprite.groupcollide(meteor_group, missile_group, True, True)
e.g. lists:
meteor_list = []:
missile_list = []:
for meteor in meteor_list[:]:
for missile in missile_list[:]:
collide = # [...] check for collision
if collide:
meteor_list.remove(meteor)
missile_list.remove(missile)

Related

How to know which sprite has collided with a bullet in its group?

Im making a game where a player shoots bullets at incoming boulders, which destroys them. It all functions well and good, but it seems a bit plain when the boulder's destroyed. I want to display an explosion image where the boulder was destroyed. To do this, I need to know which exact boulder has been destroyed in my boulder group.
Relevant code
pygame.sprite.groupcollide(boulder_grp, bullet_grp, True, True)
The boulder_grp contains the boulders that spawns, and the bullet_grp contains the bullet that the player shoots. I want to know which boulder in the grp was destroyed, so I can blit an image at that exact spot. How do I go about doing this? Thank you.
pygame.sprite.groupcollide returns a dictionary with the sprites.
collide_dict = pygame.sprite.groupcollide(boulder_grp, bullet_grp, True, True)
for boulder in collide_dict:
print(boulder.rect)

Pygame problem: how to make an object disappear after collision and create a new object over and over?

I am trying to create a game that places a cat which I can move left and right at the bottom of the screen.
Then I make a ball appear at a random position on the top of the screen and fall down the screen at a steady rate.
If the cat catches the ball by colliding with it, I make the ball disappear.
Meanwhile, I make a new ball each time the cat catches the ball or whenever the ball disappears off the bottom of the screen.
My question is: how do I make the ball disappear after being caught by the cat and then create a new ball over and over?
Currently I can think of two approaches to this problem:
First, I create a single ball. Once the cat catches the ball, I write some codes to make THAT ball disappear. Then, write some codes to create a new ball. Put these codes in a loop somehow to keep a ball disappear and reappear over and over. The problem is, I am not sure which codes to write to make a ball disappear and then create a new one.
Second approach is, I create a group of balls balls = Group() using pygame.sprite. I let those balls drop one after another. When a cat catches a ball, I remove that ball from the group of balls. Then let another ball in the group drop.
for ball in balls.copy():
if cat.rect.colliderect(ball.rect):
balls.remove(ball)
This second approach has somehow created another problem. When I wanted to specify the collision between the cat and each ball, I sometimes received the error message saying that either the name "ball" is not define or the name "balls" is not defined. Plus, I don't know whether it is really necessary to use a for loop whenever I want to specify a cat-ball collision. I feel that there has to be a more straight-forward and efficient way than this.
Would the first approach be better? If yes, please let me know how to make a ball disappear and to create a new ball immediately afterwards. THANK YOU SO MUCH!!
Probably the easiest way is to create a sprite with the following template:
class Ball(pygame.sprite.Sprite):
""" falls and restarts the ball position when ball hits the cat or the ground"""
def __init__(self):
""" setting up the ball image, rect, caught_by_cat flag etc. """
def update(self):
if self.hits_the_ground():
self.reset()
elif self.caught_by_cat:
self.reset()
else:
self.fall()
def reset(self):
""" randomize the ball position and set the ball rect to this position and resets the caught_by_cat flag """
def hits_the_ground(self):
""" checks whether ball hits the bottom of the board """
def fall(self):
""" move the ball rectangle with a steady speed """
and then after the initialization and adding it to the pygame.sprite.Group, in the game event loop simply:
sprite_group.update()
sprite_group.draw(screen)
Check for collision
if cat_collides_with_ball:
ball.caught_by_cat = True
This way you don't need to create many ball objects, you just need to restart the ball rect attribute to draw it in other place.

How to speed up or optimize for loop used for collision detection using pygame/pytmx?

I am using a for loop combined with .colliderect() for collision detection while attempting to make a game using pygame, the loop gets too slow with ~340 wall Rectangles, I was wondering if it could be faster somehow because it severely effects the game play loop?
I have tried using coordinate points on different places on every wall but only works if you're moving certain amounts of pixels at a time and every time you half the movement speed it quadruples the amount of coordinate points you save.
#disregard indent, this is all in an update function that is called every time that a player decides to move.
self._old_position = self.position
PlayerRectangle = pygame.Rect(self.position[0]+ x,self.position[1]+y,16,16)
cwd = os.getcwd()
tmxData = load_pygame(cwd+"\\Maps\\TestfileMap.tmx")
movement = True
for obj in self.walls:
if(pygame.Rect(obj[0],obj[1],16,16).colliderect(PlayerRectangle)):
movement = False
self.move_back()
else:
continue
if movement:
self.position[0] += x
self.position[1] += y
self.stats["position"]=self.position
self.rect.topleft = self.position
self.feet.midbottom = self.rect.midbottom
The provided code works, however it is too slow, I was wondering if there is a different method in collision detection or if there is a way to make what is shown faster, it bogs down things greatly. Thank you
EDIT:
So the solution was basically that I had load_pygame that ran literally every time it looped simply take out the line that does that and it clears things up a lot more, for further optimization change the line that makes a Rect for each object and just use a list of Rects that are already constructed, this limits function calls.
Without a minimal working example it is tough to give assertive advice.
As I stated in the comments, there is a spurious call to a "load_pygame" function that seems to read file data inside this code - that alone could be the cause of your slowdown. Moreover, the data read is not used in the collision detection.
The other advice I't have is to let your walls rectangles pre-calculated in a sprite group, instead of creating new rectangles for all walls in every check with:
pygame.Rect(obj[0],obj[1],16,16).colliderect(PlayerRectangle)). then you can use the sprite method "spritecollideany" - https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollideany - that should optimize the rectangle-collision verification.
(This will also require your Player object to be a Sprite if it is not already).

Pygame collision detect with object and rect

Yep, I'm asking another question about this program :D
Anyway, I currently a program that creates two lines on the screen with a gap in between them that can scroll. From here, I obviously need to see if the two objects are colliding. Since I only have one sprite and one rectangle I thought it was slightly pointless and overkill to make two classes for them. However, I can only find tutorials relating to classes which I obviously don't need. So, my question really is:
Is it possible to test for collision between a standard image and a Pygame rect? If it is not, how can I convert either the image, rectangle or both the sprites to do this. (All preferably without using classes.)
Note: the image and rectangle are created in the following ways (if it makes a difference)
bird = pygame.image.load("bird.png").convert_alpha()
pipeTop = pygame.draw.rect(screen, (0,200,30), Rect((scrollx,0),(30,height)))
pipeBottom = pygame.draw.rect(screen, (0,200,30), Rect((scrollx,900),(30,-bheight)))
An image by itself does not have a position. You cannot test collision between a rect and something that is not placed in the world. I would recommend to create a class Bird along with a class Pipe that will both subclass pygame.Sprite.
Pygame already has collision detection built in.
A short example
bird = Bird()
pipes = pygame.Group()
pipes.add(pipeTop)
pipes.add(pipeBottom)
while True:
if pygame.sprite.spritecollide(bird,pipes):
print "Game Over"
EDIT:
Don't be afraid of classes, you will have to use them anyways sooner or later.
If you really don't want to use sprites, you can use the birds rect and the pipe and call collide_rect to check if they overlap.
EDIT2:
an example Bird class modified from pygame docs
class Bird(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bird.png").convert_alpha()
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
You could then add methods such as move, which will move the bird down with the force of gravity.
The same would apply for the Pipe but instead of loading an image, you can create an empty Surface, and fill it with a color.
image = pygame.Surface(width,height)
image.fill((0,200,30)
You can just get the x and y values and compare them:
if pipe.x < bird.x < pipe.x+pipe.width:
#collision code
pass

Loading and blitting images per frame?

I have a simple top down vertical shooter a la Galaga that I'm messing around with. However, after looking through the documentation I've become a bit confused on how to efficiently load images, and whether to blit them every frame or not. All of the images are loaded through a few classes which inherit the pygame sprite class. At the moment, I'm loading the image as a class level attribute as such:
class Laser(pygame.sprite.Sprite):
image = None
def __init__(self, start_x, start_y):
pygame.sprite.Sprite.__init__(self)
self.pos_x = start_x
self.pos_y = start_y
if Laser.image is None:
Laser.image = pygame.image.load('img/laser_single.png')
self.image = Laser.image
self.rect = self.image.get_rect()
self.rect.topleft = [self.pos_x, self.pos_y]
I'm hoping this prevents Python from loading a new instance of the image into memory every time I create a new Laser(). But will this work as I anticipate?
The second issue stems from blitting all of the active sprites onto the pygame surface. At the moment I loop through a list of Laser(), Enemy(), and whatnot objects and blit each one individually before calling pygame.display.update(). It seems redundant to have to blit each object individually, so I'm asking whether or not this is the most efficient method pygame implements. Or, is there a way to blit every object at once and see some sort of performance improvement?
I'm hoping this prevents Python from loading a new instance of the image into memory every time I create a new Laser(). But will this work as I anticipate?
Yes. Not only does this save memory, since every instance will just have a reference to the class variable, it will also increase performance because the image is only loaded once.
The second issue ssems from blitting all of the active sprites onto the pygame surface.
This depends. If your framerate is OK, then stick with it. If your framerate starts dropping, have a look at DirtySprite, and maybe this article.
Another python class that comes in handy is the LayeredDirty sprite group, which will automatically handle dirty sprites and updating only the relevant screen parts instead of the entire screen for you.
It seems redundant to have to blit each object individually
With sprite groups, as e.g. the LayeredDirty or the simple Group, you add your sprites to the group once and just call the draw method of the sprite group.
sprites = pygame.sprite.LayeredDirty((sprite1, sprite2, sprite3))
sprites.add(sprite4)
...
sprites.draw(pygame_screen)
Using sprite groups will also enable you to do simple collision detection between groups using pygame.sprite.groupcollide and pygame.sprite.spritecollideany

Categories

Resources