PyGame库编写飞机大战小游戏并打包为exe文件

一、pygame简介

1.历史

Pygame最初是由Pete Shinner编写的一个利用SDL(Simple DirectMedia Layer)写成的游戏库,SDL是一个用于控制多媒体的夸平台C库,Pete最初创造pygame的目的是为了让Loki更有效的工作。Loki是一家公司,致力于向Linux上移植Windows的游戏的一家公司,不过已经倒闭了,不过Pete Shinner写的pygame库却留了下来,并且不断发展更新直到现在(ps:唉~ 人才进行工作,而天才进行创造啊~~)。截止这篇博客编写日期为止,pygame最新的版本为1.9.6

Pygame是跨平台Python模块,专为电子游戏设计,包含图像、声音。建立在SDL基础上,允许实时电子游戏研发而无需被低级语言(如机器语言汇编语言和)束缚。

2.pygame常用模块

用pygame写一个2D游戏那是绰绰有余,但是如果你想写一个大型3D游戏,我还是劝你醒一醒。比如飞机大战、植物大战僵尸等等。

关于pygame的安装我就不在赘述了,网上有很多教程,一般有三种安装方式,官网下载安装命令行安装,以及在PyCharm中的Project Interpreter中搜索pygame安装,这里推荐第一种和第三种安装方式,因为有时候在PyCharm中使用pygame的时候,会出现智能提示不全的情况,我认为是在安装的过程中,pygame加载没完成引起的(命令行使用pip install pygame容易产生这个问题),PyCharm一直有这个缺陷,不过已经越来越好了,相信以后这个问题能够得到解决。

模块名说明
pygame.init()开发游戏过程中的第一行代码,作用是加载pygame中导入到模块,如果查看源码你会看到 两行注释 "init() -> (numpass, numfail) initialize all imported pygame modules"括号里的第一个参数意思是加载通过的模块数,第二参数意思是失败的模块数
pygame.image.**:load()、save()、get_extended()、tostring()、fromstring()、frombuffer(),这些方法一般最常用的是load函数,当然还有另外几个函数,他们都可以再pygame的官方文档上找到介绍,这里就不在赘述
pygame.display控制窗口和屏幕的显示,具体方法参照pygame的官方文档
pygame.draw绘制图形用到的模块包括线和点最常用的是pygame.draw.rect()
pygame.event管理事件,比如点击时间,键盘按压事件等
pygame.font加载和呈现字体
pygame.key读取键盘按键
pygame.locals这个模块包含了 Pygame 定义的各种常量。使用 from pygame.locals import * 导入,比如HWSURFACE、KEYDOWN、KEYUP 等
pygame.mixer用于加载和播放声音
pygame.mouse鼠标点击事件
pygame.Rect管理矩形区域
pygame.SurfacePygame 中用于表示图像的对象。
pygame.time用于监控时间
pygame.music控制流音频
pygame.Color用于颜色表示
pygame.*pygame本身包含的函数方法:init、quit、get_init、error、get_error、set_error、get_sdl_version、get_sdl_byteorder等等

这些都是最常用的模块,除此之外,pygame还包含cursors、Overlay、math等模块以及camera、cdrom(磁盘存储)等等。如果英文过了四级的可以去看看pygame的官方文档 pygame官方文档 关于不同模块包含的方法,在这个文档中也是可以找到的。


Pygame会接受用户的各种操作(比如按键盘,移动鼠标等)产生事件。事件随时可能发生,而且量也可能会很大,Pygame的做法是把一系列的事件存放一个队列里,逐个的处理。
其实用pygame做游戏,最本质的就是这段代码,一个死循环等待监听事件并作出相应的处理。

# **死循环**中监听鼠标和键盘的事件
while True:
    # 获取窗口中的时间监听->列表
    even_list = pygame.event.get()
    # 遍历所有事件
    for event in even_list:
        # 如果是鼠标点击关闭事件
        if event.type == pygame.QUIT:
            # 停止游戏引擎
            pygame.quit()
            # 关闭窗口,退出游戏,
            sys.exit()

二、一个窗口小游戏要做的事情

一般小游戏需要用到的常用的基础操作如下:

  • 导入与初始化
  • 窗口相关操作
  • 图像相关操作
  • 事件相关操作
  • 音效相关操作
  • 文字显示操作

"""
飞机大战游戏

"""
import pygame
import sys

# 实例化pygame
pygame.init()
# 创建游戏窗口[宽:高]->窗口对象
window = pygame.display.set_mode([512, 768])

# 设置窗口的title
pygame.display.set_caption("飞机大战")

# 设置窗口的图标icon,加载一个图片对象
logo_image = pygame.image.load("res/images/app.ico")
pygame.display.set_icon(logo_image)

# 游戏背景图片添加到窗口上,blit(需要添加的对象,(x坐标,y坐标))
bg_image = pygame.image.load('res/images/img_bg_level_1.jpg')
window.blit(bg_image, (0, 0))

# 刷新窗口显示添加的元素
pygame.display.update()

# **死循环**中监听鼠标和键盘的事件
while True:
    # 获取窗口中的时间监听->列表
    even_list = pygame.event.get()
    # 遍历所有事件
    for event in even_list:
        # 如果是鼠标点击关闭事件
        if event.type == pygame.QUIT:
            # 停止游戏引擎
            pygame.quit()
            # 关闭窗口,退出游戏,
            sys.exit()

        if event.type == pygame.KEYDOWN:
            # 是否按下esc
            if event.key == pygame.K_ESCAPE:
                # 停止游戏引擎
                pygame.quit()
                sys.exit()

    # 监听键盘长按事件 -> 元组(0没按下,1长按了) 字母对应阿斯克码
    pressed_keys = pygame.key.get_pressed()

    # 判断向上是否被长按(1)
    if pressed_keys[pygame.K_UP]:
        print("向上")

    # 判断向下是否被长按(1)
    if pressed_keys[pygame.K_DOWN]:
        print("向下")

    # 判断向左是否被长按(1)
    if pressed_keys[pygame.K_LEFT]:
        print("向左")

    # 判断向右是否被长按(1)
    if pressed_keys[pygame.K_RIGHT]:
        print("向右")

最基本的操作就是上面代码中呈现的那样,除此之外,还有硬盘存取、音效播放等等操作。加载图片之后启动,然后就能看到如下的运行效果


然后在此基础上,让地图动起来,然后再添加玩家飞机,敌机 子弹和声音

三、飞机大战游戏

开始用pygame开发小游戏之前,必须清楚python中的坐标系,在Python中坐标系的原点在屏幕的左上角,向右和向下分别是x轴和y轴的正方向。游戏飞机的移动其实就是飞机图片在这个坐标系中坐标的变换而已。

1.准备工作

    飞机大战中涉及的对象:

    用面向对象的思想进行开发,先来锊清楚游戏中有几个对象:游戏窗口、玩家飞机、敌机、子弹、背景图、文字、声音

    飞机大战中事件的处理

  • 玩家飞机上下左右运动
  • 空格发射子弹
  • 退出

2. 模块设计

  • 游戏窗口模块
  • 飞机模块
  • 子弹模块
  • 分数模块
  • 地图模块

游戏窗口模块

    首先是游戏运行的主类,窗口类(GameWindow),窗口类完成游戏吃初始化以及事件监听、事件处理以及游戏运行等工作,主要框架代码如下(篇幅限制,具体方法实现请参考目录五

导入模块
import pygame

# 定义常量 记录窗口的宽和高
WINDOW_WIDTH = 512
WINDOW_HEIGHT = 768

# 自定义游戏窗口类
class GameWindow(object):

    # 构造函数
    def __init__(self):
        # 实例化窗口对象
        self.window = pygame.display.set_mode([WINDOW_WIDTH, WINDOW_HEIGHT])
        # 加载资源图片
        self.icon_img = pygame.image.load("res/app.ico")
        # 设置窗口icon
        pygame.display.set_icon(self.icon_img)
        # 设置窗口标题
        pygame.display.set_caption("飞机大战")

    # 1.处理各种矩形坐标移动
    def action(self):
        pass

    # 2.根据矩形坐标,重新对元素进行绘制
    def draw(self):
        pass

    # 3.处理窗口中的监听事件
    def event(self):
        pass

    # 4.刷新窗口
    def update(self):
        pass

    # 开始执行程序
    def run(self):
        # 死循环
        while True:
            # 处理矩形移动
            self.action()
            # 绘制元素
            self.draw()
            # 处理窗口的事件监听
            self.event()
            # 刷新窗口
            self.update()


# 程序的入口
def main():
    # 实例化game
    game = GameWindow()
    # 执行程序
    game.run()

if __name__ == '__main__':
    main()

子弹模块
    飞机模块中包含两个类,玩家飞机类和游戏中的敌机类,玩家飞机类在初始化的时候要先实实例化子弹类。然后在类中定义响应的方法

import pygame


class Bullet(object):

    def __init__(self):
        # 加载子弹图片
        self.bullet_img = pygame.image.load("res/images/bullet_10.png")
        # 图片矩形
        self.bullet_img_rect = self.bullet_img.get_rect()
        # 子弹状态
        self.is_shot = False
        # 子弹移动速度
        self.speed = 5

    def move_up(self):
        """
        子弹向上移动(玩家飞机)
        :return:
        """
        self.bullet_img_rect.move_ip(0, -self.speed)
        if self.bullet_img_rect[1] <= -self.bullet_img_rect[3]:
            self.is_shot = False

    def move_down(self):
        """
        子弹向下移动(敌方飞机)
        :return:
        """
        pass

飞机模块
    飞机模块中包含两个类,玩家飞机类和敌机类,其中玩家飞机类要实例化子弹类。

import pygame
from game import bullet
import random

WINDOW_WIDTH = 512
WINDOW_HEIGHT = 768


class HeroPlane:

    def __init__(self):
        # 飞机图片
        self.img = pygame.image.load('res/images/hero2.png')
        # 英雄飞机的外框矩形 -> (x,y, 宽像素,高像素)
        self.img_rect = self.img.get_rect()
        # 飞机的初始位置
        self.img_rect.move_ip((WINDOW_WIDTH - self.img_rect[2]) / 2, (WINDOW_HEIGHT - self.img_rect[3]) - 50)
        # 飞机的移动速度
        self.speed = 3

        # 子弹弹夹
        self.bullet_list = [bullet.Bullet() for _ in range(6)]

    def move_up(self):
        # 边界检查
        if self.img_rect[1] >= 0:
            self.img_rect.move_ip(0, -self.speed)  # move_ip(x.y)

    def move_down(self):
        # 边界检查
        if self.img_rect[1] <= WINDOW_HEIGHT - self.img_rect[3]:
            self.img_rect.move_ip(0, self.speed)  # move_ip(x.y)

    def move_left(self):
        # 边界检查
        if self.img_rect[0] >= 0:
            self.img_rect.move_ip(-self.speed, 0)  # move_ip(x.y)

    def move_right(self):
        # 边界检查
        if self.img_rect[0] <= WINDOW_WIDTH - self.img_rect[2]:
            self.img_rect.move_ip(self.speed, 0)  # move_ip(x.y)

    def shoot(self):
        # 遍历所有子弹
        for bul in self.bullet_list:
            if not bul.is_shot:
                # 设置子弹发射位置
                bul.bullet_img_rect[0] = self.img_rect[0] + (self.img_rect[2] - bul.bullet_img_rect[2]) / 2
                bul.bullet_img_rect[1] = self.img_rect[1] - (bul.bullet_img_rect[3] - 10)
                # 更新子弹状态
                bul.is_shot = True
                # 一次只能发射一颗子弹
                break


class EnemyPlane:

    def __init__(self):
        # 随机加载敌机图片
        self.num = str(random.randint(1, 7))
        self.img = pygame.image.load('res/images/img-plane_' + self.num + '.png')

        # 敌机的外框矩形
        self.img_rect = self.img.get_rect()
        # 敌机的初始位置
        self.img_rect.move_ip((WINDOW_WIDTH - self.img_rect[2]) / 2, 0)
        # 敌机的移动速度
        self.speed = 3
        # 随机初始化位置
        self.reset()

    def move_down(self):
        """
        敌机向下移动
        :return:
        """
        self.img_rect.move_ip(0, self.speed)

        # 敌机位置回收
        if self.img_rect[1] >= WINDOW_HEIGHT:
            self.reset()

    def reset(self):
        """
        敌机初始化位置
        :return:
        """
        self.img_rect[0] = random.randint(0, WINDOW_WIDTH - self.img_rect[2])
        self.img_rect[1] = self.img_rect[3]
        self.speed = random.randint(3, 5)

地图模块

import random
import pygame

WINDOW_WIDTH = 512
WINDOW_HEIGHT = 768


class GameMap(object):

    def __init__(self):
        self.num = str(random.randint(1, 5))
        # 图片1,2无缝连接,上下滚动
        self.img_1 = pygame.image.load("./res/images/img_bg_level_" + self.num + ".jpg")
        self.img_2 = pygame.image.load("./res/images/img_bg_level_" + self.num + ".jpg")
        # 记录背景图片的y轴坐标
        self.img1_y = -WINDOW_HEIGHT
        self.img2_y = 0
        # 背景移动速度
        self.speed = 2

    def move_down(self):
        """
        背景图片向下移动
        :return:
        """
        # 地图的y轴重置
        if self.img1_y >= 0:
            self.img1_y = -WINDOW_HEIGHT
            self.img2_y = 0

        self.img1_y += self.speed
        self.img2_y += self.speed

分数模块

import pygame
import random


class GameScore:
    __cls_score = 0

    def __init__(self, font_size):
        self.font = pygame.font.SysFont("SimHei", font_size)
        self.text_obj = self.font.render("分数:0", 1, (255, 255, 255))

    def set_text(self):
        self.__cls_score += 1
        # 随机颜色
        r = random.randint(0, 255)
        g = random.randint(0, 255)
        b = random.randint(0, 255)
        self.text_obj = self.font.render("分数:%d" % self.__cls_score, 1, (r, g, b))


下面这几个模块都需要在GameWindow中进行实例化并加载到窗口中,然后在运行游戏中检测键盘和鼠标等事件的发生,做出相应的处理。游戏其实就是一个大循环,动画特效其实就是图片切换以及在坐标系中的坐标变换。(详细的游戏图片、音效素材以及代码在 目录五 中查看


然后可以另外创建一个启动文件start.py 用来启动游戏

from game_main import GameWindow

if __name__ == '__main__':
    window = GameWindow()
    window.run()

游戏运行效果

除此之外还可以将游戏打包中exe可执行文件,参照 目录四

四、游戏打包成exe可执行文件

1、准备

使用的打包工具是pyinstaller ,所以需要使用命令行在pycharm的Terminal终端运行pip install pyinstaller 进行安装不过要首先安装pywin32

pip install pywin32
pip install pyinstaller

2、打包

打包方式很很多种,比如将工程文件复制到其他文件夹,然后在工程的入口文件(比如:main.py)同级的目录打开命令行(shift+鼠标右键)然后运行打包命令,不过这种方式需要你将pyinstaller安装到电脑,而如果是用pycharm中安装的,一般都是安装到虚拟环境中。


推荐方式:

第一步: pycharm中打开Terminal终端进入虚拟环境,运行命令(命令参数参照下表)

pyinstaller -D -w -i app.ico start.py

第二步:把res文件夹复制到 …\dist\start 下,就是生成exe文件的目录下,res是游戏需要的图片和音频资源,然后运行exe文件就可以开始游戏了

因为使用的是生成依赖文件(-D)的打包方式,所以打包之后的文件要比只生成exe文件的打包方式(-F)文件要大一点,但是相对的运行速度要快一点
打包成功之后的问价结构如图1,删除无用的文件如图2

图1图2

可执行文件start.exe就在dist文件下,找到之后就可以运行游戏了。


可选参数功能说明
-F只在dist中生产一个demo.exe文件。
-D默认选项,除了demo.exe外,还会在在dist中生成很多依赖文件,推荐使用
-c默认选项,只对windows有效,使用控制台,就像编译运行C程序后的黑色弹窗。
-w只对windows有效,不使用控制台。
-p设置导入路径,一般用不到。
-i将file.icon设置为exe文件的图标,推荐一个icon网站:icon

3、注意事项

在打包过程中会遇到的常见问题:打包成功,但是运行之后会报错,比如 Failed to execute script…
产生的原因有很多,参考以下两种因素

1.打包文件中含有系统无法识别的文字

2.工程需要的资源没有放到上文所列的目录


五、完整代码以及游戏素材下载地址

GitHub 资源地址ps:码字不容易,赏个“Star”吧!!

这个小游戏只实现了基本的功能,如果你感兴趣还可以进行迭代开发,比如增加飞机碰撞、子弹击中效果、飞机切换子弹、飞机变身等等功能。


附录1 pygame中表示键盘按键的常用常量列表

pygame ConstantASCIIDescription
K_BACKSPACE\bbackspace
K_TAB\ttab
K_CLEARclear
K_RETURN\rreturn
K_PAUSEpause
K_ESCAPE^[escape
K_SPACEspace
K_EXCLAIM!exclaim
K_QUOTEDBL"quotedbl
K_HASH#hash
K_DOLLAR$dollar
K_AMPERSAND&ampersand
K_QUOTEquote
K_LEFTPAREN(left parenthesis
K_RIGHTPAREN)right parenthesis
K_ASTERISK*asterisk
K_PLUS+plus sign
K_COMMA,comma
K_MINUS-minus sign
K_PERIOD.period
K_SLASH/forward slash
K_00数字键((K_数字))
K_aa字母键(K_小写字母)
K_DELETEdelete

详细按键常量查询 pygame官方文档


参考博客:
https://blog.csdn.net/qq_38526635/article/details/82688786
https://blog.csdn.net/Hubz131/article/details/86718684
https://www.jianshu.com/p/48f6dea265eb

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值