Rapid spawning in Pygame

4.2k views Asked by At

Okay, i just started using pygame a week ago but i think i understand the basics. My game is really simple, move the balloon left and right to dodge the incoming screws. I successful made the balloon move left and right, but i'm very unfamiliar with classes and i dont know any other way to rapidly spawn multiple screws on the screen. Any help would be greatly appreciated.

Heres my code:

import pygame
from pygame.locals import *
import sys
import time
pygame.init()

screen = pygame.display.set_mode((600,600))
pygame.display.set_caption('Baloon Pop')


baloon_size = (70,70)

white = (255,255,255)
cyan = (0,255,255)
red = (255,0,0)

screen.fill(cyan)

baloon = pygame.image.load('/users/Gaming/Desktop/rsz_baloon.png')
screw = pygame.image.load('/users/Gaming/Desktop/rsz_screw_png3029.png')



FPS = 30
fps_time = pygame.time.Clock()

baloonX = 280
baloonY = 500

import random
screwX = random.randint(0,600)
screwY = 20

LEFT = "left"
RIGHT = "right"
movement = "down"




while True:
    screwY = screwY+10
    screen.fill(cyan)
    screen.blit(screw, (screwX,screwY))
    screen.blit(baloon, (baloonX,baloonY))

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_RIGHT:
                baloonX = baloonX+30
                if baloonX >= 580:
                    baloonX = baloonX -30
            elif event.key == K_LEFT:
                baloonX = baloonX -30
                if baloonX <=-30:
                    baloonX = baloonX+30






    pygame.display.update()
    fps_time.tick(FPS)
2

There are 2 answers

0
AudioBubble On

First of all, I would recommend using a class to control your player. This makes drawing and detecting collisions easier. A simple way to do this is:

class Player:

    def __init__(self, x, speed):
        self.x = x
        self.speed = speed
        self.moveright = 0
        self.moveleft = 0

    def update(self, time_passed):
        if self.moveright:
            self.x += self.speed * time_passed
        if self.moveleft:
            self.x -= self.speed * time_passed

Then, a similar class to control your enemy.

class Enemy:

    def __init__(self, x, speed):
        self.x = x
        self.y = 0
        self.speed = speed

    def update(self, time_passed):
        self.y += self.speed*time_passed

Here, time_passed is your Clock() object's tick value. screen is your pygame.display surface.

Now that you have your enemy objectified, you can create a list to store the instances of the enemy class.

As I've suggested, using this mechanism can smooth your game quite a bit. Using an increment on each KEYDOWN event is not recommended.

Create a list to store the enemy instances and create player instance:

Enemylist = []
Player1 = Player(<SomeXValue>,0.1)

Moving on to the creation of random enemies. If you need to create, say a screw (that is, a Enemy()) every n loops, you can create a flag that activates in that interval. You can have a variable 'count', that decides this. Initial value can be 0 and then increase it at every loop by 1. (Example: if you want an enemy spawning every 5 loops, replace n by 5.)

if count % n == 0:    
    Enemylist.append(Enemy(random.randint(0,<YourScreenSize>),0.1))
if count > 1000:
    count = 0

That is, spawn an enemy at a random place in the screen, and have them move down. 0.1 is just a sample speed.

Now, to the loop section... Your for event loop should have a KEYDOWN and a KEYUP check, to ensure single keypresses. Here, Player1 is the name of the Player class instance.

for event in pygame.event.get():

    if event.type == QUIT:
        pygame.quit()
        sys.exit()
    if event.type == KEYDOWN:
        if event.key == K_RIGHT:
            Player1.moveright = 1
        if event.key == K_LEFT:
            Player1.moveleft = 1
    if event.type == KEYUP:
        if event.key == K_RIGHT:
            Player1.moveright = 0
        if event.key == K_LEFT:
            Player1.moveleft = 0

Add checks to ensure that the player does not get out of the screen.

The player's movement is now complete, to the requirements of this game.

Call the update() function of the Player, the enemies and flip the screen.

Player1.update(time_passed)
<blit background image or color here>
screen.blit(<image_name>, (Player1.x, <PlayerYPosition>))
for enemy in Enemylist:
    enemy.update(time_passed)
    screen.blit(<image_name>, (enemy.x, enemy.y))
count += 1
pygame.display.update()

Now, add the collision checks to complete your game. Additionally, you can remove the instances that have passed out of the screen to save memory.

0
Anthony Pham On

To make things less complicated for your program, you will need some classes for your balloon and your screws. The first class, which will be for your player, will look like this, assuming you only move left to right:

class Player(pygame.sprite.Sprite):
    def __init__(self, image_file, location):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(image_file)
        self.rect = pygame.image.get_rect()
        self.rect.top, self.rect.left = location

This piece of code will make your balloon a sprite, ready for detection for collisions. Your screws' class will look similar, only with a extra move and location function and speed in the __init__ part of the class:

class Screws(pygame.sprite.Sprite):
    def __init__(self, image_file, left, speed):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(image_file)
        self.rect = pygame.image.get_rect()
        self.rect.top = 50
        self.rect.left = left
        self.speed = speed

    def move(self):
        self.rect = self.rect.move(self.speed)

This class makes the screws sprites, also ready for detection. This finishes the classes section. Now to the grouping of these sprites and etc.:

balloon = Player('/users/Gaming/Desktop/rsz_baloon.png', [a, b])     
screw = Screws('/users/Gaming/Desktop/rsz_screw_png3029.png', random.randint(0, <ScreenSize>), c)
ScrewGroup = pygame.sprite.Group()

Once again, the variables are changeable but the higher c is, the faster the screws will fall. a and b will decide the location of the balloon, and random.randint() will decide the location of your screws. self.rect.top is a way to find the location by using it as the location of the rect's "top" side. In this case it stays the same. Same for, self.rect.left but it is the location of the rect's "left" side. Now going to the moving part of the balloon, to avoid excessive presses of the keys UP and DOWN (and tiredness), add these line of code right after:

delay = 100
interval = 50
pygame.key.set_repeat(delay, interval)
on = True
screwy = 0

delay is the amount of milliseconds between each KEYDOWNis activated and interval is amount of milliseconds to wait until beginning the repeated KEYDOWN's. This will help the user by making him just hold down the LEFT of RIGHT arrow key, and the balloon will continue going in the desired direction. The on and screwy variable will be discussed in the next section. Next, the while loop is almost there:

while True:
    screen.blit(balloon.image, balloon.rect)
    while int(screwy - 1) > -1:
        screen.blit(screw.image, screw.rect)
    pygame.display.flip()

The second while loop spawns as many screws as it can as long the value of screwy - 1 is less than -1 (negative 1). This also flips the screen to avoid any "tracks" left on the screen. Now to the moving part of the balloon:

for event in pygame.event.get():
    #Remember to do this : from pygame.locals import *#
    if event.type == QUIT:
        on = False
        #Remember to import sys!#
        sys.exit()
    elif event.type == pygame.KEYDOWN:
        if event.key = K_LEFT:
            balloon.rect.left -= 30
        elif event.key == K_RIGHT:
            #This can only work if (width_of_the_picture - a) is equal to 30#
            balloon.rect.left += int(width_of_the_picture - a)

This will allow you to move the balloon (you can hold the key down to kepp the balloon moving like in a real video game). Next will be the continued spawning of the screws:

if screwy < 10:
    ScrewGroup.append(Screws('/users/Gaming/Desktop/rsz_screw_png3029.png', random.randint(0, b), [0, 15]))
    screwy += 1

This will spawn screws at random places (same self.rect.top value to make it look realistic). b will be equal to the width of your screen in this case. Finally, the sprite detection:

if pygame.sprite.spritecollide(balloon, ScrewGroup, True):     
    on = False
    sys.exit()
    pass

This detects if the balloon has collided with the screws. If that is true, well you can decide. You could just exit the while loop and exit the program, which is one way to do it. But if you plan on doing something like print 'Game Over!, add that line before doing on = False/sys.exit() which will exit the loop/program immediately. You will need to re-blit the images and allow the exit of the screen to be smooth (pygame.quit):

    screen.fill([255, 255, 255])
    screen.blit(balloon.image, balloon.rect)
    while int(screwy - 1) > -1:
        screen.blit(screw) 
    pygame.display.flip()
pygame.quit()

Remember to put the pygame.quit() outside the while loop or the screen will immediately disappear. Of course, make some code to prevent the balloon from exiting the screen. Changing the KEYDOWN section to this should do it:

elif event.type == pygame.KEYDOWN:
    if event.key == K_LEFT:
        if int(balloon.rect.left) - 30 < 0:
            pass
        elif int(balloon.rect.left) - 30 >= 0:
            balloon.rect.left -= 30
    elif event.key == K_RIGHT:
        if int(balloon.rect.left) + (<Width_OF_Balloon> - a) > <Width_OF_Screen>:
            pass
        elif int(balloon.rect.left) + (<Width_OF_Balloon> - a) <= <Width_OF_Screen>:
            #This can only work if (<Width_OF_Balloon> - a) is equal to 30#
            baloon.rect.left + (<Width_OF_Balloon> - a)

If going left/right once makes the balloon partly leaves the screen, the program will not allow the balloon to go left/right by doing nothing with pass. This should improve your program significantly and answer your question. I hope this helps you!