读万卷书,行万里路——木子成
基于Python中pygame模块的二维游戏
a:向左移动
d:向右移动
空格:暂停
游戏速度会根据游戏得分加速
extreme_speed.py
import pygame
import function as fun
from settings import Settings
from pygame.sprite import Group
from car import Car
from impact_effects import ImpactEffects
from autodrome import Line
from game_button import GameButton
from score import ScoreCard
def run_game():
"""游戏主干部分"""
pygame.init() # 初始化游戏并且创建一个游戏界面
pygame.display.set_caption("极限飞驰") # 显示在游戏界面左上角
game_settings = Settings() # 设置类,包含绝大部分参数
# 生成主屏幕
screen = pygame.display.set_mode((game_settings.screen_width, game_settings.screen_height))
car = Car(screen, game_settings) # 飞车类
impact_effects = ImpactEffects(screen, game_settings) # 撞车效果类
active_line = Line(screen, game_settings) # 赛道实线类
button = GameButton(game_settings, screen) # 游戏开始、结束类
score = ScoreCard(screen, game_settings) # 游戏得分类
dotted_lines = Group() # 虚线编组
obstacles = Group() # 障碍物编组
fun.track_init(screen, game_settings, dotted_lines, obstacles, car) # 开始游戏主循环
while True:
# 监控键盘和鼠标响应
fun.check_events(game_settings, button, obstacles, screen, car, dotted_lines)
# 游戏点击开始或者继续并且游戏非暂停
if game_settings.game_start and game_settings.game_pause is False:
car.update() # 更新赛车
dotted_lines.update(car, dotted_lines) # 更新虚线(模拟赛车前进)
obstacles.update(obstacles) # 更新障碍物
# 检测是否撞车
fun.check_crash(game_settings, car, obstacles, impact_effects)
# 绘制屏幕
fun.draw_screen(screen, game_settings, active_line, car, dotted_lines,
impact_effects, obstacles, button, score)
pygame.display.flip() # 让最近绘制的屏幕可见
run_game() # 运行游戏
function.py
import pygame
import sys
import random
from autodrome import DottedLine
from car import Obstacles
def dl_make(screen, game_settings, dotted_lines):
""""生成虚线"""
for dl_x in game_settings.dl_xs:
new_dl = DottedLine(screen, game_settings)
new_dl.rect.left = dl_x
dotted_lines.add(new_dl)
def obs_make(screen, game_settings, obstacles):
"""生成障碍物"""
obs_make_number = 0 # 记录每次生成个数,确保小于4个大于1个
for obs_x in game_settings.obs_xs:
if random.randint(0, 10) < game_settings.obs_number_ratio: # 随机生成个数
obs_make_number += 1
if obs_make_number <= game_settings.way_num: # 确保小于4个
new_obs = Obstacles(screen, game_settings, obs_x)
obstacles.add(new_obs)
if obs_x == game_settings.obs_xs[-1] and obs_make_number == 0: # 确保大于1个
new_obs = Obstacles(screen, game_settings, obs_x)
obstacles.add(new_obs)
def track_init(screen, game_settings, dotted_lines, obstacles, car):
"""游戏初始化"""
obstacles.empty() # 清空所有障碍物
dotted_lines.empty() # 清空所有虚线
dl_make(screen, game_settings, dotted_lines) # 重新生成虚线
obs_make(screen, game_settings, obstacles) # 重新生成障碍物
game_settings.impact = False # 关闭撞车效果显示开关
car.rect.centerx = game_settings.way_width / 2 # 重新定位赛车位置
# 初始化游戏速度
game_settings.game_speed = 0.7
game_settings.dl_moving_speed = game_settings.game_speed
game_settings.obs_up_speed = game_settings.game_speed - 0.4
game_settings.obs_down_speed = game_settings.game_speed + 0.2
game_settings.score = 0 # 初始化分数
def count_top_score(game_settings):
"""计算历史最高得分"""
with open("score/score.txt", 'r+') as file: # 打开最高得分储存文件
top_score = file.read()
if int(top_score) < game_settings.score:
file.seek(0) # 文件读取指针指到文件开头
file.truncate() # 清空文件内容(若无上一步将无法清空)
file.write(str(game_settings.score)) # 写入最高得分
def keydown_events(game_settings, event):
"""响应按下按键"""
if event.key == pygame.K_a:
game_settings.moving_left = True # 按下a按键打开左移开关
elif event.key == pygame.K_d:
game_settings.moving_right = True # 按下d按键打开右移开关
elif event.key == pygame.K_SPACE: # 按下空格按键暂停
game_settings.game_pause = bool(1 - game_settings.game_pause) # 对布尔达参数取反
def keyup_events(game_settings, event):
"""响应松开按键"""
if event.key == pygame.K_a:
game_settings.moving_left = False # 松开a按键关闭左移开关
elif event.key == pygame.K_d:
game_settings.moving_right = False # 松开d按键关闭右移开关
def mouse_down(mouse_x, mouse_y, button, game_settings, obstacles, screen, car, dotted_lines):
"""响应点击鼠标"""
if button.rect.collidepoint(mouse_x, mouse_y): # 检测鼠标点击位置是否为按钮
game_settings.game_start = True # 游戏开始
if game_settings.game_first:
button.continuation() # 制作“继续”按钮
game_settings.game_first = False # 游戏第一次运行开关关闭
else:
track_init(screen, game_settings, dotted_lines, obstacles, car) # 游戏初始化
def check_events(game_settings, button, obstacles, screen, car, dotted_lines):
"""监控键盘和鼠标响应"""
for event in pygame.event.get():
if event.type == pygame.QUIT: # 强制退出
sys.exit()
elif event.type == pygame.KEYDOWN:
keydown_events(game_settings, event)
elif event.type == pygame.KEYUP:
keyup_events(game_settings, event)
# 响应点击鼠标并且游戏在结束或者未开始界面
elif event.type == pygame.MOUSEBUTTONDOWN and game_settings.game_start is False:
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_down(mouse_x, mouse_y, button, game_settings, obstacles, screen,
car, dotted_lines)
def draw_screen(screen, game_settings, active_line, car, dotted_lines, impact_effects,
obstacles, button, score):
"""绘制屏幕"""
screen.fill(game_settings.bg_color) # 绘制屏幕
if game_settings.game_first is False: # 游戏第一次开始不显示游戏各部分
active_line.draw()
for dotted_line in dotted_lines.sprites():
dotted_line.draw()
car.draw()
obstacles.draw(screen)
score.draw()
if game_settings.game_start is False: # 游戏结束打印“继续”按钮
button.draw()
if game_settings.impact: # 撞车后显示撞车效果
impact_effects.draw()
def check_crash(game_settings, car, obstacles, impact_effects):
"""检测撞车、定位撞车位置"""
if pygame.sprite.spritecollideany(car, obstacles): # 检测撞车
obs = pygame.sprite.spritecollide(car, obstacles, False)[0] # 返回碰撞车辆精灵
if car.rect.left + 1 == obs.rect.right or car.rect.right - 1 == obs.rect.left:
if car.rect.bottom <= obs.rect.bottom:
impact_effects.rect.centery = (car.rect.bottom + obs.rect.top) / 2
if car.rect.left + 1 == obs.rect.right:
impact_effects.rect.centerx = car.rect.left
else:
impact_effects.rect.centerx = car.rect.right
else:
impact_effects.rect.centery = (car.rect.top + obs.rect.bottom) / 2
if car.rect.left + 1 == obs.rect.right:
impact_effects.rect.centerx = car.rect.left
else:
impact_effects.rect.centerx = car.rect.right
elif car.rect.top + 1 == obs.rect.bottom:
if car.rect.left > obs.rect.left:
impact_effects.rect.centerx = (car.rect.left + obs.rect.right) / 2
else:
impact_effects.rect.centerx = (car.rect.right + obs.rect.left) / 2
impact_effects.rect.centery = car.rect.top
game_settings.impact = True
game_settings.game_start = False
count_top_score(game_settings)
def count_score(game_settings):
"""计算得分、游戏加速"""
game_settings.score += 1
if game_settings.score % game_settings.add_speed_blanking == 0 and \
game_settings.game_speed < game_settings.max_speed:
game_settings.game_speed += game_settings.speed_increment
game_settings.dl_moving_speed = game_settings.game_speed
game_settings.obs_up_speed = game_settings.game_speed - 0.4
game_settings.obs_down_speed = game_settings.game_speed + 0.2
impact_effects.py
import pygame
class ImpactEffects:
"""撞车效果"""
def __init__(self, screen, game_settings):
self.screen = screen
self.game_settings = game_settings
self.image = pygame.image.load('images/impact_effects.bmp')
self.rect = self.image.get_rect()
self.screen_rect = self.screen.get_rect()
def draw(self):
self.screen.blit(self.image, self.rect)
game_button.py
import pygame
class GameButton:
"""游戏开始、继续按钮"""
def __init__(self, game_settings, screen):
self.screen = screen
self.game_settings = game_settings
self.screen_rect = self.screen.get_rect()
self.width = self.game_settings.g_button_width
self.height = self.game_settings.g_button_height
self.button_color = self.game_settings.button_color
self.text_color = self.game_settings.text_color
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.image = self.font.render("Play", True, self.text_color, self.button_color)
self.image_rect = self.image.get_rect()
self.image_rect.center = self.rect.center
def draw(self):
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.image, self.image_rect)
def continuation(self):
self.image = self.font.render("Continue", True, self.text_color, self.button_color)
self.image_rect = self.image.get_rect()
self.image_rect.center = self.rect.center
car.py
import pygame
from pygame.sprite import Sprite
import function as fun
class Car:
"""赛车类"""
def __init__(self, screen, game_settings):
self.screen = screen
self.game_settings = game_settings
self.image = pygame.image.load('images/car_up.bmp')
self.rect = self.image.get_rect()
self.screen_rect = self.screen.get_rect()
self.rect.centerx = self.game_settings.way_width / 2
self.rect.bottom = self.game_settings.screen_height
def draw(self):
self.screen.blit(self.image, self.rect)
def update(self):
# 左右移动不能超出界限
if self.game_settings.moving_left and self.rect.left > 0:
self.rect.x -= self.game_settings.car_moving_speed
elif self.game_settings.moving_right and self.rect.right < \
self.game_settings.screen_width:
self.rect.x += self.game_settings.car_moving_speed
class Obstacles(Sprite):
"""障碍物"""
def __init__(self, screen, game_settings, obs_x):
super(Obstacles, self).__init__()
self.screen = screen
self.game_settings = game_settings
self.obs_x = obs_x
# 分车道设置障碍物
if self.obs_x < self.game_settings.screen_width / 2:
self.image = pygame.image.load('images/car_down.bmp')
else:
self.image = pygame.image.load('images/car_up.bmp')
self.rect = self.image.get_rect()
self.rect.centerx = self.obs_x
self.rect.bottom = self.game_settings.score_height
self.y = float(self.rect.y) # 浮点化坐标,方便调节速度
self.rect.y = self.y
def update(self, obstacles):
if self.rect.x < self.game_settings.screen_width / 2:
self.y += self.game_settings.obs_down_speed
self.rect.y = self.y
else:
self.y += self.game_settings.obs_up_speed
self.rect.y = self.y
# 循环后保存最后一个障碍物,防止多个坐标符合新建障碍物
obstacle = Obstacles(self.screen, self.game_settings, 0)
for obs in obstacles.sprites():
obstacle = obs
if obs.rect.top >= self.game_settings.screen_height:
obstacles.remove(obs)
fun.count_score(self.game_settings) # 计算分数
if obstacle.rect.top >= self.game_settings.obs_distance + \
self.game_settings.g_button_height:
fun.obs_make(self.screen, self.game_settings, obstacles) # 生成新的障碍物
score.py
import pygame
class ScoreCard:
"""得分类"""
def __init__(self, screen, game_settings):
self.screen = screen
self.game_settings = game_settings
self.screen_rect = self.screen.get_rect()
self.width = self.game_settings.score_width
self.height = self.game_settings.score_height
self.bg_color = self.game_settings.score_bg_color
self.text_color = self.game_settings.score_text_color
self.name_font = pygame.font.SysFont(None, 30) # 分数名字字体大小
self.score_font = pygame.font.SysFont(None, 40) # 分数字体大小
self.rect = pygame.Rect(0, 0, self.width, self.height) # 分数显示栏背景
self.rect.top = 0
def draw_current_score(self):
"""绘制当前得分"""
self.cs = "CurrentScore"
self.cs_surface = self.name_font.render(self.cs, True, self.text_color, self.bg_color)
self.cs_rect = self.cs_surface.get_rect()
self.cs_rect.centerx = self.width / 4
self.cs_rect.top = self.rect.top
self.current_score = str(self.game_settings.score)
self.current_score_surface = self.score_font.render(self.current_score, True, self.text_color, self.bg_color)
self.current_score_rect = self.current_score_surface.get_rect()
self.current_score_rect.centerx = self.width / 4
self.current_score_rect.bottom = self.rect.bottom
self.screen.blit(self.cs_surface, self.cs_rect)
self.screen.blit(self.current_score_surface, self.current_score_rect)
def draw_highest_score(self):
"""绘制最高得分"""
self.hs = "HighestScore"
self.hs_surface = self.name_font.render(self.hs, True, self.text_color, self.bg_color)
self.hs_rect = self.hs_surface.get_rect()
self.hs_rect.centerx = self.width * 3 / 4
self.hs_rect.top = self.rect.top
with open('score/score.txt') as file:
self.highest_score = file.read()
self.highest_score_surface = self.score_font.render(self.highest_score, True, self.text_color, self.bg_color)
self.highest_score_rect = self.highest_score_surface.get_rect()
self.highest_score_rect.centerx = self.width * 3 / 4
self.highest_score_rect.bottom = self.rect.bottom
self.screen.blit(self.hs_surface, self.hs_rect)
self.screen.blit(self.highest_score_surface, self.highest_score_rect)
def draw(self):
"""绘制得分"""
self.screen.fill(self.bg_color, self.rect)
self.draw_highest_score()
self.draw_current_score()
autodromr.py
import pygame
from pygame.sprite import Sprite
import function as fun
class Line:
"""实线类"""
def __init__(self, screen, game_settings):
self.screen = screen
self.game_settings = game_settings
self.screen_rect = self.screen.get_rect()
self.width = game_settings.active_line_width
self.height = game_settings.active_line_height
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.game_settings.screen_height
self.color = self.game_settings.al_color
def draw(self):
pygame.draw.rect(self.screen, self.color, self.rect)
class DottedLine(Sprite):
"""虚线类"""
def __init__(self, screen, game_settings):
super(DottedLine, self).__init__()
self.screen = screen
self.game_settings = game_settings
self.width = game_settings.dotted_line_width
self.height = game_settings.dotted_line_height
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.bottom = self.game_settings.score_height
self.color = self.game_settings.dotted_line_color
self.y = float(self.rect.y) # 浮点化坐标,方便调节速度
def draw(self):
pygame.draw.rect(self.screen, self.color, self.rect)
def update(self, car, dotted_lines):
self.y += self.game_settings.dl_moving_speed
self.rect.y = self.y
# 循环后保存最后一个障碍物,防止多个坐标符合新建虚线
dl = DottedLine(self.screen, self.game_settings)
for dotted_line in dotted_lines.sprites():
dl = dotted_line
if dl.rect.top >= self.game_settings.screen_height:
dotted_lines.remove(dl)
if dl.rect.top > self.height + self.game_settings.g_button_height:
fun.dl_make(self.screen, self.game_settings, dotted_lines)
settings.py
class Settings:
"""设置类"""
def __init__(self):
# 屏幕参数
self.screen_height = 750
self.screen_width = 440
self.bg_color = (195, 195, 195)
# 得分背景参数
self.g_button_width = 150
self.g_button_height = 50
self.button_color = (255, 235, 0)
self.text_color = (0, 0, 0)
# 得分参数
self.score_height = 60
self.score_width = self.screen_width
self.score_bg_color = (255, 235, 0)
self.score_text_color = (0, 0, 0)
# 实线参数
self.active_line_height = self.screen_height - self.score_height
self.active_line_width = 20
self.al_color = (255, 235, 0)
# 虚线参数
self.dotted_line_height = 150
self.dotted_line_width = self.active_line_width
self.dotted_line_color = self.al_color
# 生成虚线x轴坐标、障碍物x轴坐标
self.way_num = 4
self.way_width = (self.screen_width - 3 * self.active_line_width) / self.way_num
self.obs_xs = [0, 0, 0, 0]
for num in range(len(self.obs_xs)):
self.obs_xs[num] = num * (self.active_line_width + self.way_width) + self.way_width / 2
self.dl_xs = [0, 0]
for num in range(len(self.dl_xs)):
self.dl_xs[num] = 2 * num * (self.way_width + self.active_line_width) + self.way_width
# 游戏开关
self.moving_left = False
self.moving_right = False
self.game_first = True
self.game_start = False
self.impact = False
self.game_pause = False
self.score = 0
# 随机生成障碍物几率
self.obs_number_ratio = 5
# 障碍物生成初始间隔距离
self.obs_distance = 500
# 游戏速度
self.car_moving_speed = 1
self.game_speed = 0.7
self.dl_moving_speed = self.game_speed
self.obs_up_speed = self.game_speed - 0.4
self.obs_down_speed = self.game_speed + 0.2
self.max_speed = 2.0
self.speed_increment = 0.05
self.add_speed_blanking = 5