怎么运行python外星人入侵_Python入门项目:外星人入侵

前言

作为一个编程小白选手,经过一段时间的基础语法学习,终于迎来了首个项目学习,从一步步的模仿到理解到每个步骤的思路想法,每个类之间的联系与构造,还有些编程中的一些小细节,如何养成一个编程的好习惯。

分析

外星人入侵作为一个入门小游戏项目,整体来说思路是比较简单,代码行也不多,总计就是500+行,除去一些缩进空行,就没有多少了。当我们去做一个项目的时候,无论是难的还是简单的,都是需要进行一个分析,这样我们实际去写的时候,思路就会清晰很多,把一个项目的功能拆分出来,再各个实现,最后串联起来。

言归正传,这个项目主要分为9个模块,9个模块及每个模块的作用如下:

alien.py →class_Alien 主要是用来放绘制外星人的图像及位置信息

bullet.py→class_Bullet 主要是用来放置绘制飞船发射子弹图像及位置信息

button.py→class_Button 主要是用来放置绘制游戏开始按钮的图像及位置信息

game_stats.py→ class Game_stats 主要是游戏中的数据参数,如得分,游戏等级等等

scoreboard.py→ class Scoreboard 主要是用来放置游戏中各个计分板的图像绘制和位置信息

settings.py→class Settings 主要是用来放置游戏中初始化参数,像屏幕大小颜色等等

ship.py→class Ship 主要用来放置绘制飞船的图像及位置信息

alien_invasion.py 主要是用来放置运行游戏的主方法

game_functions.py 主要是用来放置游戏功能的实现,如屏幕的图像更新,飞船的移动,飞船如何发射子弹等等,基本功能代码都会集中在这个包里面

image.png

步骤如下:

创建游戏窗口→添加飞船图像→控制飞船移动→添加子弹图像及移动→

添加一个外星人图像→添加一群外星人图像及移动→检测外星人图像与子弹碰撞,与飞船碰撞和碰到屏幕底端的情况及反应→添加游戏开始按钮图像及反应→添加计分板图像:包括玩家得分,历史最高得分,难度级别显示,玩家剩余飞船数量

实现

创建游戏窗口

首先来创建游戏窗口,创建一个alien_invasion.py的包,导入pygame库(这个项目是基于pygame的库下完成的),创建一个def run_game()的主方法,pygame.init()初始化背景参数,创建一个screen的对象pygame.display.set_mode()函数传入我们设置好的宽高参数就可以创建窗口,用(1200(宽),650(高))一个元组的实参传入(具体参数可以根据自身屏幕大小设置),再用函数pygame.display.set_caption()传入str可以命名窗口,将显示在窗口的左上角。我们创建的screen对象是一个surface(面板),还有接下来我们创建的飞船,子弹,都是一个surface(面板)set_mode函数返回的就是一个surface。

接下来就是游戏需要的主循环,用来不断更新的屏幕的图像和玩家行为,直至玩家退出游戏,我们用while True来循环,并在里面设置一个检测pygame.QUIT的事件跳出循环,避免造成死循环。用函数pygame.event.get()可以得到玩家鼠标和按键响应事件,再用一个for in循环得到每个事件,并进行判断是否有pygame.QUIT事件,如果有就用sys.exit()函数来退出游戏跳出循环(这个时候我们就需要导入sys,在代码最上面import sys)最后再用pygame.display.flip()绘制的图像显示出来,这个是放于run_game()最后的地方。代码如下:

image.png

此时运行run_game(),我们将得到一个黑色的窗口:

image.png

接下来我们给窗口上色,用的是RGB值,用红色,绿色,蓝色组成,每个值取值范围为0到255,创建一个screen_color赋值(230,230,230),我们需要不断更新这个颜色,所以放在while循环里,用fill()函数,传入我们的screen_color实参,在我们创建的窗口屏幕screen上,所以代码如下:

def run_game():

pygame.init()

screen = pygame.display.set_mode((1200,650))

pygame.display.set_caption("Alien Invasion")

screen_color = (230,230,230)

while True:

for event in pygame.event.get():

if event.type == pygame.QUIT:

sys.exit()

screen.fill(screen_color)

pygame.display.flip()

运行得到画面:

image.png

建议可以熟悉下pycharm的快捷键,这样会更快的提高,例如当你不熟悉一个函数的使用时候,可以选中之后按Ctrl + B或者Ctrl + 鼠标点击函数可以直接跳转到函数的声明,会帮助你更加理解函数的使用,变量也是可以这样操作,当你看完之后可以按Alt + 左键跳回就非常放便

快捷键使用

setting模块(存放游戏设置参数)

接下来可以创建一个setting.py的模块,里面创建一个Settting类,用于存放游戏参数,这样以来就方便引用,而且当你需要修改的时候不需要全部都去修改,而是只在Setting的类中修改即可,然后回到alien_invasion.py中导入Setting创建一个Setting的对象ai_settings,之后只需用对象.变量名即可引用,整理结果如下:

class Setting():

def __init__(self):

#初始化游戏的设置

#屏幕参数设置

self.screen_width = 1200

self.screen_height = 650

self.screen_color = (230,230,230)

import pygame

import sys

from setting import Setting

def run_game():

pygame.init()

ai_settings = Setting()

screen = pygame.display.set_mode(

(ai_settings.screen_width,ai_settings.button_height))

pygame.display.set_caption("Alien Invasion")

while True:

for event in pygame.event.get():

if event.type == pygame.QUIT:

sys.exit()

screen.fill(ai_settings.screen_color)

pygame.display.flip()

run_game()

创建飞船

接下来我们就要创建飞船了,使用位图.bmp文件,需要在项目文件夹中创建一个Images的文件夹,将图片放入文件夹中,再创建一个ship.py的模块,里面有一个class Ship类并进行初始化,def init(self,screen)这里需要传入我们创建的窗口screen,因为我们的飞船是出现在这个窗口屏幕上的,然后需要读取我们飞船的图片,创建一个self.image接收,用pygame.image.loda(Images’/ship.bmp)前面为文件夹名,后面会文件名,再用get_rect()函数来得到这个图片的矩形rect对象,这个rect对象会有一个坐标位置信息由四个参数组成(left,top,width,height),width和height又表示rect矩形的宽和高

image.png

图像都是从原点(0,0)创建的

top和left可以理解成图像距离原点的坐标

bottom是rect矩形下边缘距离x轴的距离,也可理解成y坐标

width就是rect图像的宽度,height是rect图像的高度

*center是图像的中心,然后分出centerx(中心在x轴上的坐标)

centery(中心在y轴上的坐标)

right 可以理解成rect矩形图像右边缘x轴坐标

所以我们导入ship.bmp,创建ship_rect接受用函数get_rect()得到飞船rect矩形图像的坐标位置信息。要将飞船放于screen屏幕的底部中间,所以要将飞船的bottom等于屏幕的bottom,在将飞船的centerx等于screen屏幕的centerx,之后在创建一个绘制飞船图像的方法def blitme(),方法里面用blit()在屏幕上screen绘制飞船,传入飞船的图像self.image和飞船图像的self.rect,代码如下:

class Ship():

def __init__(self,screen):

self.screen = screen

#飞船参数

self.image = pygame.image.load('images/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

def blitme(self):

#绘制飞船

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

然后在alien_invasion中导入Ship的类,创建一个Ship类对象,需要传入screen参数,最后在循环中使用ship.blitme()方法绘制飞船图像

ship == Ship(screen)

whlie True:

...

ship.blitme()

控制飞船

接下来就是控制飞船移动的,还是在遍历event.get()里用if语句判断,如果检测到事件是按下键盘事件,然后在用if语句判断,按下键盘事件的按钮是不是对应的方向键,根据上面的坐标图,向左键则是ship.rect.centerx -=1,向右则是ship.rect.centerx += 1;当玩家持续按下左右键的时候,就需要判断按下按键事件和松开按键事件,先在Ship类里面的init方法里添加两个变量self.moving_right 和self.moving_left并赋值False,在创建一个方法def update()用if语句判断如果为True的反应

def update():

if moving_right:

self.rect.centerx += 1

if moving_left:

self.rect.centerx -= 1

再回到for循环判断,按下和松开的响应:

while True:

for event in pygame.event.get():

if event.type == pygame.QUIT:

sys.exit()

elif event.type == pygame.KEYDOWN:

if event.key == pygame.K_RIGHT:

ship.moving_right = True

elif event.key ==pygame.K_LEFT:

ship.moving_left = True

elif event.type == pygame.KEYUP:

if event.key == pygame.K_RIGHT:

ship.moving_right = False

elif event.key == pygame.K_LEFT:

ship.moving_left = False

接下来我们需要限制飞船的运行范围,从上面的坐标图来看,我们可以判断飞船图像的right如果小于screen窗口的right,那就是还可以继续向右移动,如果飞船的left是大于0的话,那就是还没有到屏幕坐边缘,还可以继续向左移动,所以我们可以在ship.update里面加多一个判断条件,用and连接,只有同时满足这两个条件,飞船才能继续移动,因为要用到飞船的rect和screen的rect,所以需要在update方法里面传入ship和screen作为参数,代码如下:

def update(self):

if moving_right and self.rect.right < self.screen_rect.right:

self.rect.centerx += 1

if moving_left and self.rect.left > 0:

self.rect.centerx -= 1

如果觉得飞船移动的太慢的话,可以设置一个飞船速度的变量,建议赋值浮点值,这样可以更细致的控制飞船速度,所以我们把+1-1换成+-我们嘎刚刚设置的飞船速度变量,但是rect是整数,整数不能和浮点值加减,所以先把rect.centerx转换为浮点值float赋给变量center,最后加减完之后再赋值给center赋值给rect.centerx

def __init__(self):

...

self.ship_centerx = float(self.rect.centerx)

def ship_update(self):

#更新飞船位置

if self.ship_moving_left and self.rect.left > 0:

self.ship_centerx -= self.ai_settings.ship_speed_factor

if self.ship_moving_right and self.rect.right < self.screen_rect.right:

self.ship_centerx += self.ai_settings.ship_speed_factor

self.rect.centerx = self.ship_centerx

注意这里飞船速度的变量是在Setting里创建的,所以我们在Ship里面要导入Setting类,并在init里面传入ai_settings对象,ship对象里面我们也要传入相应的实参ai_settings

class Ship():

def __init__(self,ai_settings,screen):

...

self.ai_settings = ai_settings

ship = Ship(ai_settings,screen)

game_functions的作用(整理代码,让代码更容易管理和解读)

while True里的循环现在代码比较多,也为了更好理解代码,现在创建一个game_functions的模块,用于存放功能代码,在里面创建def check_events(按键和鼠标响应事件)和screen_update(窗口更新,把需要在窗口上更新的图像归在里面),check_events里面还可以在分出响应按下和松开的两个方法。最后在alien_invasion.py中导入game_function(用import game_function as gs 导入,并简化为gs,通过gs来调用就可以了)如下:

import sys

import pygame

def check_event(ai_settings,screen,ship):

#响应按键和鼠标事件

for event in pygame.event.get():

if event.type == pygame.QUIT:

sys.exit()

elif event.type == pygame.KEYDOWN:

check_keydown(event,ai_settings,screen,ship)

elif event.type == pygame.KEYUP:

check_keyup(event,ship)

def check_keydown(event,ai_settings,screen,ship):

#响应按下

if event.key == pygame.K_RIGHT:

ship.ship_moving_right = True

elif event.key == pygame.K_LEFT:

ship.ship_moving_left = True

def check_keyup(event,ship):

#响应松开

if event.key == pygame.K_RIGHT:

ship.ship_moving_right = False

if event.key == pygame.K_LEFT:

ship.ship_moving_left = False

def screen_update(ai_settings,ship,screen):

#更新屏幕图像

screen.fill(ai_settings.screen_color)

ship.blitme()

pygame.display.flip()

import game_functions as gs

...

while True:

#监听鼠标和按键事件

gs.check_event(ai_settings,screen,ship)

ship.ship_update()

gs.screen_update(ai_settings,ship,screen)

子弹的创建

接下来创建飞船需要的子弹,在bullet模块里创建,过程跟上面的飞船差不多,但是需要通过继承Sprite类使用精灵将游戏相关的元素编组并操作编组中的所有元素,同时子弹是没有bmp位图的,所有需要用pygame.Rect函数来绘制,需要传输x,y轴上的坐标和子弹的宽高,同时在Settging里面创建子弹需要的宽高和颜色,子弹的速度,我们在原点创建,之后在把子弹设置子弹飞船的中心,这样看起来就是子弹从飞船中射出,所以子弹的矩形图像就是:

from pygame.sprite import Sprite

class Bullet(Sprite):

def __init__(self,ai_settings,screen,ship):

'''

python2.7中需要super(self,Bullet).init()这种写法,python3也可以这样写,

但是也可省略,用下面这种写法

'''

super().init()

self.rect = pygame.Rect(0,0,ai_setting.bullet_width,ai_setting.bullet_height)

子弹的更新方法,因为是从飞船射出,所以是在屏幕从下往上的,所以self.rect.y -= 子弹速度,可以借鉴飞船移动速度用float值。

子弹的绘制用pygame.draw.rect(Surface,color,rect)或者Surface.fill(color,rect)都可以

在alien_invasion中创建一个子弹编组bullets = Group()用于存放每一个创建的子弹。在while循环里面通过编组bullets调用update方法可以对编组中每个元素调用update方法。

子弹的射击

子弹的射击同样通过按键响应事件判断,如果玩家按下的是空格键,就创建一个子弹对象,并将子弹加入子弹编组里面,在通过遍历子弹编组在屏幕上绘制出每个子弹,bullets.sprites()可以返回一个列表。

def screen_update(ai_settings,screen,ship,bullets):

...

for bullet in bullets.sprites():

bullet.draw_bullet()

子弹管理

会发现发射的子弹虽然到达了屏幕顶端之后消失,其实并没有消失,子弹位置还在不断往Y轴负数区域更新,这样会到导致子弹越来越多,可以用过bullets.copy()复制一个bullets,遍历判断子弹的位置,如果子弹的bottom位置超过屏幕也就是0,那就使用Group.remove()函数,将超过屏幕的子弹删除,同时我们限制子弹的数量,通过len(bullets)得到子弹编组的数量,用if语句判断如果小于我们设定的子弹数量就创建子弹

外星人创建

外星人的创建就和飞船的创建比较相似,因为都是有bmp位图的,就不需要我们自己创建,只需要将外星人的图片读取进来就可以了,唯一不同的就是外星人是一群的,所以我们要借鉴创建子弹的方法,继承Spirte类,创建一个外星人列表,同时要计算窗口可以容纳多少外星人,在使用for循环嵌套创建外星人群,最后在screen_update中用编组调用draw(surface)方法绘制

每个外星人最初都是从(0,0)的地方创建,所以我们将外星人的宽度设置成外星人的左边距,将外星人的高度设置为外星人的上边距

计算屏幕可以容纳多少个外星人,每行容纳的外星人空间可以通过窗口的宽度 - 两个外星人宽度(预设屏幕两端的空余空间),将其设置为available_space_x

同时两个外星人之间需要有一定空间,所以设定一个外星人实际需要的空间是外星人宽度的两倍,所以得出每行可以容纳的外星人数量为:

number_aliens_x = int(available_space_x/(2*alien_width))

我们在game_functions.py创建一个alien_fleet()方法,用于创建外星人群:

def alien_fleet():

alien = Alien(ai_settings,screen)

alien_width = alien.rect.width

available_space_x = ai_settings.screen_width - 2*alien_width

number_aliens_x = int(available_space_x/(2*alien_width))

for alien_number in range(number_aliens_x):

alien = Alien(ai_settings,screen)

alien.x = alien.width + 2 *alien_width *alien_number

alien.rect.x = alien.x

aliens.add(alien)

接下来计算屏幕可以放下几行外星人,存放外星人的空间 = 窗口高度 - 第一行外星人的上边距 - 外星人之间的间隔 - 留给玩家反应的距离 - 飞船的距离,将前面三种参数都设置成外星人高度,即为3*外星人高度 - 飞船高度,屏幕存放外星人的空间为: available_space_y = ai_settings.screen_height - 3 * alien_height - ship._height

可以存放的行数为:number_rows = int(available_space_y / (2 * alien_height)),结合起来代码:

def alien_fleet():

...

for row_number in range(number_rows):

for alien_number in range((number_aliens_x):

...

alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number

aliens.add(alien)

最后整理下代码,将其整理四个方法,def get_number_aliens_x();

def get_number_rows();def create_alien();def create_fleet(),分别完成获取外星人每行存放多少个外星人,获取可以存放几行外星人,for循环里面创造外星人及创建外星人群

外星人群的移动

设置外星人群的平行移动速度alien_speed_factor = 1,下降速度alien_drop_speed = 10,alien_direction = 1为左右移动(1为向右移动,-1为向左移动),通过外星人群的left和right属性判断是否有到达窗口边缘,如果有,那么外星人群alien.rect.y += 下降速度(根据坐标图可以看出下降y轴坐标是加大的),在alien类创建一个check_edges()方法和一个update方法

def check_edges(self):

if self.rect.right >= screen.rect.right:

return True

if self.rect.left <= 0:

return True

def updates(self):

self.x += self.settings.alien_speed_factor * self.settings.alien_fleet_direction

self.rect.x = self.x

在game_functions.py中创建check_fleet_edges()方法,根据check_edges()返回的结果,在进行一次if判断,如果为True,就for循环遍历一次aliens.sprites列表,将每个外星人都下移,并将alien_direciton *= -1,这样外星人的左右方向就会改变

检测子弹和外星人接触

在game_functions.py里按创建一个def check_alien_bullet_collisions()方法,使用函数pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb, collided=None),接受子弹编组和外星人编组,和两个True,是检测两个编组中的精灵的rect是有否重叠,两个True是删除碰撞的两个精灵。当所有外星人都被打完,就可以用len(aliens)判断,如果为等于0,就使用bullets.empy()函数清空编组然后调用create_fleet()创造新的外星人群

def bullet_update(bullets,ai_settings,screen,ship,aliens,stats,sb):

#更新子弹位置

bullets.update()

#删除子弹

for bullet in bullets.copy():

if bullet.rect.bottom <= 0:

bullets.remove(bullet)

check_bullets_aliens_collisions(ai_settings,screen,ship,bullets,aliens,stats,sb)

def check_bullets_aliens_collisions(ai_settings,screen,ship,bullets,aliens,stats,sb):

#检查子弹是否有击中外星人

#如果有击中,就删除对应的子弹和外星人

collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)

接下来可以检测外星人与飞船碰撞的情况,函数pygame.sprite.spritecolideany(sprite, group, collided=None),接受一个精灵和一个编组,检测是否有重叠,并且返回一个布尔值,根据这个布尔值来进行if判断,飞船数量-1,子弹清空,外星人清空,重新创建外星人群,同时将飞船重新放回屏幕下端中央,并将这些功能整理成ship_hit函数,飞船的数量,需要创建一个专门管理游戏数据的类,命名game_stats.py→ class Game_stats。

class Setting():

def __init__(self):

...

self.ship_allow = 3

class Game_stats():

def __init__(self,ai_settings):

#初始化游戏数据统计

self.ai_settings = ai_settings

self.ships_break = self.ai_settings.ship_allow

from time import Sleep

def aliens_update(ai_settings,screen,ship,stats,aliens,sb,bullets):

#检测是否有外星人碰到边缘,并更新外星人位置

check_fleet_edges(ai_settings,aliens)

check_aliens_bottom(ai_settings,screen,ship,stats,aliens,bullets)

aliens.update()

if pygame.sprite.spritecollideany(ship,aliens):

#当外星人和飞船碰撞反应

ship_hit(ai_settings,screen,ship,stats,aliens,sb,bullets)

def ship_hit(ai_settings,screen,ship,stats,aliens,sb,bullets):

if stats.ships_break > 0:

#减少一只飞船的数量

stats.ships_break -= 1

#更新飞船记分牌

sb.prep_ships()

#清空飞船列表和子弹列表

aliens.empty()

bullets.empty()

#重新创造一组外星人,并把飞船放到屏幕下端中央

creat_fleet(ai_settings,screen,ship,aliens)

ship.ship_centerx = ship.screen_rect.centerx

#停止0.5秒,以便让玩家知道飞船撞到,或者外星人碰到底端

sleep(0.5)

遍历aliens编组,如果有外星人到达屏幕底端,也需要调用ship_hit方法

def check_aliens_bottom(ai_settings,screen,ship,stats,aliens,bullets):

#检查外星人是否有碰到底端及其反应

screen_rect = screen.get_rect()

for alien in aliens.sprites():

if alien.rect.bottom >= screen_rect.bottom:

ship_hit(ai_settings,screen,ship,stats,aliens,bullets)

break

def aliens_update(ai_settings,screen,ship,stats,aliens,sb,bullets):

...

check_aliens_bottom(ai_settings,screen,ship,stats,aliens,bullets)

游戏开始的按钮

首先在games_stats.py中设置游戏状态为True,可以ship_hit中加一个if,else的判断,如果飞船数量大于0就执行刚刚的代码,否则就游戏状态设置成为False,再回到alien_invasion.py中的while循环开头加上if判断,如果游戏状态为True才执行。

接下来就需要来绘制play按钮了,按钮的绘制与子弹一样,只不过要加上字,就需要用到pygame提供的将文字熏染成图像的函数,首先用函数pygame.font.SysFont(None,48)用一个变量接收,None位置传入的是字体,None使用默认字体,48为大小。再用变量调用render(self, text, antialias, color, background=None),text需要的文字,antlalia为抗锯齿选项,接收True和False,让文字更平滑,后面两个参数就是文字颜色和背景颜色。并且让文字的放于按钮的中间,最后创建draw_button方法,分别把背景和文字的图像绘制出来即可。

class Button():

def __init__(self,ai_settings,screen,msg):

#初始化按钮参数

self.screen = screen

self.ai_settings = ai_settings

self.screen_rect = screen.get_rect()

#设置按钮颜色及文本颜色、文本字体

self.color = ai_settings.button_color

self.msg_color = ai_settings.text_color

self.font = pygame.font.SysFont(None,48)

#设置按钮位置

self.rect = pygame.Rect(0,0,ai_settings.button_width,ai_settings.button_height)

self.rect.center = self.screen_rect.center

#设置文本位置

self.msg_image = self.font.render(msg,True,self.msg_color,self.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.color,self.rect)

self.screen.blit(self.msg_image,self.msg_image_rect)

检测鼠标点击play按钮

先把游戏状态设置为False,用if语句判断,如果为False,就绘制按钮。

在检测按键鼠标事件的if判断加上的鼠标判断。pygame.mouse.get_pos()函数可以检测鼠标点击,返回一个(x,y)的坐标。用函数

collidepoint(self,x,y),检测是否在rect矩形内有碰撞,传入参数x,y坐标,返回结果是布尔值。如果为Ture,就让游戏状态为True。

当结束之后,play的按钮会出现,这是会发现的情况是继续游戏,而不是重新开始,如果我们还需要加上一下重置游戏的设置,除了让参数回到初始化,还需要将子弹清空,外星人清空,并创建新的外星人群,并让飞船回到原来的位置。

def check_play_button(ai_settings,screen,ship,bullets,aliens,stats,sb,play_button,mouse_x,mouse_y):

button_clickde = play_button.rect.collidepoint(mouse_x,mouse_y)

#检测鼠标点击行为是否在paly按钮区域及反应

if button_clickde and not stats.game_state:

ai_settings.game_initialization()

stats.game_state = True

stats.reset_stats()

aliens.empty()

bullets.empty()

#创造外星人群并将飞船放于屏幕下端中央

creat_fleet(ai_settings, screen, ship, aliens)

ship.ship_centerx = ship.screen_rect.centerx

游戏开始之后会发现,现在点击按钮的位置,还是会重置游戏,所以在判断加上游戏状态为False的时候才重置,而且让鼠标光标在游戏开始之后隐藏起来,pygame.mouse.set_visible(False),并在游戏结束的时候将设置为True显示出来。

游戏难度的升级

可以设置一个倍数变量,在判断外星人数量为0的if判断里让原来的参数乘与这个倍数,同时也要在游戏开始判断里重置参数,达到重新开始的效果

游戏计分板

需要记录玩家游戏得分,最高得分记录,难度的等级,还有玩家的剩余飞船数量图标,需要设置初始得分为0,每个外星人的分值,初始难度为1。

还记得之前检测子弹和外星人碰撞的函数groupcollide,返回的是一个字典存储在collisions里,我们遍历collisions字典中的value值,就可以得到外星人被子弹碰撞的数量,将它乘以我们设置好的外星人的分值,并将分值用render函数渲染成图片绘制出来,同时做一个if判断,如果当前得分高过历史最高得分,就将它赋值给历史最高得分变量,注意的是这个历史最高得分变量不会因为游戏重新开始而重置,同样用render函数绘制出来。

难度等级可放于外星人群数量等于0的if判断中,如果为True,等级变量+1,同样用render函数绘制。

创建一个ships的编组,Ship类就需要继承Sprite,将玩家飞船数量遍历出来按照外星人群的方法绘制,并加到ships编组中。在飞船与外星人碰撞的if判断中绘制。

def check_bullets_aliens_collisions(ai_settings,screen,ship,bullets,aliens,stats,sb):

#检查子弹是否有击中外星人

#如果有击中,就删除对应的子弹和外星人

collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)

if collisions:

#击中外星人玩家得分

for alines in collisions.values():

stats.score += ai_settings.alien_points * len(alines)

sb.prep_point()

#记录玩家最高得分

if stats.score > stats.hight_score:

stats.hight_score = stats.score

sb.prep_hightscore()

if len(aliens) == 0:

#删除现有的子弹并生成新的外星人

bullets.empty()

#升级难度级别

stats.level += 1

#更新难度级别记分牌,增加游戏难度,并创建新外星人群

sb.prep_level()

...

def ship_hit(ai_settings,screen,ship,stats,aliens,sb,bullets):

if stats.ships_break > 0:

#减少一只飞船的数量

stats.ships_break -= 1

#更新飞船记分牌

sb.prep_ships()

...

class Scoreboard():

def __init__(self,ai_settings,screen,stats):

...

#记分牌图像

self.prep_point()

self.prep_hightscore()

self.prep_level()

self.prep_ships()

def prep_point(self):

# rounded_score = int(round(self.stats.score,-1))

# self.score = str(self.stats.score)

#玩家得分转化为渲染图像

score ='{:,}'.format(self.stats.score)

self.score_image = self.font.render(score,True,self.point_color,self.ai_settings.screen_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_hightscore(self):

#将玩家最高得分转化为渲染的图像

hight_score = '{:,}'.format(self.stats.hight_score)

self.hight_score_image = self.font.render(hight_score,True,self.point_color,self.ai_settings.screen_color)

#将最高法记分牌放于屏幕上端中央

self.hight_score_rect = self.hight_score_image.get_rect()

self.hight_score_rect.centerx = self.screen_rect.centerx

self.hight_score_rect.top = 20

def prep_level(self):

#将等级转换为渲染的图像

self.level_str = str(self.stats.level)

self.level_image = self.font.render(self.level_str,True,self.point_color,self.ai_settings.screen_color)

#将等级记分牌放于得分牌下发

self.level_rect = self.level_image.get_rect()

self.level_rect.right = self.screen_rect.right - 20

self.level_rect.top = self.score_rect.bottom + 5

def prep_ships(self):

#显示飞船数量

self.ships = Group()

for ships_number in range(self.stats.ships_break):

ship = Ship(self.ai_settings,self.screen)

ship.rect.x = 10 + ships_number * ship.rect.width

ship.rect.top = self.screen_rect.top + 10

self.ships.add(ship)

def draw_score(self):

#绘制记分牌图像

self.screen.blit(self.score_image,self.score_rect)

self.screen.blit(self.hight_score_image, self.hight_score_rect)

self.screen.blit(self.level_image,self.level_rect)

self.ships.draw(self.screen)

易错点

小游戏项目完成了,现在就可以运行体验了。

整个编程下来,感觉容易出错导致报错的,往往都是一些非常细节的东西,例如:

调用类方法或者类对象的调用错误,或者传入的参数没有对不上,出现漏掉或者位置错误,当然也可以指定参数赋值,但是过于繁琐,建议写方法之前,可以先不用传参数,写完之后在将方法中需要的参数填上在复制粘贴统一即可。

代码的整洁性,代码块不易过长,会导致不好理解,而且修改的时候也不好修改,要养成写完一个功能代码之后整理一下,将功能细分成更多小功能代码块,这个需要日后多次项目经历培养形成。

*注释,对每个代码块进行注释,让阅读者更快的了解代码的作用性,注释的规范,可放于代码的上方或者下方,用#号键注释,最好注释之后可以空行,会更美观。

你可能感兴趣的:(怎么运行python外星人入侵)