python-飞机大战开发

摘要

使用pygame和在小甲鱼论坛中提供的免费素材,制作了一款由键盘控制飞机移动躲避、攻击敌人的2D游戏——飞机大战。

1.引言

  pygame[1]跨平台Python模块,专为电子游戏设计。包含图像、声音。创建在SDL基础上,允许实时电子游戏研发而无需被低端语言,如C语言或是更低端的汇编语言束缚。基于这样一个设想,所有需要的游戏功能和理念都(主要是图像方面)完全简化位游戏逻辑本身,所有的资源结构都可以由高级语言提供,如Python。pygame原为代替突然停止的pySDL[2]

 

图1 pygame

 

pygame应用程序能够在Android手机和平板运行,采用pygame对于Andorid的子集(pgs4a)。[3] 支持Android的声音,振动,键盘和加速。[4]但缺点是没有办法在iOS上运行pygame应用程序。其它pgs4a的主要限制是缺乏对于多点触控的支持, 这使得双指缩放,以及旋转无法使用。另一个pygame在Android子集的替代方案是Kivy[8]它包含了多点触控及iOS的支持。

作为各种3D游戏引擎日趋成熟和易用的今天,pygame作为一款只能开发2D游戏的引擎显得略为过失。然而,使用pygame开发游戏的过程简单、便捷,对于游戏的逻辑开发相对其他3D游戏引擎来说更加简便;作为游戏开发的初学者、对游戏开发感兴趣的人以及测试游戏运行逻辑的测试人员来说,pygame都是一款十分不错的轻量级工具;初学者可以简单的理清开发框架;测试人员对于测试游戏内逻辑的效率也将大大提高。   

3. 实现代码。对代码分段进行介绍,先干什么,然后干什么,就像人家写博客一样,可以让一个学习python的人可以按照你的思路理解你每一步代码的实现过程;

4. 实验。展示你的实验结果。

5. 总结和展望。总结你的工作,指出你的不足。

2.系统架构

   Pygame中有很多的功能模块,可以根据自身游戏的开发需求使用某些模块。

 

模块名

功能

pygame.cdrom

访问光驱

pygame.cursors

加载光标

pygame.display

访问设备显示

pygame.draw

绘制形状、线和点

pygame.event

管理事件

pygame.font

使用字体

pygame.image

加载和存储图片

pygame.joystick

使用手柄或类似的东西

pygame.key

读取键盘按键

pygame.mixer

声音

pygame.mouse

鼠标

pygame.movie

播放视频

pygame.music

播放音频

pygame.overlay

访问高级视频叠加

pygame.rect

管理矩形区域

pygame.sndarray

操作声音数据

pygame.sprite

操作移动图像

pygame.surface

管理图像和屏幕

pygame.surfarray

管理点阵图像数据

pygame.time

管理时间和帧信息

pygame.transform

缩放和移动图像

表1pygame中可用的模块

 

模块名

功能

pygame.surface

管理图像和屏幕

pygame.time

管理时间和帧信息

pygame.mouse

鼠标

pygame.music

播放音频

pygame.display

访问设备显示

pygame.draw

绘制形状、线和点

pygame.event

管理事件

pygame.font

使用字体

pygame.image

加载和存储图片

pygame.mixer

声音

pygame.key

读取键盘按键

pygame.rect

管理矩形区域

pygame.draw

绘制形状、线和点

pygame.sprite

操作移动图像

表2飞机大战中使用的模块

 

 

 

图2 飞机大战游戏设计模型

 

 

图4游戏游玩逻辑

 

图5补给刷新获取活动图

3. 实现代码

  import pygame

import sys

import traceback

from pygame.locals import *

import plane

import enemy

import bullet

import supply

import random

 

pygame.init()

bg_size = width, height = 480, 700

screen = pygame.display.set_mode(bg_size)

pygame.display.set_caption("py")

 

background = pygame.image.load("images/background.png").convert()

 

BLACK = (0,0,0)

GREEN = (0, 255, 0)

RED = (255, 0, 0)

WHITE = (255,255,255)

 

#载入游戏音乐

pygame.mixer.music.load("sound/game_music.ogg")

pygame.mixer.music.set_volume(0.2)

bullet_sound = pygame.mixer.Sound("sound/bullet.wav")

bullet_sound.set_volume(0.2)

bomb_sound = pygame.mixer.Sound("sound/use_bomb.wav")

bomb_sound.set_volume(0.2)

supply_sound = pygame.mixer.Sound("sound/supply.wav")

supply_sound.set_volume(0.2)

get_bomb_sound = pygame.mixer.Sound("sound/get_bomb.wav")

get_bomb_sound.set_volume(0.2)

get_bullet_sound = pygame.mixer.Sound("sound/get_bullet.wav")

get_bullet_sound.set_volume(0.2)

upgrade_sound = pygame.mixer.Sound("sound/upgrade.wav")

upgrade_sound.set_volume(0.2)

enemy3_fly_sound = pygame.mixer.Sound("sound/enemy3_flying.wav")

enemy3_fly_sound.set_volume(0.2)

enemy1_down_sound = pygame.mixer.Sound("sound/enemy1_down.wav")

enemy1_down_sound.set_volume(0.2)

enemy2_down_sound = pygame.mixer.Sound("sound/enemy2_down.wav")

enemy2_down_sound.set_volume(0.2)

enemy3_down_sound = pygame.mixer.Sound("sound/enemy3_down.wav")

enemy3_down_sound.set_volume(0.5)

me_down_sound = pygame.mixer.Sound("sound/me_down.wav")

me_down_sound.set_volume(0.2)

 

def add_small_enemies(group1, group2, num):

    for i in range(num):

        e1 = enemy.SmallEnemy(bg_size)

        group1.add(e1)

        group2.add(e1)

 

def add_mid_enemies(group1, group2, num):

    for i in range(num):

        e1 = enemy.MidEnemy(bg_size)

        group1.add(e1)

        group2.add(e1)

 

def add_big_enemies(group1, group2, num):

    for i in range(num):

        e1 = enemy.BigEnemy(bg_size)

        group1.add(e1)

        group2.add(e1)

 

def inc_speed(target, inc):

    for each in target:

        each.speed += inc

 

def main():

    pygame.mixer.music.play(-1)

   

    clock = pygame.time.Clock()

 

    # 中弹图片索引

    e1_destroy_index = 0

    e2_destroy_index = 0

    e3_destroy_index = 0

    me_destroy_index = 0

 

    me = plane.plane(bg_size)

   

    enemies = pygame.sprite.Group()

 

    # 生成敌方小型飞机

    small_enemies = pygame.sprite.Group()

    add_small_enemies(small_enemies, enemies, 15)

 

    mid_enemies = pygame.sprite.Group()

    add_mid_enemies(mid_enemies, enemies, 5)

 

    big_enemies = pygame.sprite.Group()

    add_big_enemies(big_enemies, enemies, 1)

 

    switch_image = True

    delay = 100

    score = 0

    score_font = pygame.font.Font("font/font.ttf", 36)

   

    bullets = []

 

    # 生成普通子弹

    bullet1 = []

    bullet1_index = 0

    BULLET1_NUM = 8

    for i in range(BULLET1_NUM):

        bullet1.append(bullet.Bullet1(me.rect.midtop))

 

    # 生命数量

    life_image = pygame.image.load("images/life.png").convert_alpha()

    life_rect = life_image.get_rect()

    life_num = 3

 

    # 游戏结束画面

    gameover_font = pygame.font.Font("font/font.ttf", 48)

    again_image = pygame.image.load("images/again.png").convert_alpha()

    again_rect = again_image.get_rect()

    gameover_image = pygame.image.load("images/gameover.png").convert_alpha()

    gameover_rect = gameover_image.get_rect()

 

    DOUBLE_BULLET_TIME = USEREVENT + 1

 

    # 解除我方飞机无敌状态

    INVINCEBLE_TIME = USEREVENT + 2

 

 

    # 生成超级子弹

    bullet2 = []

    bullet2_index = 0

    BULLET2_NUM = 16

    for i in range(BULLET2_NUM*2):

        bullet2.append(bullet.Bullet2((me.rect.centerx - 20, me.rect.centery)))

        bullet2.append(bullet.Bullet2((me.rect.centerx + 20, me.rect.centery)))

 

    # 标记是否使用超级子弹

    is_double_bullet = False

 

    level = 1

 

    # 全屏炸弹

    bomb_image = pygame.image.load("images/bomb.png").convert_alpha()

    bomb_rect = bomb_image.get_rect()

    bomb_font = pygame.font.Font("font/font.ttf", 48)

    bomb_num = 6

 

    # 5秒发放一个补给包

    bullet_supply = supply.Bullet_Supply(bg_size)

    bomb_supply = supply.Bomb_Supply(bg_size)

    SUPPLY_TIME = USEREVENT

    pygame.time.set_timer(SUPPLY_TIME, 5 *1000)

 

    # 阻止重复读取成绩记录文件

    recorded = False

 

    # 标志是否暂停游戏

    paused = False

    paused_nor_image = pygame.image.load("images/pause_nor.png").convert_alpha()

    pause_pressed_image = pygame.image.load("images/pause_pressed.png").convert_alpha()

    resume_nor_image = pygame.image.load("images/resume_nor.png").convert_alpha()

    resume_pressed_image = pygame.image.load("images/resume_pressed.png").convert_alpha()

    paused_rect = paused_nor_image.get_rect()

    paused_rect.left, paused_rect.top = width - paused_rect.width -10, 10

    paused_image = paused_nor_image

 

    while True:

        for event in pygame.event.get():

            if event.type == QUIT:

                pygame.quit()

                sys.exit()

            elif event.type == MOUSEBUTTONDOWN:

                if event.button == 1 and paused_rect.collidepoint(event.pos):

                    paused = not paused

                    if paused:

                        pygame.time.set_timer(SUPPLY_TIME, 0)

                        pygame.mixer.music.pause()

                        pygame.mixer.pause()

                        paused_image = resume_pressed_image

                    else:

                        pygame.time.set_timer(SUPPLY_TIME, 5 * 1000)

                        pygame.mixer.music.unpause()

                        pygame.mixer.unpause()

                        paused_image = pause_pressed_image

            elif event.type == MOUSEMOTION:

                if paused_rect.collidepoint(event.pos):

                    if paused:

                        paused_image = resume_pressed_image

                    else:

                        paused_image = pause_pressed_image

                else:

                    if paused:

                        paused_image = resume_nor_image

                    else:

                        paused_image = paused_nor_image

            elif event.type == KEYDOWN:

                if event.key == K_ESCAPE:

                    sys.exit()

                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

            elif event.type == SUPPLY_TIME:

                supply_sound.play()

                if random.choice([True, False]):

                    bomb_supply.reset()

                else:

                    bullet_supply.reset()

            elif event.type == DOUBLE_BULLET_TIME:

                is_double_bullet = False

                pygame.time.set_timer(DOUBLE_BULLET_TIME, 0)

            elif event.type == INVINCEBLE_TIME:

                me.invincible = False

                pygame.time.set_timer(INVINCEBLE_TIME, 0)

 

 

        # 根据用户的得分增加难度

        if level == 1 and score > 1000:

            level = 2

            upgrade_sound.play()

            add_small_enemies(small_enemies, enemies, 3)

            add_mid_enemies(mid_enemies, enemies, 2)

            add_big_enemies(big_enemies, enemies, 1)

        elif level == 2 and score > 20000:

            level = 3

            upgrade_sound.play()

            add_small_enemies(small_enemies, enemies, 5)

            inc_speed(small_enemies, 1)

        elif level == 3 and score > 40000:

            level = 4

            upgrade_sound.play()

            add_big_enemies(big_enemies, enemies, 2)

            inc_speed(small_enemies, 1)

            inc_speed(mid_enemies, 1)

        elif level == 4 and score > 100000:

            level = 5

            upgrade_sound.play()

            add_small_enemies(small_enemies, enemies, 5)

            add_mid_enemies(mid_enemies, enemies, 3)

            add_big_enemies(big_enemies, enemies, 2)

            inc_speed(small_enemies, 1)

            inc_speed(mid_enemies, 1)

 

        screen.blit(background, (0,0))

 

        if life_num and not paused:

            # 绘制全屏炸弹补给并检测是否获得

            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()

                    bomb_num += 1

                    bomb_supply.active = False

 

            #发射子弹

            if not(delay % 10):

                bullet_sound.play()

                if is_double_bullet:

                    bullets = bullet2

                    bullets[bullet2_index].reset((me.rect.centerx - 22, me.rect.centery))

                    bullets[bullet2_index + 1].reset((me.rect.centerx + 22, me.rect.centery))

                    bullet2_index = (bullet2_index + 2) % BULLET2_NUM

                else:

                    bullets = bullet1

                    bullets[bullet1_index].reset(me.rect.midtop)

                    bullet1_index = (bullet1_index + 1) % BULLET1_NUM

 

            #检测子弹是否击中敌机

            for b in bullets:

                if b.active:

                    b.move()

                    screen.blit(b.image, b.rect)

                    enemy_hit = pygame.sprite.spritecollide(b, enemies, False, pygame.sprite.collide_mask)

                    if enemy_hit:

                        b.active = False

                        for e in enemy_hit:

                            if e in mid_enemies or e in big_enemies:

                                e.hit = True

                                e.energy -= 1

                                if e.energy == 0:

                                    e.active = False

                            else:

                                e.active = 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

                    pygame.time.set_timer(DOUBLE_BULLET_TIME, 18 * 1000)

                    bullet_supply.active = False

 

            # 绘制大型敌机

            for each in big_enemies:

                if each.active:

                    each.move()

                    if each.hit:

                        screen.blit(each.image_hit, each.rect)

                        each.hit = False

                    else:

                        if switch_image:

                            screen.blit(each.image1, each.rect)

                        else:

                            screen.blit(each.image2, each.rect)

                    if each.rect.bottom == -50:

                        enemy3_fly_sound.play(-1)

 

                    # 绘制血槽

                    pygame.draw.line(screen, BLACK, \

                            (each.rect.left, each.rect.top - 5),\

                            (each.rect.right, each.rect.top - 5), \

                            2)

                   

                    energy_remain = each.energy / enemy.BigEnemy.energy

                    if energy_remain > 0.2:

                        energy_color = GREEN

                    else:

                        energy_color = RED

                    pygame.draw.line(screen, energy_color, \

                            (each.rect.left, each.rect.top -5), \

                            (each.rect.left + each.rect.width * energy_remain, \

                            each.rect.top - 5), \

                            2)

 

                else:

                    if not(delay % 3):

                        if e3_destroy_index == 0:

                            enemy3_down_sound.play()

                        screen.blit(each.destroy_images[e3_destroy_index], each.rect)

                        e3_destroy_index = (e3_destroy_index+1)%6

                        if e3_destroy_index == 0:

                            me_down_sound.stop()

                            score += 10000

                            each.reset()

 

            # 绘制中型敌机

            for each in mid_enemies:

                if each.active:

                    each.move()

 

                    if each.hit:

                        screen.blit(each.image_hit, each.rect)

                        each.hit = False

                    else:

                        screen.blit(each.image1, each.rect)

 

                    # 绘制血槽

                    pygame.draw.line(screen, BLACK, \

                            (each.rect.left, each.rect.top - 5),\

                            (each.rect.right, each.rect.top - 5), \

                            2)

                   

                    energy_remain = each.energy / enemy.MidEnemy.energy

                    if energy_remain > 0.2:

                        energy_color = GREEN

                    else:

                        energy_color = RED

                    pygame.draw.line(screen, energy_color, \

                            (each.rect.left, each.rect.top -5), \

                            (each.rect.left + each.rect.width * energy_remain, \

                            each.rect.top - 5), \

                            2)

                else:

                    if not(delay % 3):

                        if e2_destroy_index ==0:

                            enemy2_down_sound.play()

                        screen.blit(each.destroy_images[e2_destroy_index], each.rect)

                        e2_destroy_index = (e2_destroy_index+1)%4

                        if e2_destroy_index == 0:

                            score += 5000

                            each.reset()

 

            # 绘制小型敌机

            for each in small_enemies:

                if each.active:

                    each.move()

                    screen.blit(each.image1, each.rect)

                else:

                    if not(delay % 3):

                        if e1_destroy_index ==0:

                            enemy1_down_sound.play()

                        screen.blit(each.destroy_images[e1_destroy_index], each.rect)

                        e1_destroy_index = (e1_destroy_index+1)%4

                        if e1_destroy_index == 0:

                            score += 1000

                            each.reset()   

 

            key_pressed = pygame.key.get_pressed()

            if key_pressed[K_w] or key_pressed[K_UP]:

                me.moveUp()

            if key_pressed[K_s] or key_pressed[K_DOWN]:

                me.moveDown()

            if key_pressed[K_a] or key_pressed[K_LEFT]:

                me.moveLeft()

            if key_pressed[K_d] or key_pressed[K_RIGHT]:

                me.moveRight()

 

            # 检测我方飞机是否被撞

            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 me.active:

                if switch_image:

                    screen.blit(me.image1, me.rect)

                else:

                    screen.blit(me.image2, me.rect)

            else:

                me_down_sound.play()

                if not(delay % 3):

                    screen.blit(each.destroy_images[me_destroy_index], each.rect)

                    me_destroy_index = (me_destroy_index+1)%4

                    # 剩余生命数量

                    if me_destroy_index == 0:

                        life_num -= 1

                        me.reset()

                        pygame.time.set_timer(INVINCEBLE_TIME, 3 * 1000)

 

             # 绘制剩余炸弹数量

            bomb_text = bomb_font.render("x %d" % bomb_num, True, WHITE)

            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 - 5 - text_rect.height))

 

 

            if life_num:

                for i in range(life_num):

                    screen.blit(life_image,\

                        (width - 10 - (i+1)*life_rect.width, \

                        height - 10 - life_rect.height))

           

            score_text = score_font.render(str("Score: %s" % score), True, WHITE)

            screen.blit(score_text, (10,5))

        elif life_num == 0:

            pygame.mixer.music.stop()

            pygame.mixer.stop()

 

            # 停止发放补给

            pygame.time.set_timer(SUPPLY_TIME, 0)

 

            if not recorded:

                recorded = True

                # 读取历史最高分

                with open("record.txt", "r") as f:

                    record_score = int(f.read())

           

                if score > record_score:

                    with open("record.txt", "w") as f:

                        f.write(str(score))

 

            # 绘制结束画面

            record_score_text = score_font.render("Best: %d" % record_score,True,WHITE)

            screen.blit(record_score_text, (50,50))

           

            gameover_text1 = gameover_font.render("Your Score: ", True, WHITE)

            gameover_text1_rect = gameover_text1.get_rect()

            gameover_text1_rect.left, gameover_text1_rect.top = \

                                (width - gameover_text1_rect.width) // 2, height // 2

            screen.blit(gameover_text1, gameover_text1_rect)

 

           

            gameover_text2 = gameover_font.render(str(score), True, WHITE)

            gameover_text2_rect = gameover_text2.get_rect()

            gameover_text2_rect.left, gameover_text2_rect.top = \

                                (width - gameover_text2_rect.width) // 2, \

                                gameover_text1_rect.bottom + 10

            screen.blit(gameover_text2, gameover_text2_rect)

 

            again_rect.left, again_rect.top = \

                        (width - again_rect.width) // 2,\

                        gameover_text2_rect.bottom + 50

            screen.blit(again_image, again_rect)

 

            gameover_rect.left, gameover_rect.top = \

                        (width - again_rect.width) // 2, \

                        again_rect.bottom + 10

            screen.blit(gameover_image, gameover_rect)

 

            # 检测用户的鼠标操作

            # 如果用户按下鼠标左键

            if pygame.mouse.get_pressed()[0]:

                pos = pygame.mouse.get_pos()

                if again_rect.left < pos[0] < again_rect.right and \

                   again_rect.top < pos[1] < again_rect.bottom:

                    main()

                elif gameover_rect.left < pos[0] < gameover_rect.right and \

                     gameover_rect.top < pos[1] < gameover_rect.bottom:

                     pygame.quit()

                     sys.exit()

 

        screen.blit(paused_image, paused_rect)

 

        # 切换图片

        if not(delay % 5):

            switch_image = not switch_image

 

        delay -= 1

        if not delay:

            delay = 100

 

        if pygame.display.get_active():

            paused = paused

        else:

             paused = True

 

        pygame.display.flip()

        clock.tick(60)

 

if __name__ == "__main__":

    try:

        main()

    except SystemExit:

        pass

    except:

        traceback.print_exc()

        pygame.quit()

        input()

   

4. 实验

游戏实际运行画面:

https://www.bilibili.com/video/BV1MD4y1Q7c4

 

游戏本体:

链接: https://pan.baidu.com/s/1P6_u-3mwxTS5wgOsZwUvMg 提取码: x4d6

 

源码:

链接: https://pan.baidu.com/s/1Oeg0QSBmOZCigs-6m5J2ww 提取码: u3g5

5. 总结和展望

     游戏作为人们消遣的娱乐方式,更多的是使人放松和愉悦。通过学习pygame,我掌握基础的游戏开发框架。但是由于时间的原因,游戏内敌人种类少,自身使用的武器偏少,导致游戏后期缺少挑战。作为一个完整的游戏,游戏缺少完善的奖励机制,也导致后期缺乏游玩的动力。希望以后我可以开发更多好玩有趣的游戏。

参考文献:

[1]  https://web.archive.org/web/20190918143814/http://www.pygame.org/wiki/about     [2019-12-30]. 原始内容存档于2019-09-18

[2]      https://libregamewiki.org/Pygame

   [3]      http://pygame.renpy.org/

   [4] https://web.archive.org/web/20141019204533/http://pygame.renpy.org/api.html v [2014-10-14]. (原始内容存档于2014-10-19).

posted @ 2020-07-02 17:20  ZYM1999  阅读(289)  评论(0)    收藏  举报