13-1 星星 :找一幅星星图像,并在屏幕上显示一系列整齐排列的星星。
13-2 更逼真的星星 :为让星星的分布更逼真,可随机地放置星星。
代码:
settings.py(定义一些必须的基本属性和初始值)
class Settings():
def __init__(self):
self.screen_width = 1200
self.screen_height = 600
star.py(为了实现星星大小不一,使用smoothscale进行缩放,缩放值随机)
import pygame
from pygame.sprite import Sprite
from random import randint
class Star(Sprite):
def __init__(self):
# 继承父类
super(Star, self).__init__()
# 加载图片
self.image = pygame.image.load('../images/star.png')
# 平滑缩放到(宽,高)
random_number = randint(10, 30)
self.image = pygame.transform.smoothscale(self.image, (random_number, random_number))
self.rect = self.image.get_rect()
game_functions.py(可修改数字5来控制星星显示疏密)
from star import Star
from random import randint
def create_stars(settings, stars):
star = Star()
# 数字5(越大越少)控制疏密
number_stars_x = int(settings.screen_width / (5 * star.rect.width))
number_stars_y = int(settings.screen_height / (5 * star.rect.height))
for number_y in range(number_stars_y):
for number_x in range(number_stars_x):
random_number_x = randint(0, settings.screen_width)
random_number_y = randint(0, settings.screen_height)
star = Star()
# # 规则排列
# star.rect.x = 2 * star.rect.width * number_x
# star.rect.y = 2 * star.rect.height * number_y
# 不规则排列
star.rect.x = random_number_x
star.rect.y = random_number_y
stars.add(star)
babysbreath.py(主函数)
import pygame
import sys
from pygame.sprite import Group
from settings import Settings
import game_functions as gf
def run_game():
pygame.init()
settings = Settings()
screen = pygame.display.set_mode((settings.screen_width, settings.screen_height))
# # 全屏显示
# screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
pygame.display.set_caption('满天星')
stars = Group()
gf.create_stars(settings, stars)
stars.draw(screen)
while True:
# 需加事件,不然秒退
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
13-4 连绵细雨 :修改为完成练习 13-3 而编写的代码,使得一行雨滴消失在屏幕底端后,屏幕顶端又出现一行新雨滴,并开始往下落。
优化为“下雨天”,效果如下图:
代码:
settings.py(定义一些必须的基本属性和初始值)
class Settings():
def __init__(self):
self.screen_width = 1200
self.screen_height = 600
raindrop.py(为了效果更逼真,雨滴落下速度使用随机数random_number / 5)
import pygame
from pygame.sprite import Sprite
from random import randint
class Raindrop(Sprite):
def __init__(self):
# 继承父类
super(Raindrop, self).__init__()
# 加载图片
self.image = pygame.image.load('../images/raindrop.png')
# 平滑缩放到(宽,高)
random_number = randint(10, 30)
self.image = pygame.transform.smoothscale(self.image, (random_number, random_number))
self.rect = self.image.get_rect()
def update(self):
"""向下移动雨滴"""
random_number = randint(1, 10)
self.rect.y += random_number / 5
game_functions.py(使用random_number_y = randint(0, 300),让雨从半空飘落)
from raindrop import Raindrop
from random import randint
def create_raindrops(settings, raindrops):
"""创建很多雨滴"""
raindrop = Raindrop()
# 数字4(越大越少)控制稀疏
number_raindrops_x = int(settings.screen_width / (4 * raindrop.rect.width))
number_raindrops_y = int(settings.screen_height / (4 * raindrop.rect.height))
for number_y in range(number_raindrops_y):
for number_x in range(number_raindrops_x):
create_raindrop(raindrops)
def update_raindrops(screen, raindrops):
"""更新所有雨滴的位置"""
screen_rect = screen.get_rect()
for raindrop in raindrops.sprites():
if raindrop.rect.bottom >= screen_rect.bottom:
raindrops.remove(raindrop)
create_raindrop(raindrops)
raindrops.update()
def create_raindrop(raindrops):
"""创建单个雨滴"""
random_number_x = randint(0, 1200)
random_number_y = randint(0, 300)
raindrop = Raindrop()
# 不规则排列
raindrop.rect.x = random_number_x
raindrop.rect.y = random_number_y
raindrops.add(raindrop)
rain.py(主函数)
import pygame
import sys
from pygame.sprite import Group
from settings import Settings
import game_functions as gf
def run_game():
pygame.init()
settings = Settings()
screen = pygame.display.set_mode((settings.screen_width, settings.screen_height))
pygame.display.set_caption('下雨天')
raindrops = Group()
gf.create_raindrops(settings, raindrops)
while True:
# 设置背景色(不设置会导致雨滴整个拖下来)
screen.fill((125, 125, 125))
# 需加事件,不然秒退
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
gf.update_raindrops(screen, raindrops)
raindrops.draw(screen)
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
13-5 抓球 :创建一个游戏,在屏幕底端放置一个玩家可左右移动的角色。让一个球出现在屏幕顶端,且水平位置是随机的,并让这个球以固定的速度往下落。如果角色与球发生碰撞(表示将球抓住了),就让球消失。每当角色抓住球或球因抵达屏幕底端而消失后,都创建一个新球。
13-6 游戏结束 :在为完成练习 13-5 而编写的代码中,跟踪玩家有多少次未将球接着。在未接着球的次数到达三次后,结束游戏。
合并为一个“抓球游戏”,效果图如下(详细查看https://jingyan.baidu.com/article/597a0643bcb5a5712a524311.html):
代码:
settings.py(定义一些必须的基本属性和初始值)
class Settings():
def __init__(self):
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
# 人移动速度
self.people_move_speed = 1.5
# 球落下速度
self.ball_drop_speed = 0.5
#统计信息设置
# 游戏得分
self.game_score = 0
# 接到一个球多少分
self.ball_score = 10
# 丢失多少球结束游戏
self.ball_miss_number = 3
ball.py(因为有两处用到球图片,只是大小不一致,定义一个缩放函数scale)
import pygame
from pygame.sprite import Sprite
class Ball(Sprite):
def __init__(self, settings):
super(Ball, self).__init__()
self.settings = settings
self.image = pygame.image.load('../images/ball.png')
self.rect = self.image.get_rect()
self.center = float(self.rect.centerx)
def scale(self, multiple):
# 图片缩放
self.image = pygame.transform.smoothscale(self.image, (multiple, multiple))
def update(self):
# 球落下
self.center += self.settings.ball_drop_speed
self.rect.centery = self.center
people.py(self.center = float(self.rect.centerx),因为设置的移动速度存在小数位,这里要加上float,不然效果会有问题)
import pygame
class People():
def __init__(self, settings, screen):
self.settings = settings
self.screen = screen
self.image = pygame.image.load('../images/people.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
# 在飞船的属性 center 中存储小数值(不然会导致小数位不起作用)
self.center = float(self.rect.centerx)
# 移动标志
self.moving_right = False
self.moving_left = False
def update(self):
""" 根据移动标志调整人的位置 """
# 更新人的 center 值,而不是 rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.settings.people_move_speed
if self.moving_left and self.rect.left > 0:
self.center -= self.settings.people_move_speed
# 根据 self.center 更新 rect 对象
self.rect.centerx = self.center
def blitme(self):
"""绘制人"""
# blit绘制到缓冲区,不显示于屏幕(需要flip才能显示到屏幕)
self.screen.blit(self.image, self.rect)
button.py
import pygame.font
class Button():
def __init__(self, screen, msg):
"""初始化按钮的属性"""
self.screen = screen
self.screen_rect = screen.get_rect()
# 设置按钮的尺寸和其他属性
self.width, self.height = 150, 50
self.button_color = (125, 125, 125)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 30)
# 创建按钮的rect对象,并使其居中
self.rect = pygame.Rect(0, 0, self.width, self.height)
# 按钮的标签只需创建一次
self.prep_msg(msg)
def prep_msg(self, msg):
"""将msg渲染为图像,并使其在按钮上居中"""
self.rect.center = self.screen_rect.center
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)
scoreboard.py(由于绘制左上角的球图片缩小了,所以球之间的间距也要按比例缩小“ball.rect.x”)
import pygame
from pygame.sprite import Group
from ball import Ball
class Scoreboard():
"""docstring for Scoreboard"""
def __init__(self, settings, screen):
self.settings = settings
self.screen = screen
self.screen_rect = screen.get_rect()
# 球缩放值
self.scaleValue = 20
# 显示得分信息时使用的字体设置
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
self.prep_score()
self.prep_balls()
def prep_score(self):
"""将得分转换为渲染的图像"""
rounded_score = int(round(self.settings.game_score, -1))
score_str = '{:,}'.format(rounded_score)
self.score_image = self.font.render(score_str, True, self.text_color, self.settings.bg_color)
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 10
self.score_rect.top = 10
def prep_balls(self):
# 显示还能丢失多少球
self.balls = Group()
for number in range(self.settings.ball_miss_number):
ball = Ball(self.settings)
# 缩放球大小并赋值位置
ball.scale(self.scaleValue)
ball.rect.x = 10 + ball.rect.width * (self.scaleValue / ball.rect.width) * number
ball.rect.y = 10
self.balls.add(ball)
def show_score(self):
self.balls.draw(self.screen)
self.screen.blit(self.score_image, self.score_rect)
game_functions.py
import sys
import pygame
from ball import Ball
from button import Button
from random import randint
# 事件
def check_events(people):
""" 响应按键和鼠标事件 """
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, people)
elif event.type == pygame.KEYUP:
check_keyup_events(event, people)
def check_keydown_events(event, people):
""" 响应按键 """
if event.key == pygame.K_RIGHT:
people.moving_right = True
elif event.key == pygame.K_LEFT:
people.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_events(event, people):
""" 响应松开 """
if event.key == pygame.K_RIGHT:
people.moving_right = False
elif event.key == pygame.K_LEFT:
people.moving_left = False
# 球
def update_balls(settings, balls, people, screen, scoreboard):
"""更新球的位置"""
# 创建球(一直1个)
if len(balls) == 0:
create_ball(settings, balls, screen)
# 球碰撞到人,就消失
if pygame.sprite.spritecollideany(people, balls):
balls.empty()
settings.game_score += settings.ball_score
scoreboard.prep_score()
# 球到底则消失
screen_rect = screen.get_rect()
for ball in balls.sprites():
if ball.rect.bottom >= screen_rect.bottom:
balls.remove(ball)
# 减少可落下的数量并更新显示球数量
settings.ball_miss_number -= 1
scoreboard.prep_balls()
# 当球未接住的数据达到设置的数量,则绘制“Game Over”并结束游戏
if settings.ball_miss_number == 0:
button = Button(screen, 'Game Over')
button.draw_button()
return
balls.update()
def create_ball(settings, balls, screen):
"""创建单个球"""
ball = Ball(settings)
# 球初始位置(顶部,水平位置随机)
screen_rect = screen.get_rect()
number_x = randint(0, settings.screen_width)
ball.rect.centerx = number_x
ball.rect.top = screen_rect.top
balls.add(ball)
catchball.py(主函数)
import pygame
from pygame.sprite import Group
from settings import Settings
import game_functions as gf
from people import People
from scoreboard import Scoreboard
def run_game():
pygame.init()
settings = Settings()
# screen = pygame.display.set_mode((settings.screen_width, settings.screen_height))
# 全屏显示
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
pygame.display.set_caption('抓球')
# 创建人和球
people = People(settings, screen)
# 创建球的编组
balls = Group()
# 创建记分牌
scoreboard = Scoreboard(settings, screen)
while True:
# 设置背景色
screen.fill(settings.bg_color)
# 检查玩家输入
gf.check_events(people)
# 更新人位置并绘制到屏幕
people.update()
people.blitme()
# 更新球位置并绘制到屏幕
gf.update_balls(settings, balls, people, screen, scoreboard)
balls.draw(screen)
# 显示得分
scoreboard.show_score()
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
Tips:所使用的图标可自行上“https://www.iconfont.cn/”查找。