pygame implements aircraft wars - code analysis of the second edition

Table of contents

1. Original code

Part 1: The sprite definition part

Part 2: Game running part

2. Code Analysis

(1) The overall structure of the code

1. Main() is defined

2. Create game objects

3. Start the game

(2) Partial code analysis

1. Definition of elves

2. Definition of the main game category

3. The main program runs

4. Detailed analysis

3. Experience


The soul and core of pygame are sprites and groups. The first version of the code uses Rect objects to realize the collision detection of bullets, planes, and enemy planes. The second version finds a plane war game with sprites and groups as the core. . Let's analyze its code, and then add its functions to enhance personal understanding and mastery of sprites and groups. . Not much to say, the code.

1. Original code

Part 1: The sprite definition part

Some functions of the code are added, which may affect the overall structure, please forgive me!

import random
import pygame
#窗口的设置
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
# 刷新的帧率
FRAME_PER_SEC = 60
# 创建敌机的定时器常量,自定义事件
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 英雄发射子弹时间,自定义事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1
#同时发射的子弹的个数
NUM_BULLENT_FIRE = 5
# 初始化声音
pygame.mixer.init()


#创建GameSprite精灵
class GameSprite(pygame.sprite.Sprite):
    def __init__(self, image_name, speed1=1,speed2=1):
        #继承父类方法
        super().__init__()

        self.image = pygame.image.load(image_name) #通过图像生成surface对象
        self.rect = self.image.get_rect() #生成rect对象
        self.speed1 = speed1 #设置垂直速度,如果不设置则默认为1
        self.speed2 = speed2#设置水平速度,如果不设置则默认为1,主要是飞机应用

    def update(self):
        # 1.在屏幕的垂直方向下移动
        self.rect.y += self.speed1

class Background(GameSprite):  #基于GameSprite生成精灵
    def __init__(self, is_alt=False):
        # 1.调用父类方法实现精灵的创建
        super().__init__("./image/background.png")
        # 2.判断是否是交替图像,如果是,需要设置初始位置
        if is_alt:
            self.rect.y = -self.rect.height

    def update(self):
        # 1.调用父类的方法实现
        super().update()
        # 2.判断是否飞出屏幕,如果是,需要从精灵组删除敌机
        if self.rect.y >= SCREEN_RECT.height:
            print("飞出屏幕,需要从精灵组删除。。。。")
            self.rect.y = -self.rect.height


class Enemy(GameSprite):
    def __init__(self):
        # 1.调用父类方法,创建敌机精灵,同时指定敌机图片
        super().__init__("./image/enemy0.png")
        # 2.指定敌机的初始随机速度
        self.speed1 = random.randint(1, 3)
        # 3.指定敌机的初始随机位置
        self.rect.bottom = 0
        max_x = SCREEN_RECT.width - self.rect.width
        self.rect.x = random.randint(0, max_x)

    def update(self):
        super().update()
        # 2.判断是否飞出屏幕,如果是,需要从精灵组删除敌机
        if self.rect.y >= SCREEN_RECT.height:
            # print("飞出屏幕,需要从精灵组删除。。。。")
            self.kill()

    def __delete__(self):
        # print("敌机挂了。。。。%s" % self.rect)
        pass


class Hero(GameSprite):
    def __init__(self):
        # 1.调用父类方法,设置image speed
        super().__init__("./image/hero1.png", 0, 0)
        # 2.设置英雄的初始位置
        self.rect.centerx = SCREEN_RECT.centerx
        self.rect.bottom = SCREEN_RECT.bottom - 80

        # 3.创建子弹的精灵组
        self.bullets = pygame.sprite.Group()

    def update(self):
        # 英雄在水平方向运行
        self.rect.x += self.speed2
        # 英雄在垂直方向运行
        self.rect.y -= self.speed1
        # 控制英雄不能离开屏幕
        if self.rect.x < 0:
            self.rect.x = 0
        elif self.rect.y < 0:
            self.rect.y = 0
        elif self.rect.right > SCREEN_RECT.right:
            self.rect.right = SCREEN_RECT.right
        elif self.rect.bottom> SCREEN_RECT.bottom:
            self.rect.bottom = SCREEN_RECT.bottom


    def fire(self,num):  #修改:2022-4-13 添加num参数,并根据数量控制好子弹位置
        # print("发射子弹。。。")
        bullet_num = num  #单独定义同时发射的子弹数可以为后期游戏过程中改变子弹发射数作基础
        for i in range(0,bullet_num):
            # 1.创建子弹精灵
            bullet = Bullet()
            # 2.设置精灵的位置
            #修改:将竖向子弹改成横向子弹
            # bullet.rect.bottom = self.rect.y - i * 20
            bullet.rect.bottom = self.rect.y
            bullet.rect.centerx = self.rect.centerx - 20 * (i - (bullet_num-1)/2)
            # 3.将精灵添加到精灵组
            self.bullets.add(bullet)
        #添加子弹发出的声音
        sound_bullet=SoundSprite("sound/bullet.mp3")
        sound_bullet.play()


class Bullet(GameSprite):
    def __init__(self):
        # 调用父类方法,设置子弹图片,设置初始速度
        super().__init__("./image/bullet.png", -4, 0) #第二和第三个参数是初始速度

    def update(self):
        # 调用父类方法,让子弹沿垂直方向飞行,
        super().update()
        # 判断子弹是否飞出屏幕
        if self.rect.bottom < 0:
            # kill方法把精灵从精灵组删除
            self.kill()

    def __del__(self):
        # print("子弹被销毁。。")
        pass

Part 2: Game running part

import pygame
from plane_sprites import *

Score = 0

class PlaneGame(object):
    """飞机大战主游戏"""

    def __init__(self):
        print("游戏初始化")
        pygame.init()

        # 1.创建游戏的窗口
        self.screen = pygame.display.set_mode(SCREEN_RECT.size)
        # 2.创建游戏的时钟
        self.clock = pygame.time.Clock()
        # 3.调用私有方法,精灵和精灵组的创建
        self.__create_sprites()
        # 4.设置定时器事件  创建敌机
        pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
        pygame.time.set_timer(HERO_FIRE_EVENT, 500)

       

    def __create_sprites(self):
        # enemy = GameSprite("./images/enemy1.png")
        # enemy1 = GameSprite("./images/enemy1.png")
        # enemy_group = pygame.sprite.Group(enemy, enemy1)
        bg1 = Background()
        bg2 = Background(True)
        # lable_score = LabelSprite(self.screen, str(Score), 0, 0, 40)

        self.back_group = pygame.sprite.Group(bg1, bg2)
        # 创建敌机的精灵组
        self.enemy_group = pygame.sprite.Group()
        # 创建英雄精灵和精灵组
        self.hero = Hero()
        self.hero_group = pygame.sprite.Group(self.hero)
       



    def start_game(self):
        print("游戏开始")

        global Score
        while True:
            # 1.设置刷新帧率
            self.clock.tick(FRAME_PER_SEC)
            # 2.事件监听
            self.__event_handler()
            # 3.碰撞检测
            num_shooted=self.__check_collide()
            Score += num_shooted *100

            # 4.更新/绘制精灵组
            self.__update_sprites()
            # 5.更新显示
            pygame.display.update()

    def __event_handler(self):
        for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type == pygame.QUIT:
                print("游戏结束,退出程序!!")
                PlaneGame.__game_over()
            elif event.type == CREATE_ENEMY_EVENT:
                # print("敌机出场。。。")
                # 创建敌机精灵
                enemy = Enemy()
                # 将敌机精灵添加到敌机精灵组
                self.enemy_group.add(enemy)
            elif event.type == HERO_FIRE_EVENT:
                self.hero.fire(NUM_BULLENT_FIRE)
            # elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
            #     print("向右移动。。。")
        # 使用键盘提供的方法获取键盘按键
        keys_pressed = pygame.key.get_pressed()
        # 判断元祖中对应的按键索引值
        if keys_pressed[pygame.K_RIGHT]:
            # print("向右移动。。。")
            self.hero.speed2 = 2
        elif keys_pressed[pygame.K_LEFT]:
            self.hero.speed2 = -2
        else:
            self.hero.speed2 = 0

        if keys_pressed[pygame.K_UP]:
            # print("向右移动。。。")
            self.hero.speed1 = 2
        elif keys_pressed[pygame.K_DOWN]:
            self.hero.speed1 = -2
        else:
            self.hero.speed1 = 0


    def __check_collide(self):
        # 1.子弹摧毁敌机
        shooted=pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
       
        sound_enemydown=SoundSprite("sound/enemy0_down.mp3")
        if len(shooted) > 0:
            sound_enemydown.play()
            # print("碰撞的个数:{0}".format(len(shooted)))

        return len(shooted)

        # 2.敌机撞毁英雄
        enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True)
       
        if len(enemies) > 0:

            self.hero.kill()
            # 结束游戏
            PlaneGame.__game_over()


    def __update_sprites(self):
        self.back_group.update()
        self.back_group.draw(self.screen)
        self.enemy_group.update()
        self.enemy_group.draw(self.screen)

        self.hero_group.update()
        self.hero_group.draw(self.screen)

        self.hero.bullets.update()
        self.hero.bullets.draw(self.screen)


    # 下面的是静态方法
    @staticmethod
    def __game_over():
        print("游戏结束啦!!!")
        pygame.quit()
        exit()


if __name__ == '__main__':
    # 创建游戏对象
    game = PlaneGame()
    # 启动游戏
    game.start_game()

 Note: The code has been modified on the original basis in two places:
1. Added sound and sound effects

2. Change the original bullet to horizontal

2. Code Analysis

(1) The overall structure of the code

1. Main() is defined

We can see that the main function defines an object whose method is start_game and runs this method.

2. Create game objects

 Creating a game object mainly consists of the following parts:

(1) Initialization

(2) Generate sprite body and group

(3) Contents of starting the game

(4) Detect events and process them

(5) Detect collisions between groups and sprites

(6) Update display

(7) Exit

3. Start the game

(1) Set refresh frame rate

(2).Event monitoring

(3) Collision detection

(4) Update/draw sprite group

(5) Update display

(2) Partial code analysis

1. Definition of elves

(1) Create a GameSprite sprite, the parent class is pygame.sprite.Sprite
class GameSprite( pygame.sprite.Sprite ):
    def __init__(self):
        #Inherit the parent class method
        super().__init__()

        ....

    def update(self):
      ......

 (2) Define Background, Enemy, Hero, Bullet, the parent class is GameSprite

Background sprite:

class Background(GameSprite): 
    def __init__(self, is_alt=False):
       .....

    def update(self):
        ......

Nemesis

class Enemy(GameSprite):
    def __init__(self):
       .......

    def update(self):
       ........

Hero Elf

  class Hero(GameSprite):
    def __init__(self):
        .......

    def update(self):..
       .........

    def fire(self,num):  

          ......

Bullet sprite 2,
class Bullet( GameSprite ):
    def __init__(self):
     ..........

    def update(self):
     .............

2. Definition of the main game category

(1) Initialize the game

    def __init__(self):
           .......

(2) Create sprites

    def __create_sprites(self):
         ........

(3) Start the game

    def start_game(self):
        ...........

(4) Detecting collisions

    def __check_collide(self):
       .......

(5) Sprite update
    def __update_sprites(self):
       
   (6) Game over
    def __game_over():
        

   3. The main program runs

if __name__ == '__main__':
    # Create game object
    game = PlaneGame()
    # Start game
    game.start_game()

4. Detailed analysis

(1) Collision detection

        # 1. Bullets destroy enemy planes
       shoted=pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
        # 2. Enemy planes destroy heroes
        enemies = pygame.sprite.spritecollide(self.hero.hero, self .enemy_group, True)
   two detection methods, one is group-to-group collision detection, and the other is individual-to-group detection

Grammar description:

pygame.sprite.groupcollide() – Collision detection for all sprites in two sprite groups

groundcollide(group1,group2,dokill1,dokill2,collided = None)

If dokill1=True: If group1 and group2 collide, the elves in group1 will be automatically destroyed

If dokill2=True: If group1 and group2 collide, the sprites in group2 will be automatically destroyed

pygame.sprite.spritecollide() — determine the collision between a sprite and sprites in the specified sprite group


spritecollide(sprite, group, dokill, collided = Noone)

If the dokill value is True, the collided sprites in the specified sprite group will be automatically removed

The collided parameter is the callback function used to calculate the collision. If not specified, each sprite must have a rect attribute

The return value is a list of sprites that collide with sprites in the sprite group

(2) Group update and binding

        self.enemy_group.update()
        self.enemy_group.draw(self.screen)

   Grammar description:

Group.draw(surface)

Description: Call surface.blit() for each sprite in the sprite group in turn, and draw the sprites in the sprite group on the surface in turn

Group.update()

Description: Call the update() method for each sprite in the sprite group in turn, and the update() method needs to be implemented in the sprite class you define.

(3) Other details

For other issues such as keyboard operation, music playback, refresh rate, event operation, etc., please refer to my previous articles, which have corresponding introductions. I believe you can figure out the entire code.

3. Experience

Through the analysis and study of the code segment, the main gains are:

1. The overall structure of the code segment is reasonable, making full use of sprites and groups. Compared with the Rect object operation, there are fewer cycles and the error rate of the code is reduced.

2. The code is well modularized and easy to understand, which is a great inspiration for me to write code in the next step.

3. The application of the code to sprites and groups is very inspiring to individuals and also very useful for reference


 

Guess you like

Origin blog.csdn.net/sygoodman/article/details/124395983