preface

From the previous nine articles, hoppers bunny has been written, and this section takes it one step further and adds a cloud background.

Add cloud background

The general steps for adding a cloud background are as follows.

  • 1. Write the cloud class
  • 2. Load the cloud image
  • 3. Randomly generated clouds
  • 4. Clouds move in sync

Step by step to write, the first is to create the cloud class, the code is as follows.

# sprites.py

class Cloud(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = CLOUD_LAYER
        self.groups = game.all_sprites, game.clouds
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = random.choice(self.game.cloud_images)
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        # Random location
        scale = random.randrange(50.101) / 100
        self.image = pg.transform.scale(self.image, (int(self.rect.width * scale),
                                                     int(self.rect.height * scale)))
        self.rect.x = random.randrange(WIDTH - self.rect.width)
        self.rect.y = random.randrange(- 500..- 50)

    def update(self):
        # Clouds are eliminated when they are twice as high
        if self.rect.top > HEIGHT * 2:
            self.kill()
Copy the code

The code content is very similar to the previous content and will not be analyzed in detail.

But if you look closely, you’ll see that the Cloud class’s __init__() method creates self._layer and adds it to the appropriate groups as follows.

self.groups = game.all_sprites, game.clouds
pg.sprite.Sprite.__init__(self, self.groups)
Copy the code

This is an optimization point, which will be discussed later.

Once you’ve built the Cloud class, all you need to do is load images, generate randomly, and move synchronously.

# main.py/Game

    def load_data(self): # load data
        #... Omit irrelevant code
        # Load cloud image
        self.cloud_images = []
        for i in range(1.4):
            self.cloud_images.append(pg.image.load(os.path.join(img_dir, 'cloud{}.png'.format(i))).convert())
        
    def new(self):
        self.score = 0
        #... Omit irrelevant code
        # Create clouds
        for i in range(8):
            c = Cloud(self)
            c.rect.y += 500
        self.run()
        
    def update(self):
         # when the player reaches 1/4 of the game frame (note that the head of the game frame is 0, the bottom is the length of the game frame, and the top part of the game frame is reached)
        if self.player.rect.top <= HEIGHT / 4:
            # Player position movement (move down)
            self.player.pos.y += max(abs(self.player.vel.y), 2)
            # Randomly generate new clouds
            if random.randrange(100) < 10:
                Cloud(self)
            # Clouds move in sync
            for cloud in self.clouds:
                cloud.rect.y += max(abs(self.player.vel.y / 2), 2)
            # Enemies move down in sync
            for mob in self.mobs:
                mob.rect.y += max(abs(self.player.vel.y), 2)
            # When the platform is outside the game frame, log it out to avoid resource waste
            for plat in self.platforms:
                # Platform movement position (move down, move the same distance as the player, so that the player can still stand on the original platform)
                plat.rect.y += max(abs(self.player.vel.y), 2)
                if plat.rect.top >= HEIGHT: 
                    plat.kill()
                    # Score increase - Platform destroy, score add
                    self.score += 10
            
Copy the code

Woo ~, done. If in doubt, pull down the Github code and look at it.

To optimize the

Now that we’ve added the cloud class, let’s do some optimizations.

First, the optimization of collision detection, if you look carefully, you will find that the player object and the object of ontology has not come into contact with the enemy, triggering the collision detection, the game is over, the causes of this phenomenon is that the players, the enemy, any element of the game is a corresponds to the size of the rectangle. The default form of collision detection is to detect these two squares, in which case the two elements themselves may not touch, but the corresponding squares do, so collision detection is triggered.

To avoid this, use PyGame’s mask mechanism to create masks for both Player and Mob.

# sprites.py

class Player(pg.sprite.Sprite):
    def animate(self):
        #... Omit irrelevant code
        self.mask = pg.mask.from_surface(self.image) Create a mask

class Mob(pg.sprite.Sprite):
    
    def update(self):
        #... Omit irrelevant code
        self.rect = self.image.get_rect()
        self.mask = pg.mask.from_surface(self.image) Create a mask
        self.rect.center = center
        #... Omit irrelevant code
Copy the code

The pyGame.sprite. Collide_mask callback will do

def update(self):
        #... Omit irrelevant code
        # Collision Detection - If you hit an enemy, the game ends
        mob_hits = pg.sprite.spritecollide(self.player, self.mobs, False, pg.sprite.collide_mask)
        if mob_hits:
            self.playing = False
Copy the code

In addition, there is another problem that needs to be optimized, which is the layer relationship of elements. After adding cloud objects, the problem of layer relationship becomes obvious. As the background of cloud, it will block player objects, enemy objects, platform objects, etc., which is not reasonable.

Start by defining the layers on which the different elements will appear.

# settings.py

# Different elements in different layers
PLAYER_LAYER = 2 # players
PLATFORM_LAYER = 1 # platform
POW_LAYER = 1 # props
MOB_LAYER = 2 # the enemy
CLOUD_LAYER = 0 # cloud
Copy the code

Then add layer logic for each of the different element objects

#Sprite.py

class Player(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = PLAYER_LAYER # Corresponding layer
        self.groups = game.all_sprites # group
        pg.sprite.Sprite.__init__(self, self.groups) Add, instantiate
        #... Omit irrelevant code

class Platform(pg.sprite.Sprite):
    def __init__(self, game, x, y):
        self._layer = PLATFORM_LAYER # Corresponding layer
        self.groups = game.all_sprites, game.platforms # group
        pg.sprite.Sprite.__init__(self, self.groups) Add, instantiate
        #... Omit irrelevant code

class Pow(pg.sprite.Sprite):
    def __init__(self, game, plat):
        self._layer = POW_LAYER
        self.groups = game.all_sprites, game.powerups
        pg.sprite.Sprite.__init__(self, self.groups)
        #... Omit irrelevant code
        
class Mob(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = MOB_LAYER
        self.groups = game.all_sprites, game.mobs
        pg.sprite.Sprite.__init__(self, self.groups)
        #... Omit irrelevant code
        
class Cloud(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = CLOUD_LAYER
        self.groups = game.all_sprites, game.clouds
        pg.sprite.Sprite.__init__(self, self.groups)
        #... Omit irrelevant code
Copy the code

After optimization, and then modify a new Game class () method, using pygame. Sprite. LayeredUpdates () to instantiate all_sprites object.

LayeredUpdates is a set of sprites that can work with layers and draw elements in sequence.

# main.py

class Game:
    def new(self):
        self.score = 0
        # self.all_sprites = pg.sprite.Group()
        # Add layers to avoid overlapping elements (e.g. background cloud blocking platform and player)
        self.all_sprites = pg.sprite.LayeredUpdates()
        self.platforms = pg.sprite.Group()
        self.powerups = pg.sprite.Group() # Speed-jump item
        self.mobs = pg.sprite.Group() # Enemy objects
        self.clouds = pg.sprite.Group() # cloud Object
        self.player = Player(self)
        self.all_sprites.add(self.player)
        for plat in PLATFORM_LIST:
            p = Platform(self, *plat)
            # self.all_sprites.add(p)
            # self.platforms.add(p)
        self.mob_timer = 0
        # Background music for the game
        pg.mixer.music.load(os.path.join(self.snd_dir, 'Happy Tune.ogg'))
        # Create clouds
        for i in range(8):
            c = Cloud(self)
            c.rect.y += 500
        self.run()
Copy the code

The net effect is as follows.

The tail

This is the end of Hoppy Bunny and the end of the Pygame series of articles.

Github: github.com/ayuLiao/jum…

Pygame also has a lot of interesting features that aren’t present in Bungee Jumping.

As mentioned in the first article of this series, these articles are just study notes, along with study notes for the following two games: masturbation and ZOMBIE fighting RPG.

If you are interested, please tell me so that I will be motivated to continue sharing. I think I will share algorithms and computer basics in the form of comics in the future according to my personal plan. I hope you like it.

Finally, again, the learning is from: kidscancode.org/, the game is not original…

If the article is helpful to you, please click “watching” to give the author a little encouragement, thanks to Horn.