Pygame Realizes Airplane Wars - Third Edition - Copy Edition

Table of contents

 

1. Function introduction:

Two, the original code

(1) Wizard definition part

(2) Game running part

3. Code analysis

(1) Implementation of the game start interface

(2) Two-player single-player game control

(3) Using custom events

(4) Collision detection using Sprite and Group

(5) Obtain keyboard control keys


On the basis of in-depth study and research on the second edition, according to the mastery of the principles of Sprite and Group applications in the second edition, and in line with the principle that learning is continuous tossing, according to the second edition of Airplane Wars, I have completed the simulation Write version of Aircraft Wars. Not much to say, first upload the original code, please don't make fun of it!

1. Function introduction:

1. On the game start interface, you can choose single-player or double-player game

2. Two-machine combat is possible, with different bullets

3. Heroes have a limit on the number of planes

4. The enemy plane has a BOSS, which can fire bullets

5. There are sound effects and music

6. There are props for adding bullets, up to 6 rounds of bullets can be added, and the bullets will be cleared to 1 after the hero dies

Two, the original code

(1) Wizard definition part

#引入库
import random
import pygame
# from pygame.locals import *

#系统常量设置
#窗口的设置
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
# 刷新的帧率
FRAME_PER_SEC = 60
# 创建敌机的定时器常量,自定义事件
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 英雄发射子弹时间,自定义事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1
#敌机发射子弹的时间,自定义事件

#出现加子弹道具的事件
CREAT_BULLET_ADD_ENENT =pygame.USEREVENT + 2

#出现敌机发射子弹的事件
CREAT_ENEMYPLANE_FIRE_ENENT = pygame.USEREVENT + 3
#英雄同时发射的子弹的个数
NUM_BULLENT_FIRE_HERO1 = 1
NUM_BULLENT_FIRE_HERO2 = 1



#文件引用设置
image_background = "./image/background.png"
image_enemy = "./image/enemy0.png"
image_enemy1 ="./image/enemy1.png"
image_hero1 = "./image/hero1.png"
image_hero2 = "./image/hero2.png"
image_addbullet ="./image/prop_type_0.png"

image_bullet0 = "./image/bullet.png"
image_bullet1 = "./image/bullet1.png"
image_bullet2 = "./image/bullet2.png"


font_path = "font/STXINGKA.TTF"

sound_bullet = "sound/bullet.mp3"
sound_ememydown0 = "sound/enemy0_down.mp3"
sound_backround = "sound/game_music.mp3"

#精灵定义
#创建GameSprite精灵
class GameSprite(pygame.sprite.Sprite):
    """输入路径和是否为标签"""
    def __init__(self, file_name, is_label=False,content=None,size=30):
        #继承父类方法
        super().__init__()
        if not is_label:
            #为图片输入时
            self.image = pygame.image.load(file_name) #通过图像生成surface对象
            self.rect = self.image.get_rect() #生成rect对象
        else:
            #为文字输入时
            # self.myfont = pygame.font.SysFont(file_name, size)  #注意:用sysfont不支持汉字的显示,改成font后显示
            self.myfont = pygame.font.Font(file_name, size)
            self.image = self.myfont.render(content, True, (0, 0, 0))
            self.rect = self.image.get_rect()  # 生成rect对象
        #以上代码生成surface对象image和对应原rect对象
        # #返回image和rect
        # return self.image,self.rect

    #实现移动
    def update(self,x_speed=0,y_speed=0):
        self.rect.x += x_speed
        self.rect.y += y_speed

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

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

#敌机类
class Enemy(GameSprite):
    def __init__(self,filename,bottom_speed_num=1,top_speed_num=3):
        # 1.调用父类方法,创建敌机精灵,同时指定敌机图片
        super().__init__(filename)
        # 2.指定敌机的初始随机速度
        self.speed = random.randint(bottom_speed_num, top_speed_num)

        # 3.指定敌机的初始随机位置
        self.rect.bottom = 0
        max_x = SCREEN_RECT.width - self.rect.width
        self.rect.x = random.randint(0, max_x)

        # 创建敌机子弹精灵组
        self.emeny_bullet_group = pygame.sprite.Group()

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

    def enemyfire(self,file_bullet,num=1):  #修改:2022-4-13 添加num参数,并根据数量控制好子弹位置
        print("敌机发射子弹。。。")
        bullet_num = num  #单独定义同时发射的子弹数可以为后期游戏过程中改变子弹发射数作基础

        for i in range(0,bullet_num):
            # 1.创建子弹精灵
            emeny_bullet = Bullet(file_bullet,3)
            # 2.设置精灵的位置
            emeny_bullet.rect.bottom = self.rect.y + self.rect.height
            emeny_bullet.rect.centerx = self.rect.centerx - 20 * (i - (bullet_num-1)/2)
            # emeny_bullet.rect.centerx = self.rect.centerx
            print("敌机子弹的位置,top为{0};X中心为{1}".format(emeny_bullet.rect.bottom,emeny_bullet.rect.centerx))
            # 3.将精灵添加到精灵组
            self.emeny_bullet_group.add(emeny_bullet)
            # print(len(self.emeny_bullet_group)) #有精灵却不能在planegame调用??

            # return self.emeny_bullet_group
            #加入方法也有问题,返回组的话没有数据
            # print(emeny_bullet)

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

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

    def update(self,x_speed=0,y_speed=0):
        super().update(x_speed, y_speed)
        # # 英雄在水平方向运行
        # 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,file_bullet):  #修改:2022-4-13 添加num参数,并根据数量控制好子弹位置
        # print("发射子弹。。。")
        bullet_num = num  #单独定义同时发射的子弹数可以为后期游戏过程中改变子弹发射数作基础

        for i in range(0,bullet_num):
            # 1.创建子弹精灵
            bullet = Bullet(file_bullet,-3)
            # 2.设置精灵的位置
            bullet.rect.bottom = self.rect.y
            bullet.rect.centerx = self.rect.centerx - 20 * (i - (bullet_num-1)/2)

            # 3.将精灵添加到精灵组
            self.bullets.add(bullet)

class Bullet(GameSprite):
    def __init__(self,filename,speed):
        # 调用父类方法,设置子弹图片,设置初始速度
        super().__init__(filename)
        self.yspeed=speed

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

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

class Label(GameSprite):
    def __init__(self,content,x,y): # 引用时需要输入内容及相应的位置
        # 调用父类方法,设置文字字体位置
        super().__init__(font_path, True, content,30)
        #设置文字显示位置
        self.rect.x = x
        self.rect.y = y


    def update(self,x_speed=0,y_speed=0):
        # 调用父类方法,标签位置不动
        super().update(0,0)


#创建SoundSprite精灵
class SoundSprite(pygame.sprite.Sprite):
    def  __init__(self,sound_name):
        super().__init__()
        self.sound_name=sound_name

    def play(self,is_music=False):
        self.sound = pygame.mixer.Sound(self.sound_name)
        pygame.mixer.Sound.play(self.sound)
        if is_music :
            self.music=pygame.mixer.music.load(self.sound_name)
            pygame.mixer.music.play(-1)

(2) Game running part

import pygame.sprite
from planesprite_new import *


class PlaneGame(object):
    """飞机大战主游戏"""
    # 用于计分的变量
    Score = 0
    #英雄是否死亡
    death_of_hero1 = True
    death_of_hero2 = True
    #游戏是否开始
    begin_game = False

    #游戏是否暂停
    pause_game = False

    #英雄的同时发的子弹数
    NUM_HERO_01 = 1
    NUM_HERO_02 = 1

    #英雄的飞机数
    NUM_PLANE_HERO1 = 100
    NUM_PLANE_HERO2 = 10

    #敌机1的数量
    NUM_ENEMY = 0
    #大飞机是否出现
    has_bigenemy = False

    ENEMYLIFE_TOP = 100 # 大飞机的寿命数
    bigenemylife = ENEMYLIFE_TOP





    def __init__(self):

        print("游戏初始化")
        pygame.init()
        pygame.mixer.init() #初始化声音

        pygame.display.set_caption("飞机大战")
        #加载游戏开始界面
        while not self.begin_game:
            self.__creat_select_scence()

        # 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)
        pygame.time.set_timer(CREAT_BULLET_ADD_ENENT,50000)
        pygame.time.set_timer(CREAT_ENEMYPLANE_FIRE_ENENT,3000)

        # 5.播放背景乐
        music = SoundSprite(sound_backround)
        music.play(True)

    def __create_sprites(self):

        #创建背景及背景组
        bg1 = Background()
        bg2 = Background(True)
        self.back_group = pygame.sprite.Group(bg1, bg2)
        # 创建敌机的精灵组
        self.enemy_group = pygame.sprite.Group()
        #创建大飞机的敌机组
        self.bigenemy_group = pygame.sprite.Group()

        #创建加子弹的精灵组
        self.addbullet_group = pygame.sprite.Group()
        # 创建英雄精灵和精灵组
        self.hero_group=pygame.sprite.Group()
        #必须先创建,否则后面使用会报错!
        self.hero1 = Hero(image_hero1)
        self.hero2 = Hero(image_hero2)

        #根据选择的玩家情况进行加组
        if not self.death_of_hero1:
            self.hero_group.add(self.hero1)

        if not self.death_of_hero2:
            self.hero_group.add(self.hero2)
        #此处不能判断.因为此处均为False,因此都不会生成,这里正常加组和生成,后面update控制是否输出即可
        # 由于这里加了判断,所以导致后面hero1和hero2没有定义,并且不能加组,但出现单机时第二架飞机还出现的问题!
        #将self.__creat_select_scence()放置到加载前面,并且
        #最后总结,将加载放在前面,而此处需要生成两个英雄,但是根据需要进行加组即可,后面判断显示情况即可
        # self.hero_group = pygame.sprite.Group(self.hero,self.hero2)
        #创建文本显示精灵及精灵组,代码在此处不实时更新,转移到更新处
        # self.label_score =Label("分数:"+ str(self.Score),0,0)
        # self.label_group=pygame.sprite.Group(self.label_score)
        #2020-4-16日实现文本标签的添加,并显示
    def __creat_select_scence(self):
        
        # 1-1.创建选择界面,后添加的
        label_single = Label("单人游戏", 80, 200)
        label_single.rect.right = 240 - 40
        label_double = Label("双人游戏", 280, 200)
        label_double.rect.left = 240 + 40
        label_tip = Label("提示,按空格键暂停游戏", 240, 500)
        label_tip.rect.centerx = 240
        self.screen1 = pygame.display.set_mode(SCREEN_RECT.size)
        self.screen1.fill(color=pygame.Color(255,255,255))
        # print("运行到加组位置")
        #判断输入,
        for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type == pygame.QUIT:
                print("游戏结束,退出程序!!")
                PlaneGame.__game_over()
        mouse_position = pygame.mouse.get_pos() #获得鼠标位置
        mouse_x = mouse_position[0]
        mouse_y = mouse_position[1]
        mouse_pressed = pygame.mouse.get_pressed()#获得鼠标按键
        is_leftbutton = mouse_pressed[0]
        #判断鼠标是否在标签上,是否点击
        if (mouse_x > label_single.rect.x) and (mouse_x < (label_single.rect.x + label_single.rect.width)) and (
                mouse_y > label_single.rect.y) and (mouse_y < (label_single.rect.y + label_single.rect.height)):
            self.lable_single = Label("单人游戏", 80, 200)
            if is_leftbutton:
                self.death_of_hero1 = False
                self.death_of_hero2 = True
                self.begin_game = True
        if (mouse_x > label_double.rect.x) and (mouse_x < (label_double.rect.x + label_double.rect.width)) and (
                mouse_y > label_double.rect.y) and (mouse_y < (label_double.rect.y + label_double.rect.height)):
            if is_leftbutton:
                self.death_of_hero1 = False
                self.death_of_hero2 = False
                self.begin_game = True

        self.begin_group = pygame.sprite.Group(label_single, label_double,  label_tip)
        # 加载开始菜单label组

        self.begin_group.draw(self.screen1)

        pygame.display.update()

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


        while self.begin_game:
            # 1.设置刷新帧率
            self.clock.tick(FRAME_PER_SEC)

            # 2.事件监听
            self.__event_handler()

            if not self.pause_game:  #空格键决定,每次都对布尔值取反!
                # 3.碰撞检测
                self.__check_collide()

                # 4.更新/绘制精灵组
                self.__update_sprites()

                # 5.更新显示
                pygame.display.update()

    def __event_handler(self):
        # if self.begin_game:
        for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type == pygame.QUIT:
                print("游戏结束,退出程序!!")
                PlaneGame.__game_over()
            elif event.type == CREATE_ENEMY_EVENT:
                 # print("敌机出场。。。")
                # 创建敌机精灵
                self.enemy = Enemy(image_enemy,2,3)
                self.enemy_group.add(self.enemy)

                self.NUM_ENEMY += 1
                if self.NUM_ENEMY %50 == 0:
                    #TODO 修改飞机敌机出现的频次
                    self.bigenemy =Enemy(image_enemy1,1,1)
                    #TODO 加入大敌机,能发射子弹,生命力为10,1
                    # #创建大敌机子弹
                    self.bigenemy.enemyfire(image_bullet0,4)
                    self.has_bigenemy = True
                    self.bigenemylife = self.ENEMYLIFE_TOP
                    #加入大敌机组
                    self.bigenemy_group.add(self.bigenemy)


            elif event.type == HERO_FIRE_EVENT:
                if not self.death_of_hero1:
                    self.hero1.fire(self.NUM_HERO_01,image_bullet1)
                if not self.death_of_hero2:
                    self.hero2.fire(self.NUM_HERO_02,image_bullet2)
            # 没有子弹,要找到原因,原因是没有将产生子弹并加入刷新
            #产生加子弹的道具事件
            elif event.type == CREAT_BULLET_ADD_ENENT:
                #利用敌机的精灵,产生加子弹的道具
                self.addbullet = Enemy(image_addbullet)
                self.addbullet_group.add(self.addbullet)

            elif event.type == CREAT_ENEMYPLANE_FIRE_ENENT:
                if self.has_bigenemy:
                    self.bigenemy.enemyfire(image_bullet0, 4)
                    # self.bigenemy.enemyfire(image_bullet0, 6)
                    # self.bigenemy.enemyfire(image_bullet0, 8)

        # 使用键盘提供的方法获取键盘按键
        keys_pressed = pygame.key.get_pressed()
            # 判断元祖中对应的按键索引值

            #hero控制键设置
        if keys_pressed[pygame.K_RIGHT]:
                # print("向右移动。。。")
            self.hero1.update(3,0)
        elif keys_pressed[pygame.K_LEFT]:
            self.hero1.update(-3,0)
        else:
            self.hero1.update(0,0)

        if keys_pressed[pygame.K_UP]:

            self.hero1.update(0,-3)
        elif keys_pressed[pygame.K_DOWN]:
            self.hero1.update(0,3)
        else:
            self.hero1.update(0,0)

        if keys_pressed[pygame.K_d]:
                # print("向右移动。。。")
            self.hero2.update(3,0)
        elif keys_pressed[pygame.K_a]:
            self.hero2.update(-3,0)
        else:
            self.hero2.update(0,0)

            # hero2控制键设置
        if keys_pressed[pygame.K_w]:

            self.hero2.update(0,-3)
        elif keys_pressed[pygame.K_s]:
                self.hero2.update(0,3)
        else:
            self.hero2.update(0,0)

        if keys_pressed[pygame.K_SPACE]:
            self.pause_game = not self.pause_game

    def __check_collide(self):
        # 1.子弹摧毁敌机
        self.sound_enemydown = SoundSprite(sound_ememydown0)
        #英雄1击中飞机
        shooted1=pygame.sprite.groupcollide(self.hero1.bullets, self.enemy_group, True, True)
        if len(shooted1) > 0:
            self.Score += len(shooted1)*100
            self.sound_enemydown.play()
            #将Score定义为playgame的类变量后,避免了相互之间引用,从而直接获得分数进行使用,过程中的调用设为全局变量的方法不好!
            # print(self.Score)
        shooted2 = pygame.sprite.groupcollide(self.hero2.bullets, self.enemy_group, True, True)
        if len(shooted2) > 0:
            self.Score += len(shooted2) * 100
            self.sound_enemydown.play()

        # 2.敌机撞毁英雄
        enemies1 = pygame.sprite.spritecollide(self.hero1, self.enemy_group, True)
        enemies2 = pygame.sprite.spritecollide(self.hero2, self.enemy_group, True)
        if len(enemies1) > 0:
            self.NUM_PLANE_HERO1-=1
            self.NUM_HERO_01 = 1
            if self.NUM_PLANE_HERO1 == 0:
                self.death_of_hero1 = True
                self.hero1.kill()

        if len(enemies2) > 0:
            self.NUM_PLANE_HERO2 -= 1
            self.NUM_HERO_02 = 1

            if self.NUM_PLANE_HERO2 == 0:
                self.death_of_hero2 = True
                self.hero2.kill()

        if self.death_of_hero1 and self.death_of_hero2:
             # 结束游戏
            PlaneGame.__game_over()

        #英雄战机听得到子弹增加道具
        #英雄1吃到道具
        eatted1 = pygame.sprite.spritecollide(self.hero1, self.addbullet_group, False)
        if len(eatted1)  > 0:
            #最多加到5个子弹
            if  self.NUM_HERO_01 <= 5:
                self.NUM_HERO_01 += 1
                self.addbullet_group.empty()
        # 英雄2吃到道具
        eatted2 = pygame.sprite.spritecollide(self.hero2, self.addbullet_group, False)
        if len(eatted2) > 0:
            if self.NUM_HERO_02 <=5:
                self.NUM_HERO_02 += 1
                self.addbullet_group.empty()
        #大敌机碰撞英雄

        #如果有大敌机,子弹互消,碰撞英雄
        if self.has_bigenemy:
            #子弹互消
            pygame.sprite.groupcollide(self.hero1.bullets, self.bigenemy.emeny_bullet_group, True, True)
            pygame.sprite.groupcollide(self.hero2.bullets, self.bigenemy.emeny_bullet_group, True, True)
            #英雄撞击大飞机
            enemies3 = pygame.sprite.spritecollide(self.hero1, self.bigenemy_group, True)
            enemies4 = pygame.sprite.spritecollide(self.hero2, self.bigenemy_group, True)
            #英雄子弹击中大飞机
            #TODO
            shooted3 = pygame.sprite.groupcollide(self.bigenemy_group, self.hero1.bullets, False,True)
            shooted4 = pygame.sprite.groupcollide(self.bigenemy_group, self.hero2.bullets, False,True)

            if len(shooted3)>0 or len(shooted4)>0:
                print("英雄子弹击中数为{0},大敌机的生命还有{1}".format(len(shooted3), self.bigenemylife))
                self.bigenemylife -=1
                if self.bigenemylife == 0 :
                    self.bigenemy.kill()
                    self.has_bigenemy = False
                    self.Score += len(shooted1) * 1000


            #英雄碰到大飞机子弹
            enemies5 = pygame.sprite.spritecollide(self.hero1, self.bigenemy.emeny_bullet_group, True)
            enemies6 = pygame.sprite.spritecollide(self.hero2, self.bigenemy.emeny_bullet_group, True)
            if len(enemies3)>0 or len(enemies5) >0:
                self.NUM_PLANE_HERO1 -= 1
                if self.NUM_PLANE_HERO1 == 0:
                    self.death_of_hero1 = True
                    self.hero1.kill()
            if len(enemies4)>0 or len(enemies6) >0:
                self.NUM_PLANE_HERO2 -= 1
                if self.NUM_PLANE_HERO1 == 0:
                    self.death_of_hero1 = True
                    self.hero1.kill()

    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.bigenemy_group.update()
        self.bigenemy_group.draw(self.screen)

        #加子弹道具组更新
        self.addbullet_group.update()
        self.addbullet_group.draw(self.screen)

        #英雄组更新
        self.hero_group.update()
        self.hero_group.draw(self.screen)

        #英雄子弹组更新
        if not self.death_of_hero1:
            self.hero1.bullets.update()
            self.hero1.bullets.draw(self.screen)
        if not self.death_of_hero2:
            self.hero2.bullets.update()
            self.hero2.bullets.draw(self.screen)

        #大敌机子弹组更新
        if self.has_bigenemy:
            self.bigenemy.emeny_bullet_group.update()
            self.bigenemy.emeny_bullet_group.draw(self.screen)

        # for enemy in self.enemy_group:
        #     enemy.emeny_bullet_group.update(y_speed=3)
        #     enemy.emeny_bullet_group.draw(self.screen)


        #一开始在一起进行了定义和加组,但是由于不能循环刷新,所以分数不变化,只好改到此处
        self.label_score = Label("分数:" + str(self.Score), 0, 0)
        self.label_group = pygame.sprite.Group(self.label_score)

        #显示英雄飞机数
        self.labe_number_of_hero1 = Label("英雄1飞机数:"+str(self.NUM_PLANE_HERO1),0,0)
        self.labe_number_of_hero1.rect.bottomright = [480,700]
        if not self.death_of_hero1:
            self.label_group.add(self.labe_number_of_hero1)

        self.labe_number_of_hero2 = Label("英雄2飞机数:" + str(self.NUM_PLANE_HERO2), 0, 0)
        self.labe_number_of_hero2.rect.bottomleft = [0, 700]
        if not self.death_of_hero2:
            self.label_group.add(self.labe_number_of_hero2)



        self.label_group.update()
        self.label_group.draw(self.screen)

        # self.enemy_bullet_group.update()
        # self.emeny_bullet_group.draw(self.screen)





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


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

3. Code analysis

(1) Implementation of the game start interface

1. Generate label_single, label_double, label_tip sprites and the Surface object of the start interface

2. Determine the event, get the mouse and exit events, determine whether it is within the range of the sprite and whether the left button is clicked

3. Add group and display

code show as below:

 def __creat_select_scence(self):
        
        # 1-1.创建选择界面,后添加的
        label_single = Label("单人游戏", 80, 200)
        label_single.rect.right = 240 - 40
        label_double = Label("双人游戏", 280, 200)
        label_double.rect.left = 240 + 40
        label_tip = Label("提示,按空格键暂停游戏", 240, 500)
        label_tip.rect.centerx = 240
        self.screen1 = pygame.display.set_mode(SCREEN_RECT.size)
        self.screen1.fill(color=pygame.Color(255,255,255))
        # print("运行到加组位置")
        #判断输入,
        for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type == pygame.QUIT:
                print("游戏结束,退出程序!!")
                PlaneGame.__game_over()
        mouse_position = pygame.mouse.get_pos() #获得鼠标位置
        mouse_x = mouse_position[0]
        mouse_y = mouse_position[1]
        mouse_pressed = pygame.mouse.get_pressed()#获得鼠标按键
        is_leftbutton = mouse_pressed[0]
        #判断鼠标是否在标签上,是否点击
        if (mouse_x > label_single.rect.x) and (mouse_x < (label_single.rect.x + label_single.rect.width)) and (
                mouse_y > label_single.rect.y) and (mouse_y < (label_single.rect.y + label_single.rect.height)):
            self.lable_single = Label("单人游戏", 80, 200)
            if is_leftbutton:
                self.death_of_hero1 = False
                self.death_of_hero2 = True
                self.begin_game = True
        if (mouse_x > label_double.rect.x) and (mouse_x < (label_double.rect.x + label_double.rect.width)) and (
                mouse_y > label_double.rect.y) and (mouse_y < (label_double.rect.y + label_double.rect.height)):
            if is_leftbutton:
                self.death_of_hero1 = False
                self.death_of_hero2 = False
                self.begin_game = True

        self.begin_group = pygame.sprite.Group(label_single, label_double,  label_tip)
        # 加载开始菜单label组

        self.begin_group.draw(self.screen1)

        pygame.display.update()

Grammar hints:

①mouse_position = pygame.mouse.get_pos() #Get the mouse position

pygame.mouse.get_pos() Get the position of the mouse cursor.

get_pos() -> (x, y)

Returns the coordinates (x, y) of the mouse cursor. This coordinate is based on the upper left corner of the window. The cursor position can be positioned outside the window, but is usually enforced within the screen.

代码:mouse_x = mouse_position[0]
           mouse_y = mouse_position[1]

It is to get the x and y values ​​of the mouse

② mouse_pressed = pygame.mouse.get_pressed()#Get the mouse button
        is_leftbutton = mouse_pressed[0]

pygame.mouse.get_pressed()
Gets the status of the mouse button (whether it is pressed).

get_pressed() -> (button1, button2, button3)

Returns a list of Boolean values ​​representing all mouse button presses. True means the mouse button is being pressed when this method is called.

Note 1: It is best to use the pygame.event.wait() method or pygame.event.get() method to get all mouse events, and then check that all events are MOUSEBUTTONDOWN, MOUSEBUTTONUP or MOUSEMOTION.

Note 2: Remember to call the pygame.event.get() method before using this method, otherwise this method will not work.

Note 3: (button1, button2, button3), generally the left button, middle button, and right button, sometimes there are differences depending on the system.

(2) Two-player single-player game control

1. Set Boolean value

#英雄是否死亡
death_of_hero1 = True
death_of_hero2 = True

2. According to the selection, set the value 

#判断鼠标是否在标签上,是否点击
        if (mouse_x > label_single.rect.x) and (mouse_x < (label_single.rect.x + label_single.rect.width)) and (
                mouse_y > label_single.rect.y) and (mouse_y < (label_single.rect.y + label_single.rect.height)):
            self.lable_single = Label("单人游戏", 80, 200)
            if is_leftbutton:
                self.death_of_hero1 = False
                self.death_of_hero2 = True
                self.begin_game = True
        if (mouse_x > label_double.rect.x) and (mouse_x < (label_double.rect.x + label_double.rect.width)) and (
                mouse_y > label_double.rect.y) and (mouse_y < (label_double.rect.y + label_double.rect.height)):
            if is_leftbutton:
                self.death_of_hero1 = False
                self.death_of_hero2 = False
                self.begin_game = True

3. According to the selected situation, select and add groups

#必须先创建,否则后面使用会报错!
        self.hero1 = Hero(image_hero1)
        self.hero2 = Hero(image_hero2)

        #根据选择的玩家情况进行加组
        if not self.death_of_hero1:
            self.hero_group.add(self.hero1)

        if not self.death_of_hero2:
            self.hero_group.add(self.hero2)

It should be noted here that all heroes must be created. If they are generated after judgment, an error will be reported.

(3) Using custom events

1. Create a custom event constant

# 创建敌机的定时器常量,自定义事件
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 英雄发射子弹时间,自定义事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1
#出现加子弹道具的事件
CREAT_BULLET_ADD_ENENT =pygame.USEREVENT + 2
#出现敌机发射子弹的事件
CREAT_ENEMYPLANE_FIRE_ENENT = pygame.USEREVENT + 3

pygame.USEREVENT, please refer to my previous articles for related content, so I won’t say more here.

2. Set the timer event and set the generation interval

 # 4.设置定时器事件
        pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
        pygame.time.set_timer(HERO_FIRE_EVENT, 500)
        pygame.time.set_timer(CREAT_BULLET_ADD_ENENT,50000)
        pygame.time.set_timer(CREAT_ENEMYPLANE_FIRE_ENENT,3000)

pygame.time.set_timer()
function: repeatedly create events on the event queue

Attributes:

set_timer(eventid, milliseconds) -> None
set_timer(eventid, milliseconds, once) -> None
sets the event type to appear in the event queue every given milliseconds. The first event doesn't appear until a certain amount of time has elapsed.
Each event type can have a separate timer attached. It is better to use the values ​​of pygame.USEREVENT and pygame.NUMEVENTS.
To disable the timer for events, set the milliseconds parameter to 0.
If the once parameter is True, the timer is only sent once.

3. Monitor events and deal with them accordingly

        for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type == pygame.QUIT:
                print("游戏结束,退出程序!!")
                PlaneGame.__game_over()
            elif event.type == CREATE_ENEMY_EVENT:
                 # print("敌机出场。。。")
                # 创建敌机精灵
                self.enemy = Enemy(image_enemy,2,3)
                self.enemy_group.add(self.enemy)

                self.NUM_ENEMY += 1
                if self.NUM_ENEMY %50 == 0:
                    #TODO 修改飞机敌机出现的频次
                    self.bigenemy =Enemy(image_enemy1,1,1)
                    #TODO 加入大敌机,能发射子弹,生命力为10,1
                    # #创建大敌机子弹
                    self.bigenemy.enemyfire(image_bullet0,4)
                    self.has_bigenemy = True
                    self.bigenemylife = self.ENEMYLIFE_TOP
                    #加入大敌机组
                    self.bigenemy_group.add(self.bigenemy)


            elif event.type == HERO_FIRE_EVENT:
                if not self.death_of_hero1:
                    self.hero1.fire(self.NUM_HERO_01,image_bullet1)
                if not self.death_of_hero2:
                    self.hero2.fire(self.NUM_HERO_02,image_bullet2)
            #产生加子弹的道具事件
            elif event.type == CREAT_BULLET_ADD_ENENT:
                #利用敌机的精灵,产生加子弹的道具
                self.addbullet = Enemy(image_addbullet)
                self.addbullet_group.add(self.addbullet)

            elif event.type == CREAT_ENEMYPLANE_FIRE_ENENT:
                if self.has_bigenemy:
                    self.bigenemy.enemyfire(image_bullet0, 4)
                   

(4) Collision detection using Sprite and Group

1. Use two collision modes, one is the collision between groups, and the other is the collision between individuals and groups. The usage of grammar has been introduced in the previous article, so I won’t say much here.

2. Mainly detect the following collisions:

(1) Hero bullets and enemy planes

(2) Heroes and enemy planes

(3) Heroes and bullet props

(4) Hero bullets and enemy bullets

(5) Hero Bullet and Great Enemy

The specific code is as follows:

    def __check_collide(self):
        # 1.子弹摧毁敌机
        self.sound_enemydown = SoundSprite(sound_ememydown0)
        #英雄1击中飞机
        shooted1=pygame.sprite.groupcollide(self.hero1.bullets, self.enemy_group, True, True)
        if len(shooted1) > 0:
            self.Score += len(shooted1)*100
            self.sound_enemydown.play()
        shooted2 = pygame.sprite.groupcollide(self.hero2.bullets, self.enemy_group, True, True)
        if len(shooted2) > 0:
            self.Score += len(shooted2) * 100
            self.sound_enemydown.play()

        # 2.敌机撞毁英雄
        enemies1 = pygame.sprite.spritecollide(self.hero1, self.enemy_group, True)
        enemies2 = pygame.sprite.spritecollide(self.hero2, self.enemy_group, True)
        if len(enemies1) > 0:
            self.NUM_PLANE_HERO1-=1
            self.NUM_HERO_01 = 1
            if self.NUM_PLANE_HERO1 == 0:
                self.death_of_hero1 = True
                self.hero1.kill()

        if len(enemies2) > 0:
            self.NUM_PLANE_HERO2 -= 1
            self.NUM_HERO_02 = 1

            if self.NUM_PLANE_HERO2 == 0:
                self.death_of_hero2 = True
                self.hero2.kill()

        if self.death_of_hero1 and self.death_of_hero2:
             # 结束游戏
            PlaneGame.__game_over()

        #英雄战机听得到子弹增加道具
        #英雄1吃到道具
        eatted1 = pygame.sprite.spritecollide(self.hero1, self.addbullet_group, False)
        if len(eatted1)  > 0:
            #最多加到5个子弹
            if  self.NUM_HERO_01 <= 5:
                self.NUM_HERO_01 += 1
                self.addbullet_group.empty()
        # 英雄2吃到道具
        eatted2 = pygame.sprite.spritecollide(self.hero2, self.addbullet_group, False)
        if len(eatted2) > 0:
            if self.NUM_HERO_02 <=5:
                self.NUM_HERO_02 += 1
                self.addbullet_group.empty()
        #大敌机碰撞英雄

        #如果有大敌机,子弹互消,碰撞英雄
        if self.has_bigenemy:
            #子弹互消
            pygame.sprite.groupcollide(self.hero1.bullets, self.bigenemy.emeny_bullet_group, True, True)
            pygame.sprite.groupcollide(self.hero2.bullets, self.bigenemy.emeny_bullet_group, True, True)
            #英雄撞击大飞机
            enemies3 = pygame.sprite.spritecollide(self.hero1, self.bigenemy_group, True)
            enemies4 = pygame.sprite.spritecollide(self.hero2, self.bigenemy_group, True)
            #英雄子弹击中大飞机
            #TODO
            shooted3 = pygame.sprite.groupcollide(self.bigenemy_group, self.hero1.bullets, False,True)
            shooted4 = pygame.sprite.groupcollide(self.bigenemy_group, self.hero2.bullets, False,True)

            if len(shooted3)>0 or len(shooted4)>0:
                self.bigenemylife -=1
                if self.bigenemylife == 0 :
                    self.bigenemy.kill()
                    self.has_bigenemy = False
                    self.Score += len(shooted1) * 1000


            #英雄碰到大飞机子弹
            enemies5 = pygame.sprite.spritecollide(self.hero1, self.bigenemy.emeny_bullet_group, True)
            enemies6 = pygame.sprite.spritecollide(self.hero2, self.bigenemy.emeny_bullet_group, True)
            if len(enemies3)>0 or len(enemies5) >0:
                self.NUM_PLANE_HERO1 -= 1
                if self.NUM_PLANE_HERO1 == 0:
                    self.death_of_hero1 = True
                    self.hero1.kill()
            if len(enemies4)>0 or len(enemies6) >0:
                self.NUM_PLANE_HERO2 -= 1
                if self.NUM_PLANE_HERO1 == 0:
                    self.death_of_hero1 = True
                    self.hero1.kill()

(5) Obtain keyboard control keys

1. Get the key value

2. Set the code corresponding to the key value as needed

# 使用键盘提供的方法获取键盘按键
        keys_pressed = pygame.key.get_pressed()
            # 判断元祖中对应的按键索引值

            #hero控制键设置
        if keys_pressed[pygame.K_RIGHT]:
                # print("向右移动。。。")
            self.hero1.update(3,0)
        elif keys_pressed[pygame.K_LEFT]:
            self.hero1.update(-3,0)
        else:
            self.hero1.update(0,0)

        if keys_pressed[pygame.K_UP]:

            self.hero1.update(0,-3)
        elif keys_pressed[pygame.K_DOWN]:
            self.hero1.update(0,3)
        else:
            self.hero1.update(0,0)

        if keys_pressed[pygame.K_d]:
                # print("向右移动。。。")
            self.hero2.update(3,0)
        elif keys_pressed[pygame.K_a]:
            self.hero2.update(-3,0)
        else:
            self.hero2.update(0,0)

            # hero2控制键设置
        if keys_pressed[pygame.K_w]:

            self.hero2.update(0,-3)
        elif keys_pressed[pygame.K_s]:
                self.hero2.update(0,3)
        else:
            self.hero2.update(0,0)

        if keys_pressed[pygame.K_SPACE]:
            self.pause_game = not self.pause_game

Code comments:

There are two ways to capture the keyboard in the code:

The first one: use the pygame.event.get() method in pygame to capture keyboard events. The keyboard events captured in this way must be counted once when they are pressed and then popped up.

 for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type == pygame.QUIT:
                print("游戏结束,退出程序!!")
                PlaneGame.__game_over()


The second type: in pygame, you can use pygame.key.get_pressed() to return all key tuples. By judging the keyboard constant, you can determine which key is pressed in the tuple. If it is pressed, the tuple will be This key information will exist. In this way, keyboard events can also be captured, and there is no need to press and then pop up, and there will be a response as soon as you press, and the flexibility is relatively high.

Sample code:

keys_pressed = pygame.key.get_pressed()
            # 判断元祖中对应的按键索引值

            #hero控制键设置
        if keys_pressed[pygame.K_RIGHT]:
                # print("向右移动。。。")
            self.hero1.update(3,0)
        elif keys_pressed[pygame.K_LEFT]:
            self.hero1.update(-3,0)

Other relevant knowledge has been introduced in previous articles. If you have any questions, you can search previous articles. If you want to discuss with me, you can leave a message, and we will make progress together.

Guess you like

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