赞
踩
源代码:
AlienInvasion/game at main · CrashBugger/AlienInvasion (github.com)
本文来自作者对《python编程-从入门到实践》的学习记录,刚刚入门python,小白一个,若有错误,欢迎大佬指出。
图片资源:
just nobibi,show me the code!话不多说,开始项目。
导包
- import pygame
- import sys
- import settings
接下来
开始进行初步准备sssssssss
- def 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 == pygame.QUIT:
- sys.exit()
- # 让最近的绘制的屏幕可见
- pygame.display.flip()
display.flip()方法让最近绘制的屏幕可见
先上代码
- def run_game():
- # 初始化游戏并创建一个屏幕对象
- pygame.init()
- screen = pygame.display.set_mode((1200, 800))
- pygame.display.set_caption("Alien Invasion")
- # shezhibeijingse
- bg_color = (230, 230, 230)
-
- # 游戏主循环
- while True:
- # 监视键盘和鼠标时间
- for event in pygame.event.get():
- if event == pygame.QUIT:
- sys.exit()
- # 每次循环重新绘制屏幕
- screen.fill(bg_color)
- # 让最近的绘制的屏幕可见
- pygame.display.flip()
这一步对前一步进行优化,创建一个settings类,用来保存我们的设置
- class Settings():
- def __init__(self):
- self.screen_width = 1200
- self.screen_height = 800
- self.bg_color = (230, 230, 230)
接下来回到主方法创建这个类的实例进行设置
- def run_game():
- # 初始化游戏,并创建一个屏幕对象
- pygame.init()
- ai_settings = settings.Settings()
- screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
- pygame.display.set_caption("Ailien Invasion")
- # bg_color = (230, 230, 230)
- while True:
- # 监听事件
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
- # 每次循环重置屏幕
- screen.fill(ai_settings.bg_color)
- # 最近绘制的屏幕可见
- pygame.display.flip()
创建一个新的文件夹,命名为images,将ship.bmp放进去,接下来创建ship类
- import pygame
-
-
- class Ship():
- def __init__(self, screen):
- self.screen = screen
- # 加载飞船图像并获取外接矩形
- self.image = pygame.image.load("D:\PyCharm\Code\project\game\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)
- def run_game():
- # 初始化游戏,并创建一个屏幕对象
- pygame.init()
- ai_settings = settings.Settings()
- screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
- pygame.display.set_caption("Ailien Invasion")
- # 创建一艘飞船
- ship = Ship(screen)
- while True:
- # 监听事件
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
- # 每次循环重置屏幕
- screen.fill(ai_settings.bg_color)
- ship.blitme()
- # 最近绘制的屏幕可见
- pygame.display.flip()
- import sys
- import pygame
- import ship
-
- from game import settings
-
-
- def check_events():
- """响应按键和鼠标事件"""
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
-
-
- def update_screen(ai_settings: settings.Settings, screen, ship: ship.Ship):
- """更新屏幕上的图像,并切换到新屏幕"""
- # 每次循环chonghuipingmu
- screen.fill(ai_settings.bg_color)
- ship.blitme()
- # 最近绘制的屏幕可见
- pygame.display.filp()
这些都是原本主循环中的方法,现在整合到一个模块
主方法在修改一下
先把刚创建的模块导包
import game_functions as gf
- def run_game():
- # 初始化游戏,并创建一个屏幕对象
- pygame.init()
- ai_settings = settings.Settings()
- screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
- pygame.display.set_caption("Ailien Invasion")
- # 创建一艘飞船
- ship = Ship(screen)
- while True:
- # 监听事件
- gf.check_events()
- # 每次循环重置屏幕
- gf.update_screen(ai_settings, screen, ship)
-
先在ship类中新加一个属性moving_right=False,当检测到移动时,置位true,并新定义update方法,更新飞船位置
- def __init__(self, screen):
- self.screen = screen
- # 加载飞船图像并获取外接矩形
- self.image = pygame.image.load("D:\PyCharm\Code\project\game\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
- # 移动标志
- self.moving_right = False
-
- def blitme(self):
- """在指定位置绘制飞船"""
- self.screen.blit(self.image, self.rect)
-
- def update(self):
- if self.moving_right:
- self.rect.centerx += 1
我们在game_functions.py的check_events方法中加入检测用户键盘的代码
- def check_events(ship: ship.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 = True
- elif event.type == pygame.KEYUP:
- if event.key == pygame.K_RIGHT:
- ship.moving_right = False
当用按下键盘时,我们在event检测其type属性,若为keydown,再进一步判断是否为向右键,并将ship类中的moving——righ属性置为true,当玩家抬起右键时,检测到keyup,将其置为false,
并在主方法中不断调用ship类的update方法
- def run_game():
- # 初始化游戏,并创建一个屏幕对象
- pygame.init()
- ai_settings = settings.Settings()
- screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
- pygame.display.set_caption("Ailien Invasion")
- # 创建一艘飞船
- ship = Ship(screen)
- while True:
- # 监听事件
- gf.check_events(ship)
- # 更新
- ship.update()
- # 每次循环重置屏幕
- gf.update_screen(ai_settings, screen, ship)
Ship类
- class Ship():
- def __init__(self, screen, ai_settings: Settings):
- self.screen = screen
- # 加载飞船图像并获取外接矩形
- self.image = pygame.image.load("D:\PyCharm\Code\project\game\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
- # 移动标志
- self.moving_right = False
- self.moving_left = False
- # 飞船设置
- self.ai_settings = ai_settings
- # 在飞船的center属性中存储小数值
- self.center = float(self.rect.centerx)
-
- def blitme(self):
- """在指定位置绘制飞船"""
- self.screen.blit(self.image, self.rect)
-
- 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 check_keydown_events(event, ship):
- if event.key == pygame.K_RIGHT:
- ship.moving_right = True
- elif event.key == pygame.K_LEFT:
- ship.moving_left = True
-
-
- 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
-
-
- def check_events(ship: ship.Ship):
- """响应按键和鼠标事件"""
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
- # 用户按下键盘
- elif event.type == pygame.KEYDOWN:
- check_keydown_events(event, ship)
- elif event.type == pygame.KEYUP:
- check_keyup_events(event, ship)
bullet.py
- import pygame
- from pygame.sprite import Sprite
-
- from game.settings import Settings
- from game.ship import Ship
-
-
- class Bullet(Sprite):
- def __init__(self, ai_settings: Settings, screen, ship: 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):
- """向上移动子弹"""
- # 更新y坐标
- self.y -= self.speed_factor
- # 覆盖
- self.rect.y = self.y
-
- def draw_bullet(self):
- """在屏幕上绘制子弹"""
- pygame.draw.rect(self.screen, self.color, self.rect)
我们在主方法导包并创建Group()实例,并将其传入while循环中的三个方法
from pygame.sprite import Group
- # 初始化游戏,并创建一个屏幕对象
- pygame.init()
- ai_settings = settings.Settings()
- screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
- pygame.display.set_caption("Ailien Invasion")
- # 创建一艘飞船
- ship = Ship(screen, ai_settings)
- # 创建用于存储子弹的编组
- bullets = Group()
- while True:
- # 监听事件
- gf.check_events(ship, bullets)
- # 更新
- ship.update()
- # 更新子弹位置
- bullets.update()
- # 每次循环重置屏幕
- gf.update_screen(ai_settings, screen, ship, bullets)
- class Bullet(Sprite):
- def __init__(self, ai_settings: Settings, screen, ship: 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):
- """向上移动子弹"""
- # 更新y坐标
- self.y -= self.speed_factor
- # 覆盖
- self.rect.y = self.y
-
- def draw_bullet(self):
- """在屏幕上绘制子弹"""
- pygame.draw.rect(self.screen, self.color, self.rect)
- while True:
- # 监听事件
- gf.check_events(ai_settings, screen, ship, 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)
- def update_bullets(bullets: Bullet):
- """更新子弹未位置"""
- # 更新位置
- bullets.update()
- # 删除已经消失的子弹
- for bullet in bullets.copy():
- if bullet.rect.bottom <= 0:
- bullets.remove(bullet)
-
-
- def fire_bullets(ai_settings: Settings, screen: surface, ship: ship.Ship, bullets: Bullet):
- # 若子弹数量小于3,创建一颗新子弹,
- if len(bullets) < ai_settings.bullets_allowed:
- new_bullet = Bullet(ai_settings, screen, ship)
- bullets.add(new_bullet)
修改其中的keydown函数
- def check_keydown_events(event, ai_settings: Settings, screen, ship, bullets: Bullet):
- 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_bullets(ai_settings, screen, ship, bullets)
我们首先改变一下游戏退出的方式,按下Q键即可退出,在game_functions.py中的key_down方法加上下面一条
- elif event.key == pygame.K_q:
- sys.exit(1)
创建外星人类
- import pygame
- from pygame import surface
- from pygame.sprite import Sprite
-
- from game.settings import Settings
-
-
- class Alien(Sprite):
- """表示单个外星人的类"""
-
- def __init__(self, ai_settings: Settings, screen: surface):
- # 初始化外星人并设置其起始位置
- super(Alien, self).__init__()
- self.screen = screen
- self.ai_settings = ai_settings
- # 加载外星人图像,设置其rect属性
- self.image = pygame.image.load(r"D:\PyCharm\Code\project\game\images\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)
- # 创建外星人实例
- alien = Alien(ai_settings, screen)
并在game_functions中修改update_screen方法
game_functions.py
- def update_screen(ai_settings: Settings, screen, ship: ship.Ship, alien: Alien, bullets: Bullet):
- """更新屏幕上的图像,并切换到新屏幕"""
- # 每次循环重绘屏幕
- screen.fill(ai_settings.bg_color)
- ship.blitme()
- # 绘制外星人
- alien.blitme()
- # 重绘子弹
- for bullet in bullets.sprites():
- bullet.draw_bullet()
- # 最近绘制的屏幕可见
- pygame.display.flip()
- # 创建外星人群
- aliens = Group()
- gf.create_fleet(ai_settings, screen, aliens)
aliens是一个空的编组,前面介绍过,编组可以对其中的每个元素调用方法,并将它传入create_fleet()方法,这个方法在game_functions.py中定义
game_functions.py
- def create_fleet(ai_settings: Settings, screen, aliens: Group):
- """创建外星人群"""
- # 创建一个外星人,并计算一行可以容纳多少个外星人
- alien: 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):
- newalien = Alien(ai_settings, screen)
- newalien.x = alien_width + 2 * alien_width * alien_number
- newalien.rect.x = newalien.x
- aliens.add(newalien)
- def create_fleet(ai_settings: Settings, screen, aliens: Group):
- """创建外星人群"""
- alien = Alien(ai_settings, screen)
- number_aliens_x = get_number_aliens(ai_settings, alien.rect.width, )
- for alien_number in range(number_aliens_x):
- create_alien(ai_settings, screen, aliens, alien_number)
-
-
- def get_number_aliens(ai_settings: Settings, alien_width: int) -> int:
- """获取一行容纳的外星人数量"""
- 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_settings, screen, aliens: Group, alien_number):
- """创建一个外星人并放在当前行"""
- alien = Alien(ai_settings, screen)
- alien_width = alien.rect.width
- alien.x = alien_width + 2 * alien_width * alien_number
- alien.rect.x = alien.x
- aliens.add(alien)
将其中的几个功能提取出来
game_functions.py
- def get_number_rows(ai_settings: Settings, ship_height, alien_height) -> int:
- """计算能容纳多少行"""
- 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 create_fleet(ai_settings: Settings, screen, ship: ship.Ship, aliens: Group):
- """创建外星人群"""
- alien = Alien(ai_settings, screen)
- number_aliens_x = get_number_aliens(ai_settings, alien.rect.width)
- number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
- for alien_number in range(number_aliens_x):
- for alien_row in range(number_rows):
- create_alien(ai_settings, screen, aliens, alien_number, alien_row)
- def create_alien(ai_settings, screen, aliens: Group, alien_number, row_number):
- """创建一个外星人并放在当前行"""
- alien = Alien(ai_settings, screen)
- alien_width = alien.rect.width
- # 当前x
- alien.x = alien_width + 2 * alien_width * alien_number
- # 当前y
- alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
- alien.rect.x = alien.x
- aliens.add(alien)
首先在settings.py中添加设置参数
- # 外星人设置
- self.alien_speed_factor = 0.5#外星人水平移动速度
- self.fleet_drop_speed = 10#外星人竖直移动速度
- self.fleet_direction = 1 #移动方向: 1为向右,-1向左
- def update(self):
- """向左向右移动外星人"""
- self.x += self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction
- self.rect.x = self.x
-
- def check_edges(self):
- """如果外星人到达边缘,返回True"""
- screen_rect = self.screen.get_rect()
- if self.rect.right >= screen_rect.right:
- return True
- elif self.rect.left <= 0:
- return True
- def change_fleet_direction(ai_settings, aliens):
- """将外星人整体下移,并改变他们方向"""
- for alien in aliens.sprites():
- alien.rect.y += ai_settings.fleet_drop_speed
- ai_settings.fleet_direction *= -1
-
-
- def check_fleet_edges(ai_settings: Settings, aliens):
- """有外星人到达边缘时采取相应的措施"""
- for alien in aliens.sprites():
- if alien.check_edges():
- change_fleet_direction(ai_settings, aliens)
- break
-
-
- def update_aliens(ai_settings, aliens):
- """检查是否有外星人位于屏幕边缘,并更新调整外星人位置"""
- check_fleet_edges(ai_settings, aliens)
- aliens.update()
- # 更新aliens
- gf.update_aliens(ai_settings, aliens)
我们需要调用pygame.sprite.groupcollied方法,这个方法检测两个编组中的元素是否重合,重合的话进行处理,并返回一个字典,每个键值对为子弹-外星人。其中第一个True表示重合的话删除第一个编组中的元素,第二个true表示删除第二个编组中的元素。我们在更新子弹位置时调用这个方法,并增加主方法中的参数调用
- def update_bullets(ai_settings, screen, ship, aliens: Alien, bullets: Sprite):
- """更新子弹未位置"""
- # 更新位置
- 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)
-
-
- 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, ship, aliens)
如果飞船与外星人相撞或者外星人到底部,我们让游戏重置,重新生成外星人,并将飞船居中
ship.py
- def center_ship(self):
- """让飞船居中"""
- self.center = self.screen_rect.centerx
self.ship_limit = 3
- class GameStatus():
- """跟踪游戏的统计信息"""
-
- def __init__(self, ai_settings):
- """初始化统计的信息"""
- self.ai_settings = ai_settings
- self.reset_status()
-
- def reset_status(self):
- """初始化在游戏运行期间可能变化的信息"""
- self.ships_left = self.ai_settings.ship_limit
game_functions.py
- def update_aliens(ai_settings, stats, screen, ship, bullets, aliens):
- """检查是否有外星人位于屏幕边缘,并更新调整外星人位置"""
- check_fleet_edges(ai_settings, aliens)
- aliens.update()
- # 检测外星人和飞船是否相撞
- if pygame.sprite.spritecollideany(ship, aliens):
- ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
- # 检查外星人是否撞到底部
- check_aliens_bottom(aliens, ai_settings, stats, screen, ship, bullets)
-
-
- def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets):
- """响应外星人撞到飞船"""
- # 将飞船数量ship_left减一
- status.ships_left -= 1
- # 清空外星人和子弹
- aliens.empty()
- bullets.empty()
- # 创建一群新的外星人,重置飞船
- create_fleet(ai_settings, screen, ship, aliens)
- ship.center_ship()
- # 暂停
- sleep(0.5)
-
-
- def check_aliens_bottom(aliens: Alien, ai_settings: Settings, stats: GameStatus,
- screen: Surface, ship: ship.Ship, bullets):
- """检查是否有外星人到达底部"""
- screen_rect = screen.get_rect()
- for alien in aliens:
- if alien.rect.bottom >= screen_rect.bottom:
- # 像飞船撞到飞船一样处理
- ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
- break
我们调用update_aliens方法时,顺便检查一下飞船与外星人是否相撞,通过
pygame.sprite.spritecollideany(ship, aliens)
方法,第一个参数为一个飞船对象,第二个参数为一个编组,
game_functions.py的__ini__t中加入
- # 游戏刚启动处于活动状态
- self.game_active = True
- def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets):
- """响应外星人撞到飞船"""
- # 将飞船数量ship_left减一
- if status.ships_left > 0:
- status.ships_left -= 1
- # 清空外星人和子弹
- aliens.empty()
- bullets.empty()
- # 创建一群新的外星人,重置飞船
- create_fleet(ai_settings, screen, ship, aliens)
- ship.center_ship()
- # 暂停
- sleep(0.5)
- else:
- status.game_active = False
- while True:
- # 监听事件
- gf.check_events(ai_settings, screen, ship, bullets)
- if stats.game_active:
- # 更新
- ship.update()
- # 更新子弹
- gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
- # 更新aliens
- gf.update_aliens(ai_settings, stats, screen, ship, bullets, aliens)
- # 每次循环重置屏幕
- gf.update_screen(ai_settings, screen, ship, aliens, bullets)
- import pygame.font
- class Button():
- def __init__(self, ai_settings: Settings, screen: Surface, 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):
- """将msg渲染为图像,并将其在按钮上居中"""
- self.msg_image = self.font.render(self, 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)
- def update_screen(ai_settings: Settings, screen, stats: GameStatus, ship: ship.Ship, aliens: Group, bullets: Bullet,
- play_button: Button):
- """更新屏幕上的图像,并切换到新屏幕"""
- # 每次循环重绘屏幕
- screen.fill(ai_settings.bg_color)
- 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()
主方法中创建按钮实例,并将其传入update_screen方法中
- # 创建play按钮
- play_button = Button(ai_settings, screen, "Play")
- def check_events(ai_settings, screen, ship, bullets, stats: GameStatus, play_button: Button):
- """响应按键和鼠标事件"""
- for event in pygame.event.get():
- # 用户按下键盘
- if event.type == pygame.KEYDOWN:
- check_keydown_events(event, ai_settings, screen, ship, bullets)
- elif event.type == pygame.KEYUP:
- check_keyup_events(event, ship)
- elif event.type == pygame.K_SPACE:
- fire_bullets(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: GameStatus, play_button: Button, mouse_x, mouse_y):
- """在玩家单击play时开始游戏"""
- if play_button.rect.collidepoint(mouse_x, mouse_y):
- stats.game_active = True
其中mouse.get_pos返回一个元组,表示鼠标点击的坐标,我们用play_button.rect.collidepoint方法判断是否与按钮坐标重合
接下来重置游戏,因为上面仅对第一次点击有用
再次修改check_lpay_button方法
- def check_play_button(ai_settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens, ship,
- bullets):
- """在玩家单击play时开始游戏"""
- if play_button.rect.collidepoint(mouse_x, mouse_y):
- # 重置游戏并统计信息
- stats.reset_status()
- stats.game_active = True
- # 清空外星人和子弹的列表
- bullets.empty()
- aliens.empty()
- # 创建外星人,并让飞船居中
- create_fleet(ai_settings, screen, ship, aliens)
- ship.center_ship()
我们新加了这个方法的参数,不要忘了在调用这个方法的位置加上(pycharm中ctrl+alt+h可以查看调用次方法的方法)
现在还是有个问题,如果玩家在点击按钮后再次点击,会出现再次重置的情况,所以我们再加入对stats.game_active的判断,同时再让点击后光标不可见
- def check_play_button(ai_settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens, ship,
- bullets):
- """在玩家单击play时开始游戏"""
- button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
- if button_clicked and not stats.game_active:
- #让光标不可见
- pygame.mouse.set_visible(False)
- # 重置游戏并统计信息
- stats.reset_status()
- stats.game_active = True
- # 清空外星人和子弹的列表
- bullets.empty()
- aliens.empty()
- # 创建外星人,并让飞船居中
- create_fleet(ai_settings, screen, ship, aliens)
- ship.center_ship()
修改ship_hit方法,让游戏重置时,鼠标可见
- def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets):
- """响应外星人撞到飞船"""
- # 将飞船数量ship_left减一
- if status.ships_left > 0:
- status.ships_left -= 1
- # 清空外星人和子弹
- aliens.empty()
- bullets.empty()
- # 创建一群新的外星人,重置飞船
- create_fleet(ai_settings, screen, ship, aliens)
- ship.center_ship()
- # 暂停
- sleep(0.5)
- else:
- status.game_active = False
- pygame.mouse.set_visible(True)
- class Settings():
- def __init__(self):
- """ 初始化游戏时的静态设置"""
- self.screen_width = 1200
- self.screen_height = 800
- self.bg_color = (230, 230, 230)
- # 飞船设置
- self.ship_speed_factor = 1.0
- self.ship_limit = 3
- # 子弹设置
- self.bullet_speed_factor = 0.7
- self.bullet_width = 3
- self.bullet_height = 15
- self.bullet_color = 60, 60, 60
- # 限制子弹数量
- self.bullets_allowed = 3
- # 外星人设置
- self.alien_speed_factor = 0.5 # 外星人水平移动速度
- self.fleet_drop_speed = 10 # 外星人竖直移动速度
- self.fleet_direction = 1 # 移动方向: 1为向右,-1向左
- # 以什么样的速度加快游戏节奏
- self.speedup_scale = 1.1
- self.initialize_dynamic_settings()
-
- def initialize_dynamic_settings(self):
- """初始化游戏进行而变化的设置"""
- self.ship_speed_factor = 1.5
- self.bullet_speed_factor = 3
- self.alien_speed_factor = 1
- self.fleet_direction = 1
-
- def increase_speed(self):
- """提高速度设置"""
- self.ship_speed_factor *= self.speedup_scale
- self.alien_speed_factor *= self.speedup_scale
- self.alien_speed_factor *= self.speedup_scale
增加了一个倍率属性和两个方法
- def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
- ship,
- bullets):
- """在玩家单击play时开始游戏"""
- button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
- if button_clicked and not stats.game_active:
- #重置游戏节奏
- ai_settings.initialize_dynamic_settings()
- 。。。。。。。。。。。。。。。。。。。。。。。
接下来修改
game_functions.py中的check_bullet_alien_collision
- def check_bullet_alien_collision(ai_settings: 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, ship, aliens)
- def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
- ship,
- bullets):
- """在玩家单击play时开始游戏"""
- button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
- if button_clicked and not stats.game_active:
- #重置游戏节奏
- ai_settings.initialize_dynamic_settings()
- def reset_status(self):
- """初始化在游戏运行期间可能变化的信息"""
- self.ships_left = self.ai_settings.ship_limit
- self.score = 0
新建scoreboard.py模块,并创建Scoreboard类:
- class Scoreboard():
- """显示得分信息的类"""
-
- def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
- """初始化得分涉及的属性"""
- self.screen = screen
- self.screen_rect = screen.get_rect()
- self.ai_settings = ai_settings
- self.status = status
- # 显示得分时的字体设置
- self.text_color = (30, 30, 30)
- self.font = pygame.font.SysFont(None, 48)
- # 准备初始得分图像
- self.prep_score()
-
- def prep_score(self):
- """将得分转换为渲染图像"""
- score_str = str(self.status.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 = 20
-
- def show_score(self):
- """在屏幕上显示得分"""
- self.screen.blit(self.score_image, self.score_rect)
- # 创建一个记分牌
- sb = Scoreboard(ai_settings, screen, stats)
并将其传入循环中的update_screen方法中
game_functions.py
- def update_screen(ai_settings: Settings, screen, stats: GameStatus, ship: ship.Ship, aliens: Group, bullets: Bullet,
- play_button: Button, sb: Scoreboard):
- """更新屏幕上的图像,并切换到新屏幕"""
- 。。。。。。。。。。。。。。
- # 显示得分
- sb.show_score()
- # 最近绘制的屏幕可见
- pygame.display.flip()
接下来我们开始增加分数
settings.py中加入分数设置
- def initialize_dynamic_settings(self):
- """初始化游戏进行而变化的设置"""
- self.ship_speed_factor = 1.5
- self.bullet_speed_factor = 0.7
- self.alien_speed_factor = 0.7
- self.fleet_direction = 1
- # 计分
- self.alien_points = 50
- def check_bullet_alien_collision(ai_settings: Settings, screen, ship, aliens, bullets, stats: GameStatus,
- sb: Scoreboard):
- # 检查是否有子弹击中外星人
- 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()
- # 更新外星人
- if len(aliens) == 0:
- bullets.empty()
- # 增加游戏节奏
- ai_settings.increase_speed()
- create_fleet(ai_settings, screen, ship, aliens)
其中collisions是一个字典,我们拿到他的值,这个值是一个列表,其中包括我们一颗子弹打掉的全部飞船(不排除一箭双雕),所以求出列表长度来算分
不要忘了在形参中加入新的值并修改调用此方法传入的参数
接下来我们设置游戏难度提高后的点数
settings.py
- class Settings():
- def __init__(self):
- 。。。。。。。。。。。。。。
- # 点数提高速度
- self.score_scale = 1.5
-
- def increase_speed(self):
- 。。。。。。。。。。。。。。
- """提高点数"""
- self.alien_points = int(self.alien_points * self.score_scale)
为了让记分牌跟以前那种街机风格的记分牌一样,每三位用“,”隔开,我们修改prep_score方法
scoreboard.py
- def prep_score(self):
- """将得分转换为渲染图像"""
- # 将得分圆整
- rounded_score = int(round(self.status.score, -1))
- score_str = "{:,}".format(rounded_score)
- # score_str = str(self.status.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 = 20
其中round()中的-1表示我们向10取整
- def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
- """初始化得分涉及的属性"""
- 。。。。。。。。。。。。
- self.prep_high_score()
-
- def show_score(self):
- """在屏幕上显示得分"""
- 。。。。。。。。。。。。。。。。。
- self.screen.blit(self.high_score_image, self.high_score_rect)
-
- def prep_high_score(self):
- """将最高得分转换为渲染的图像"""
- high_score = int(round(self.status.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 = self.score_rect.top
接下来我们判断是否诞生了最高分
game_functions.py加入
- def check_high_score(stats: GameStatus, sb: Scoreboard):
- """检查是否诞生了最高分"""
- if stats.high_score < stats.score:
- stats.high_score = stats.score
- sb.prep_high_score()
我们需要在check_bullet_collision中调用分数判断
- def check_bullet_alien_collision(ai_settings: Settings, screen, ship, aliens, bullets, stats: GameStatus,
- sb: Scoreboard):
- # 检查是否有子弹击中外星人
- 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)
- 。。。。。。。。。。。。。。。
- def reset_status(self):
- """初始化在游戏运行期间可能变化的信息"""
- 。。。。。。。。。。。。。
- self.level = 1
scoreboard.py
- def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
- """初始化得分涉及的属性"""
- 。。。。。。。。。。。。。。。。
- self.prep_level()
- def show_score(self):
- """在屏幕上显示得分"""
- 。。。。。。。。。。。。。。。。。。
- self.screen.blit(self.level_image, self.level_rect)
- def prep_level(self):
- """将等级渲染为图像"""
- self.level_image
- self.level_image = self.font.render(str(self.status.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
game_functions.py中修改
- def check_bullet_alien_collision(ai_settings: Settings, screen, ship, aliens, bullets, stats: GameStatus,sb: Scoreboard):
- 。。。。。。。。。。。。。。。。。。。。。。。。。
- # 更新外星人
- if len(aliens) == 0:
- bullets.empty()
- # 增加游戏节奏
- ai_settings.increase_speed()
- # 如果外星人都被消灭,就提高一个等级
- stats.level += 1
- sb.prep_level()
- create_fleet(ai_settings, screen, ship, aliens)
为了每次点击play按钮时重置记分牌,我们修改
- def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
- ship, bullets, sb: Scoreboard, ):
- """在玩家单击play时开始游戏"""
- ...........................................
- # 重置得分牌图像
- sb.prep_score()
- sb.prep_high_score()
- sb.prep_level()
- # 清空外星人和子弹的列表
- bullets.empty()
- aliens.empty()
- # 创建外星人,并让飞船居中
- create_fleet(ai_settings, screen, ship, aliens)
- ship.center_ship()
我们新传进来了个参数sb(ScoreBoard类)
- class Ship(Sprite):
- def __init__(self, screen, ai_settings: Settings):
- """初始化飞船,并设置其起始位置"""
- super(Ship, self).__init__()
- 。。。。。。。。。。。。。。。。。。。。。。。。。。。。
让飞船继承sprite类,这样我们可以加入编组中管理
scoreboard.py类修改
-
- class Scoreboard():
- """显示得分信息的类"""
-
- def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
- """初始化得分涉及的属性"""
- 。。。。。。。。。。。。。。
- self.prep_ships()
-
-
- def show_score(self):
- """在屏幕上显示得分"""
- 。。。。。。。。。。。。。。。。。。
- self.ships.draw(self.screen)
-
-
-
- def prep_ships(self):
- """显示还剩下多少飞船"""
- self.ships = Group()
- for ship_number in range(self.status.ships_left):
- ship = Ship(self.screen, self.ai_settings)
- ship.rect.x = 10 + ship_number * ship.rect.width
- ship.rect.y = 10
- self.ships.add(ship)
game_functions.py修改,调用prep_ships方法
- def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
- ship, bullets, sb: Scoreboard, ):
- """在玩家单击play时开始游戏"""
- 。。。。。。。。。。。。。。。。。。
- # 重置得分牌图像
- sb.prep_score()
- sb.prep_high_score()
- sb.prep_level()
- sb.prep_ships()
- # 清空外星人和子弹的列表
- bullets.empty()
- aliens.empty()
- # 创建外星人,并让飞船居中
- create_fleet(ai_settings, screen, ship, aliens)
- ship.center_ship()
接下来我们在飞船撞到外星人时更新ships编组
- def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets, sb: Scoreboard):
- """响应外星人撞到飞船"""
- # 将飞船数量ship_left减一
- if status.ships_left > 0:
- status.ships_left -= 1
- # 更新记分牌
- sb.prep_ships()
- 。。。。。。。。。。。。。。。。。。
我们又加了一个sb参数,按ctrl+alt+h查看调用此方法的函数,依次修改函数形参
最终结果
完结撒花,本来没想到需要这么多字,打的我手疼(哭)。。。。没有辛劳也有苦劳,祝点赞的各位看官bug少少,头发多多~~~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。