Making rows of sprites re-appear at top of screen in pygame - python

I'm trying to make the rows of sprites reappear at the top of the screen when they disappear off of the bottom. I'm able to make the sprites move downwards and off the screen but don't know to get them to reappear as soon as one row moves off. Here is the code I have so far:
import pygame
import sys
from pygame.sprite import Sprite
class Rain:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((1200, 800))
pygame.display.set_caption("Rain Drops")
self.bg_color = (100,100,100)
self.rain_drops = pygame.sprite.Group()
self._create_drop()
drop = Drop()
def run_rain(self):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
self.screen.fill(self.bg_color)
self._update_drops()
self.rain_drops.draw(self.screen)
pygame.display.flip()
def _create_drop(self):
drop = Drop()
drop.rect.x = -10
drop.rect.y = 4
drop_start_x = drop.rect.x
drop_start_y = drop.rect.y
for row_number in range(5):
for drop_number in range(20):
drop = Drop()
drop.rect.x = drop_start_x + 70 * drop_number
drop.rect.y = drop_start_y + 100 * row_number
self.rain_drops.add(drop)
def _update_drops(self):
self.rain_drops.update()
class Drop(Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('smaller_drop.bmp')
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 1
rain = Rain()
rain.run_rain()

This sort of thing is best done in your sprite's .update() function. Check to see if the y co-ordinate of the sprites Rect is past the height of the window, and if-so move it back to the top.
However, you have hard-coded the window dimensions inside the display initialisation function. We need to know the window length in the update, so these need to be moved out to constant variables.
class Rain:
WINDOW_WIDTH = 1200
WINDOW_HEIGHT= 800
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((self.WINDOW_WIDTH, self.WINDOW_HEIGHT))
pygame.display.set_caption("Rain Drops")
Now in the DropSprite it's pretty easy to compare the position against the window height. if the position is greater than the window height,
class Drop(Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('smaller_drop.bmp')
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 1
# if we go off the bottom of the window, move back to top
if ( self.rect.y > Rain.WINDOW_HEIGHT ):
self.rect.y = 0
You may also want the drop to start just above the screen so it "falls" into view. In this case start it with an x of (0 - self.rect.height), since the x,y of a rect is the top-left co-ordinate. Moving it to 0 makes it suddenly appear at the top. Moving it to -height puts it just above the player's view.

Related

How to clear "trails" in sprite animation in pygame? [duplicate]

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

My sprite is leaving a black trail when it moves in Pygame. Can I get help identifying my error? [duplicate]

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

Send obstacles from right side of screen in Pygame

I'm making a basic dodger game where you play as a fish and need to avoid getting hit by obstacles. I want the obstacles to come from the right side of the screen at a random velocity (from a certain range, let's just say 4 to 6). I already have the fish/underwater gravity mechanics working. But I have no idea where to start with the obstacles.
This is my main python script:
import pygame
from assets.player import Bob
# Screen properties
pygame.init()
# Important Variables
run = True
game_speed = 0
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()
FPS = 60
# Properties
bg_game_surface = pygame.image.load('images/game-background.png')
player = Bob(game_speed)
#############
# Functions #
#############
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.blit(bg_game_surface, (0, 0))
player.update(screen)
pygame.display.update()
clock.tick(FPS)
pygame.quit()
And this is the class for my player:
import pygame
class Bob:
def __init__(self, game_speed):
self.img = pygame.image.load('images/player/bob.png').convert_alpha()
self.img_up = pygame.image.load('images/player/bob_up.png').convert_alpha()
self.rect = self.img.get_rect()
self.game_speed = game_speed
def update(self, screen):
key = pygame.key.get_pressed()
### Collision
if self.rect.top <= 0:
self.rect.centery = (screen.get_height() - self.img.get_height() * 2) + 10
elif self.rect.top >= screen.get_height() - self.img.get_height() * 2:
self.rect.centery = 20
### Movement
if key[pygame.K_w] or key[pygame.K_UP]:
self.rect.centery -= 6
self.img = pygame.image.load('images/player/bob_up.png').convert_alpha()
### Gravity
else:
self.img = pygame.image.load('images/player/bob.png').convert_alpha()
self.rect.centery += 4
screen.blit(self.img, self.rect)
My folders are sorted like this:
Right now, I'd like that the trash.py is also a class with a function called "update". When that function is executed, obstacles (preferably in the form of an image) come out of the right side of the display, doing what I said above.
Any help is appreciated. Thanks in advance :)
You should make a sprite class group, but first you need to import the random module:
import random
Now you can create the Obstacle Sprite class, this is just an example:
class Obstacle(pygame.sprite.Sprite):
def __init__(self, pos): #pos is the starting position
super().__init__()
self.image = pygame.Surface((10,10)) # You can change this with pygame.image.load('...')
self.rect = self.image.get_rect(topleft = pos)
self.vel = random.randint(4,6) #random speed of the obstacle
def collisions(self, player): #collisions function
if self.rect.colliderect(player.rect):
print('collision!') #write what you want to happen
def movement(self): #the blocks are moving left
if self.rect.x > -100: #just putting some limits
self.rect.x -= self.vel
def update(self, player): #try to put all the other functions in the update one
self.collisions(player)
self.movement()
Once you created the class, you only need to create some objects of that class (every obstacle).
First we create a sprite group before of the while loop:
obstacles = pygame.sprite.Group()
Then, inside your main loop, you need a condition that generates the obstacles.
I've done something like this, but you really should change it:
if x < 10: #I created x before the while (x=0)
posx = SCREEN_WIDTH + 50 #the starting x will be 50 pixels right of the screen
posy = random.randint(0, SCREEN_HEIGHT) #Im setting this to random
obs = Obstacle((posx, posy))
obstacles.add(obs)
x +=1
This basically creates 9 obstacles when we start the game, with different random x speed and y location, and draw them on the screen. If you collide with one of the blocks, the code just prints "collision!" (see the Obstacle class). Hope it was helpful and I didn't miss something.

pygame sprite.Group - passing arguments out of a Group

I'm trying out pygame, using Sprites & Groups, and getting myself a little confused around passing arguments.
Basically I have a wrapper class MyGame() as my_game(). In that I have pygame.sprite.Group() - all_sprites.
I then have a class Alien(pygame.sprite.Sprite) for my aliens, and I add each alien to my all_sprites Group.
I want my Group of aliens to track across the screen (which they do) and then all drop as a block, a group (which they don't).
I can loop through my all_sprites.Group in my main loop after the self.all_sprites.update() and see if alien.rect.right > WIDTH or alien.rect.left < 0 , then loop through the Group again calling alien.end_row() then break the loop so it doesn't run for each alien on the screen edge, but that seems very clunky.
I've tried setting a flag in MyGame self.alien_drop = False but when I try to set my_game.alien_drop = True in the Alien class, it doesn't recognise my_game - not defined. I'm a little confused as MyGame is creating the instances of Alien, so they should be enclosed by the scope of MyGame?
I can pass MyGame self.alien_drop into my Alien init() but when I update the Alien self.alien_drop it doesn't update MyGame.alien_drop. Which I get because it's created a new variable local to Alien.
There doesn't seem to be a way to pass an argument through Group.update() which I guess is because it's just calling the .update() on all Sprite inside the group. I can't see an easy way to modify the Group.update() function so that I can pass values, and in fairness I probably don't want to go mucking around in there anyway.
I also can't return True back through update().
I'm kinda stuck at this point...
I know the self.aliens.row_end() probably won't work, I'll have to loop through self.aliens and call each alien.row_end(), but at the moment it's not even getting to that point.
import pygame
WIDTH = 360 # width of our game window
HEIGHT = 480 # height of our game window
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
class MyGame():
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.all_sprites = pygame.sprite.Group()
self.alien_drop = False
for row_index in range(4):
for column_index in range(6):
alien = Alien(20 + (column_index * 40), 20 + (row_index *40))
alien.add(self.all_sprites)
while True:
self.screen.fill(BLACK)
self.all_sprites.update()
if self.alien_drop:
self.aliens.row_end()
self.all_sprites.draw(self.screen)
pygame.display.update()
self.clock.tick(60)
class Alien(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((25, 25))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.image.fill(GREEN)
self.dx = 1
def update(self):
self.rect.x += self.dx
if self.rect.right >= WIDTH or self.rect.left <= 0:
my_game.alien_drop = True
def row_end(self):
self.dx *= -1
self.rect.y += 40
if __name__ == "__main__":
my_game = MyGame()
So to do the traditional Space Invaders move and drop, the entire row drops when any of the invaders hits either side.
In the example below I have modified the Alien.update() to detect when the screen-side is hit, but if-so, only set a Boolean flag Alien.drop to True.
The algorithm becomes: First move all the aliens. Next, check if any alien hit the side-wall by checking this flag. If so, move every alien down 1 step & stop checking.
The Alien.row_end() function moves the aliens down. It also needs to clear the Alien.drop flag, so they don't all move down again.
import pygame
WIDTH = 360 # width of our game window
HEIGHT = 480 # height of our game window
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
class MyGame():
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.all_sprites = pygame.sprite.Group()
self.alien_drop = False
for row_index in range(4):
for column_index in range(6):
alien = Alien(20 + (column_index * 40), 20 + (row_index *40))
alien.add(self.all_sprites)
exiting = False
while not exiting:
# Handle events
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
exiting = True # exit this loop
self.screen.fill(BLACK)
self.all_sprites.update()
for alien1 in self.all_sprites:
if ( alien1.shouldDrop() ):
### If any alien drops, we all drop
for alien2 in self.all_sprites:
alien2.row_end()
break # only drop once
if self.alien_drop:
self.aliens.row_end()
self.all_sprites.draw(self.screen)
pygame.display.update()
self.clock.tick(60)
class Alien(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((25, 25))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.image.fill(GREEN)
self.dx = 1
self.drop = False # should I drop-down a level
def update(self):
self.rect.x += self.dx
if self.rect.right >= WIDTH or self.rect.left <= 0:
self.drop = True
else:
self.drop = False
def shouldDrop( self ):
""" In this alien in a position where it should drop down
one row in the screen-space (i.e.: it's hit a side) """
return self.drop
def row_end(self):
self.dx *= -1
self.rect.y += 40
self.drop = False # drop drop again just yet
if __name__ == "__main__":
my_game = MyGame()

Pygame - Creating a "Enemy" class, and then importing it into game

Okay, so basically what I'm trying to do is keep the main file a little cleaner and I'm starting with the "Zombie" enemy by making it's own file which will most likely contain all enemies, and importing it in.
So I'm confused on how I'd set-up the Class for a sprite, you don't have to tell me how to get it to move or anything like that I just want it to simply appear. The game doensn't break when I run it as is, I just wanted to ask this question before I goto sleep so I can hopefully get a lot done with the project done tomorrow (School related)
Code is unfinished like I said just wanted to ask while I get some actual sleep just a few google searches and attempts.
Eventually I'll take from the advice given here to make a "Hero" class as well, and as well as working with importing other factors if we have the time.
Zombie code:
import pygame
from pygame.locals import *
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
# self.images.append(img)
# self.image = self.images[0]
self.rect = self.image.get_rect()
zombieX = 100
zombieY = 340
zombieX_change = 0
Main Code:
import pygame
from pygame.locals import *
import Zombie
# Intialize the pygame
pygame.init()
# Create the screen
screen = pygame.display.set_mode((900, 567))
#Title and Icon
pygame.display.set_caption("Fighting Game")
# Add's logo to the window
# icon = pygame.image.load('')
# pygame.display.set_icon(icon)
# Player
playerImg = pygame.image.load('images/character.png')
playerX = 100
playerY = 340
playerX_change = 0
def player(x,y):
screen.blit(playerImg,(x,y))
Zombie.ZombieEnemy()
def zombie(x,y):
screen.blit()
# Background
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load('images/background.png')
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
BackGround = Background('background.png', [0,0])
# Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If keystroke is pressed check right, left.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#playerX_change = -2.0
BackGround.rect.left = BackGround.rect.left + 2.5
if event.key == pygame.K_RIGHT:
#playerX_change = 2.0
BackGround.rect.left = BackGround.rect.left - 2.5
# if event.type == pygame.KEYUP:
# if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
# BackGround.rect.left = 0
screen.blit(BackGround.image, BackGround.rect)
playerX += playerX_change
player(playerX,playerY)
pygame.display.flip()
Your sprite code is basically mostly there already. But as you say, it needs an update() function to move the sprites somehow.
The idea with Sprites in PyGame is to add them to a SpriteGroup, then use the group functionality for handling the sprites together.
You might want to modify the Zombie class to take an initial co-ordinate location:
class ZombieEnemy(pygame.sprite.Sprite):
def __init__( self, x=0, y=0 ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.center = ( x, y ) # NOTE: centred on the co-ords
Which allows the game to create a Zombie at a particular starting point (perhaps even a random one).
So to have a sprite group, first you need to create the container:
all_zombies = pygame.sprite.Group()
Then when you create a new zombie, add it to the group. Say you wanted to start with 3 randomly-positioned zombies:
for i in range( 3 ):
new_x = random.randrange( 0, WINDOW_WIDTH ) # random x-position
new_y = random.randrange( 0, WINDOW_HEIGHT ) # random y-position
all_zombies.add( Zombie( new_x, new_y ) ) # create, and add to group
Then in the main loop, call .update() and .draw() on the sprite group. This will move and paint all sprites added to the group. In this way, you may have separate groups of enemies, bullets, background-items, etc. The sprite groups allow easy drawing and collision detection between other groups. Think of colliding a hundred bullets against a thousand enemies!
while running:
for event in pygame.event.get():
# ... handle events
# Move anything that needs to
all_zombies.update() # call the update() of all zombie sprites
playerX += playerX_change
# Draw everything
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
all_zombies.draw( screen ) # paint every sprite in the group
pygame.display.flip()
EDIT: Added screen parameter to all_zombies.draw()
It's probably worthwhile defining your player as a sprite too, and having a single-entry group for it as well.
First you could keep position in Rect() in class. And you would add method which draws/blits it.
import pygame
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 340
self.x_change = 0
def draw(self, screen):
screen.blit(self.image, self.rect)
Next you have to assign to variable when you create it
zombie = Zombie.ZombieEnemy()
And then you can use it to draw it
zombie.draw(screen)
in
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
zombie.draw(screen)
pygame.display.flip()
The same way you can create class Player and add method draw() to class Background and then use .
background.draw(screen)
player.draw(screen)
zombie.draw(screen)
pygame.display.flip()
Later you can use pygame.sprite.Group() to keep all objects in one group and draw all of them using one command - group.draw()

Categories

Resources