安装

我用的Python3.5。

3.4版本之后去

http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame
https://pypi.python.org/pypi/Pygame

这两个网址去下载,推荐第二个,第二个清楚。

注意对应版本,我下载的是pygame-1.9.3-cp35-cp35m-win_amd64.whl,网上好多是讲Python2.*版本的,讲的太简单了,一点都不清楚,直接略过安装,难受。

下载下来是一个.whl文件,将它放在项目的根目录下,然后在dos命令窗口中,切换到项目目录,运行下列代码:

python -m pip install --user pygame-1.9.3-cp35-cp35m-win_amd64.whl

下载了哪个版本,后面的文件名就用哪个版本,不一定和我的相同。

然后创建一个文件:

import pygame
print(pygame.ver)

输出:

1.9.3

开始

创建Pygame窗口以及响应用户输入

创建一个空的Pygame窗口。

alien_invasion.py

import sysimport pygamedef run_game():#初始化游戏,并创建一个屏幕对象pygame.init()screen = pygame.display.set_mode((1200,800))pygame.display.set_caption("Alien Invasion")#开始游戏的主循环while True:#监视键盘和鼠标事件for event in pygame.event.get():if event.type == pygame.QUIT:sys.exit()#让最近绘制的屏幕可见pygame.display.flip()run_game()

pygame.init()

初始化背景设置,让Pygame能正确地工作。

pygame.display.set_mode()

创建一个游戏窗口的尺寸,整个游戏的所有图形元素都将在其中绘制。

实参(1200, 800)是一个元组,指定了窗口的尺寸。

对象screen是一个surface对象。

在Pygame中,surface是屏幕的一部分,用于显示游戏元素。

在这个游戏中,每个元素(如外星人或飞船)都是一个surface

pygame.display.set_mode()返回的surface表示整个游戏窗口,每一次循环都将自动绘制这个surface

pygame.display.set_caption()

设定游戏的名字,名字会在窗口左上角显示。

事件(event)

事件是用户玩游戏时执行的操作,如按键或移动鼠标。

为了让程序响应事件,编写一个事件循环,以侦听事件,并根据相应的事件执行相应的任务。

pygame.event.get()

获得Pygame检测到的事件。

pygame.QUIT

当我玩剑单击游戏的关闭按钮时,将检测到pygame.QUIT事件,根据此事件,调用sys.exit()来退出游戏。

pygame.display.flip()

命令Pygame让最近绘制的屏幕可见。

每次执行一次while循环都换绘制一个空屏幕,并擦去就屏幕,使得只有新屏幕可见。

我们在移动游戏元素时,pygame.display.flip()将会不断更新屏幕,以显示元素的新位置,并在原来位置隐藏元素,营造平滑移动的效果。

设置背景颜色

--snip--
def run_game():--snip--pygame.display.set_caption("Alien Invasion")# 设置背景颜色bg_color = (0, 0, 15)--snip--# 重绘屏幕screen.fill(bg_color)# 让最近绘制的屏幕可见pygame.display.flip()run_game()

颜色

在Pygame中,颜色是以RGB值指定的。

创建一个颜色元组,在while循环前指定一次就好。

screen.fill(bg_color)

用背景色填充屏幕,只接受一个实参:一种颜色。

创建设置类

编写一个名为settings的类,其中包含一个,名为Settings的类,用于将设置储存在一个地方,以免在代码中到处添加设置。

折让函数调用更加简单,且在项目增大时修改游戏的外观更容易。

settings.py

class Settings():"""储存游戏所有的设置"""def __init__(self):self.screen_width = 1200self.screen_height = 800self.bg_color = (0, 0, 15)self.caption = "Alien Invasion 1.0"

alien_invasion.py

--snip--
from settings import Settingsdef run_game():# 初始化游戏,并创建一个屏幕对象pygame.init()ai_settings = Settings()screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))pygame.display.set_caption(ai_settings.caption)--snip--# 重绘屏幕screen.fill(ai_settings.bg_color)# 让最近绘制的屏幕可见pygame.display.flip()run_game()

添加飞船图像




书上的图片丑的无法接受,自己找了好久,在这个网址找到的:
6m5m.com游戏素材资源共享平台

这个图片是png格式的,背景是透明的,转换成bmp即可。

在线图片格式转换
这个网址可以保留图片的透明背景,非常好。

在线图片切割
这个也不错。

创建ship类

ship.py

import pygameclass 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.centerxself.rect.bottom = self.screen_rect.bottomdef blitme(self):"""指定位置绘制飞船"""self.screen.blit(self.image, self.rect)

构造方法中有一个screen参数,制定了要将飞船绘制到什么地方。

pygame.image.load()

加载图像,返回一个飞船的surface对象。

rect对象

rect这个对象是用来存储成对出现的参数,比如,一个矩形框的左上角坐标、宽度和高度,RECT结构通常用于Windows编程。
———摘自百度百科

处理rect对象时,可以使用矩形四角和中心的xx和yy坐标。可通过设置这些纸来指定矩形的位置。

get_rect()

获取相应surface的属性rect

Pygame的效率之所以如此之高,一个原因是它能够像处理矩形(rect对象)一样处理游戏元素,即使它们的形状并非矩形。(诶哟不错哦)

rect对象属性

属性 意义
centerx 水平(X轴)中点坐标
centery 垂直(Y轴)中点坐标
center 矩形(rect对象)中点坐标
top 顶部(Y轴)坐标
bottom 底部(Y轴)坐标
left 左边缘(X轴)坐标
right 右边缘(X轴)坐标

blit()

接受一个surface对象和一个rect对象作为参数 。

在屏幕上的指定位置绘制图像。

重构:模块game_functions

函数check_events()

驾驶飞船

响应按键

每次按键都被注册为KETDOWN事件。

检测到KEYDOWN事件时,要判断按下的是否是特定的键。

例如,按下的如果是右箭头,就让飞船的centerx属性增加。

game_functions.py

def check_events(ship):"""响应按键和鼠标事件"""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.rect.centerx += 1elif event.key == pygame.K_LEFT:ship.rect.centerx -= 1elif event.key == pygame.K_UP:ship.rect.centery -= 1elif event.key == pygame.K_DOWN:ship.rect.centery += 1

可以移动了,但是,只能按一下,动一下。

允许不断移动

可以设置一个移动标志,为布尔型,若为True则按指定方向移动,按下指定键时变为Ture放开时变为False

ship.py

--snip--def __init__(self, screen):"""初始化飞船并设置其初始位置"""--snip--# 移动标志self.moving_right = Falseself.moving_left = Falseself.moving_up = Falseself.moving_down = Falsedef update(self):"""根据移动标志调整飞船位置"""if self.moving_down:self.rect.centery += 1if self.moving_up:self.rect.centery -= 1if self.moving_right:self.rect.centerx += 1if self.moving_left:self.rect.centerx -= 1--snip--

game_functions.py

def check_events(ship):"""响应按键和鼠标事件"""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 = Trueelif event.key == pygame.K_LEFT:ship.moving_left = Trueelif event.key == pygame.K_UP:ship.moving_up = Trueelif event.key == pygame.K_DOWN:ship.moving_down = Trueelif event.type == pygame.KEYUP:if event.key == pygame.K_RIGHT:ship.moving_right = Falseelif event.key == pygame.K_LEFT:ship.moving_left = Falseelif event.key == pygame.K_UP:ship.moving_up = Falseelif event.key == pygame.K_DOWN:ship.moving_down = False

alien_invasion.py

    while True:# 监视键盘和鼠标事件gf.check_events(ship)ship.update()gf.update(ai_settings, screen, ship)

但是这里有个缺陷,如果我先按右键再按左键,它便停下不动了,作为一个游戏爱好者这不能让人接受。

所以我想稍微改动一下。
Ship()类加入self.moving_right_step属性。
ship.py

        # 移动像素self.moving_right_step = 1self.moving_left_step = 1self.moving_up_step = 1self.moving_down_step = 1

当按下右键之后,再按下左键,左键的self.moving_left变为Trueself.moving_left_step
赋值为2。相对的看起来,加12就是向左正常移动,要的是效果嘛。

若接下来再松开左键,self.moving_left_step赋值为1self.moving_left变为False

但是,如果接下来我松开的是右键,那么我不仅要做上述的这些,而且,self.moving_left_step已经被赋值为2了,此时要检查self.moving_left是否为True,然后若为True,就将self.moving_left_step也赋值为1,避免让飞船出现突然加速的bug。

实现顺利。

秘技.左右横跳!!!

限制飞船活动范围

为了避免这个飞船飞走,需要限制它的行动。

也将飞船的速度作为设置之一。
settings.py

--snip--self.ship_speed_factor = 1

ship.py

    def update(self):"""根据移动标志调整飞船位置"""if self.moving_down and self.rect.bottom < self.screen_rect.bottom:self.rect.centery += self.moving_down_speedif self.moving_up and self.rect.top > 0:self.rect.centery -= self.moving_up_speedif self.moving_right and self.rect.right < self.screen_rect.right:self.rect.centerx += self.moving_right_speedif self.moving_left and self.rect.left > 0:self.rect.centerx -= self.moving_left_speed

重构check_events()

game_functions.py

import sysimport pygamedef check_keydown_events(event, ship, ai_settings):"""响应按键"""if event.key == pygame.K_RIGHT:ship.moving_right = Trueif ship.moving_left:ship.moving_right_speed = 2 * ai_settings.ship_speed_factorelif event.key == pygame.K_LEFT:ship.moving_left = Trueif ship.moving_right:ship.moving_left_speed = 2 * ai_settings.ship_speed_factorelif event.key == pygame.K_UP:ship.moving_up = Trueif ship.moving_down:ship.moving_up_speed = 2 * ai_settings.ship_speed_factorelif event.key == pygame.K_DOWN:ship.moving_down = Trueif ship.moving_up:ship.moving_down_speed = 2 * ai_settings.ship_speed_factordef check_keyup_events(event, ship, ai_settings):"""响应松开"""if event.key == pygame.K_RIGHT:ship.moving_right = Falseship.moving_right_speed = ai_settings.ship_speed_factorif ship.moving_left:ship.moving_left_speed = ai_settings.ship_speed_factorelif event.key == pygame.K_LEFT:ship.moving_left = Falseship.moving_left_speed = ai_settings.ship_speed_factorif ship.moving_right:ship.moving_right_speed = ai_settings.ship_speed_factorelif event.key == pygame.K_UP:ship.moving_up = Falseship.moving_up_speed = ai_settings.ship_speed_factorif ship.moving_down:ship.moving_down_speed = ai_settings.ship_speed_factorelif event.key == pygame.K_DOWN:ship.moving_down = Falseship.moving_down_speed = ai_settings.ship_speed_factorif ship.moving_up:ship.moving_up_speed = ai_settings.ship_speed_factordef check_events(ship, ai_settings):"""响应按键和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, ship, ai_settings)elif event.type == pygame.KEYUP:check_keyup_events(event, ship, ai_settings)def update(ai_settings, screen, ship):"""更新屏幕上的图像 , 并切换到新屏幕"""# 重绘屏幕screen.fill(ai_settings.bg_color)ship.blitme()# 让最近绘制的屏幕可见pygame.display.flip()

射击

好的,要开始buibuibui了。

创建Bullet类

bullet.py

import pygame
from pygame.sprite import Spriteclass 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.centerxself.rect.top = ship.rect.top# 储存用小数表示的子弹位置self.y = float(self.rect.y)self.color = ai_settings.bullet_colorself.speed = ai_settings.bullet_speeddef update(self):"""向上移动子弹"""# 更新表示资单位值的小数值self.y -= self.speedself.rect.y = self.ydef draw_bullet(self):pygame.draw.rect(self.screen, self.color, self.rect)

将子弹存储到编组中

alien_invasion.py

    # 储存子弹的编组bullets = Group()# 开始游戏的主循环while True:# 监视键盘和鼠标事件gf.check_events(ship, ai_settings, screen, bullets)ship.update()gf.update_screen(ai_settings, screen, ship, bullets)

当对编组bullets调用update()时,将对其中的每个精灵(子弹)调用update()

开火

无需修改check_keyup_events(),空格键松开什么都不会发生。

game_functions.py

    --snip--elif event.key == pygame.K_SPACE:# 创建一颗子弹,并将其加入到编组bullets中new_bullet = Bullet(ai_settings, screen, ship)bullets.add(new_bullet)--snip--elif event.type == pygame.KEYDOWN:check_keydown_events(event, ship, ai_settings, screen, bullets)def update_screen(ai_settings, screen, ship, bullets):"""更新屏幕上的图像 , 并切换到新屏幕"""# 重绘屏幕screen.fill(ai_settings.bg_color)# 绘制飞船ship.blitme()# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet()# 让最近绘制的屏幕可见pygame.display.flip()

alien_invasion.py

    while True:# 监视键盘和鼠标事件gf.check_events(ship, ai_settings, screen, bullets)ship.update()bullets.update()gf.update_screen(ai_settings, screen, ship, bullets)

删除已消失的子弹

子弹到达屏幕外部,但并没有消失,还占用着内存和处理器,所以,让它消失。

    while True:# 监视键盘和鼠标事件gf.check_events(ship, ai_settings, screen, bullets)ship.update()bullets.update()# 删除已消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)gf.update_screen(ai_settings, screen, ship, bullets)

限制子弹数量

不存在的,我不,这是我的游戏。

创建函数update_bullets()

为了让主程序尽量简单,将子弹管理部分作为一个函数。
game_functions.py

def update_bullets(bullets):# 更新子弹位置bullets.update()# 删除已消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)

alien_invasion.py

    while True:# 监视键盘和鼠标事件gf.check_events(ship, ai_settings, screen, bullets)ship.update()gf.update_bullets(bullets)gf.update_screen(ai_settings, screen, ship, bullets)

创建函数fire_bullet()

开火也作为一个函数。

game_functions.py

    elif event.key == pygame.K_SPACE:fire_bullets(bullets, ai_settings, screen, ship)--snip--
def fire_bullets(bullets, ai_settings, screen, ship):# 创建一颗子弹,并将其加入到编组bullets中new_bullet = Bullet(ai_settings, screen, ship)bullets.add(new_bullet)

射击个人优化

这个游戏只能按一下,发一颗子弹,作为一名游戏爱好者,这让我不能接受。

按住空格,什么时候不松开,它就得一个劲给我buibuibui。

所以,设置一个射击开关shooting,为布尔型,这个开关作为Ship()类的属性,而不是Bullet()类的。

当其为True ,即按下空格时,不断射击。

并且修改上面刚创建的那个函数,怎么叫个开火函数?应该是装弹函数啊,给飞船装弹,bullets.update()才是发射。

ship.py

import pygamefrom bullet import Bulletclass Ship():def __init__(self, screen, ai_settings):--snip--# 射击标志self.shooting = Falsedef update(self, bullets, ai_settings, screen, ship):# 射击if self.shooting:self.add_bullets(bullets, ai_settings, screen, ship)def add_bullets(self, bullets, ai_settings, screen, ship):# 创建一颗子弹,并将其加入到编组bullets中new_bullet = Bullet(ai_settings, screen, ship)bullets.add(new_bullet)

将装弹,添加到Ship()类中。
alien_invasion.py

    while True:--snip--ship.update(bullets, ai_settings, screen, ship)--snip--

结果,不尽人意,甚至非常恶心的一大条:

我不服,再想。

可以这样,当最后一个发射的子弹的底部的坐标距离飞船的顶部坐标为3像素的时候,才添加,而且这个应该可以读取到最后一个。

查阅官方文档,发现:

之前用的这个函数返回的是一个列表,那就好办了啊。

        # 射击if self.shooting and (len(bullets.sprites()) == 0or ship.rect.top - bullets.sprites()[-1].rect.bottom >= 20):self.add_bullets(bullets, ai_settings, screen, ship)

结果依然不尽人意,更恶心了。

可能计算机有延迟,子弹太多太快了,处理不过来,所以。

第二个解决方案,让计算机延迟执行子弹添加函数。

导入模块time
ship.py

        # 射击if self.shooting:self.add_bullets(bullets, ai_settings, screen, ship)time.sleep(0.01)

结果依然非常恶心:

那个sleep()把整个程序都变慢了,单纯的变成了慢动作而已。

再想办法。

既然每次循环是“1帧”,那何不设置一个记录帧数的变量,这个变量应该在主程序while循环之前就有,命名为ticks
alien_invasion.py

    # 计数器ticks = 0# 开始游戏的主循环while True:# 控制游戏帧率ticks += 1if ticks >= ai_settings.animate_cycle:ticks = 0# 监视键盘和鼠标事件gf.check_events(ship, ai_settings, screen, bullets)ship.update(bullets, ai_settings, screen, ship, ticks)gf.update_bullets(bullets)gf.update_screen(ai_settings, screen, ship, bullets)

shipupdate()函数添加一个新参数ticks
ship.py

        # 射击if self.shooting:if ticks % 10 == 0:self.add_bullets(bullets, ai_settings, screen, ship)

见证奇迹的时刻到了:

爽。

外星人

创建第一个外星人

创建Alien类

自己找的外星人的图片:

是不是有点熟悉,没错,如果你是《赛尔号》的骨灰老玩家,那你当年就应该体会过被它支配的恐怖,大BOSS————尤纳斯!!!!

alien.py

import pygame
from pygame.sprite import Spriteclass Alien(Sprite):"""外星人 类"""def __init__(self, ai_settings, screen):"""初始化外星人并设置其起始位置"""# 类似Ship类的设置super().__init__()self.screen = screenself.ai_settings = ai_settingsself.image = pygame.image.load("images/alien.bmp")self.rect = self.image.get_rect()self.rect.x = self.rect.widthself.rect.y = self.rect.heightself.x = float(self.rect.x)def blitme(self):"""绘制外星人"""self.screen.blit(self.image, self.rect)

game_functions.py

def update_screen(ai_settings, screen, ship, bullets, alien):"""更新屏幕上的图像 , 并切换到新屏幕"""# 重绘屏幕screen.fill(ai_settings.bg_color)# 绘制飞船ship.blitme()# 绘制外星人alien.blitme()# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet()# 让最近绘制的屏幕可见pygame.display.flip()

alien_invasion.py

    # 创建外星人实例alien = Alien(ai_settings, screen)

创建一群外星人

确定一行可以容纳多少个外星人

需要在屏幕两端留下边距,边距长与外星人宽度相等。

available_space_x = ai_settings.screen_width - (2 * alien_width)

每个外星人占据的空间应该是一个外星人的宽度的两倍,一个宽度为外星人的,一个为外星人右边的空白区域:

number_aliens_x = available_space_x / (2 * alien_width)

创建多行外星人

alien_invasion.py

    # 创建外星人群gf.create_fleet(ai_settings, screen, aliens)# 开始游戏的主循环while True:# 控制游戏帧率ticks += 1aliens.draw(screen)if ticks >= ai_settings.animate_cycle:ticks = 0# 监视键盘和鼠标事件gf.check_events(ship, ai_settings, screen, bullets)ship.update(bullets, ai_settings, screen, ship, ticks)gf.update_bullets(bullets)gf.update_screen(ai_settings, screen, ship, bullets, aliens)

game_functions.py

def update_screen(ai_settings, screen, ship, bullets, aliens):"""更新屏幕上的图像 , 并切换到新屏幕"""# 重绘屏幕screen.fill(ai_settings.bg_color)# 绘制飞船ship.blitme()# # 绘制外星人aliens.draw(screen)

创建外星人群

game_functions.py

def create_fleet(ai_settings, screen, aliens):"""创建外星人群"""# 创建一个外星人,并计算一行可以容纳多少外星人# 外星人间距为外星人宽度new_alien = Alien(ai_settings, screen)available_space_x = ai_settings.screen_width - (2 * new_alien.rect.width)number_aliens_x = int(available_space_x / (2 * new_alien.rect.width))for alien_number in range(0, number_aliens_x):# 创建一个外星人并加入当前行alien = Alien(ai_settings, screen)print(type(alien.x))alien.x = alien.rect.width * (2 * alien_number + 1)alien.rect.x = alien.xaliens.add(alien)

效果:

重构create_fleet()

game_functions.py

def create_alien(ai_settings, screen, aliens, alien_number, number_row):# 创建一个外星人并加入当前行alien = Alien(ai_settings, screen)alien.x = alien.rect.width * (2 * alien_number + 1)alien.y = alien.rect.height * 2 * number_rowalien.rect.x = alien.xalien.rect.y = alien.yaliens.add(alien)def get_number_rows(ai_settings, screen, new_alien, ship):available_space_y = ai_settings.screen_height - ship.rect.width - new_alien.rect.heightreturn int(available_space_y / (2 * new_alien.rect.height))def create_fleet(ai_settings, screen, aliens, ship):"""创建外星人群"""new_alien = Alien(ai_settings, screen)number_aliens_x = get_number_aliens(ai_settings, screen, new_alien)number_rows = get_number_rows(ai_settings, screen, new_alien, ship)for number_row in range(number_rows):for alien_number in range(number_aliens_x):create_alien(ai_settings, screen, aliens, alien_number, number_row)

因为外星人有点稍微的大所以结果有点不尽人意:

排列得太整齐了,作为一个游戏爱好者,这不能让人接受。

随机出现,也可说成,随机“不”出现,这个就好办多了:
game_functions.py

import sys
from random import randint--snip--def create_fleet(ai_settings, screen, aliens, ship):"""创建外星人群"""new_alien = Alien(ai_settings, screen)number_aliens_x = get_number_aliens(ai_settings, screen, new_alien)number_rows = get_number_rows(ai_settings, screen, new_alien, ship)for number_row in range(number_rows):for alien_number in range(number_aliens_x):create_alien(ai_settings, screen, aliens, alien_number, number_row)# 随机删除for alien in aliens.copy():if len(aliens.sprites()) > 6 and randint(0,1):aliens.remove(alien)

顺便设置下限,不能让他把所有的都给我删了,不然打谁?

效果很棒:

randint()

使用前导入random模块。

randint(a, b)返回ab之间的随机数,包括ab

源码如下:

def randint(self, a, b):"""Return random integer in range [a, b], including both end points."""return self.randrange(a, b+1)

including both end pointsrandrange(a, b+1)已经很清楚了,不再赘述。

让外星人移动

alien.py

    def check_edge(self):"""如果触碰边缘返回True"""if self.rect.right == self.screen.get_rect().right or \self.rect.left == self.screen.get_rect().left:return True

game_functions.py

def check_fleet_edges(ai_settings, aliens):"""有外星人到达边缘的措施"""for alien in aliens.sprites():if alien.check_edge():change_fleet_direction(ai_settings, aliens)breakdef change_fleet_direction(ai_settings, aliens):"""改变外星人群的运动方向"""for alien in aliens.sprites():alien.y += ai_settings.fleet_drop_speedalien.rect.y = alien.yai_settings.fleet_direction *= -1def update_aliens(ai_settings, aliens):"""更新外星人群的位置"""check_fleet_edges(ai_settings, aliens)aliens.update(ai_settings)

alien_invasion.py

        # 更新外星人状态gf.update_aliens(ai_settings, aliens)

下降速度为1,太小了,但若是设置的大了些,就会很突兀,蹦的一下下来一大截,作为一个游戏爱好者,这不能让人接受。

实现思路:
在设置里加一个下落的标志self.drop = False,若为真就“只”下落,不左右移动。
settings.py

# 下落标志self.drop = False# 下落开始时间(帧数)点self.drop_ticks = 0# 下落时间(帧数)self.drop_cycle = 60

alien.py

    def update(self, ai_settings):if ai_settings.drop:self.y += self.ai_settings.fleet_drop_speedself.rect.y = self.yelse:self.x += self.ai_settings.alien_speed_factor * ai_settings.fleet_directionself.rect.x = self.x

在外星人到达边缘时,判断外星人是否正在下降,若正在下降并且到达下降时间,就停止下降,并改变左右移动方向,开始左右移动。

若是刚碰到边缘,就将下降标志self.drop = True,记好时间点ticks,作为函数change_fleet_direction()实参,参数ticks默认为0,是可选参数,避免了多写一个函数的麻烦。

若正在下降,且还没下降完全,就不做改变,接着下降。

game_functions.py

def check_fleet_edges(ai_settings, aliens, ticks):"""有外星人到达边缘的措施"""for alien in aliens.sprites():if alien.check_edge():if ai_settings.drop and \(ticks - ai_settings.drop_ticks) % ai_settings.drop_cycle == 0:change_fleet_direction(ai_settings, aliens)breakelif ai_settings.drop is False:change_fleet_direction(ai_settings, aliens, ticks)breakdef change_fleet_direction(ai_settings, aliens, ticks=0):"""改变外星人群的运动方向"""if ticks:ai_settings.drop = Trueai_settings.drop_ticks = tickselse:ai_settings.drop = Falseai_settings.fleet_direction *= -1def update_aliens(ai_settings, aliens, ticks):"""更新外星人群的位置"""check_fleet_edges(ai_settings, aliens, ticks)aliens.update(ai_settings)

效果很满意:

射杀外星人

终于到了该buibuibui时候了。

检测子弹与外星人的碰撞

game_functions.py

def update_bullets(bullets, aliens):# 更新子弹位置bullets.update()# 删除已消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)# 检查是否有子弹击中了外星人# 若击中了,删除相应的子弹和外星人collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)

alien_invasion.py

    while True:# 计数ticks += 1aliens.draw(screen)# 监视键盘和鼠标事件gf.check_events(ship, ai_settings)# 更新飞船状态ship.update(bullets, ai_settings, screen, ship, ticks)# 更新子弹状态gf.update_bullets(bullets, aliens)

太无敌了,一会儿就死完了。

pygame.sprite.groupcollide()

将每颗子弹的rect与每个外星人的rect进行比较,并返回一个字典。

在这个字典中,每个键都是第一个参数组 中的精灵(子弹),而相应的值都是第二个参数组中的精灵(外星人)。

第三第四个参数为布尔型,第三个对应第一个参数组,第四个对应第二个参数组。

若位真True,则碰撞后对应参数组的精灵消失,反之同理。

生成新的外星人

外星人那么可怜,那就多造一些吧。

game_functions.py

def check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets):"""检查是否有子弹击中了外星人若击中了,删除相应的子弹和外星人"""collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if len(aliens) == 0:bullets.empty()create_fleet(ai_settings, screen, aliens, ship)def update_bullets(ai_settings, screen, ship, bullets, aliens):# 更新子弹位置bullets.update()# 删除已消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets)

不好意思,我依旧无敌,此游戏毫无平衡性可言。

结束游戏

好歹让我输一次啊。

检测外星人和飞船碰撞

game_functions.py

def update_aliens(ai_settings, aliens, ticks, ship):"""更新外星人群的位置"""check_fleet_edges(ai_settings, aliens, ticks)aliens.update(ai_settings)# 检测外星人与飞船的碰撞if pygame.sprite.spritecollideany(ship, aliens):print("!!!!!")

pygame.sprite.spritecollideany()

这个函数看名字就是sprite-collide-any

接受两个实参:一个精灵和一个编组。

当然,虽然ship没有继承Sprite类,但是,只要有其中的rect属性就行。

它检查编组是否有成员与精灵发生了碰撞,找到第一个它找到的第一个与飞船发生了碰撞的成员后停止遍历数组。

返回第一个与精灵(飞船)碰撞的编组中的成员(外星人)。

响应外星人和飞船碰撞

编写GameStatus类

game_stats.py

class GameStats():"""统计游戏各类信息"""def __init__(self, ai_settings):"""初始化统计信息"""self.ai_settings = ai_settingsself.reset_stats()def reset_stats(self):"""初始化运行期间可能变化的统计信息"""self.ship_left = self.ai_settings.ship_limit

game_functions.py

def ship_hit(ai_settings, screen, ship, aliens, bullets, stats):"""响应飞船遭到碰撞后的事件"""# 将ship_left 减一stats.ship_left -= 1# 清空所有子弹和外星人aliens.empty()bullets.empty()# 创建新的外星人群,并将飞船至于中央create_fleet(ai_settings, screen, aliens, ship)ship.center_ship()# 暂停sleep(0.5)

ship.py

    def center_ship(self):self.rect.centerx = self.screen_rect.centerxself.rect.bottom = self.screen_rect.bottom

有外星人到达底部

类比之前检查外星人到达边缘。

game_functions.py

def check_aliens_bottom(ai_settings, screen, aliens, bullets, stats, ship):"""有外星人到达底部的措施"""for alien in aliens.sprites():if alien.rect.bottom >= screen.get_rect().bottom:ship_hit(ai_settings, screen, ship, aliens, bullets, stats)break

与撞到飞船时的效果相同。

小bug修复

之前做的外星人平滑下降的功能,有个bug,确切的说,是两个。

第一个:

若在外星人下降时将触碰边缘的外星人消灭,外星人会一直下降,就算将其全部消灭也无济于事。

bug重现还是也是一件技术活啊,试了好几次,手残。

看代码:

game_functions.py

def check_fleet_edges(ai_settings, aliens, ticks):"""有外星人到达边缘的措施"""for alien in aliens.sprites():if alien.check_edge():if ai_settings.drop and \(ticks - ai_settings.drop_ticks) % ai_settings.drop_cycle == 0:change_fleet_direction(ai_settings)breakelif ai_settings.drop is False:change_fleet_direction(ai_settings, ticks)break

所有的方向改变的行为都是基于一个前提————触碰边缘,而控制下降的是ai_settings.drop,我把边缘的外星人消灭了。

alien.check_edge()就不会返回True,第4行以下的代码一个也运行不了,根本改变不了ai_settings.drop(下降标志),其与外星人一直下降。

就算到达底部,重新刷新一群外星人,由于下降标志不变,而外星人与屏幕左右有空隙,依然下降,噩梦循环。

所以,错了,就得改:

def check_fleet_edges(ai_settings, aliens, ticks):"""有外星人到达边缘的措施"""for alien in aliens.sprites():if alien.check_edge():if ai_settings.drop is False:change_fleet_direction(ai_settings, ticks)breakif ai_settings.drop and \(ticks - ai_settings.drop_ticks) % ai_settings.drop_cycle == 0:change_fleet_direction(ai_settings)break

将下落时的情况单独列出来,与“有外星人触碰边缘”事件并列,相互为独立事件,所以不用elif而用if

第二个:

当在下降途中外星人触碰到飞机或是屏幕底部,刷新后也会一直下降。

看代码:

game_functions.py

def create_fleet(ai_settings, screen, aliens, ship):"""创建外星人群"""new_alien = Alien(ai_settings, screen)number_aliens_x = get_number_aliens(ai_settings, new_alien)number_rows = get_number_rows(ai_settings, new_alien, ship)for number_row in range(number_rows):for alien_number in range(number_aliens_x):create_alien(ai_settings, screen, aliens, alien_number, number_row)# 随机删除for alien in aliens.copy():if len(aliens.sprites()) > 6 and randint(0, 1):aliens.remove(alien)

刷新外星人群后,同样,一直下降,也是由于无法改变ai_settings.drop下降标志。

当然,加上了上例的代码,有所改善,因为保证了每次下降的距离都会有限制。

但碰撞后还会下降一小段距离,接着上次没下降完的那一部分。

这次让下降的距离长些,好观察:

知错能改,善莫大焉:

def create_fleet(ai_settings, screen, aliens, ship):"""创建外星人群"""ai_settings.drop = Falseai_settings.fleet_direction = 1

每次创建外星人群,都初始化其运动状态。

两个bug完美解决

为自己鼓掌,piapiapiapiapiapia。

游戏结束

输了就输了,大不了再来一次。

当飞机用完之后游戏结束。

GameStats类中添加属性game_active

game_stats.py

        --snip--# 游戏刚启动时处于活动状态self.game_active = True

game_dunctions.py

def ship_hit(ai_settings, screen, ship, aliens, bullets, stats):"""响应飞船遭到碰撞后的事件"""if stats.ship_left > 0:# 将ship_left 减一stats.ship_left -= 1--snip--else:stats.game_active = False

如果玩家用完了所有飞船,将game_active置为False,游戏结束。

确定应运行游戏的哪些部分

alien_invasion.py

    while True:# 计数ticks += 1aliens.draw(screen)if stats.game_active:# 监视键盘和鼠标事件gf.check_events(ship, ai_settings)# 更新飞船状态ship.update(bullets, ai_settings, screen, ship, ticks)--snip--     

注意最后一条ship.update(bullets, ai_settings, screen, ship, ticks)是和if语句并列的。

飞船用完后,将会停止游戏。

记分

添加Play按钮

先让游戏一开始处于非活动状态。

game_stats.py

    def __init__(self, ai_settings):"""初始化统计信息"""self.ai_settings = ai_settingsself.reset_stats()# 游戏刚启动时处于非活动状态self.game_active = False

创建Button类

button.py

import pygame
from pygame.sprite import Sprite
import pygame.fontclass Button():def __init__(self, ai_settings, screen, msg):"""初始化按钮信息"""self.ai_settings = ai_settingsself.screen = screenself.screen_rect = screen.get_rect()# 设置按钮尺寸以及其他信息self.width, self.height = 200, 50self.color = (0, 255, 0)self.text_color = (255, 255, 255)self.font = pygame.font.SysFont(None, 50)# 创建按钮的rect对象将其居中self.rect = pygame.Rect(0, 0, self.width, self.height)self.rect.center = self.screen_rect.center# 将msg渲染为图像,使其在按钮上居中self.mas_image = pygame.font.render(msg, True, self.text_color, self.color)self.msg_image_rect = self.mas_image.get_rect()self.msg_image_rect.center = self.rect.centerdef draw_button(self):"""先绘出按钮,再绘制文本"""self.screen.fill(self.color, self.rect)self.screen.blit(self.mas_image, self.msg_image_rect)

pygame.font.SysFont()

官方文档:

create a Font object from the system fonts

SysFont(name, size, bold=False, italic=False) -> Font

Return a new Font object that is loaded from the system fonts. The font will match the requested bold and italic flags. If a suitable system font is not found this will fall back on loading the default pygame font. The font name can be a comma separated list of font names to look for.

就是返回一个字体对象,详细的就不翻译了,高中水平就能看得懂。

comma : 逗号

pygame.font.Font.render

draw text on a new Surface

render(text, antialias, color, background=None) -> Surface

This creates a new Surface with the specified text rendered on it. pygame provides no way to directly draw text on an existing Surface: instead you must use Font.render() to create an image (Surface) of the text, then blit this image onto another Surface.

返回了一个Surface对象。

antialias[‘æntɪ’eɪlɪəs]:抗锯齿
render:在这里翻译成“渲染”,不是传递,给予,表达 之类的……

若要问抗锯齿是什么?

放心我没那么狠,看这里看这里。

在屏幕上绘制按钮

game_functions.py

def update_screen(ai_settings, screen, ship, bullets, aliens, play_button, stats):"""更新屏幕上的图像 , 并切换到新屏幕"""# 重绘屏幕screen.fill(ai_settings.bg_color)# 若游戏处于非活动状态,绘制按钮if not stats.game_active:play_button.draw_button()play_button.draw_button()# 绘制飞船ship.blitme()# 绘制外星人aliens.draw(screen)# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet()# 让最近绘制的屏幕可见pygame.display.flip()

开始游戏

添加鼠标事件:

game_functions.py

def check_events(ship, ai_settings, stats, play_button):"""响应按键和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, ship, ai_settings)elif event.type == pygame.KEYUP:check_keyup_events(event, ship, ai_settings)elif event.type == pygame.MOUSEBUTTONDOWN:mouse_x, mouse_y = pygame.mouse.get_pos()check_play_button(stats, play_button, mouse_x, mouse_y)def check_play_button(stats, play_button, mouse_x, mouse_y):"""在玩家单击Play按钮时开始游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y):stats.game_active = True

alien_invasion.py

        # 监视键盘和鼠标事件gf.check_events(ship, ai_settings, stats, play_button)

重置游戏

每次单击Play按钮,都要重置游戏,重置统计信息、删除现有的外星人和子弹、创建一群新的外星人,并让飞船居中。

game_functions.py

def check_play_button(stats, play_button, mouse_x, mouse_y, ship, ai_settings,screen, aliens, bullets):"""在玩家单击Play按钮时开始游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y):# 重置游戏统计信息stats.reset_stats()stats.game_active = True# 清空外星人列表和子弹列表aliens.empty()bullets.empty()# 创建新的外星人群create_fleet(ai_settings, screen, aliens, ship)ship.center_ship()

将Play切换到非活动状态

存在一个问题,即:即使Play按钮不可见,玩家单击其原来所在的区域时,游戏自然会作出响应。游戏开始后,如果玩家不小心单击了Play按钮原来所处的区域,游戏奖重新开始。

可设置为,只有游戏在`game_activeFalse时,单击Play才有效。

game_functions.py

def check_play_button(stats, play_button, mouse_x, mouse_y, ship, ai_settings,screen, aliens, bullets):"""在玩家单击Play按钮时开始游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 重置游戏统计信息stats.reset_stats()stats.game_active = True--snip--

隐藏光标

开始游戏后让光标隐藏,结束后再出现。

def ship_hit(ai_settings, screen, ship, aliens, bullets, stats):"""响应飞船遭到碰撞后的事件"""if stats.ship_left > 0:--snip--else:stats.game_active = False# 显示光标pygame.mouse.set_visible(True)def check_play_button(stats, play_button, mouse_x, mouse_y, ship, ai_settings,screen, aliens, bullets):"""在玩家单击Play按钮时开始游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 隐藏光标pygame.mouse.set_visible(False)--snip--

提高等级

就这么玩下去没有什么难度,每消灭一群,都会提高外星人的难度。

修改飞船速度

settings.py

class Settings():"""储存游戏所有的设置"""--snip--# 加快游戏节奏的节奏self.speedup_scale = 1.1self.initialiize_dynamic_settings()def initialiize_dynamic_settings(self):# 飞船速度self.ship_speed_factor = 1# 外星人左右移动速度设置self.alien_speed_factor = 0.5# 下落速度self.fleet_drop_speed = 0.5# 下落时间(帧数)self.drop_cycle = 60# 动画周期self.animate_cycle = 60def increase_speed(self):"""提高速度设置"""self.ship_speed_factor *= self.speedup_scale# 外星人左右移动速度设置self.alien_speed_factor *= self.speedup_scale# 下落速度self.fleet_drop_speed *= self.speedup_scale# 下落时间(帧数)self.drop_cycle *= self.speedup_scale# 动画周期self.animate_cycle *= self.speedup_scale

game_functions.py

def check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets):"""检查是否有子弹击中了外星人若击中了,删除相应的子弹和外星人"""collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if len(aliens) == 0:bullets.empty()ai_settings.increase_speed()create_fleet(ai_settings, screen, aliens, ship)

重置速度

game_functions.py

def ship_hit(ai_settings, screen, ship, aliens, bullets, stats):"""响应飞船遭到碰撞后的事件"""if stats.ship_left > 0:--snip--else:stats.game_active = Falseai_settings.initialiize_dynamic_settings()# 显示光标pygame.mouse.set_visible(True)

记分

GameStats中添加score属性:
game_stats.py

    def __init__(self, ai_settings):"""初始化统计信息"""self.ai_settings = ai_settingsself.reset_stats()# 游戏刚启动时处于非活动状态self.game_active = False# 得分self.score = 0

显示得分

首先创建一个新类ScoreBoard,类似Button

score_board.py

import pygame.fontclass ScoreBoard():"""显示得分"""def __init__(self, ai_settings, screen, stats):self.screen = screenself.screen_rect = screen.get_rect()self.ai_settings = ai_settingsself.stats = stats# 字体设置self.text_color = (50, 50, 50)self.font = pygame.font.SysFont(None, 50, True)# 准备初始得分图像def prep_score(self):score_str = str(self.ai_settings.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.score_rect.right - 20self.score_rect.top = 20def show_score(self):"""在屏幕上显示得分"""self.screen.blit(self.score_image, self.score_rect)

创建记分牌

game_functions.py

def update_screen(ai_settings, screen, ship, bullets, aliens, play_button, stats, sb):"""更新屏幕上的图像 , 并切换到新屏幕"""# 重绘屏幕screen.fill(ai_settings.bg_color)# 显示分数sb.show_score()# 若游戏处于非活动状态,绘制按钮if not stats.game_active:play_button.draw_button()# 绘制飞船ship.blitme()--snip--

alien_invasion.py

    # 创建记分牌sb = ScoreBoard(ai_settings, screen, stats)--snip--# 更新屏幕gf.update_screen(ai_settings, screen, ship, bullets, aliens,play_button, stats, sb)

在外星人被消灭时更新得分

settings.py

    def __init__(self):--snip--# 外星人点数self.alien_points = 50def increase_speed(self):--snip--# 外星人点数self.alien_points = int(self.speedup_scale * self.alien_points)

game_functions.py

def check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets, stats, sb):"""检查是否有子弹击中了外星人若击中了,删除相应的子弹和外星人"""collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if collisions:for aliens in collisions.values():stats.score += ai_settings.alien_points * len(aliens)sb.prep_score()--snip--

最高得分

score_board.py

    def show_score(self):"""在屏幕上显示得分"""self.screen.blit(self.score_image, self.score_rect)self.screen.blit(self.high_score_image, self.high_score_rect)def prep_high_score(self):high_score_str = str(self.stats.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.centerxself.high_score_rect.top = 60

game_functions.py

def check_high_score(stats, sb):"""检查是否出现了新的最高分"""if stats.score > stats.high_score:stats.high_score = stats.scoresb.prep_high_score()

将最高分存入文件

game_functions.py

def check_high_score(stats, sb):"""检查是否出现了新的最高分"""if stats.score > stats.high_score:stats.high_score = stats.scoresb.prep_high_score()save_high_score(stats)def save_high_score(stats):"""将最高分保存入文件"""with open("high_score.json", 'w') as file:json.dump(stats.high_score, file)

game_stats.py

    --snip--# 得分self.score = 0self.high_score = 0self.get_high_score()def get_high_score(self):try:with open("high_score.json") as file:self.high_score = json.load(file)except FileNotFoundError:with open("high_score.json", 'w') as file:json.dump(self.high_score, file)

这样最高分就算重新开始游戏也不会从零开始了。

显示等级

game_stats.py

        # 难度等级self.level = 1def reset_stats(self):"""初始化运行期间可能变化的统计信息"""self.ship_left = self.ai_settings.ship_limitself.score = 0self.level = 1

score_board.py

    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)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.top = self.score_rect.bottom + 10self.level_rect.left = self.score_rect.left

game_functions.py

def check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets, stats, sb):"""检查是否有子弹击中了外星人若击中了,删除相应的子弹和外星人"""--snip--# 提高等级stats.level += 1sb.prep_level()--snip--

显示余下的飞船

ship_small.py

import pygame
from pygame.sprite import Spriteclass ShipSmall(Sprite):"""小飞船"""def __init__(self, screen):super().__init__()self.screen = screenself.image = pygame.image.load('images/ship_small.bmp')self.rect = self.image.get_rect()self.rect.top = 10

因为之前的飞船的图片太大,又做了个小的,重写了一个类。

类似于子弹一样,有多少个话多少个,写一个编组,把小飞船放进去。

score_board.py

   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_small.draw(self.screen)def prep_ships(self):"""显示还剩下多少飞船"""self.ships_small = Group()for ship_number in range(self.stats.ship_left):ship_small = ShipSmall(self.ai_settings)ship_small.rect.left = 10 + ship_number * ship_small.rect.widthself.ships_small.add(ship_small)

其余的就和之前相似,在game_functions.pyalien_invasion.py中添加函数,以及参数即可,毫无难度可言。

完整代码展示

alien_invasion.py

import pygame
from pygame.sprite import Groupfrom settings import Settings
from ship import Ship
from game_stats import GameStats
from  button import Button
from score_board import ScoreBoard
import game_functions as gfdef run_game():# 初始化游戏,并创建一个屏幕对象pygame.init()# 创建设置实例ai_settings = Settings()# 创建储存游戏信息实例stats = GameStats(ai_settings)# 创建游戏屏幕screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))# 设定游戏名pygame.display.set_caption(ai_settings.caption)# 创建Play按钮play_button = Button(ai_settings, screen, "Play")# 创建记分牌sb = ScoreBoard(ai_settings, screen, stats)# 创建飞船实例ship = Ship(screen, ai_settings)# 储存子弹的编组bullets = Group()# 储存外星人的编组aliens = Group()# 计数器ticks = 0# 创建外星人群gf.create_fleet(ai_settings, screen, aliens, ship)# 开始游戏的主循环while True:# 计数ticks += 1aliens.draw(screen)if ticks >= ai_settings.animate_cycle:ticks = 0# 监视键盘和鼠标事件gf.check_events(ship, ai_settings, stats, play_button, screen, aliens, bullets, sb )if stats.game_active:# 更新飞船状态ship.update(bullets, ai_settings, screen, ship, ticks)# 更新子弹状态gf.update_bullets(ai_settings, screen, ship, bullets, aliens, stats, sb)# 更新外星人状态gf.update_aliens(ai_settings, screen, aliens, bullets, stats, ticks, ship, sb)# 更新屏幕gf.update_screen(ai_settings, screen, ship, bullets, aliens,play_button, stats, sb)run_game()

game_functions.py

import sys
from random import randint
from time import sleep
import jsonimport pygame
from alien import Aliendef check_keydown_events(event, ship, ai_settings):"""响应按键"""if event.key == pygame.K_RIGHT:ship.moving_right = Trueif ship.moving_left:ship.moving_right_speed = 2 * ai_settings.ship_speed_factorelif event.key == pygame.K_LEFT:ship.moving_left = Trueif ship.moving_right:ship.moving_left_speed = 2 * ai_settings.ship_speed_factorelif event.key == pygame.K_UP:ship.moving_up = Trueif ship.moving_down:ship.moving_up_speed = 2 * ai_settings.ship_speed_factorelif event.key == pygame.K_DOWN:ship.moving_down = Trueif ship.moving_up:ship.moving_down_speed = 2 * ai_settings.ship_speed_factorelif event.key == pygame.K_SPACE:ship.shooting = Trueelif event.key == pygame.K_q:sys.exit()def check_keyup_events(event, ship, ai_settings):"""响应松开"""if event.key == pygame.K_RIGHT:ship.moving_right = Falseship.moving_right_speed = ai_settings.ship_speed_factorif ship.moving_left:ship.moving_left_speed = ai_settings.ship_speed_factorelif event.key == pygame.K_LEFT:ship.moving_left = Falseship.moving_left_speed = ai_settings.ship_speed_factorif ship.moving_right:ship.moving_right_speed = ai_settings.ship_speed_factorelif event.key == pygame.K_UP:ship.moving_up = Falseship.moving_up_speed = ai_settings.ship_speed_factorif ship.moving_down:ship.moving_down_speed = ai_settings.ship_speed_factorelif event.key == pygame.K_DOWN:ship.moving_down = Falseship.moving_down_speed = ai_settings.ship_speed_factorif ship.moving_up:ship.moving_up_speed = ai_settings.ship_speed_factorelif event.key == pygame.K_SPACE:ship.shooting = Falsedef check_events(ship, ai_settings, stats, play_button, screen, aliens, bullets, sb):"""响应按键和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, ship, ai_settings)elif event.type == pygame.KEYUP:check_keyup_events(event, ship, ai_settings)elif event.type == pygame.MOUSEBUTTONDOWN:mouse_x, mouse_y = pygame.mouse.get_pos()check_play_button(stats, play_button, mouse_x, mouse_y, ship, ai_settings,screen, aliens, bullets, sb)def update_screen(ai_settings, screen, ship, bullets, aliens, play_button, stats, sb):"""更新屏幕上的图像 , 并切换到新屏幕"""# 重绘屏幕screen.fill(ai_settings.bg_color)# 显示分数sb.show_score()# 绘制飞船ship.blitme()# 绘制外星人aliens.draw(screen)# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet()# 若游戏处于非活动状态,绘制按钮if not stats.game_active:play_button.draw_button()# 让最近绘制的屏幕可见pygame.display.flip()def check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets, stats, sb):"""检查是否有子弹击中了外星人若击中了,删除相应的子弹和外星人"""collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if collisions: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()# 提高等级stats.level += 1sb.prep_level()create_fleet(ai_settings, screen, aliens, ship)def update_bullets(ai_settings, screen, ship, bullets, aliens, stats, sb):# 更新子弹位置bullets.update()# 删除已消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets, stats, sb)def get_number_aliens(ai_settings, new_alien):# 创建一个外星人,并计算一行可以容纳多少外星人# 外星人间距为外星人宽度available_space_x = ai_settings.screen_width - (2 * new_alien.rect.width)return int(available_space_x / (2 * new_alien.rect.width))def create_alien(ai_settings, screen, aliens, alien_number, number_row):# 创建一个外星人并加入当前行alien = Alien(ai_settings, screen)alien.x = alien.rect.width * (2 * alien_number + 1)alien.y = alien.rect.height * (2 * number_row + 1)alien.rect.x = alien.xalien.rect.y = alien.yaliens.add(alien)def get_number_rows(ai_settings, new_alien, ship):available_space_y = ai_settings.screen_height - ship.rect.width - new_alien.rect.heightreturn int(available_space_y / (2 * new_alien.rect.height))def create_fleet(ai_settings, screen, aliens, ship):"""创建外星人群"""ai_settings.drop = Falseai_settings.fleet_direction = 1new_alien = Alien(ai_settings, screen)number_aliens_x = get_number_aliens(ai_settings, new_alien)number_rows = get_number_rows(ai_settings, new_alien, ship)for number_row in range(number_rows):for alien_number in range(number_aliens_x):create_alien(ai_settings, screen, aliens, alien_number, number_row)# 随机删除for alien in aliens.copy():if len(aliens.sprites()) > 6 and randint(0, 1):aliens.remove(alien)def check_fleet_edges(ai_settings, aliens, ticks):"""有外星人到达边缘的措施"""for alien in aliens.sprites():if alien.check_edge():if ai_settings.drop is False:change_fleet_direction(ai_settings, ticks)breakif ai_settings.drop and \(ticks - ai_settings.drop_ticks) % ai_settings.drop_cycle == 0:change_fleet_direction(ai_settings)breakdef check_aliens_bottom(ai_settings, screen, aliens, bullets, stats, ship, sb):"""有外星人到达底部的措施"""for alien in aliens.sprites():if alien.rect.bottom >= screen.get_rect().bottom:ship_hit(ai_settings, screen, ship, aliens, bullets, stats, sb)breakdef change_fleet_direction(ai_settings, ticks=0):"""改变外星人群的运动方向"""if ticks:ai_settings.drop = Trueai_settings.drop_ticks = tickselse:ai_settings.drop = Falseai_settings.fleet_direction *= -1def update_aliens(ai_settings, screen, aliens, bullets, stats, ticks, ship, sb):"""更新外星人群的位置"""check_fleet_edges(ai_settings, aliens, ticks)# 检查是否有外星人到达底端check_aliens_bottom(ai_settings, screen, aliens, bullets, stats, ship, sb)aliens.update(ai_settings)# 检测外星人与飞船的碰撞if pygame.sprite.spritecollideany(ship, aliens):ship_hit(ai_settings, screen, ship, aliens, bullets, stats, sb)def ship_hit(ai_settings, screen, ship, aliens, bullets, stats, sb):"""响应飞船遭到碰撞后的事件"""if stats.ship_left > 0:# 将ship_left 减一stats.ship_left -= 1# 清空所有子弹和外星人aliens.empty()bullets.empty()# 创建新的外星人群,并将飞船至于中央create_fleet(ai_settings, screen, aliens, ship)ship.center_ship()# 更新记分牌sb.prep_ships()# 暂停sleep(0.5)else:stats.game_active = Falseai_settings.initialiize_dynamic_settings()# 显示光标pygame.mouse.set_visible(True)def check_play_button(stats, play_button, mouse_x, mouse_y, ship, ai_settings,screen, aliens, bullets, sb):"""在玩家单击Play按钮时开始游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 隐藏光标pygame.mouse.set_visible(False)# 重置游戏统计信息stats.reset_stats()stats.game_active = True# 重置分数sb.prep_score()# 重置等级sb.prep_level()# 重置飞船数sb.prep_ships()# 清空外星人列表和子弹列表aliens.empty()bullets.empty()# 创建新的外星人群create_fleet(ai_settings, screen, aliens, ship)ship.center_ship()def check_high_score(stats, sb):"""检查是否出现了新的最高分"""if stats.score > stats.high_score:stats.high_score = stats.scoresb.prep_high_score()save_high_score(stats)def save_high_score(stats):"""将最高分保存入文件"""with open("high_score.json", 'w') as file:json.dump(stats.high_score, file)

ship.py

import pygamefrom bullet import Bulletclass Ship():def __init__(self, screen, ai_settings):"""初始化飞船并设置其初始位置"""super().__init__()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.centerxself.rect.bottom = self.screen_rect.bottom# 移动标志self.moving_right = Falseself.moving_left = Falseself.moving_up = Falseself.moving_down = False# 移动像素self.moving_right_speed = ai_settings.ship_speed_factorself.moving_left_speed = ai_settings.ship_speed_factorself.moving_up_speed = ai_settings.ship_speed_factorself.moving_down_speed = ai_settings.ship_speed_factor# 射击标志self.shooting = Falsedef update(self, bullets, ai_settings, screen, ship, ticks):"""根据移动标志调整飞船状态"""# 移动if self.moving_down and self.rect.bottom < self.screen_rect.bottom:self.rect.centery += self.moving_down_speedif self.moving_up and self.rect.top > 0:self.rect.centery -= self.moving_up_speedif self.moving_right and self.rect.right < self.screen_rect.right:self.rect.centerx += self.moving_right_speedif self.moving_left and self.rect.left > 0:self.rect.centerx -= self.moving_left_speed# 射击if self.shooting and ticks % 15 == 0:self.add_bullets(bullets, ai_settings, screen, ship)def blitme(self):"""指定位置绘制飞船"""self.screen.blit(self.image, self.rect)def add_bullets(self, bullets, ai_settings, screen, ship):# 创建一颗子弹,并将其加入到编组bullets中new_bullet = Bullet(ai_settings, screen, ship)bullets.add(new_bullet)def center_ship(self):self.rect.centerx = self.screen_rect.centerxself.rect.bottom = self.screen_rect.bottom

ship_small.py

import pygame
from pygame.sprite import Spriteclass ShipSmall(Sprite):"""小飞船"""def __init__(self, screen):super().__init__()self.screen = screenself.image = pygame.image.load('images/ship_small.bmp')self.rect = self.image.get_rect()self.rect.top = 10

settings.py

class Settings():"""储存游戏所有的设置"""def __init__(self):self.screen_width = 1500self.screen_height = 900self.bg_color = (0, 0, 15)self.caption = "Alien Invasion 1.0"# 飞船速度self.ship_speed_factor = 1# 飞船数量self.ship_limit = 3# 子弹设置self.bullet_speed = 3self.bullet_width = 3self.bullet_height = 15self.bullet_color = (255, 0, 0)# 外星人左右移动速度设置self.alien_speed_factor = 0.5# 下落速度self.fleet_drop_speed = 0.5# 左右移动方向self.fleet_direction = 1# 下落标志self.drop = False# 下落开始时间(帧数)点self.drop_ticks = 0# 下落时间(帧数)self.drop_cycle = 60# 外星人点数self.alien_points = 50# 动画周期self.animate_cycle = 60# 加快游戏节奏的节奏self.speedup_scale = 1.1self.initialiize_dynamic_settings()def initialiize_dynamic_settings(self):# 飞船速度self.ship_speed_factor = 1# 外星人左右移动速度设置self.alien_speed_factor = 0.5# 下落速度self.fleet_drop_speed = 0.5# 下落时间(帧数)self.drop_cycle = 60# 外星人点数self.alien_points = 50# 动画周期self.animate_cycle = 60def increase_speed(self):"""提高速度设置"""self.ship_speed_factor *= self.speedup_scale# 外星人左右移动速度设置self.alien_speed_factor *= self.speedup_scale# 下落速度self.fleet_drop_speed *= self.speedup_scale# 下落时间(帧数)self.drop_cycle *= self.speedup_scale# 动画周期self.animate_cycle *= self.speedup_scale# 外星人点数self.alien_points = int(self.speedup_scale * self.alien_points)

alien.py

import pygame
from pygame.sprite import Spriteclass Alien(Sprite):"""外星人 类"""def __init__(self, ai_settings, screen):"""初始化外星人并设置其起始位置"""# 类似Ship类的设置super().__init__()self.screen = screenself.ai_settings = ai_settingsself.image = pygame.image.load("images/alien.bmp")self.rect = self.image.get_rect()self.rect.x = self.rect.widthself.rect.y = self.rect.heightself.x = float(self.rect.x)self.y = float(self.rect.y)def update(self, ai_settings):if ai_settings.drop:self.y += self.ai_settings.fleet_drop_speedself.rect.y = self.yelse:self.x += self.ai_settings.alien_speed_factor * ai_settings.fleet_directionself.rect.x = self.xdef check_edge(self):"""如果触碰边缘返回True"""if self.rect.right >= self.screen.get_rect().right or \self.rect.left <= self.screen.get_rect().left:return True

game_stats.py

import jsonclass GameStats():"""统计游戏各类信息"""def __init__(self, ai_settings):"""初始化统计信息"""self.ai_settings = ai_settingsself.reset_stats()# 游戏刚启动时处于非活动状态self.game_active = False# 飞船数量self.ship_left = self.ai_settings.ship_limit# 得分self.score = 0# 最高分self.high_score = 0self.get_high_score()# 难度等级self.level = 1def reset_stats(self):"""初始化运行期间可能变化的统计信息"""self.ship_left = self.ai_settings.ship_limitself.score = 0self.level = 1def get_high_score(self):"""读取最高分"""try:with open("high_score.json") as file:self.high_score = json.load(file)except FileNotFoundError:# 若第一次玩游戏,则创建文件,将 0 写入文件with open("high_score.json", 'w') as file:json.dump(self.high_score, file)

bullet.py

import pygame
from pygame.sprite import Spriteclass 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.centerxself.rect.top = ship.rect.top# 储存用小数表示的子弹位置self.y = float(self.rect.y)self.color = ai_settings.bullet_colorself.speed = ai_settings.bullet_speeddef update(self):"""向上移动子弹"""# 更新表示资单位值的小数值self.y -= self.speedself.rect.y = self.ydef draw_bullet(self):pygame.draw.rect(self.screen, self.color, self.rect)

score_board.py

import pygame.font
from pygame.sprite import Groupfrom ship_small import ShipSmallclass ScoreBoard():"""显示得分"""def __init__(self, ai_settings, screen, stats):self.screen = screenself.screen_rect = screen.get_rect()self.ai_settings = ai_settingsself.stats = stats# 字体设置self.text_color = (255, 255, 255)self.font = pygame.font.SysFont(None, 50, italic=True)# 准备初始得分图像self.prep_score()self.prep_high_score()self.prep_level()self.prep_ships()def prep_score(self):score_str = str(self.stats.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 - 20self.score_rect.top = 10self.prep_ships()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_small.draw(self.screen)def prep_high_score(self):high_score_str = str(self.stats.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.centerxself.high_score_rect.top = 10def 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.top = self.score_rect.bottom + 10self.level_rect.left = self.score_rect.leftdef prep_ships(self):"""显示还剩下多少飞船"""self.ships_small = Group()for ship_number in range(self.stats.ship_left):ship_small = ShipSmall(self.ai_settings)ship_small.rect.left = 10 + ship_number * ship_small.rect.widthself.ships_small.add(ship_small)

但学头陀法

前心何以忘

要开心

要平安

2017 1 11 20:39:10

Python超复古小小小小小游戏项目(终)相关推荐

  1. python超简单小游戏代码-Python实现简单的猜单词小游戏

    本文实例为大家分享了Python实现猜单词小游戏的具体代码,供大家参考,具体内容如下 思路 1.一个words列表里存放若干的单词,例如:["extends", "pri ...

  2. python编程语言创始人-Python简史:开发者的小小副业如何成为全球最热编程语言?...

    原标题:Python简史:开发者的小小副业如何成为全球最热编程语言? 选自TechRepublic 作者:Nick Heath 机器之心编译 参与:魔王.张倩.杜伟 1989 年,Guido van ...

  3. python编程语言-Python简史:开发者的小小副业如何成为全球最热编程语言?

    原标题:Python简史:开发者的小小副业如何成为全球最热编程语言? 选自TechRepublic 作者:Nick Heath 机器之心编译 参与:魔王.张倩.杜伟 1989 年,Guido van ...

  4. 临清有学计算机编程的吗,『临清·第五次主题活动·少儿编程课堂』聊报小记者“小小程序员”活动...

    原标题:『临清·第五次主题活动·少儿编程课堂』聊报小记者"小小程序员"活动 人 工 智 能 少儿编程一般是指针对小学及幼儿阶段的学生设计的编程模式,把原来复杂的英文代码编程语言转换 ...

  5. python掷骰子小游戏

    python掷骰子小游戏 ''' 掷骰子猜大小小游戏 要求: 1.每次充值必须是100的整数倍,且冲100元等于50个游戏币 2.每玩一次游戏扣除两个游戏币,猜对的奖励四个游戏币 ''' import ...

  6. 半天速成Python超简网站

    什么插件我都要最新的,我觉得我能驾驭~ Python写网站还是非常简单的,如果你真的有这方面的想法, 强烈建议阅读<我用Python写网站>系列教程,从0到1建设网站. 半天速成Pytho ...

  7. python超简单超基础的免费小说爬虫

    python超简单超基础的免费小说爬虫 需要准备的环境 选取网页 思路 代码 总结 需要准备的环境 1.python 3.0及以上皆可 2.requests库,os,re 选取网页 找一个免费的小说网 ...

  8. python超轻量级kv数据库dbm

    python超轻量级kv数据库dbm 有一些小的数据需要保存到文件,但也常常要修改.dbm的键值文件存储正好解决了这个问题. 未例代码 #!/usr/bin/env python # -*- codi ...

  9. Python超简单实现跳动爱心代码/opencv/几十行代码/新手也能学会

    Python超简单实现跳动爱心代码/opencv/几十行代码/新手也能学会 1.OpenCV逐帧处理视频获得坐标像素点 从网上寻找带有此同款爱心视频,原视频出处--<点燃我,温暖你>,截取 ...

最新文章

  1. 统治世界的 10 大算法,你知道几个?
  2. 保护了无数医护人员的N95口罩,原来是华裔科学家和一位学生共同发明的!
  3. 轻量级高精度分割网络推荐
  4. window 桌面开发_C#桌面开发的未来WebWindow
  5. 6、函数返回值、this、递归及回调函数
  6. Bootstrap 4:如何使顶部固定的Navbar保持在容器中而不拉伸?
  7. java dom创建xml文件_Java 如何使用dom方式读取和创建xml文件
  8. JDBC中使用PreparedStatement执行SQL语句并管理结果集
  9. 数据库切换为mysql中出现的问题:Error loading MySQLdb module.
  10. 编程之美读书笔记1.1——让CPU占用率曲线听你的指挥
  11. json文件转换成label.png等一系列文件
  12. win10磁盘管理_Win10磁盘管理器:轻松和安全地调整Win10的分区大小
  13. 当前网络安全风险及举例
  14. 《我是北大旁听生/郑球洋》
  15. nodejs爬虫淘宝详情图
  16. 进销存系统_用户信息更新密码修改(3)
  17. 对金钱金额的处理--每三位间隔‘,‘,末尾保留两位小数
  18. 潍职单招试题计算机模拟题,2018年四川省高职单招数学试题.doc
  19. 有效控制物流运输过程成本10项措施
  20. 中国高校计算机大赛--网络技术挑战赛

热门文章

  1. 使用Fetch请求,如何下载二进制流格式的文件
  2. [翻译]利用顶点位移的VR畸变校正
  3. IBM System X服务器操作系统安装
  4. Android进阶 二十四 Android UI---界面开发推荐颜色
  5. 【数字图像处理】二维(2D)线性插值的应用
  6. bootstrap日期插件的使用示例
  7. stm32读取编码器的两种方式
  8. Java 中的 JDK,JRE 和 JVM 有什么区别和联系?
  9. ubuntu查找qt安装的路径_Ubuntu上Qt安装以及配置完整步骤
  10. Android TCP/TP协议