作为新手自学Python的第二十一天,技术低微,希望可以通过这种方式督促自己学习。
个人学习环境:python3.9,PyCharm 2021.3.2 (Community Edition)
利用python完成飞机大战是一个非常经典的练手项目,本次主要内容是添加敌人飞机及战斗,并完成最终的打包操作。
在这一节中,我们会多封装一个Enemy类,通过检测子弹和敌人飞机的碰撞进行消灭敌机。从而完成最终的游戏制作。
# 导入pygame库
import pygame
# 导入pygame库中的一些常量
from pygame.locals import *
# 导入sys库中的exit函数
from sys import exit
from random import randint
# 定义窗口的分辨率
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640
# 使用精灵模块(pygame.sprite),将游戏内的一类元素进行编组
# 玩家类
class Hero(pygame.sprite.Sprite):
def __init__(self, hero_surface, hero_init_pos):
pygame.sprite.Sprite.__init__(self)
self.image = hero_surface
self.rect = self.image.get_rect()
self.rect.topleft = hero_init_pos
self.speed = 6
self.is_hit = False
# 子弹1的Group
self.bullets1 = pygame.sprite.Group()
# 控制射击行为
def single_shoot(self, bullet1_surface):
buttle1 = Buttle(bullet1_surface, self.rect.midtop)
self.bullets1.add(buttle1)
# 控制飞机移动
def move(self, offset):
x = self.rect.left + offset[pygame.K_RIGHT] - offset[pygame.K_LEFT]
y = self.rect.top + offset[pygame.K_DOWN] - offset[pygame.K_UP]
if x < 0:
self.rect.left = 0
elif x > SCREEN_WIDTH - self.rect.width:
self.rect.left = SCREEN_WIDTH - self.rect.width
else:
self.rect.left = x
if y < 0:
self.rect.top = 0
elif y > SCREEN_HEIGHT - self.rect.height:
self.rect.top = SCREEN_HEIGHT - self.rect.height
else:
self.rect.top = y
# 子弹类
class Buttle(pygame.sprite.Sprite):
def __init__(self, buttle_surface, buttle_init_pos):
pygame.sprite.Sprite.__init__(self)
self.image = buttle_surface
self.rect = self.image.get_rect()
self.rect.topleft = buttle_init_pos
self.speed = 8
# 控制子弹移动
def update(self):
self.rect.top -= self.speed
# 子弹超出屏幕范围后,调用kill()移除
if self.rect.top < -self.rect.height:
self.kill()
# 敌人类
class Enemy(pygame.sprite.Sprite):
def __init__(self, enemy_surface, enemy_init_pos):
pygame.sprite.Sprite.__init__(self)
self.image = enemy_surface
self.rect = self.image.get_rect()
self.rect.topleft = enemy_init_pos
self.speed = 2
# 爆炸动画画面索引
self.down_index = 0
def update(self):
self.rect.top += self.speed
if self.rect.top > SCREEN_HEIGHT:
self.kill()
def run_game():
# 定义画面帧率
FRAME_RATE = 60
# 定义动画周期(帧率)
ANIMATE_CYCLE = 30
ticks = 0
clock = pygame.time.Clock()
# dict 字典
offset = {pygame.K_LEFT: 0, pygame.K_RIGHT: 0, pygame.K_UP: 0, pygame.K_DOWN: 0}
# 玩家坠毁图片索引
hero_down_index = 1 # new
# 初始化游戏
pygame.init() # 初始化 pygame
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT]) # 初始化窗口
pygame.display.set_caption('飞机大战') # 设置窗口标题
# 载入背景图
background = pygame.image.load('./image/background.png')
# 游戏结束图
gameover = pygame.image.load('./image/gameover.png')
# Hero图片
hero1 = pygame.image.load('./image/me1.png')
hero2 = pygame.image.load('./image/me2.png')
hero3 = pygame.image.load('./image/me_destroy_1.png')
hero4 = pygame.image.load('./image/me_destroy_2.png')
hero5 = pygame.image.load('./image/me_destroy_3.png')
hero6 = pygame.image.load('./image/me_destroy_4.png')
hero_surface = [hero1, hero2, hero3, hero4, hero5, hero6]
hero_pos = [200, 500]
# bullet1模型
buttle1_surface = pygame.image.load('./image/bullet1.png')
# enemy图片
enemy1_surface = pygame.image.load('./image/enemy1.png')
enemy1_down_surface1 = pygame.image.load('./image/enemy1_down1.png')
enemy1_down_surface2 = pygame.image.load('./image/enemy1_down2.png')
enemy1_down_surface3 = pygame.image.load('./image/enemy1_down3.png')
enemy1_down_surface4 = pygame.image.load('./image/enemy1_down4.png')
enemy1_down_surface = [enemy1_down_surface1, enemy1_down_surface2, enemy1_down_surface3, enemy1_down_surface4]
# 创建玩家
hero = Hero(hero_surface[0], hero_pos)
# 创建敌人组
enemy1_group = pygame.sprite.Group()
# 创建击毁敌人组
enemy1_down_group = pygame.sprite.Group()
# 事件循环(main loop)
while True:
# 控制游戏最大帧率
clock.tick(FRAME_RATE)
# 绘制背景
screen.blit(background, (0, 0))
# 改变飞机图片制造动画
if ticks >= ANIMATE_CYCLE:
ticks = 0
# 改变飞机动画
if hero.is_hit:
if ticks % (ANIMATE_CYCLE // 2) == 0:
hero_down_index += 1
hero.image = hero_surface[hero_down_index]
if hero_down_index == 5:
break
else:
hero.image = hero_surface[ticks // (ANIMATE_CYCLE // 2)]
# 射击
if ticks % 10 == 0:
hero.single_shoot(buttle1_surface)
# 控制子弹
hero.bullets1.update()
# 绘制子弹
hero.bullets1.draw(screen)
# 产生敌机
if ticks % 30 == 0:
enemy = Enemy(enemy1_surface,
[randint(0, SCREEN_WIDTH - enemy1_surface.get_width()), -enemy1_surface.get_height()])
enemy1_group.add(enemy)
# 控制敌机
enemy1_group.update()
# 绘制敌机
enemy1_group.draw(screen)
# 检测敌机与子弹的碰撞
enemy1_down_group.add(pygame.sprite.groupcollide(enemy1_group, hero.bullets1, True, True))
for enemy1_down in enemy1_down_group:
screen.blit(enemy1_down_surface[enemy1_down.down_index], enemy1_down.rect)
if ticks % (ANIMATE_CYCLE // 2) == 0:
if enemy1_down.down_index < 3:
enemy1_down.down_index += 1
else:
enemy1_down_group.remove(enemy1_down)
# 检测敌机与玩家的碰撞
enemy1_down_list = pygame.sprite.spritecollide(hero, enemy1_group, True)
if len(enemy1_down_list) > 0:
enemy1_down_group.add(enemy1_down_list)
hero.is_hit = True
# 绘制飞机
screen.blit(hero.image, hero.rect)
ticks += 1
# 更新屏幕
pygame.display.update()
# 处理游戏退出
# 从消息队列中循环取
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
# 控制方向
if event.type == pygame.KEYDOWN:
if event.key in offset:
offset[event.key] = hero.speed
elif event.type == pygame.KEYUP:
if event.key in offset:
offset[event.key] = 0
# 移动飞机
hero.move(offset)
# 跳出主循环
screen.blit(gameover, (90, 300))
# 玩家坠毁后退出游戏
while True:
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
run_game()
在Hero类中增加一个is_hit属性,用来检测敌人飞机和自己是否发生了碰撞,从而结束游戏的标志;
利用randint函数完成敌机随机生成在屏幕顶端,按一定时间间隔生成敌机;
使用Pygame自带的grouppcollide()函数检测子弹是否与敌机发生碰撞;
使用Pygame自带的spritecollide()函数检测飞机和敌机是否发生碰撞。
至此,游戏基础整体结构完成。
(五)打包成exe文件
代码编写完成以后我们需要将项目打包为exe文件才能够让别人也玩到我们做的飞机大战,我们使用pyinstaller来完成打包操作。
1、安装pipstaller
首先是安装pystaller,使用命令行执行
pip install pystaller
2、执行打包操作
先使用cmd打开你的py文件所在的文件夹,执行如下命令
pyinstaller -F -w -n AirplaneWars main.py
等待命令执行完毕,会发现目录下多出了几个文件夹
其中生成的exe文件存放在dist文件夹下,我们需要将我们的图片文件夹复制一份放入dist文件夹即可运行我们的AirplantWars.exe。
下面列出常用的pyinstaller选项。
-h,--help | 查看该模块的帮助信息 |
---|---|
-F,-onefile | 产生单个的可执行文件 |
-D,--onedir | 产生一个目录(包含多个文件)作为可执行程序 |
-a,--ascii | 不包含 Unicode 字符集支持 |
-d,--debug | 产生 debug 版本的可执行文件 |
-w,--windowed,--noconsolc | 指定程序运行时不显示命令行窗口(仅对 Windows 有效) |
-c,--nowindowed,--console | 指定使用命令行窗口运行程序(仅对 Windows 有效) |
-o DIR,--out=DIR | 指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件 |
-p DIR,--path=DIR | 设置 Python 导入模块的路径(和设置 PYTHONPATH 环境变量的作用相似)。也可使用路径分隔符(Windows 使用分号,Linux 使用冒号)来分隔多个路径 |
-n NAME,--name=NAME | 指定项目(产生的 spec)名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字 |