今日Python练手小游戏——打飞机

by admin on 2019年12月15日

**打飞机文字版 

不要猜中的小游戏

Python编写微信打飞机小游戏(八)

 

  现在飞机已经能够带着血条突突突飞下来让玩家虐,于是我们给用户一点打飞机的成就感——计分系统以及难度递增机制。

  1、计分系统

  首先,我们在main()函数中添加全局变量(score)并初始化为零用以统计当前用户的打飞机得分:

score = 0  # 统计用户得分

  接下来只需要在敌机销毁时对score进行累加就可以了,我们在这里先给敌机标个价:小型敌机一架值500分,中型敌机一架值2000分,大型敌机一架值6000分,然后在敌机损毁时根据敌机类型选择加多少分,对于小型敌机:

            for each in small_enemies:  # 绘制小型敌机并自动移动
                if each.active:
                    # 绘制小型敌机
                else:
                    if e1_destroy_index == 0:
                        # 敌机损毁音效
                    if not (delay % 3):
                        # 播放损毁画面
                        if e1_destroy_index == 0:
                            score += 500  
                            # reset()

  中型敌机:

            for each in mid_enemies:  # 绘制中型敌机并自动移动
                if each.active:
                    # 绘制中型敌机# 绘制血槽
                else:
                    if e2_destroy_index == 0:
                        # 播放损毁音效
                    if not (delay % 3):
                        # 绘制损毁画面
                        if e2_destroy_index == 0:
                            score += 2000  
                            # reset()

  大型敌机:

            for each in big_enemies:  # 绘制大型敌机并自动移动
                if each.active:  # 如果飞机正常存在
                    # 绘制大型敌机# 绘制血槽
                    if each.rect.bottom == -50:
                        # 播放大型飞机的音效(循环播放)
                else:  # 如果飞机已撞毁
                    # 出场音效停止
                    if e3_destroy_index == 0:
                        # 播放飞机撞毁音效
                    if not (delay % 3):  # 每三帧播放一张损毁图片# 大型敌机有六张损毁图片
                        if e3_destroy_index == 0:  # 如果损毁图片播放完毕,则重置飞机属性
                            score += 6000  
                            # reset()

  需要指出一点,这里之所以添加“if e3_destroy_index ==
0:”是因为整个飞机损毁的过程是由四帧(或六帧)图像的播放来完成的,如果不加这个限制,则在飞机损毁过程中每播放一帧就加一次分,这样小型机和中型机损毁一次就要加4次分,大型机损毁一次就要加6次分,因此需要规定每次飞机损毁时只在播放损毁最后一帧画面之后再进行加分、复位,播放声音等操作。

  2、显示分数

  在游戏过程中,需要将玩家得分实时显示在界面的左上角,这就涉及到Pygame的font模块的应用。首先在main函数初始化过程(while循环之前)调用font模块成员函数创造系统字体对象:

score_font = pygame.font.SysFont(arial, 48)  # 定义分数字体

  这里的score_font对象采用系统字体“arial”,字号为48。之后通过调用字体对象的render()成员函数将当前分数存于字体对象中,并返回字体的surface格式对象,用以接下来的屏幕打印工作:

        score_text = score_font.render(Score : %s % str(score), True, color_white)
        screen.blit(score_text, (10, 5))

  这里先将当前分数通过str()函数转换成字符串,然后格式化到“Score:”的字符串中,第二个参数表示当前字体在打印时自动开启抗锯齿功能,第三个参数为字体颜色。建议将这两句代码放在while循坏体内比较靠前的位置,以便使代码更为整洁。运行程序,分数能够正确的、实时的显示在屏幕上,接下来我们来给游戏增加难度——难度分级递增机制。

  3、设计难度分级标准

  我们的目的是随着用户得分的提高,游戏对应难度也逐渐加大,首先需要在main()函数添加一个全局标志位level用以指示当前游戏处于那种难度:

level = 1  # 游戏难度级别

  接下来简单的制定以下难度门槛:分数处于(0,5000)为第一级难度,处于(5000,30000)为第二级难度,处于(30000,60000)为第三级难度:

        # ====================定义难度递进操作====================
        if level == 1 and score > 5000:  # 如果达到第二难度等级,则增加3架小型敌机,2架中型敌机,1架大型敌机,并提升小型敌机速度
            level = 2
       pass
     elif level == 2 and score > 30000:  # 如果达到第三难度等级
            level = 3
       pass
      elif level == 3 and score > 60000:  # 如果达到第四难度等级
            level = 4
        pass

  接下来开始根据对应难度等级来给游戏加难度,如何才算增加难度,无非是增加敌机数量,增加敌机速度,因此我们定义两个函数来完成这个功能,首先定义增加飞机数量的函数,刚好我们之前已经定义好了这个函数:

# ====================敌方飞机生成控制函数====================
def add_small_enemies(group1, group2, num):

def add_mid_enemies(group1, group2, num):

def add_big_enemies(group1, group2, num):

  关于这些函数的具体作用参见之前博文,接下来定义速度增加函数,原理就是增加敌机对象内部speed成员变量的值即可:

# ====================提升敌机速度====================
def inc_speed(target, inc):
    for each in target:
        each.speed += inc

  target为待加速的敌机精灵组,inc为加速的程度(大致可以成为加速度吧)。

  4、完善难度分级递增机制

  定义完“add_small_enemies”、“inc_speed”等操作函数后,我们给出完整的难度分级递增机制部分代码(建议放在while循环的开始部分):

        # ====================定义难度递进操作====================
        if level == 1 and score > 5000:  # 如果达到第二难度等级,则增加3架小型敌机,2架中型敌机,1架大型敌机,并提升小型敌机速度
            level = 2
            level_up_sound.play()
            add_small_enemies(small_enemies, enemies, 3)
            add_mid_enemies(mid_enemies, enemies, 2)
            add_big_enemies(big_enemies, enemies, 1)
            inc_speed(small_enemies, 1)
        elif level == 2 and score > 30000:  # 如果达到第三难度等级
            level = 3
            level_up_sound.play()
            add_small_enemies(small_enemies, enemies, 3)
            add_mid_enemies(mid_enemies, enemies, 2)
            add_big_enemies(big_enemies, enemies, 1)
            inc_speed(small_enemies, 1)
            inc_speed(mid_enemies, 1)
        elif level == 3 and score > 60000:  # 如果达到第四难度等级
            level = 4
            level_up_sound.play()
            add_small_enemies(small_enemies, enemies, 3)
            add_mid_enemies(mid_enemies, enemies, 2)
            add_big_enemies(big_enemies, enemies, 1)
            inc_speed(small_enemies, 1)
            inc_speed(mid_enemies, 1)
            inc_speed(big_enemies, 1)

  这段代码的分级机制很简单,每增加一级难度,就在之前的基础上添加三架小型敌机,两架中型敌机和一架大型敌机,同时将各个机型的速度均加一,在难度提升的瞬间还有特殊音效播放。ok程序到这里应该可以正确运行,试玩一下发现,当分数超过30000分时敌机已经铺天盖地的飞了下来,为了玩家的生存,我们将在下一篇博文中为我方飞机添加超级武器:超级子弹和全屏炸弹。

 

现在飞机已经能够带着血条突突突飞下来让玩家虐,于是我们给用户一点打飞机的成就感计分系统以及难…

Python编写微信打飞机小游戏(十)

  在这篇博文里,我们将为我方飞机添加三条生命。

  1、加载相关资源

  在目前的游戏中,我方飞机是拥有不死生命的,损毁了就在下方复活,这显然不合逻辑,因此需要为我放飞机增加生命数,并在屏幕右下方显示对应图标。首先在main()函数中设置初始化生命数并加载小飞机图片:

    life_image = pygame.image.load(image/life.png).convert()
    life_rect = life_image.get_rect()
    life_num = 3  # 一共有三条命

  2、将“life_num”加入程序循环判断语句中

  既然我方飞机拥有了生命属性,那就意味着我们不能够再向之前那样随意的摆放while()循环中的代码了,很多代码在执行前都需要判断life_num是否为零,比如说发射子弹,我方飞机都已经挂掉了,再发射子弹就显得不尽合理了吧。

  因此我们需要考虑哪些代码在执行前需要判断life_num是否为零,具体来说,只要涉及到和精灵有关的操作都应该在life_num
>
0时进行,因此我们将下列操作放在life_num的判断条件之外:(1)打印背景图片和分数的操作。因为不能因为我方飞机挂掉就让屏幕一篇漆黑,背景还是应该有的;(2)事件响应机制。因为我方飞机挂掉之后同样需要响应用户事件,比如鼠标单击事件、系统退出事件。除了以上这两个操作之外,其余操作都应该放在“if
life_num:”的条件循环之内,因此可能需要我们将之前的代码顺序做一些调整:

    while running:
        # 将背景图片打印到内存的屏幕上# 检测用户的退出及暂停操作

        if life_num  
             # 绘制全屏炸弹数量和剩余生命数量# 检测用户的键盘操作# 绘制补给并检测玩家是否获得# 子弹与敌机的碰撞检测
             # 我方飞机碰撞检测
             # 绘制我方飞机# 绘制敌方飞机
             # …………………………

        elif life_num == 0:  # 生命值为零,绘制游戏结束画面
             pass
        # 将内存中绘制好的屏幕刷新到设备屏幕上
        # 设置帧数为60

  3、life_num递减操作

  重新安排完代码结构后,接下来只需在我方飞机损毁时,将life_num减一:

            if me.active:# 绘制我方飞机的两种不同的形式else:
                if not (delay % 3):
                    # 绘制我方飞机损毁画面
                    if me_destroy_index == 0:
                        life_num -= 1# 我方飞机重生

  当然这里也用了“me_destroy_index ==
0”的限定条件,以保证在损毁期间只减一次生命数(详见之前博文)。

  4、绘制life_num图标

  在游戏过程中,为了显示当前玩家还有多少生命数,需要通过在屏幕右下方显示小飞机图标来指示,玩家当前有几条命就显示几个图标。小图标的资源已将在开始时加载好了,我们先给出完整的显示代码,稍后解释:

                for i in range(life_num):
                    screen.blit(life_image, (width - 10 - (i + 1) * life_rect.width, height - 10 - life_rect.height))

  这里只是做了一些简单的数学运算,“(width – 10 – (i + 1) *
life_rect.width, height – 10 –
life_rect.height)”这段代码就能够实现根据当前的life_num的值画出对应数量的小飞机图标。

  5、为我方飞机添加初始化无敌机制

  程序编写到这里貌似已经实现了预期目标,不过其实这里面有一个影藏的BUG,需要多次实现才会出现。因为我方飞机在重生时都是在固定位置出现的,那么就有这样一种可能性:就是在我方飞机重生时,在屏幕下方正中间(我方飞机重生的位置)正好有一架敌机,这就导致我方飞机一出生就挂掉,根本没有躲避的事件,虽然这种BUG出现的几率不大,但我们还是在这里提供一个解决方案,就是让我方飞机在重生之后具有三秒的无敌时间。因此我方飞机又多了一个属性:无敌。

  首先向我方飞机类的内部添加用来表示当前无敌状态的标志位:

self.invincible = False  # 飞机初始化时有三秒的无敌时间

  对应的在reset()成员函数中将其置为true:

        self.invincible = True

  接下来我们通过事件触发系统来设计一个定时器,用以记录无敌时间,首先在main函数中定义这个计时器:

invincible_time = USEREVENT + 2  # 接触我方飞机无敌时间定时器

  由于之前已经定义了两个用户事件了(supply_timer,double_bullet_timer),因此这里指定的标号为USEREVENT

  • 2。

  然后在我方飞机reset之后(invincible置为true,无敌状态开始),激活这个定时器,计时开始(3秒):

            if me.active:# 绘制我方飞机的两种不同的形式else:
                if not (delay % 3):
                    # 绘制我方飞机损毁画面
                    if me_destroy_index == 0:
                        me.reset()  # 我方飞机重生并开始无敌时间计时
                        pygame.time.set_timer(invincible_time, 3 * 1000)

  激活定时器之后,编写事件响应函数。若该事件被触发,说明三秒的无敌时间已过,需要将我方飞机的无敌标志位置为false,并关闭计时器:

        for event in pygame.event.get():  # 响应用户的偶然操作
            if event.type == QUIT: 
                # 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出elif event.type == invincible_time:  # 如果无敌时间已过
                me.invincible = False
                pygame.time.set_timer(invincible_time, 0)

  最后,我们修改一下我方飞机的碰撞检测函数,使得当我方飞机与敌机发生碰撞时,只有在我方飞机的invincible变量为false(非无敌状态)的情况下,才执行我方飞机和敌机的销毁操作:

            # ====================我方飞机碰撞检测====================
            enemies_down = pygame.sprite.spritecollide(me, enemies, False, pygame.sprite.collide_mask)
            if enemies_down and not me.invincible:  # 如果碰撞检测返回的列表非空,则说明已发生碰撞,若此时我方飞机处于无敌状态
                me.active = False
                for e in enemies_down:
                    e.active = False  # 敌机损毁

  这里就是在原有的if语句调价的基础上添加“not
me.invincible”判断,且二者为“与”操作的关系。好的,程序到此就告一段落,顺利的实现了预期功能,在下一篇博文中我们在给游戏添加一个暂停的功能。

 

 

在这篇博文里,我们将为我方飞机添加三条生命。 1、加载相关资源
在目前的游戏中,我方飞机是拥有不…

Python编写微信打飞机小游戏(九)

  在之前的博文中我们为游戏添加了随分数累加的难度递增机制,这就带来一个问题:当到达后面的难度等级后,敌方飞机铺天盖地而来,我方小飞机根本应付不过来,因此在这篇博文中我们为我方飞机赋予必杀技——随机补给全屏炸弹和超级子弹。

  首先来简单描述这两个必杀技,全屏炸弹是指在游戏过程中,当用户按下空格键时,就触发一枚全屏炸弹(如果当前有的话),此时屏幕上的所有敌机立即销毁。超级子弹是指玩家在接收到指定补给包之后,我方飞机能够一次发射两发子弹,攻击力加倍。ok,开始工作。

  1、定义supply模块

  定义一个名为supply.py的模块用以保存超级武器的相关属性,包含全屏炸弹补给类和超级子弹补给类。对于全屏炸弹补给类,这里先给出完整代码:

# ====================定义超级炸弹补给包====================
class BombSupply(pygame.sprite.Sprite):
    def __init__(self, bg_size):
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.image.load(image/ufo2.png)
        self.rect = self.image.get_rect()
        self.width, self.height = bg_size[0], bg_size[1]
        self.rect.left, self.rect.bottom = randint(0, self.width - self.rect.width), -100
        self.speed = 5
        self.active = False
        self.mask = pygame.mask.from_surface(self.image)

    def move(self):
        if self.rect.top < self.height:
            self.rect.top += self.speed
        else:
            self.active = False

    def reset(self):
        self.active = True
        self.rect.left, self.rect.bottom = randint(0, self.width - self.rect.width), -100

  可见BombSupply类结构与之前敌方飞机的类结构非常相似,其实补给包本身就和敌机具有相同的属性:随机位置初始化,以一定速度向下移动,需要进行掩膜碰撞检测(只不过碰撞之后不会造成我方敌机损毁)等等。类似的,超级子弹补给类的代码如下,两者在结构上完全相同,不再赘述。

# ====================定义超级子弹补给包====================
class BulletSupply(pygame.sprite.Sprite):
    def __init__(self, bg_size):
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.image.load(image/ufo1.png)
        self.rect = self.image.get_rect()
        self.width, self.height = bg_size[0], bg_size[1]
        self.rect.left, self.rect.bottom = randint(0, self.width - self.rect.width), -100
        self.speed = 5
        self.active = False
        self.mask = pygame.mask.from_surface(self.image)

    def move(self):
        if self.rect.top < self.height:
            self.rect.top += self.speed
        else:
            self.active = False

    def reset(self):
        self.active = True
        self.rect.left, self.rect.bottom = randint(0, self.width - self.rect.width), -100

  2、实例化超级炸弹

  值得注意的一点是,在游戏初始是自带3个全屏炸弹的,因此我们先不启动补给发放机制,先把这三发超级炸弹使用好。

  首先在游戏运行时需要将全屏炸弹的图标和剩余数量显示在画面左下角的位置,由于炸弹数量是可变的(我方飞机可能吃到补给包),因此在main函数中初始化一个全局变量用来保存当前全屏炸弹的数量:

bomb_num = 3  # 初始为三个炸弹

  然后加载全屏炸弹的小图标并获得其位置属性,注意之前supply.py模块中加载的图片是补给包的图标(带小降落伞的),并非全屏炸弹的图标:

    bomb_image = pygame.image.load(image/bomb.png)  # 加载全屏炸弹图标
    bomb_rect = bomb_image.get_rect()
    bomb_front = score_font

  注意既然接下来既然需要显示字符(全屏炸弹数量),我们就直接应用之前创建好的用来打印分数的字体对象即可(详见上篇博文)。接下来的工作就是在main()函数的while循环中将图片以及炸弹数量实时的绘制到屏幕上:

            # ====================绘制全屏炸弹数量和剩余生命数量====================
            bomb_text = bomb_front.render(× %d % bomb_num, True, color_black)
            bomb_text_rect = bomb_text.get_rect()
            screen.blit(bomb_image, (10, height - 10 - bomb_rect.height))
            screen.blit(bomb_text, (20 + bomb_rect.width, height - 10 - bomb_text_rect.height))

  注意这里由于要求炸弹图标的乘号以及炸弹数量并排显示,因此需要获取图片与字体的尺寸,至于如何摆放,相信大家稍加推敲就能理解的。此时运行程序,屏幕左下角正常显示图标。

  3、触发全屏炸弹

  既然已经把全屏炸弹显示出来了,仅仅放在那震慑是不够的,每当用户按下一次空格键,就触发一枚全屏炸弹,屏幕上所有敌机立即损毁。当然,我们首先需要知道用户什么时候按下了空格键:

        # ====================检测用户的退出及暂停操作====================
        for event in pygame.event.get():  # 响应用户的偶然操作
            if event.type == QUIT:  
                # 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出elif event.type == KEYDOWN:
                if event.key == K_SPACE:  # 如果检测到用户按下空格键
                    if bomb_num:  # 如果炸弹数量大于零,则引爆一颗超级炸弹
                        bomb_num -= 1
                        bomb_sound.play()
                        for each in enemies:
                            if each.rect.bottom > 0:  # 屏幕上的所有敌机均销毁
                                each.active = False

  由于空格键的按下属于偶然操作,因此采用“pygame.event.get()”的事件响应机制,代码结构简单,即当检测到用户键盘按下并且对应键值为“K_SPACE”时,炸弹数量减一(如果炸弹数量大于零),播放全屏炸弹音效、屏幕上所有敌机损毁,在这里也能体现出当初我们在实例化敌机对象时将敌机统一放在了“enemies”这个精灵组结构中的优势了,操作异常便捷。

  4、开启补给机制

  全屏炸弹顺利施放,接下来开启补给机制。设定为每10秒发放一次补给,我们通过触发时间机制来完成这个功能,即将补给发放定义为一个用户时间,每隔30秒触发一次,在响应时间的过程中初始化补给包,首先在main()函数中定义事件:

    supply_timer = USEREVENT  # 补给包发放定时器
    pygame.time.set_timer(supply_timer, 10 * 1000)  # 定义每10秒发放一次补给包

  注意Pygame模块中为每个事件通过宏定义的方式定义了一个标号,用以区分各个时间。我们在定义自己的用户事件的时候也要人为的为其赋值一个事件标号,为了保证用户定义的事件标号与系统事件标号不冲突,Pygame特意提供了“USEREVENT”这个宏,标号在(0,USEREVENT-1)的事件为系统时间,因此此处我们将定义的用户事件指定为“USEREVENT”是不会和系统事件发生冲突的。事件定义完之后调用time类中的set_timer()函数设定事件触发的事件间隔,注意这里的时间是以毫秒为单位的,因此需要乘以1000转换成秒。

  当然要想顺利的打印补给包,首先需要对其进行实例化(和敌方飞机的实例话类似):

    # ====================实例化补给包====================
    bullet_supply = supply.BulletSupply(bg_size)
    bomb_supply = supply.BombSupply(bg_size)

  接下来在whlie()循环内部编写补给定时器“supply_timer”的事件响应函数:

        # ====================检测用户的退出及暂停操作====================
        for event in pygame.event.get():  # 响应用户的偶然操作
            if event.type == QUIT:  
                # 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出elif event.type == supply_timer:  # 响应补给发放的事件消息
                if choice([True, False]):
                    bomb_supply.reset()
                else:
                    bullet_supply.reset()

 

  事件响应函数的主要任务是确定当前应该实例化何种补给包,是全屏炸弹补给包还是超级子弹补给包,这种选择应该是随机的,因此可以用“choice()”实现随机选择机制,然后再根据随机结果将对应的补给包进行复位操作(reset会将对应的补给对象的active变量激活),继而引导接下来的补给发放机制。

  5、检测用户是否获得补给包

  在通过事件响应函数确定当前发放的补给类型之后,接下里就需要检测我方飞机是否顺利拾获了补给包,核心是通过碰撞检测机制来完成,首先判断是否获得了全屏炸弹补给包:

            # ====================绘制补给并检测玩家是否获得====================
            if bomb_supply.active:  # 如果是超级炸弹补给包
                bomb_supply.move()
                screen.blit(bomb_supply.image, bomb_supply.rect)
                if pygame.sprite.collide_mask(bomb_supply, me):  # 如果玩家获得超级炸弹补给包
                    get_bomb_sound.play()
                    if bomb_num < 3:
                        bomb_num += 1
                    bomb_supply.active = False

  程序思路很清晰,如果当前全屏炸弹补给包被激活,则打印其图标并开始自动移动,在移动过程中若与我方飞机发生了碰撞(碰撞检测返回true),则认为玩家顺利获取了全屏炸弹补给包,此时再判断玩家剩余全屏炸弹的数量,如果小于三个,则加一,之后再将补给包对象的active变量置为false,关闭补给对象。

  接下来判断用户是否获得超级子弹补给包,这里有一点需要注意,就是在获得全屏炸弹之后,我方飞机将以此发射两发子弹,因此需要设置一个标志位“is_double_bullet”来表示当前的子弹状态,“is_double_bullet
= false”表示当前发射的是普通子弹,“is_double_bullet =
true”表示当前发射的是超级子弹,首先在main函数中声明变量并初始化:

is_double_bullet = False   # 是否使用超级子弹标志位

  然后开始进行超级子弹补给包的碰撞检测:

           if bullet_supply.active:  # 如果是超级子弹补给包
                bullet_supply.move()
                screen.blit(bullet_supply.image, bullet_supply.rect)
                if pygame.sprite.collide_mask(bullet_supply, me):
                    get_bullet_sound.play()
                    is_double_bullet = True
                    bullet_supply.active = False

  既然添加了两种子弹属性,因此有必要在绘制子弹之前进行一把判断了,不过到现在为止我们貌似还没有定义超级子弹类,之前在定义bullet.py模块的时候曾经提到过,Bullet1代表普通子弹,Bullet2代表超级子弹,在这里我们将bullet模块中的Bullet2的类定义代码补上,和Bullet1的结构完全相同:

# ====================定义超级子弹====================
class Bullet2(pygame.sprite.Sprite):
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(image/bullet2.png)
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = position
        self.speed = 14
        self.active = True
        self.mask = pygame.mask.from_surface(self.image)

    def move(self):
        if self.rect.top < 0:
            self.active = False
        else:
            self.rect.top -= self.speed

    def reset(self, position):
        self.rect.left, self.rect.top = position
        self.active = True

  在定义好Bullet2之后,需要在主程序中实例化超级子弹。和之前普通子弹的实例化方式类似(参见之前博文),只是这里在子弹实例话的顺序位置安排上有一点小技巧,先上代码:

    # ====================生成超级子弹====================
    bullet2 = []
    bullet2_index = 0
    bullet2_num = 10  # 定义子弹实例化个数
    for i in range(bullet2_num//2):
        bullet2.append(bullet.Bullet2((me.rect.centerx - 33, me.rect.centery)))
        bullet2.append(bullet.Bullet2((me.rect.centerx + 30, me.rect.centery)))

  可见,这里的超级子弹是成对实例化的(10发子弹,分为5次循环),即敌机左边一发右边一发,这样只要在将来打印的过程中一次打印相邻的两个子弹对象,就能产生“一次射两发”的效果了,类似的,如何实现一次射三发、四发,大家应该也都能掌握了。

  接下来需要做的就是对原有的子弹显示程序进行修改,增加一项判断分支,代码如下:

            if not (delay % 10):  
                # 播放子弹音效if not is_double_bullet:  
                    # 发射普通子弹else:  # 如果当前是超级子弹状态
                    bullets = bullet2
                    bullets[bullet2_index].reset((me.rect.centerx - 33, me.rect.centery))
                    bullets[bullet2_index + 1].reset((me.rect.centerx + 30, me.rect.centery))
                    bullet2_index = (bullet2_index + 2) % bullet2_num

  果不其然,这里在打印超级子弹的时候一次激活子弹组中的相邻两发子弹,即实现左右两边各发射一发子弹的效果。这里之所有采用bullets这个中间变量,是为了简化接下来的子弹与敌机的碰撞检测(不必再区分普通子弹和超级子弹)。至于(me.rect.centerx

  • 33, me.rect.centery)、(me.rect.centerx – 33,
    me.rect.centery)这两个坐标位置也是经验值,正好代表我方飞机的两个发动机的位置。

  6、设置超级子弹时间定时器

  运行程序,上面的所有预期功能都正常事项,不过存在一个问题,就是一旦我们获取了超级子弹补给包,则我方飞机就会无休止的发射超级子弹,这显然是不合理的,毕竟超级武器还是超级武器,一直都能用就不能体现其珍贵了,因此我们添加一个机制,即获得超级子弹补给包之后,超级子弹转态只能持续一定时间,规定时间过后自动变为普通子弹。

  同样我们通过事件触发机制完成这个功能,首先在main()函数中定义这个事件:

    double_bullet_timer = USEREVENT + 1  # 超级子弹持续时间定时器

  在成功获取超级子弹补给包之后,开启这个定时器:

            if bullet_supply.active:  # 如果是超级子弹补给包
                pass
                if pygame.sprite.collide_mask(bullet_supply, me):
                    pass
                    pygame.time.set_timer(double_bullet_timer, 18 * 1000)

  可见这里我们将超级子弹的持续时间设置为18秒。然后编写对应的事件响应函数,在响应过程中关闭超级子弹状态即可:

        # ====================检测用户的退出及暂停操作====================
        for event in pygame.event.get():  # 响应用户的偶然操作
            if event.type == QUIT:  
                # 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出elif event.type == double_bullet_timer:
                is_double_bullet = False
                pygame.time.set_timer(double_bullet_timer, 0)

  注意这里将时间触发时间设置为零表示关闭了这个事件,等待下一次获得超级子弹之后再重新开启。今天的博文就到这里,内容有点多,大家慢慢看吧。

 

在之前的博文中我们为游戏添加了随分数累加的难度递增机制,这就带来一个问题:当到达后面的难度等…

**

背景

和朋友闲待着两个人无聊,都想吃苹果又都不想洗,想起玩过的一个酒桌上猜数字的游戏,然后在手机上写了出来,三局两胜输了去洗苹果……

思路: 
1:主飞机从底层y = 1生成,左右移动
,每次左右x轴随机移动n个位置,并且移送过的地方每一个位置都会生成一个子弹,并发射。如果此位置Y轴正好有小飞机,则击落一架且一次只能击落,加一分。(击落多架除非下一次随机位置还在原地)

游戏介绍

零到99(包括边界),随机一个数两人(N人也可以)轮流猜测,如果没猜中,新的数将做为边界,一步步缩小可以猜测的范围,直到猜中为止,比如随机数是“32”,屏幕首先打印出:

当前范围是: 0 --- 100

第一次猜:50,屏幕返回

当前范围是: 0 --- 50

第二次猜:25,屏幕返回

当前范围是: 25 --- 50

第N次猜中:32,屏幕返回,并结束循环

^_^

2:小飞机到底层时消失,即y = 0时候消失。(这个条件暂时没用)

准备工作

此前我们的代码都是在交互环境中完成的,是时候来点不一样的了。在学前班里我们介绍了一些文本编辑器,现在召唤一个你喜欢的,然后开始编写吧。

多行的Python代码稍微有些不同,下面的代码可以当做是一个参考。良好的代码编写应该是有缩进的,这样方面人类阅读和理解,Python解释器对缩进的要求是严格的,也就是说你必须进行缩进,缩进量通常为’2个空格或者4个空格’,注意不要用’Tab’和4个空格混用,因为不同编辑器可能会对’Tab’的缩进理解不同。

那么问题来了,什么时候需要缩进呢?大概像下图中的缩进一样:

图片 1

缩进描述的是属于关系,每有一层属于关系就需要缩进一次。

写好了程序后,比如存为abc.py
在windows中进入相行目录,比如C盘下的py_ex,在Windows的命令行中输入:

C:\>python c:\py_ex\abc.py

或者

cd c:\py_ex\abc.py
C:\py_ex\>python abc.py

在Linux或OSX的终端中输入python +
文件路径,比如要运行家目录内py_ex中的abc.py文件:

$ python ~/py_ex/abc.py

3:小飞机从顶层y = 10水平位置生成,x位置随机,每次随机生成三个

代码(Python3)

下面的代码是Python3的,如果是Python2你还需要在import之前再加入一行# -*- coding: utf-8 -*-,来确保代码内的中文字符可以运行。

import random

rand_num = random.randint(0, 99)
left = 0
right = 99
while True:
    print('当前范围是:', left, '---', right)
    test = int(input('再猜一下\n'))
    if test == rand_num:
        print('^_^')
        break
    if test > rand_num:
        right = test
    else:
        left = test

4:主飞机本身长度y=1。当主飞机和小飞机下一个坐标都为(x,1)时,碰撞。游戏结束。

代码注解

第一行我们先引入了一个可以产生随机数的包,这里我们需要的是整数所以使用random.randint()方法,这个方法接收两个参数做为左右边界,返回一个包含边界的随机整数。

第六行,一个永恒循环来接收N次猜数,如果猜中break跳出循环。

第八行的input是一个内建函数用来接收输入信息,默认接收到的输入会是str类型,我们对其进行了强制类型转换,这里不严谨,没有进行严格的输入验证。

 

如果有疑问

有疑问是正常的,因为像ifwhile这样的控制语句我们之前并没有提过,但是没关系,即使没有提到过,你也一样看得懂,不是吗?
这里看明白就好,后续的章节我们会详细介绍。

之前版本运行完成会发现如下一个bug:小飞机生成因为是随机的,所以可能随机生成n个,其中有
n=<1个飞机位置会重复,所以会出现以下现象 :

只发射了6个炮弹,却在水平方向打掉了十多个飞机,这是因为生成飞机重复了,甚至有三四个飞机随机生成在一个y
= 10的水平位置上。

问题的BUG已经完善,加一个小飞机刷出条件不重叠。 

代码如下:

import random as r

import sys

legal_x = [0,10]

legal_y = [0,25]

class Big:

    def __init__(self):

        """在y=1随机生成炮台位置"""

        self.x = r.randint(0,10)

        self.y = 1

 

    def move(self):

        self.Interval = []

        """随机移动左右方向并移动到新的位置(x,1)"""

        self.step = r.randint(0,legal_x[1])

        self.direction = r.randint(-1,1)#方向,-1为左,0不移动,1为右

        new_x = self.x + self.direction * self.step

        mew_y = self.y

        """判断是否越界"""

        if new_x > legal_x[1]:

            pos_x = legal_x[1] - (new_x - legal_x[1])

            pos_y = mew_y

        elif new_x < legal_x[0]:

            pos_x = legal_x[0] - new_x

            pos_y = mew_y

        else:

            pos_x = new_x

            pos_y = mew_y

 

        """炮台移动前后对应坐标"""

        if self.x > pos_x:

            for i in range(pos_x,self.x + 1 ):

                self.Interval.append(i)

            print("炮台从坐标x=%d移动到x=%d,沿途轰了%d炮"%(self.x,pos_x,self.x + 1 -pos_x ))

            print(">>>轰出%d个炮的位置是x ="% (self.x + 1 -pos_x),end = "")

            print(self.Interval)

 

 

        elif self.x < pos_x:

            for i in range(self.x,pos_x + 1):

                self.Interval.append(i)

            print("炮台从坐标x=%d移动到x=%d,沿途轰了%d炮"%(self.x,pos_x,pos_x + 1 -self.x ))

            print(">>>轰出%d个炮的位置是x ="% (pos_x + 1 -self.x),end = "")

            print(self.Interval)

        else:

            self.Interval.append(pos_x)

            print(">>>炮台原地轰了一炮")

            print(">>>轰炮的坐标是x = %s"% str(self.Interval))

 

        """初始化炮台到移动的目标"""

        self.x = pos_x

        self.y = pos_y

        return (pos_x,pos_y)

 

class Small:

    def __init__(self):

        """在y=25随机生成小飞机位置"""

        self.x = r.randint(0,legal_x[1])

        self.y = legal_y[1]

 

    def move(self):

        """固定移动,每次向下一步"""

        new_x = self.x

        mew_y = self.y - 1

        """判断是否越界"""

        if mew_y <= legal_y[0]:

            self.x = r.randint(0,legal_x[1])

            self.y = legal_y[1]

        else:

            self.x = new_x

            self.y = mew_y            

        return (new_x , mew_y)

 

class Boom:

    """核武器"""

    def __init__(self):

        self.x = r.randint(0,legal_x[1])

        self.y = 1

 

 

def DAFEIJI(n):  

    Scorer = 0

    list_s = []

    big_air = Big()

    """激光炮台出场"""

 

 

    i = r.randint(9,10)

    while n:

        list_pos = []

        boom = Boom()

        """核武器生成"""       

        for numbers in range(1 ,i + 1):            

            small_air = Small()#小飞机出场数量位置随机,设置不重叠

            if small_air.x not in list_pos:

                list_pos.append(small_air.x)

                list_s.append(small_air)

            else:

                continue                

        pos = big_air.move()

        n = n - 1

        if pos != (boom.x ,boom.y):

            for each in list_s[:]:

                pos_small = each.move()

                if pos == pos_small:

                    print(">>>>>>>很不幸! 您的炮台撞小飞机了....GG!!")#这个几率.....

                    print("本次打飞机的分数是:%d" % Scorer)

                    sys.exit(0)          

                elif pos_small[0] in (big_air.Interval):

                    """一条直线打的,其实是激光炮"""

                    print("一架小飞机被打掉..")

                    Scorer += 1

                    list_s.remove(each)

        else:

            print("炮台加载了核武器...======================清屏!=======================..")

            Scorer += len(list_s)

            list_s.clear()

    print("本次打飞机的分数是:%d" % Scorer)

#==============================主程序==================================

 

 

DAFEIJI(5)

初始界面y值越大,游戏结束几率越小。 

 

想了解更多Python知识或者想与更多的朋友一起学习交流,可以加群705673780,群文件内更多免费资料可供学习哦~

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图