🎥前言
本篇文章将介绍python游戏【外星人入侵】代码的环境安装
,具体介绍如何将游戏的最高分写入文件并在下次启动时读取、生成 .exe可执行文件
、如何趣味性的改变游戏
。游戏相关的所有源码已经在文章游戏实现———————游戏源码
部分。
🎮游戏介绍:
玩家控制着一艘出现在屏幕底部的飞船,使用左右键来左右移动飞船,使用空格键来发射子弹,玩家的任务就是击落每个从屏幕上方移动下来的外星人,当外星人碰到飞船或者屏幕底部时该飞船被摧毁,每个玩家可拥有艘飞船,当三艘飞船都被摧毁时结束游戏,并得出最高分。
由于不能上传视频,给大家看一下游戏图片效果
有三个备用飞船,中间的数字是最高得分,每次打开游戏会加载,右边最上面的数字是当前得分,下面的数字是当前关卡等级。
💼安装Pygame
首先,使用pip来安装Pygame(目前下载的编译器大部分都是自带pip的,如是更老的版本,大家可以去搜索一下相关的安装方法,也不难),由于Python编译器在安装的过程中,大多数人其实那时候还不会python,然后在安装Pygame的时候发现编译器的路径不一样,或者也找不到了,终端命令也没用,很难搞。所以我们直接使用 Pycharm 来安装Pygame,这时候,Pycharm的优势就体现出来了。这种的安装还是看视频教程比较靠谱,给大家推荐一个短且非常有用的安装视频。
Pygame安装我是用的是里面的方法2.1,亲测有效。
🔋游戏的实现
由于网上有很多【外星人入侵】小游戏的教程,我就不过多赘述原理了,讲的也不一定比别人好,代码直接在本模块后给大家,主要将一下如何将游戏得到的最高分写入文件,并在下次运行游戏时读取这个最高分。
读写并存储【外星人入侵】游戏最高分
此项目含有多个python文件,如果要将最高分写入文件,需要在此项目下文件夹中再多创建一个空白的.pkl文件
文件,这个文件负责读写玩游戏时取得的最高分,要实现读取,还需要在读写最高分文件的python程序中导入pickel
模块。每次检测到即将退出游戏,就保存当前最高分。
圈起来的部分就是主要进行读写最高分操作的python文件中需要添加的代码,同时,别忘了在文件夹中自己创建一个.pkl 的空白文件,否则就会报错FileNotFoundError: [Errno 2] No such file or directory: 'high_score.pkl' zhey
这一点大家一定要注意。
下图是项目文件中添加的.pkl文件,这个文件的名称必须与代码中读写文件的那个文件名相同。
在识别点击错号就退出
这部分代码中,也要调用一个保存最高分的函数 save_high_score()
如此,就完美实现了将最高分存入文档,并在下次运行游戏时读取最高分。
参考博客:Python——Pygame实现Alien Invasion之用pickle类保存最高分至本地 这个博客讲的还是蛮清晰的,但由于没有讲到必须要创建一个.pkl文件,很多人都会在这里出错。
游戏源码
alien_invasion.py
import pygame from settings import Settings # 导入外观设置类 from ship import Ship from alien import Alien import game_functions as gf # as gf是指定别名,为了方便 from pygame.sprite import Group from game_stats import GameStates from button import Button from scoreboard import Scoreboard def run_game(): pygame.init() # 创建 设置类 的实例,用来访问并修改 Settings类 里的内容 ai_settings = Settings() # 创建一个名为 screen 的显示窗口,后面的参数是设置类里的屏幕属性 screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height)) ship = Ship(ai_settings, screen) pygame.display.set_caption("外星人入侵") # 创建Play按钮 play_button = Button(ai_settings, screen, "Play") stats = GameStates(ai_settings) sb = Scoreboard(ai_settings, screen, stats) bullets = Group() aliens = Group() gf.create_fleet(ai_settings, screen, ship, aliens) # 开始游戏主循环 while True: gf.check_events(ai_settings,screen,stats,play_button,ship,aliens,bullets,sb) if stats.game_active: #重新绘制(更新屏幕上的子弹和飞船) ship.update() gf.update_bullets(ai_settings, screen, stats,sb,ship, aliens, bullets) gf.update_aliens(ai_settings, ship, aliens, stats, screen, bullets,sb) gf.update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button) run_game()
game_functions.py
import sys import pygame from bullet import Bullet from alien import Alien from time import sleep def check_keydown_events(event, ai_settings, screen, ship, bullets): if event.key == pygame.K_RIGHT: ship.moving_right = True elif event.key == pygame.K_LEFT: ship.moving_left = True elif event.key == pygame.K_SPACE: fire_bullet(ai_settings, screen, ship, bullets) def check_keyup_events(event, ship): if event.key == pygame.K_RIGHT: ship.moving_right = False elif event.key == pygame.K_LEFT: ship.moving_left = False elif event.key == pygame.K_q: sys.exit() def check_events(ai_settings,screen,stats,play_button,ship,aliens,bullets,sb): for event in pygame.event.get(): # 用户点击错号,就识别出来,退出游戏(退出程序) if event.type == pygame.QUIT: stats.save_high_score() sys.exit() # 按下方向键,触发KEYDOWN事件 elif event.type == pygame.KEYDOWN: check_keydown_events(event, ai_settings, screen, ship, bullets) # 松开方向键,触发KEYUP事件 elif event.type == pygame.KEYUP: check_keyup_events(event, ship) elif event.type==pygame.MOUSEBUTTONDOWN: mouse_x,mouse_y=pygame.mouse.get_pos() check_play_button(ai_settings,screen,stats,play_button,ship,aliens,bullets,mouse_x,mouse_y,sb) def check_play_button(ai_settings,screen,stats,play_button,ship,aliens,bullets,mouse_x,mouse_y,sb ): button_clicked=play_button.rect.collidepoint(mouse_x,mouse_y) if button_clicked and not stats.game_active: #重置游戏设置 ai_settings.initialize_dynamic_settings() #隐藏光标 pygame.mouse.set_visible(False) # 重置游戏统计信息 stats.reset_stats() stats.game_active=True # 清空外星人列表和子弹列表 sb.prep_score() sb.prep_high_score() sb.prep_level() sb.prep_ships() aliens.empty() bullets.empty() #创建一群新的外星人,并让飞船居中 create_fleet(ai_settings,screen,ship,aliens) ship.center_ship() def update_screen(ai_settings,screen,stats,sb,ship,aliens,bullets,play_button): # 每次循环完都要刷新屏幕,并用背景色充满屏幕 screen.fill(ai_settings.bg_color) # 在飞船和外星人后面重绘子弹 for bullet in bullets.sprites(): bullet.draw_bullet() # 使用 Ship类中的blitme方法 在底部绘制飞船 ship.blitme() aliens.draw(screen) sb.show_score() #如果游戏处于非活动状态 if not stats.game_active: play_button.draw_button() # 让最近绘制的屏幕可见 pygame.display.flip() def update_bullets(ai_settings, screen, stats,sb,ship, aliens, bullets): bullets.update() for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet) check_bullet_alien_collisions(ai_settings, screen,stats,sb, ship, aliens, bullets) def check_bullet_alien_collisions(ai_settings, screen,stats,sb,ship, aliens, bullets): collisions = pygame.sprite.groupcollide(bullets, aliens, True, True) if collisions: stats.score+=ai_settings.alien_points sb.prep_score() for aliens in collisions.values(): stats.score+=ai_settings.alien_points*len(aliens) sb.prep_score() check_high_score(stats, sb) # 检查外星人是否被消灭完 if (len(aliens) == 0): bullets.empty() ai_settings.increase_speed() create_fleet(ai_settings, screen, ship, aliens) #提高等级 stats.level+=1 sb.prep_level() create_fleet(ai_settings, screen, ship, aliens) #检查是否是最高分 def check_high_score(stats, sb): if stats.score > stats.high_score: stats.high_score = stats.score sb.prep_high_score() def fire_bullet(ai_settings, screen, ship, bullets): # 如果玩家按下空格,就创建一个子弹实例,并将其加入到编组bullets中 if len(bullets) < ai_settings.bullets_allowed: new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet) def get_number_aliens_x(ai_settings, alien_width): available_space_x = ai_settings.screen_width - 2 * alien_width number_aliens_x = int(available_space_x / (2 * alien_width)) return number_aliens_x def create_alien(ai_setting, screen, aliens, alien_number, row_number): alien = Alien(ai_setting, screen) alien_width = alien.rect.width alien.x = alien_width + 2 * alien_width * alien_number alien.rect.x = alien.x alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number aliens.add(alien) def create_fleet(ai_settings, screen, ship, aliens): alien = Alien(ai_settings, screen) number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width) number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height) for row_number in range(number_rows): for alien_number in range(number_aliens_x): create_alien(ai_settings, screen, aliens, alien_number, row_number) # 计算外星人有多少行 def get_number_rows(ai_settings, ship_height, alien_height): available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height) number_rows = int(available_space_y / (2 * alien_height)) return number_rows def check_fleet_edges(ai_seetings, aliens): for alien in aliens.sprites(): if alien.check_edges(): change_fleet_direction(ai_seetings, aliens) break # 将整体外星人下移,并改变他们的方向 def change_fleet_direction(ai_seetings, aliens): for alien in aliens.sprites(): alien.rect.y += ai_seetings.fleet_drop_speed ai_seetings.fleet_direction *= -1 # 检查是否有外星人位于屏幕边缘,并更新整群外星人的位置 def update_aliens(ai_settings, ship, aliens, stats, screen, bullets,sb): check_fleet_edges(ai_settings, aliens) aliens.update() if pygame.sprite.spritecollideany(ship, aliens): ship_hit(ai_settings, stats, screen, ship, aliens, bullets,sb) check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets,sb) def ship_hit(ai_settings, stats, screen, ship, aliens, bullets,sb): if stats.ships_left > 0: # 相应被外星人撞到的飞,将ship_left减1 stats.ships_left -= 1 sb.prep_ships() # 清空子弹和外星人 aliens.empty() bullets.empty() # 创建一群新的外星人,并将飞船放到屏幕底端中央 create_fleet(ai_settings, screen, ship, aliens) ship.center_ship() # 暂停 sleep(0.5) else: stats.game_active = False pygame.mouse.set_visible(True) def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets,sb): # 检查是否有外星人到达了屏幕底端 screen_rect = screen.get_rect() for alien in aliens.sprites(): if alien.rect.bottom >= screen_rect.bottom: # 与飞船被撞到是一样的方法 ship_hit(ai_settings, stats, screen, ship, aliens, bullets,sb) break
ship.py
import pygame from pygame.sprite import Sprite class Ship(Sprite): def __init__(self,ai_settings,screen): super(Ship,self).__init__() self.screen=screen self.ai_settings=ai_settings self.image=pygame.image.load('ship.bmp') #加载同路径下的图片 self.rect=self.image.get_rect() self.screen_rect=screen.get_rect() #获取飞船图像的外接矩形 # 让新飞船放在屏幕底部中央 self.rect.centerx=self.screen_rect.centerx self.rect.bottom=self.screen_rect.bottom self.center=float(self.rect.centerx) self.moving_right=False self.moving_left = False def update(self): # 左右移动飞船 if self.moving_right and self.rect.right<self.screen_rect.right: self.center+=self.ai_settings.ship_speed_factor if self.moving_left and self.rect.left>0: self.center-=self.ai_settings.ship_speed_factor self.rect.centerx=self.center def blitme(self): self.screen.blit(self.image, self.rect) def center_ship(self): self.center=self.screen_rect.centerx
alien.py
import pygame from pygame.sprite import Sprite class Alien(Sprite): def __init__(self,ai_settings,screen): super(Alien,self).__init__() self.ai_settings = ai_settings self.screen=screen self.image = pygame.image.load('alien.bmp') self.rect=self.image.get_rect() self.rect.x=self.rect.width self.rect.y=self.rect.height self.x=float(self.rect.x) def blitme(self): self.screen.blit(self.image,self.rect) def update(self): #向左或者向右移动外星人(撞墙后就反向移动) self.x+=(self.ai_settings.alien_speed_factor* self.ai_settings.fleet_direction) self.rect.x=self.x #如果外星人撞到了屏幕边缘,就返回True def check_edges(self): screen_rect=self.screen.get_rect() if self.rect.right>=screen_rect.right: return True elif self.rect.left<=0: return True
bullet.py
import pygame from pygame.sprite import Sprite class Bullet(Sprite): def __init__(self,ai_settings,screen,ship): super().__init__() self.screen=screen # 在(0,0)处创建一个表示矩形的子弹 self.rect=pygame.Rect(0,0,ai_settings.bullet_width, ai_settings.bullet_height) self.rect.centerx=ship.rect.centerx self.rect.top=ship.rect.top self.y=float(self.rect.y) self.color=ai_settings.bullet_color self.speed_factor=ai_settings.bullet_speed_factor def update(self): #更新表示子弹的小数值 self.y-=self.speed_factor #更新表示子弹的rect位置 self.rect.y=self.y def draw_bullet(self): #在屏幕上绘制子弹 pygame.draw.rect(self.screen,self.color,self.rect)
button.py
import pygame.font class Button(): def __init__(self,ai_settings,screen,msg): self.screen=screen self.screen_rect=screen.get_rect() #设置按钮的尺寸和其他属性 self.width,self.height=200,50 self.button_color=(0,255,0) self.text_color=(255,255,255) self.font=pygame.font.SysFont(None,48) #创建按钮的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): 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)
scoreboard.py
import pygame.ftfont from ship import Ship from pygame.sprite import Group class Scoreboard(): def __init__(self,ai_settings,screen,stats): self.screen=screen self.screen_rect=screen.get_rect() self.ai_settings=ai_settings self.stats=stats # 显示得分时的字体 self.text_color=(30,30,30) self.font=pygame.font.SysFont("arial",48) self.prep_score() self.prep_high_score() self.prep_level() self.prep_ships() def prep_score(self): rounded_score=int(round(self.stats.score,-1)) score_str="{:,}".format(rounded_score) self.score_image=self.font.render(score_str,True,self.text_color, self.ai_settings.bg_color) self.score_rect=self.score_image.get_rect() self.score_rect.right=self.screen_rect.right-20 self.score_rect.top=20 def prep_high_score(self): high_score=int(round(self.stats.high_score,-1)) high_score_str = "{:,}".format((high_score)) self.high_score_image=self.font.render(high_score_str,True, self.text_color,self.ai_settings.bg_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=20 def prep_level(self): self.level_image=self.font.render(str(self.stats.level),True, self.text_color,self.ai_settings.bg_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_ships(self): self.ships=Group() for ship_number in range(self.stats.ships_left): ship=Ship(self.ai_settings,self.screen) ship.rect.x=10+ship_number*ship.rect.width ship.rect.y=10 self.ships.add(ship) 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.ships.draw(self.screen)
game_stats.py
import pickle class GameStates(): def __init__(self,ai_settings): self.ai_settings=ai_settings self.reset_stats() self.game_active=False self.high_score=0 def reset_stats(self): #初始化在游戏期间可能变化的统计信息 self.ships_left=self.ai_settings.ship_limit self.score = 0 self.level = 1 self.load_high_score() def save_high_score(self): f=open("high_score.pkl",'wb') pickle.dump(str(self.high_score),f,0) f.close() def load_high_score(self): f=open("high_score.pkl",'rb') try: str_high_score=pickle.load(f) self.high_score=int(str_high_score) except EOFError: self.high_score=0 finally: f.close()
settings.py
class Settings(): def __init__(self): self.screen_width=1350 self.screen_height=780 self.bg_color=(230,230,230) #飞船设置 self.ship_limit=3 #子弹设置 self.bullet_width=300 self.bullet_height=15 #深灰色子弹 self.bullet_color=60,60,60 self.bullets_allowed=4 # 外星人设置 self.fleet_drop_speed=25 self.speedup_scale=1.1 #外星人点数的提高速度 self.score_scale=1.5 self.initialize_dynamic_settings() def initialize_dynamic_settings(self): self.ship_speed_factor=1 self.bullet_speed_factor=3 self.alien_speed_factor = 0.7 # fleet_direction 为1表示向右移动,为-1表示向左移动 self.fleet_direction = 1 #计分 self.alien_points=30 def increase_speed(self): self.ship_speed_factor*=self.speedup_scale self.bullet_speed_factor*=self.speedup_scale self.alien_speed_factor*=self.speedup_scale self.alien_points=int(self.alien_points*self.score_scale)
宇宙飞船和外星人的 .bmp类型文件
📺将游戏程序整理为 .exe文件
第一步:在pycharm中安装Pyinstaller库
有中文插件的话就打开左上方文件——设置——项目——python解释器——点击加号,搜索Pyinstaller,安装即可。
在pycharm底部会显示安装正在进行,安装结束后这个进程就消失,意味着安装成功。
第二步:生成 .exe可执行程序
在pycharm终端中输入 pyinstaller -F alien_invasion.py
,如果你的游戏主程序不是 alien_invasion.py ,也可使用
pyinstaller -F 主程序.py的形式
在终端输入后点击回车
这个命令运行结束后,在此项目底下会生成一个 dist 文件夹,里面包含一个可执行程序.exe文件,这个.exe文件目前还是不能运行的,还要将程序所需文件添加进 dist 中,如外星人和宇宙飞船的bmp图,如果你创建了读取最高分的文件,还要将这个文件添加进这个文件中。
如此,这个exe程序就能正常运行了。
错误处理
先声明一下,并不是所有的设备都会出错,如果上述操作都无误,还出现了 .exe文件闪退的情况,可能是字体有问题,将代码中的字体由 None 改为一个系统自带的字体如"arial"
然后将上面生成的文件都删除,再次在终端输入 pyinstaller -F alien_invasion.py
,生成 dist 文件后,再将相关文件复制进 dist 文件即可。
如果要将可执行程序发送给好友,需要将本模块提到的文件全部放入一个文件夹中再压缩为.zip文件,好友点击文件中的.exe文件即可运行游戏,好友不需要下载任何的编译器。
🪔将【外星人入侵】改变为【梅西vsC罗】及其他趣味改编方法
相信了解过这个游戏的同学都知道如何修改以及修改后的效果了吧。
在网上找一张画质比较高的两个人物图片,先抠图并缩放图片(尺寸一定要和外星人、宇宙飞船的图片尺寸差不多),再利用电脑自带的画图功能将这两张图片转化为 .bmp格式,将这两个图片加入到项目中。
我这里给大家两张图片,大家可以直接保存。如果大家想创作别的人物的话,可以按照我上面的步骤来。
这就可以设置谁是‘外星人’,谁是‘宇宙飞船’了,我就将梅西设置为外星人,C罗设置为宇宙飞船了啊(仅供娱乐)
假如我将一张命名为CR7.bmp
,一张命名为MESSI.bmp
然后将ship.py
文件代码中的ship.bmp换成 CR7.bmp
将alien
文件代码中的alien.bmp换成MESSI.bmp
当然,由于抠图的底色是白的,而我们代码中设置的RGB值(230,230,230)是浅灰色底色,看起来就像图片放在屏幕上一样,很难看。所以还要将这个参数改为(255,255,255),RGB值(255,255,255)是纯白色,可以与抠图的底色完全融合。
最后,将游戏名改为 梅西vsC罗(可以不改)
这样,【梅西vsC罗】就做好了。