Python编程:从入门到实践 第13章课后习题。
13-1 星星:找一幅星星图像,并在屏幕上显示一系列整齐排列的星星。
13-2 更逼真的星星 :为让星星的分布更逼真,可随机地放置星星。本书前面说过,可像下面这样来生成随机数:
from random import randint
random_number = randint(-10,10)
效果:
import sys
import pygame
from random import randint
from pygame.sprite import Sprite, Group
# 参见b站一个台湾人写法,不用def run_game(),直接写。
pygame.init()
# 设置画布大小
canvas_width = 1200
canvas_height = 900
# 参见台湾人的写法,他将所有页面上的元素都归到一个sprite里面,然后在while里面全部刷新。
all_sprites = Group()
# 设置画布
canvas = pygame.display.set_mode((canvas_width, canvas_height))
class Star(Sprite):
def __init__(self):
super(Star, self).__init__()
self.image = pygame.image.load('star.svg')
# 参见另外一个同学的写法,在这里随机缩小星星大小
random_size = randint(10, 30)
self.image = pygame.transform.smoothscale(self.image,
(random_size, random_size))
self.rect = self.image.get_rect()
def create_star():
# 创建一个对象
star = Star()
# 随机看x轴和y轴能创建多少个(数量)
horizontal_star_num = int(canvas_width / (randint(5, 10) * star.rect.width))
vertical_star_num = int(canvas_height / (randint(5, 10) * star.rect.height))
# print(horizontal_star_num, vertical_star_num)
for y in range(vertical_star_num):
for x in range(horizontal_star_num):
# 在画布范围内随机星星的x轴,y轴的位置
random_x_axis = randint(0, canvas_width)
random_y_axis = randint(0, canvas_height)
star = Star()
star.rect.x = random_x_axis
star.rect.y = random_y_axis
all_sprites.add(star)
create_star()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
all_sprites.draw(canvas)
pygame.display.update()
在页面元素不多,或者元素大多都是sprites的情况下,把所有元素丢到一个sprites里面,然后更新我觉得有道理啊,否则每次都要记得在刷新画布的地方更新,容易乱。
13-3 雨滴:寻找一幅雨滴图像,并创建一系列整齐排列的雨滴。让这些雨滴往下落,直到到达屏幕底端后消失。 13-4 连绵细雨:修改为完成练习13-3而编写的代码,使得一行雨滴消失在屏幕底端后,屏幕顶端又出现一行新雨滴,并开始往下落。 效果:
setting.py
class Setting():
def __init__(self):
self.canvas_width = 1200
self.canvas_height = 900
rain.py
import pygame
from pygame.sprite import Sprite
from random import randint
class Rain(Sprite):
def __init__(self):
super(Rain, self).__init__()
self.image = pygame.image.load("images/raindrop.svg")
rain_random_size = randint(5, 25)
self.image = pygame.transform.smoothscale(
self.image, (rain_random_size, rain_random_size))
self.rect = self.image.get_rect()
def update(self, *args, **kwargs) -> None:
random_drop_speed = randint(1, 5)
self.rect.y += random_drop_speed / 3
game_function.py
import sys
import pygame
from random import randint
from rain import Rain
def check_events():
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def available_spaces(settings):
"""确认canvas的x,y轴能放多少雨滴"""
# 需要用到每个雨滴的rect,因此生成
raindrop = Rain()
# randint可调整雨滴疏密
axis_x_avail_num = int(settings.canvas_width / (
raindrop.rect.width * randint(1, 10)))
axis_y_avail_num = int(settings.canvas_height / (
raindrop.rect.height * randint(10, 20)))
print(axis_x_avail_num, axis_y_avail_num)
return axis_x_avail_num, axis_y_avail_num
def create_a_raindrop(raindrops, settings):
"""生成一颗雨滴"""
# 随机生成一颗雨滴的x,y坐标
rain_axis_x = randint(0, settings.canvas_width)
rain_axis_y = randint(0, settings.canvas_height)
# 生成雨滴
raindrop = Rain()
# 将随机x,y坐标带入新生成的雨滴
raindrop.rect.x = rain_axis_x
raindrop.rect.y = rain_axis_y
raindrops.add(raindrop)
def create_raindrops(raindrops, settings):
"""生成大雨"""
axis_x_avail_num, axis_y_avail_num = available_spaces(settings)
for y in range(axis_y_avail_num):
for x in range(axis_x_avail_num):
create_a_raindrop(raindrops, settings)
def update_raindrops(canvas, raindrops, settings):
"""更新雨滴位置"""
canvas_rect = canvas.get_rect()
for raindrop in raindrops.sprites():
if raindrop.rect.top > canvas_rect.bottom:
raindrops.remove(raindrop)
# print(len(raindrops))
create_a_raindrop(raindrops, settings)
raindrops.update()
main.py
import pygame
from pygame.sprite import Group
import game_function as gf
from setting import Setting
def run_game():
pygame.init()
settings = Setting()
canvas = pygame.display.set_mode(
(settings.canvas_width, settings.canvas_height))
raindrops= Group()
gf.create_raindrops(raindrops, settings)
while True:
canvas.fill((100,100,100))
gf.check_events()
gf.update_raindrops(canvas,raindrops, settings)
raindrops.draw(canvas)
pygame.display.flip()
run_game()
还是分成几个文件吧。这个好像比较吃资源啊,单位的台式机不是特行,如果生成的雨滴多的话,居然还卡一下~~
13-5 抓球:创建一个游戏,在屏幕底端放置一个玩家可左右移动的角色。让一个球出现在屏幕顶端,且水平位置是随机的,并让这个球以固定的速度往下落。如果角色与球发生碰撞(表示将球抓住了),就让球消失。每当角色抓住球或球因抵达屏幕底端而消失后,都创建一个新球。
13-6 游戏结束:在为完成练习13-5而编写的代码中,跟踪玩家有多少次未将球接着。在未接着球的次数到达三次后,结束游戏。
settings.py
class Settings():
def __init__(self):
# 画布设置
self.canvas_width = 1200
self.canvas_height = 900
# 接球人移动速度
self.catcher_speed_factor = 1.2
# 球下落速度
self.ball_drop_speed_factor = 0.3
# 错过了多少球的统计
self.ball_missed = 0
catcher.py
import pygame
class Catcher():
def __init__(self, canvas):
# 画布设置
self.canvas = canvas
self.image = pygame.image.load("images/catcher.svg")
self.image = pygame.transform.smoothscale(self.image.convert_alpha(),
(80, 80))
# 获取外接矩形
self.rect = self.image.get_rect()
self.canvas_rect = canvas.get_rect()
# 定位小人
self.rect.centerx = self.canvas_rect.centerx
self.rect.bottom = self.canvas_rect.bottom
# 运用浮点坐标
self.center_x = float(self.rect.centerx)
self.center_y = float(self.rect.centery)
# 按住键盘方向的ticker
self.move_right_ticker = False
self.move_left_ticker = False
self.move_up_ticker = False
self.move_down_ticker = False
def update_catcher(self, settings):
if self.move_right_ticker and self.rect.right < \
self.canvas_rect.right:
self.center_x += settings.catcher_speed_factor
if self.move_left_ticker and self.rect.left > 0:
self.center_x -= settings.catcher_speed_factor
if self.move_up_ticker and self.rect.top > 0:
self.center_y -= settings.catcher_speed_factor
if self.move_down_ticker and self.rect.bottom < \
self.canvas_rect.bottom:
self.center_y += settings.catcher_speed_factor
self.rect.centerx = self.center_x
self.rect.centery = self.center_y
def blit_catcher(self):
# 显示小人
self.canvas.blit(self.image, self.rect)
ball.py
import pygame
from pygame.sprite import Sprite
from random import randint
class Ball(Sprite):
def __init__(self, canvas, settings):
super(Ball, self).__init__()
self.canvas = canvas
self.image = pygame.image.load("images/ball.svg")
self.image = pygame.transform.smoothscale(
self.image.convert_alpha(), (50, 50))
self.rect = self.image.get_rect()
self.canvas_rect = canvas.get_rect()
# 将球定位在画布顶
self.rect.top = self.canvas_rect.top
# 随机设置球在画布顶的位置,两边要各去掉一个球的宽度
self.rect.centerx = randint(
self.rect.width, settings.canvas_width - self.rect.width)
self.center_y = float(self.rect.centery)
def update(self, settings, **kwargs) -> None:
self.center_y += settings.ball_drop_speed_factor
self.rect.centery = self.center_y
game_function.py
import sys
import time
import pygame
from ball import Ball
def update_canvas(ball_sprite, canvas, catcher, settings):
canvas.fill((100, 100, 100))
##更新小人
catcher.blit_catcher()
catcher.update_catcher(settings)
##更新球
ball_sprite.draw(canvas)
ball_sprite.update(settings)
def create_a_ball(ball_sprite, canvas, settings):
# 创建一个球
ball = Ball(canvas, settings)
ball_sprite.add(ball)
def create_balls(ball_sprite, canvas, catcher, settings):
# 创建循环球
if len(ball_sprite) == 0:
create_a_ball(ball_sprite, canvas, settings)
# 如果球和人碰撞,则球的sprite清空
elif pygame.sprite.spritecollideany(catcher, ball_sprite):
ball_sprite.empty()
for ball in ball_sprite.sprites():
if ball.rect.bottom > settings.canvas_width:
ball_sprite.remove(ball)
# print(len(ball_sprite))
settings.ball_missed += 1
print("You have missed "
+ str(settings.ball_missed)
+ " balls, game will be over if 3 balls are missed")
check_missed(settings)
# 每次miss后停半秒
time.sleep(0.5)
def check_missed(settings):
if settings.ball_missed == 3:
print("Maximum missed, game over.")
sys.exit()
else:
pass
def check_events(catcher):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_event(event, catcher)
elif event.type == pygame.KEYUP:
check_keyup_event(event, catcher)
def check_keydown_event(event, catcher):
if event.key == pygame.K_RIGHT:
catcher.move_right_ticker = True
elif event.key == pygame.K_LEFT:
catcher.move_left_ticker = True
elif event.key == pygame.K_UP:
catcher.move_up_ticker = True
elif event.key == pygame.K_DOWN:
catcher.move_down_ticker = True
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_event(event, catcher):
if event.key == pygame.K_RIGHT:
catcher.move_right_ticker = False
elif event.key == pygame.K_LEFT:
catcher.move_left_ticker = False
elif event.key == pygame.K_UP:
catcher.move_up_ticker = False
elif event.key == pygame.K_DOWN:
catcher.move_down_ticker = False
main.py
import pygame
import game_functions as gf
from pygame.sprite import Group
from catcher import Catcher
from settings import Settings
def run_game():
pygame.init()
# 设置一个游戏开始的ticker
running = True
# 导入设置
settings = Settings()
# 设置画布
canvas = pygame.display.set_mode(
(settings.canvas_width, settings.canvas_height))
# 生成人物和小球
catcher = Catcher(canvas)
ball_sprite = Group()
while running:
gf.check_events(catcher)
gf.update_canvas(ball_sprite, canvas, catcher, settings)
gf.create_balls(ball_sprite, canvas, catcher, settings)
pygame.display.flip()
# print(len(ball_sprite))
run_game()
今晚时间有限,所以没有如教材上的那样用game stats.py来控制球,直接print了结果。
学习的过程中在StackOverflow上发现,导入图片最好用self.image.convert_alpha()
,或者.convert
,这样能提高效率。
回头可以尝试一下,把小人也用sprite来生成,这样应该可以练习groupcollide
。
先过国庆去了。