简介
使用python实现pygame版的飞机大战游戏;
环境:Windows系统+python3.8.0
游戏规则:
1.点击“PLAY”或者按键“P”开始游戏;
2.敌机根据设置频率从顶部随机位置生成,生成后向下移动;
3.飞船在底部中间生成,玩家使用上下左右键控制飞船移动,敲击空格键发射子弹;
4.子弹打到敌机,该敌机产生爆炸效果并累计分数到右上角;
5.消灭10只飞机后,等级升高,敌机生成频率变快,下落速度也变快;
6.当三条命都消失了,游戏结束。
游戏运行效果如下:
实现过程
1.新建文件“file.py”,用来存储信息到文件和读取文件的信息,本例用来存储和读取最高分;
import pickle # filename = 'file/stats.pkl' # 存储信息到文件 def save_file(obj, filename): statsObj = load_file(filename) if statsObj == 0: # 不存在文件时,直接保存字典 with open(filename, 'wb') as f: pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) else: # 存在文件时,只修改文件中的最高分 for key, val in statsObj.items(): # 获取文件最高分的值(当文件字段不止一个时候使用) if key == 'highScore': statsObj[key] = obj['highScore'] obj = statsObj with open(filename, 'wb') as f: pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) # 读取信息 def load_file(filename): try: with open(filename, 'rb') as f: return pickle.load(f) except FileNotFoundError: # 不存在文件则输入错误信息 msg = "Sorry, the file " + filename + " does not exist." print(msg) return 0 # obj = {'highScore': 20, 'points': 5} # obj = {'highScore': 50} # save_file(obj, filename) # filedata = load_file(filename) # print(filedata)
2.k新建文件settings.py,用来定义一些必须的基本属性和初始值;
import file as f class Settings(): def __init__(self): self.screen_width = 480 self.screen_height = 660 self.bg_color = (230, 230, 230) # 子弹设置(宽、高、颜色、最大数量) self.bullet_width = 5 self.bullet_height = 15 self.bullet_color = 255, 255, 255 # 敌机移动频率 self.enemy_frequency = 0 # 加快游戏节奏的速度 self.speedup_scale = 1.1 # 分数的提高速度 self.score_scale = 1.5 self.initialize_settings() # 初始化统计信息 self.reset_stats() # 统计信息文件路径 self.filename = 'file/stats.pkl' # 游戏刚启动时处于非活动状态 self.game_active = False # 读取文件的最高分,在任何情况下都不应重置最高得分 statsObj = f.load_file(self.filename) if statsObj == 0: # 不存在文件则显示最高分0 highScore = 0 else: for key, val in statsObj.items(): # 获取文件最高分的值(当文件字段不止一个时候使用) if key == 'highScore': highScore = val self.high_score = highScore def initialize_settings(self): """初始化随游戏进行而变化的设置""" self.player_move_speed = 2.5 self.bullet_speed = 3 self.enemy_move_speed = 1 # 记分 self.one_points = 50 def increase_speed(self): """提高速度设置""" # self.player_move_speed *= self.speedup_scale self.bullet_speed *= self.speedup_scale self.enemy_move_speed *= self.speedup_scale self.one_points = int(self.one_points * self.score_scale) def reset_stats(self): """初始化在游戏运行期间可能变化的统计信息""" # 可射失的数量 self.player_limit = 3 # 射击分数 self.score = 0 # 等级 self.level = 1 # 打中多少矩形升一级 self.level_number = 10 # 生成敌机频率间隔 self.enemy_frequency_space = 50
3.新建文件enemy.py,用来定义敌机类(位置topleft随机生成)和声明方法move;
import pygame import random from pygame.sprite import Sprite class Enemy(Sprite): def __init__(self, enemy_down_imgs, settings): super(Enemy, self).__init__() self.image = pygame.image.load('images/enemy1.png') self.rect = self.image.get_rect() self.rect.topleft = [random.randint(0, settings.screen_width - self.rect.width), 0] self.down_imgs = enemy_down_imgs self.speed = settings.enemy_move_speed self.down_index = 0 # 敌机移动,边界判断及删除在游戏主循环里处理 def move(self): self.rect.top += self.speed
4.新建文件player.py,用来定义玩家类(可上下左右移动)和相应的方法;
import pygame from pygame.sprite import Sprite class Player(Sprite): def __init__(self, settings, screen): super(Player, self).__init__() self.settings = settings self.screen = screen self.screen_rect = self.screen.get_rect() # 引入飞船图片并定位 self.image = pygame.image.load('images/player.png') self.rect = self.image.get_rect() self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom # 移动标志 self.move_left = False self.move_right = False self.move_down = False self.move_up = False def rotate(self, angle): # 图片旋转 self.image = pygame.transform.rotate(self.image, angle) def scale(self, multiple): # 图片缩放 self.image = pygame.transform.smoothscale(self.image, (multiple, multiple)) def update(self): if self.move_left and self.rect.left > self.screen_rect.left: self.rect.centerx -= self.settings.player_move_speed if self.move_right and self.rect.right < self.screen_rect.right: self.rect.centerx += self.settings.player_move_speed if self.move_down and self.rect.bottom < self.screen_rect.bottom: self.rect.centery += self.settings.player_move_speed if self.move_up and self.rect.top > 0: self.rect.centery -= self.settings.player_move_speed def draw_player(self): """绘制飞船到屏幕""" self.screen.blit(self.image, self.rect)
5.新建文件“bullet.py”,用来定义子弹类(位置在飞船的顶部,并往上移动)和相应的方法;
import pygame from pygame.sprite import Sprite class Bullet(Sprite): """ 一个对飞船发射的子弹进行管理的类 """ def __init__(self, settings, screen, player): """ 在飞船所处的位置创建一个子弹对象 """ super(Bullet, self).__init__() self.screen = screen # 在 (0,0) 处创建一个表示子弹的矩形,再设置正确的位置 self.rect = pygame.Rect(0, 0, settings.bullet_width, settings.bullet_height) self.rect.centerx = player.rect.centerx # 飞船顶部 self.rect.bottom = player.rect.top # 存储用小数表示的子弹位置 self.y = float(self.rect.y) self.color = settings.bullet_color self.speed = settings.bullet_speed def update(self): """向上移动子弹""" # 更新表示子弹位置的小数值(子弹往右) self.y -= self.speed # 更新表示子弹的rect的位置 self.rect.y = self.y def draw_bullet(self): """在屏幕上绘制子弹""" pygame.draw.rect(self.screen, self.color, self.rect)
6.新建文件“button.py”,用来定义按钮类和相应方法,本例使用于绘制“PLAY”按钮;
import pygame.font class Button(): def __init__(self, screen, msg): """初始化按钮的属性""" self.screen = screen self.screen_rect = screen.get_rect() # 设置按钮的尺寸和其他属性 self.width, self.height = 100, 30 self.button_color = (216, 30, 6) self.text_color = (255, 255, 255) self.font = pygame.font.SysFont(None, 36) # 创建按钮的rect对象,并使其居中 self.rect = pygame.Rect(0, 0, self.width, self.height) self.rect.center = self.screen_rect.center # 按钮的标签只需创建一次 self.prep_msg(msg) def prep_msg(self, msg): """将msg渲染为图像,并使其在按钮上居中""" self.msg_image = self.font.render(msg, True, self.text_color, self.button_color) self.msg_image_rect = self.msg_image.get_rect() self.msg_image_rect.center = self.rect.center def draw_button(self): # 绘制一个用颜色填充的按钮,再绘制文本 self.screen.fill(self.button_color, self.rect) self.screen.blit(self.msg_image, self.msg_image_rect)
7.新建文件“scoreboard.py”,用来定义记分板,本例使用于绘制左上角飞船(生命数)、顶部中间的“最高分”、右上角的“积分”和“等级”;
import pygame.font from pygame.sprite import Group from player import Player class Scoreboard(): """显示得分信息的类""" def __init__(self, settings, screen): """初始化显示得分涉及的属性""" self.screen = screen self.screen_rect = screen.get_rect() self.settings = settings # 显示得分信息时使用的字体设置 self.text_color = (255, 255, 255) self.font = pygame.font.SysFont(None, 30) # 飞船缩放值 self.scaleValue = 20 # 准备初始得分图像\最高得分\等级 self.prep_score() self.prep_high_score() self.prep_level() self.prep_players() def prep_score(self): """将得分转换为渲染的图像""" rounded_score = int(round(self.settings.score, -1)) score_str = '{:,}'.format(rounded_score) self.score_image = self.font.render(score_str, True, self.text_color) # 将得分放在屏幕右上角 self.score_rect = self.score_image.get_rect() self.score_rect.right = self.screen_rect.right -20 self.score_rect.top = 10 def prep_high_score(self): """ 将最高得分转换为渲染的图像 """ high_score = int(round(self.settings.high_score, -1)) high_score_str = "{:,}".format(high_score) self.high_score_image = self.font.render(high_score_str, True, self.text_color) # 将最高得分放在屏幕顶部中央 self.high_score_rect = self.high_score_image.get_rect() self.high_score_rect.centerx = self.screen_rect.centerx self.high_score_rect.top = self.score_rect.top def prep_level(self): """将等级转换为渲染的图像""" self.level_image = self.font.render(str(self.settings.level), True, self.text_color) # 将等级放在得分下方 self.level_rect = self.level_image.get_rect() self.level_rect.right = self.score_rect.right self.level_rect.top = self.score_rect.bottom + 10 def prep_players(self): """ 显示还余下多少艘飞船 """ self.players = Group() for player_number in range(self.settings.player_limit): player = Player(self.settings, self.screen) # 缩放球大小并赋值位置 player.scale(self.scaleValue) player.rect.x = 10 + player.rect.width * player_number * 0.5 player.rect.y = self.score_rect.top self.players.add(player) def show_score(self): """在屏幕上显示得分""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) self.screen.blit(self.level_image, self.level_rect) # 绘制飞船 self.players.draw(self.screen)
8.新建文件“game_functions.py”,存放跟游戏有关的所有业务逻辑函数(代码有详细的注释信息);
import sys import pygame from bullet import Bullet from enemy import Enemy import file as f # 事件 def check_events(settings, screen, player, play_button, scoreboard, bullets, fireSound): """ 响应按键和鼠标事件 """ for event in pygame.event.get(): if event.type == pygame.QUIT: save_file(settings) sys.exit() elif event.type == pygame.KEYDOWN: check_keydown_events(event, settings, screen, player, scoreboard, bullets, fireSound) elif event.type == pygame.KEYUP: check_keyup_events(event, player) elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x, mouse_y = pygame.mouse.get_pos() check_play_button(settings, play_button, scoreboard, mouse_x, mouse_y) def check_keydown_events(event, settings, screen, player, scoreboard, bullets, fireSound): """ 响应按键 """ if event.key == pygame.K_DOWN: player.move_down = True elif event.key == pygame.K_UP: player.move_up = True elif event.key == pygame.K_LEFT: player.move_left = True elif event.key == pygame.K_RIGHT: player.move_right = True elif event.key == pygame.K_SPACE: fireSound.play() # 点击空格键创建一颗子弹 fire_bullet(settings, screen, player, bullets) elif event.key == pygame.K_p: start_game(settings, scoreboard) elif event.key == pygame.K_q: save_file(settings) sys.exit() def check_keyup_events(event, player): """ 响应松开 """ if event.key == pygame.K_DOWN: player.move_down = False elif event.key == pygame.K_UP: player.move_up = False elif event.key == pygame.K_LEFT: player.move_left = False elif event.key == pygame.K_RIGHT: player.move_right = False def check_play_button(settings, play_button, scoreboard, mouse_x, mouse_y): """在玩家单击Play按钮时开始新游戏""" button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y) if button_clicked and not settings.game_active: start_game(settings, scoreboard) def start_game(settings, scoreboard): """开始游戏""" # 重置游戏设置 settings.initialize_settings() # 隐藏光标 pygame.mouse.set_visible(False) # 重置游戏统计信息 settings.reset_stats() settings.game_active = True # 重置记分牌图像 scoreboard.prep_score() scoreboard.prep_high_score() scoreboard.prep_level() scoreboard.prep_players() def save_file(settings): # 保持文件 obj = {'highScore': settings.high_score} f.save_file(obj, settings.filename) # 敌机 def update_enemies(settings, screen, scoreboard, enemies, enemies_down, enemy_down_imgs, player, bullets, explosiveSound): # 生成敌机,需要控制生成频率 if settings.enemy_frequency % settings.enemy_frequency_space == 0: enemy1 = Enemy(enemy_down_imgs, settings) enemies.add(enemy1) settings.enemy_frequency += 1 if settings.enemy_frequency >= 100: settings.enemy_frequency = 0 for enemy in enemies: # 移动敌机 enemy.move() # 敌机与玩家飞机碰撞效果处理 两个精灵之间的圆检测 if pygame.sprite.collide_circle(enemy, player): enemies_down.add(enemy) enemies.remove(enemy) settings.player_limit -= 1 scoreboard.prep_players() break # 移动出屏幕后删除飞机 if enemy.rect.top < 0: enemies.remove(enemy) # 敌机被子弹击中效果处理 # 将被击中的敌机对象添加到击毁敌机 Group 中,用来渲染击毁动画 # 方法groupcollide()是检测两个精灵组中精灵们的矩形冲突 enemies1_down = pygame.sprite.groupcollide(enemies, bullets, True, True) if enemies1_down: explosiveSound.play() # 计算分数并渲染 for enemys in enemies1_down.values(): settings.score += settings.one_points * len(enemys) scoreboard.prep_score() # 渲染最高分 check_high_score(settings, scoreboard) # 等达到等级数量升级并渲染新等级 settings.level_number -= 1 if settings.level_number == 0: settings.increase_speed() settings.level += 1 scoreboard.prep_level() # 还原为4(同settings一致) settings.level_number = 10 # 加快生成敌机 if settings.enemy_frequency_space > 10: settings.enemy_frequency_space -= 10 # 遍历key值 返回的碰撞敌机 for enemy_down in enemies1_down: # 点击销毁的敌机到列表 enemies_down.add(enemy_down) # 敌机被子弹击中效果显示 for enemy_down in enemies_down: if enemy_down.down_index == 0: pass if enemy_down.down_index > 7: enemies_down.remove(enemy_down) continue #显示碰撞图片 screen.blit(enemy_down.down_imgs[enemy_down.down_index // 2], enemy_down.rect) enemy_down.down_index += 1 # 显示精灵 enemies.draw(screen) # 子弹 def fire_bullet(settings, screen, player, bullets): """创建子弹""" new_bullet = Bullet(settings, screen, player) bullets.add(new_bullet) def update_bullets(screen, bullets): """更新子弹的位置,并删除已消失的子弹""" # 更新子弹的位置 bullets.update() # 删除已消失的子弹并同时更新飞船的生命 for bullet in bullets.copy(): if bullet.rect.top < screen.get_rect().top: bullets.remove(bullet) # 分数 def check_high_score(settings, scoreboard): """检查是否诞生了新的最高得分""" if settings.score > settings.high_score: settings.high_score = settings.score scoreboard.prep_high_score() # 屏幕 def update_screen(settings, screen, player, play_button, scoreboard, enemies, bullets): """ 更新屏幕上的图像,并切换到新屏幕 """ # 绘制飞船到屏幕 player.draw_player() # 绘制子弹到屏幕 for bullet in bullets.sprites(): bullet.draw_bullet() # 渲染记分牌信息 scoreboard.show_score() # if settings.player_limit == 0: settings.game_active = False settings.reset_stats() # 清空矩形列表和子弹列表 enemies.empty() bullets.empty() screen_rect = screen.get_rect() player.rect.centerx = screen_rect.centerx player.rect.bottom = screen_rect.bottom # 如果游戏处于非活动状态,就绘制 Play 按钮 if not settings.game_active: play_button.draw_button() # 让最近绘制的屏幕可见 pygame.display.flip()
9.新建文件shootingenemy.py,主函数用来初始化程序,并同步更新程序的信息;
import pygame from pygame.sprite import Group from settings import Settings from button import Button from player import Player import game_functions as gf from scoreboard import Scoreboard def run_game(): pygame.init() # 初始化全部音频,并加载爆炸声音乐 pygame.mixer.init() # 等待1s pygame.time.delay(1000) pygame.mixer.music.load('file/bgsound.mp3') # -1代表无限循环(背景音乐) pygame.mixer.music.play(-1) # 爆炸声 explosiveSound = pygame.mixer.Sound('file/explosiveSound.wav') # 枪声 fireSound = pygame.mixer.Sound('file/fireSound.wav') # 游戏循环帧率设置 clock = pygame.time.Clock() settings = Settings() screen = pygame.display.set_mode((settings.screen_width, settings.screen_height)) # 全屏显示 # screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) pygame.display.set_caption('飞机大战') # 左上角图标 ic_launcher = pygame.image.load('images/ic_launcher.png').convert_alpha() pygame.display.set_icon(ic_launcher) # 背景图 background = pygame.image.load('images/background.png').convert_alpha() # 敌机图片 enemy_img1= pygame.image.load('images/enemy1.png') enemy_img2= pygame.image.load('images/enemy2.png') enemy_img3= pygame.image.load('images/enemy3.png') enemy_img4= pygame.image.load('images/enemy4.png') # 敌机不同状态的图片列表,多张图片展示为动画效果 enemy_down_imgs = [] enemy_down_imgs.append(enemy_img1) enemy_down_imgs.append(enemy_img2) enemy_down_imgs.append(enemy_img3) enemy_down_imgs.append(enemy_img4) # 储存敌机 enemies = Group() # 存储被击毁的飞机,用来渲染击毁动画 enemies_down = Group() # 创建Play按钮 play_button = Button(screen, 'Play') # 创建飞船 player = Player(settings, screen) # 创建子弹的编组 bullets = Group() # 创建记分牌 scoreboard = Scoreboard(settings, screen) while True: # 绘制背景 screen.blit(background, (0, 0)) # 控制游戏最大频率 clock.tick(60) # 检查玩家输入(不加会导致一直加载) gf.check_events(settings, screen, player, play_button, scoreboard, bullets, fireSound) if settings.game_active: # 更新飞船位置 player.update() # 更新敌机 gf.update_enemies(settings, screen, scoreboard, enemies, enemies_down, enemy_down_imgs, player, bullets, explosiveSound) # 更新子弹位置 gf.update_bullets(screen, bullets) # 更新屏幕信息 gf.update_screen(settings, screen, player, play_button, scoreboard, enemies, bullets) run_game(),
10.在文件shootingenemy.py目录路径下,执行命令“python shootingenemy.py”弹出窗口,即可对其操作游玩。
结语
该游戏加入了背景音乐、射击声、子弹射中敌机的爆炸声和爆炸效果、生命数、积分、等级、最高分和开始按钮等元素,大家也可以自行加入其它好玩的元素。
更多关于python游戏的精彩文章请点击查看以下专题:
python俄罗斯方块游戏集合
python经典小游戏汇总
python微信跳一跳游戏集合
更多有趣的经典小游戏实现专题,分享给大家:
C++经典小游戏汇总
JavaScript经典游戏 玩不停
java经典小游戏汇总
javascript经典小游戏汇总
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]