Python实现飞机大战的新手练习项目。
目前看了一个月左右的Python入门教程,推荐图灵教材《Python编程 - 从入门到实践》,看完基础的语法部分,可以开始做一些小项目的练手,进一步巩固基础知识。
飞机大战是一个有趣而又可以综合运用入门语法的项目。该项目非常能训练入门时的逻辑思维,对初级的语法有相对全面的运用。尤其是首次学习中遇到的“类”,对于自己这种编程新手来说比较抽象,该项目有很多类的使用,跟着教材进行编写,同时融入自己的创意,一个项目下来,能基本掌握类的使用。
现公布自己编写的源代码如下,与有兴趣学习的朋友共享。
启动游戏画面如下:https://www.zhihu.com/video/1221594693538430976
模块一:主程序 ——综合调用各子模块
import pygame
from setting import Setting
from rocket import Rocket
import game_functions as gf
from pygame.sprite import Group
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboard
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init()
game_setting = Setting()
screen = pygame.display.set_mode((game_setting.width, game_setting.height))
pygame.display.set_caption("UFO Fight......Developed by Hansen")
play_button = Button(screen, "PLAY GAME")
# 创建一艘火箭
rocket = Rocket(screen, game_setting)
# 创建存储子弹的编组
bullets = Group()
# 创建外星人
aliens = Group()
# 创建一个用于存储统计信息的实例, 并创建记分牌
stats = GameStats(game_setting)
sb = Scoreboard(game_setting, screen, stats)
# 开始游戏主循环
while True:
gf.check_events(rocket, game_setting, screen, bullets, stats, play_button,
aliens, sb)
if stats.game_active:
rocket.update()
gf.update_bullet(rocket, bullets, aliens, stats, game_setting, sb)
gf.update_alien(aliens, rocket, stats, bullets, game_setting, screen,
sb)
# 设定创建一个alien的速率
stats.program_run += 1
if stats.program_run % game_setting.alien_generate == 0:
gf.create_alien(rocket, game_setting, screen, aliens, stats)
gf.update_screen(screen, game_setting, rocket, bullets, aliens, stats,
play_button, sb)
run_game()
模块二:创建可以自由移动的飞船类(程序中命名为Rocket)
import pygame
from pygame.sprite import Sprite
class Rocket(Sprite):
def __init__(self, screen, game_setting):
"""初始化飞船"""
super().__init__()
self.screen = screen
self.game_setting = game_setting
# 加载火箭图片并获得外接矩形
self.image = pygame.image.load('images/rocket.png')
self.rect = self.image.get_rect()
self.screen_rect = self.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.moving_up = False
self.moving_down = False
# 在火箭的属性center中存储小数值
self.center = float(self.rect.centerx)
# 在火箭的属性bottom中存储小数值
self.bottom = float(self.rect.bottom)
def update(self):
# 控制左右移动并限制移动边界
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.game_setting.rocket_speed
if self.moving_left and self.rect.left > 0:
self.center -= self.game_setting.rocket_speed
# 控制上下移动并限制移动边界
if self.moving_up and self.rect.top > 0:
self.bottom -= self.game_setting.rocket_speed
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.bottom += self.game_setting.rocket_speed
# 根据self.center更新rect对象
self.rect.centerx = self.center
# 根据self.middle更新rect对象
self.rect.bottom = self.bottom
def blitme(self):
self.screen.blit(self.image, self.rect)
def set_center(self):
"""让飞船在屏幕上居中"""
self.center = self.screen_rect.centerx
self.bottom = self.screen_rect.bottom
模块三:创建准备从飞船发射的子弹类
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, game_setting, rocket, screen):
super().__init__()
self.screen = screen
# 在(0,0)位置创建表示子弹的矩形,并设置其位置到火箭发射处
self.rect = pygame.Rect(0, 0, game_setting.bullet_width,
game_setting.bullet_height)
self.rect.centerx = rocket.rect.centerx
self.rect.top = rocket.rect.top
# 加载子弹颜色和速度参数
self.color = game_setting.bullet_color
self.speed = game_setting.bullet_speed
# 在子弹的Y轴方向设置浮点型存储值,可以更精准移动
self.Y = float(self.rect.top)
# 管理子弹的位置
def update(self):
self.Y -= self.speed
# 更新子弹在Y方向的位置
self.rect.top = self.Y
# 在屏幕中画出子弹
def draw_bullet(self):
pygame.draw.rect(self.screen, self.color, self.rect)
模块四:创建随机从屏幕上出现的外星飞船类
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
def __init__(self, rocket, game_setting, screen, stats):
super().__init__()
self.screen = screen
self.game_setting = game_setting
self.stats = stats
# 加载Alien图片并获得外接矩形
self.image = pygame.image.load('images/alien.png')
self.rect = self.image.get_rect()
# 加载alien移动速度
self.speed = game_setting.alien_speed
# 绘制alien, 让其在水平轴线上随机出现,与画面上边框距离为火箭的高度
self.rect.x = self.rect.width
self.rect.y = 50
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def update(self):
# 更新alien在Y轴的位置
self.y += self.speed * \
pow(self.game_setting.speedup_scale, (self.stats.game_level - 1))
self.rect.y = self.y
def blitme(self):
# 在指定位置绘制alien
self.screen.blit(self.image, self.rect)
模块五:创建游戏开始和结束时出现在屏幕的启动按钮
import pygame.ftfont
class Button():
def __init__(self, screen, msg):
"""初始化按钮属性"""
self.screen = screen
self.screen_rect = screen.get_rect()
# 设置按钮的属性
self.width, self.height = 220, 50
self.button_color = (65, 65, 65)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
# 创建按钮并在屏幕居中
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
# 按钮标签只需创建一次
self.prep_msg(msg)
def prep_msg(self, msg):
"""将msg渲染为图像,并使其在按钮上居中"""
self.msg_image = self.font.render(msg, True, self.text_color,
self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
# 绘制一个用颜色填充的按钮,再绘制文本
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
模块六:创建用于记录游戏中得分的信息库
import pygame.ftfont
class Scoreboard():
"""显示得分信息的类"""
def __init__(self, game_setting, screen, stats):
"""初始化得分涉及的属性"""
self.game_setting = game_setting
self.stats = stats
self.screen = screen
self.screen_rect = screen.get_rect()
self.max_point = 0
# 显示得分信息时使用的字体设置
self.text_color = (30, 30, 30)
self.text_color_maxpoint = (125, 215, 60)
self.font = pygame.font.SysFont(None, 36)
self.font_rocket_left = pygame.font.SysFont(None, 60)
# 渲染得分的图像
self.prep_score()
# 显示游戏等级
self.prep_game_level()
def prep_score(self):
"""将得分转化为一幅渲染的图像"""
# 显示前置"Point: "
score_prompt = "Point: " + str(self.stats.score)
self.score_prompt_image = self.font.render(score_prompt, True,
self.text_color, self.game_setting.bg_color)
self.score_prompt_rect = self.score_prompt_image.get_rect()
self.score_prompt_rect.right = self.screen_rect.right - 10
self.score_prompt_rect.top = 0
# 在屏幕上显示得分
self.screen.blit(self.score_prompt_image, self.score_prompt_rect)
def prep_game_level(self):
"""将游戏等级显示在画面中"""
# 显示前置"Level: "
level_prompt = "Level: " + str(self.stats.game_level)
self.level_prompt_image = self.font.render(level_prompt, True,
self.text_color,
self.game_setting.bg_color)
self.level_prompt_rect = self.level_prompt_image.get_rect()
self.level_prompt_rect.right = self.screen_rect.right - 10
self.level_prompt_rect.top = self.score_prompt_rect.bottom + 5
# 在屏幕上显示游戏等级
self.screen.blit(self.level_prompt_image, self.level_prompt_rect)
def prep_max_point(self):
"""在屏幕上显示最高得分"""
# 设置属性
self.max_point_str = "Highest Point: " + str(self.max_point)
self.max_point_str_image = self.font.render(self.max_point_str, True,
self.text_color_maxpoint, self.game_setting.bg_color)
# 将得分放在屏幕右上角
self.max_point_str_rect = self.max_point_str_image.get_rect()
self.max_point_str_rect.x = self.screen_rect.centerx - 100
self.max_point_str_rect.y = 0
# 绘制最高得分
self.screen.blit(self.max_point_str_image, self.max_point_str_rect)
模块七:创建用于程序中各项参数的设定
class Setting():
"""存储游戏所有的设置类"""
def __init__(self):
# 游戏屏幕设置
self.width = 700
self.height = 620
self.bg_color = (255, 255, 255)
# 火箭移动速度设置
self.rocket_speed = 0.8
# 剩余火箭数量
self.rocket_limit = 3
# 子弹参数设置
self.bullet_width = 3
self.bullet_height = 12
self.bullet_speed = 0.8
self.bullet_color = (60, 60, 60)
self.qty_allowed = 4
# Alien的设置
# alien 向下移动速度
self.alien_speed = 0.04
# 生成alien的速度
self.alien_generate = 600
# 以什么样的速度加快alien的节奏
self.speedup_scale = 1.1
# 击中每个alien的得分
self.alien_points = 50
模块八:存储游戏过程中各变量的状态
class GameStats():
"""跟踪游戏的统计信息"""
def __init__(self, game_setting):
"""初始化统计信息"""
self.game_setting = game_setting
self.reset_stats()
self.program_run = 0
self.aliens_kill = 0
def reset_stats(self):
"""初始化游戏运行期间可能变化的统计信息"""
self.rocket_left = self.game_setting.rocket_limit
self.score = 0
self.game_level = 1
self.aliens_kill = 0
# 刚启动时游戏处于活动状态
self.game_active = False
self.program_run = 0
模块九:存储游戏主程序中调用的各函数及其子函数
import sys
import pygame
from bullet import Bullet
from alien import Alien
import random
from time import sleep
import math
import json
def check_events(rocket, game_setting, screen, bullets, stats, play_button,
aliens, sb):
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
# 关闭游戏窗口时,记录最高得分
max_point_write(sb)
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(rocket, event, game_setting, screen, bullets,
stats, sb)
elif event.type == pygame.KEYUP:
check_keyup_events(rocket, event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
check_play_button(stats, play_button, aliens, bullets, rocket,
game_setting, screen, mouse_x, mouse_y)
# 单击开始游戏时,加载最高得分
max_point_load(sb)
def check_play_button(stats, play_button, aliens, bullets, rocket, game_setting,
screen, mouse_x, mouse_y):
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
# 当游戏处于静态时,才可以check button状态
if not stats.game_active and button_clicked:
# 隐藏光标
pygame.mouse.set_visible(False)
if play_button.rect.collidepoint(mouse_x, mouse_y):
# 重置游戏统计信息
stats.reset_stats()
stats.game_active = True
# 清空alien列表和bullet列表
aliens.empty()
bullets.empty()
# 创建新的aliens,并让rocket居中
create_alien(rocket, game_setting, screen, aliens, stats)
rocket.set_center()
def check_keydown_events(rocket, event, game_setting, screen, bullets, stats, sb):
if event.key == pygame.K_RIGHT:
rocket.moving_right = True
elif event.key == pygame.K_LEFT:
rocket.moving_left = True
elif event.key == pygame.K_UP:
rocket.moving_up = True
elif event.key == pygame.K_DOWN:
rocket.moving_down = True
elif event.key == pygame.K_SPACE:
if len(bullets) < game_setting.qty_allowed + stats.game_level:
new_bullet = Bullet(game_setting, rocket, screen)
bullets.add(new_bullet)
elif event.key == pygame.K_q:
# 按'q'键退出时,记录最高得分
max_point_write(sb)
sys.exit()
def check_keyup_events(rocket, event):
if event.key == pygame.K_RIGHT:
rocket.moving_right = False
elif event.key == pygame.K_LEFT:
rocket.moving_left = False
elif event.key == pygame.K_UP:
rocket.moving_up = False
elif event.key == pygame.K_DOWN:
rocket.moving_down = False
def update_screen(screen, game_setting, rocket, bullets, aliens, stats,
play_button, sb):
# 每次循环时重绘屏幕
screen.fill(game_setting.bg_color)
rocket.blitme()
for bullet in bullets.sprites():
bullet.draw_bullet()
aliens.draw(screen)
# 显示得分
sb.prep_score()
# 显示最高得分
check_largest_point(stats, sb)
# 显示游戏等级
sb.prep_game_level()
# 绘制剩余rocket
prep_rocket(stats, sb, game_setting, screen)
# 如果游戏处于非活动状态,就绘制按钮
if not stats.game_active:
play_button.draw_button()
# 提升游戏难度等级
level_up(stats, aliens, bullets, rocket, game_setting, screen)
# 绘制屏幕并显示
pygame.display.flip()
def update_bullet(rocket, bullets, aliens, stats, game_setting, sb):
# 更新每一颗子弹的位置
bullets.update()
# 删除消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom < rocket.rect.height:
bullets.remove(bullet)
# 检测子弹是否击中alien,若击中,则删除相应的子弹和alien
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
# 若子弹击中alien,则计入加分
if collisions:
stats.score += game_setting.alien_points
sb.prep_score()
stats.aliens_kill += 1
def update_alien(aliens, rocket, stats, bullets, game_setting, screen, sb):
# 更新alien在Y轴方向的位置
aliens.update()
if pygame.sprite.spritecollideany(rocket, aliens):
rocket_hit(stats, aliens, bullets, rocket, game_setting, screen)
check_alien_bottom(stats, aliens, bullets, rocket, game_setting, screen)
def create_alien(rocket, game_setting, screen, aliens, stats):
alien = Alien(rocket, game_setting, screen, stats)
available_space_x = game_setting.width - 2 * alien.rect.width
number_alien_x = math.floor(available_space_x / (alien.rect.width * 1.5))
# 创建在X轴方向上随机出现的alien
alien = Alien(rocket, game_setting, screen, stats)
index = random.randint(0, number_alien_x)
alien.x = alien.rect.width + (alien.rect.width * 1.5) * index
alien.rect.x = alien.x
aliens.add(alien)
def prep_rocket(stats, sb, game_setting, screen):
rocket_pits = 'A'
for rocket in range(stats.rocket_left):
for rocket_pit in rocket_pits:
rocket_pit_image = sb.font_rocket_left.render(rocket_pit, True,
sb.text_color,
game_setting.bg_color)
rocket_pit_image_rect = rocket_pit_image.get_rect()
rocket_pit_image_rect.x = (rocket_pit_image_rect.width + 5) * rocket
rocket_pit_image_rect.y = 0
screen.blit(rocket_pit_image, rocket_pit_image_rect)
def rocket_hit(stats, aliens, bullets, rocket, game_setting, screen):
if stats.rocket_left > 0:
# alien和rocket撞击后,rocket的数量减1
stats.rocket_left -= 1
# 清空外星人和子弹列表
aliens.empty()
bullets.empty()
# 暂停1秒
sleep(1)
# 重新创建alien
create_alien(rocket, game_setting, screen, aliens, stats)
# 重新创建飞船
rocket.set_center()
# 单个等级击杀的alien清零
stats.aliens_kill = 0
else:
stats.game_active = False
pygame.mouse.set_visible(True)
def check_alien_bottom(stats, aliens, bullets, rocket, game_setting, screen):
"""侦测是否有alien达到屏幕底端"""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# 若达到屏幕底端,则当作与rocket撞击
rocket_hit(stats, aliens, bullets, rocket, game_setting, screen)
break
def level_up(stats, aliens, bullets, rocket, game_setting, screen):
if stats.aliens_kill >= 30:
# 等级数字提升
stats.game_level += 1
# 清空外星人和子弹列表
aliens.empty()
bullets.empty()
# 重新创建alien
create_alien(rocket, game_setting, screen, aliens, stats)
# 重新创建飞船
rocket.set_center()
# 暂停1秒
sleep(1)
# 单个等级击杀的alien清零
stats.aliens_kill = 0
def check_largest_point(stats, sb):
"""检查是否诞生了最高分"""
if sb.max_point < stats.score:
sb.max_point = stats.score
sb.prep_max_point()
def max_point_write(sb):
"""记录最高得分到文件中"""
filename = 'max_point.json'
with open(filename, 'w') as f_object:
json.dump(sb.max_point, f_object)
def max_point_load(sb):
"""从文件中加载最高得分"""
filename = 'max_point.json'
with open(filename) as f_object:
sb.max_point = json.load(f_object)
程序完成。