12-1 蓝色天空:创建一个背景为蓝色的Pygame窗口。
12-2 游戏角色 :找一幅你喜欢的游戏角色位图图像或将一幅图像转换为位图。创建一个类,将该角色绘制到屏幕中央,并将该图像的背景色设置为屏幕背景色,或将屏幕背景色设置为该图像的背景色。
两问合在一起回答了:
setting.py
class Settings:
"""第12章练习题的设置"""
def __init__(self):
"""初始化设置"""
self.bg_color = (100, 149, 237)
self.screen_width = 1200
self.screen_height = 800
game_function.py
import sys
import pygame
def check_events():
"""响应鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def update_screen(pcc_setting, screen, person):
"""更新屏幕上的图像,并切换屏幕"""
# 每次循环都重绘屏幕
screen.fill(pcc_setting.bg_color)
person.blitme()
# 让最近的绘制屏幕可见
pygame.display.flip()
person.py
import pygame
class Person:
def __init__(self, screen):
"""初始化人物并设置人物位置"""
self.screen = screen
# 加载人物外形并获取其外接矩形
self.image = pygame.image.load('images/person.svg')
self.image = pygame.transform.smoothscale(self.image, (80, 80))
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# 将人物放到画面中央
self.rect.centerx = self.screen_rect.centerx
self.rect.centery = self.screen_rect.centery
def blitme(self):
"""在指定位置绘制人物"""
self.screen.blit(self.image, self.rect)
如果要调整图片大小,也可以使用下面的代码:
self.image = pygame.transform.scale(self.image, (50, 50))
12-3 火箭:编写一个游戏,开始时屏幕中央有一个火箭,而玩家可使用四个方向键上下左右移动火箭。请务必确保火箭不会移到屏幕外面。
settings.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
class GlobalSettings():
"""关于游戏的全局设置"""
def __init__(self):
self.canvas_width = 1200
self.canvas_height = 800
self.bg_color = (100, 149, 237)
self.rocket_speed_factor = 1.3
rocket.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import pygame as pg
class Rocket():
def __init__(self, roc_settings, canvas):
"""初始化火箭的设置"""
# 图像在canvas上
self.canvas = canvas
self.settings = roc_settings
# 导入火箭,调整大小,get rect
self.image = pg.image.load('images/rocket.svg')
self.image = pg.transform.scale(self.image, (80, 80))
self.rect = self.image.get_rect()
self.canvas_rect = canvas.get_rect()
# 火箭放到屏幕中央
self.rect.centerx = self.canvas_rect.centerx
self.rect.centery = self.canvas_rect.centery
# 因为centerx和centery只能存整数,而rocket_speed_factor可能是小数,所以需要调整
self.center_x = float(self.rect.centerx)
self.center_y = float(self.rect.centery)
# self.center_y_position = float(self.rect.centery)
# 不断移动的标志
self.move_right = False
self.move_left = False
self.move_up = False
self.move_down = False
def refresh_rocket(self):
"""刷新火箭位置"""
if self.move_right and self.rect.right < self.canvas_rect.right:
self.center_x += self.settings.rocket_speed_factor
if self.move_left and self.rect.left > self.canvas_rect.left:
self.center_x -= self.settings.rocket_speed_factor
if self.move_up and self.rect.top > self.canvas_rect.top:
self.center_y -= self.settings.rocket_speed_factor
if self.move_down and self.rect.bottom < self.canvas_rect.bottom:
self.center_y += self.settings.rocket_speed_factor
# 根据self.center更新rect对象 #看了两个小时,才发现这个地方没写。。或者说,没抄,囧。
self.rect.centerx = self.center_x
self.rect.centery = self.center_y
def blitRocket(self):
"""在canvas上显示火箭"""
self.canvas.blit(self.image, self.rect)
game_function.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import pygame as pg
def listen_key_mouse_events(rocket):
"""监听鼠标和键盘"""
# 退出
for event in pg.event.get():
if event.type == pg.QUIT:
sys.exit()
# 监听鼠标按键事件(按下按键)
elif event.type == pg.KEYDOWN: # 不停的忘了这个.type,哎~~~
# 如果有按下的动作,则调用 keydown
listen_keydown(event, rocket)
# 监听鼠标按键事件(按键抬起)
elif event.type == pg.KEYUP:
# 如果有抬起的动作,则调用 keyup
listen_keyup(event, rocket)
def listen_keydown(event, rocket):
"""监听按下按键的动作"""
# 如果是按下方向键,则rocket“不断移动的标志”为True
if event.key == pg.K_RIGHT:
rocket.move_right = True
elif event.key == pg.K_LEFT:
rocket.move_left = True
elif event.key == pg.K_UP:
rocket.move_up = True
elif event.key == pg.K_DOWN:
rocket.move_down = True
def listen_keyup(event, rocket):
"""监听抬起按键的动作"""
# 如果是抬起方向键,则rocket“不断移动的标志”为False
if event.key == pg.K_RIGHT:
rocket.move_right = False
elif event.key == pg.K_LEFT:
rocket.move_left = False
elif event.key == pg.K_UP:
rocket.move_up = False
elif event.key == pg.K_DOWN:
rocket.move_down = False
def refresh_canvas(roc_setting, canvas, rocket):
"""刷新画布"""
# 填充背景色
canvas.fill(roc_setting.bg_color)
# 刷新火箭
rocket.blitRocket()
# 刷新画布canvas
pg.display.flip()
main.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import pygame as pg
from settings import GlobalSettings
import game_function as gf
from rocket import Rocket
def run_game():
"""启动游戏"""
pg.init()
# 根据setting的内容创建一个对象,roc = rocket。
roc_setting = GlobalSettings()
# 设置画布
canvas = pg.display.set_mode(
(roc_setting.canvas_width, roc_setting.canvas_height))
# 设置标题
pg.display.set_caption("python从入门到实践第12章 12-3习题")
# 创建火箭
rocket = Rocket(roc_setting, canvas)
# 开始全局循环
while True:
gf.listen_key_mouse_events(rocket)
rocket.blitRocket()
rocket.refresh_rocket()
gf.refresh_canvas(roc_setting, canvas, rocket)
run_game()
12-4 按键:创建一个程序,显示一个空屏幕。在事件循环中,每当检测到pygame.KEYDOWN 事件时都打印属性event.key 。运行这个程序,并按各种键,看看 Pygame如何响应。
import sys, pygame
def run_game():
pygame.init()
screen = pygame.display.set_mode((1200,800))
pygame.display.set_caption("python从入门到实践 12-4")
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
print(event.key)
pygame.display.flip()
run_game()
这题简单粗暴,按下去不同的按键显示不同数字啊~
12-5 侧面射击:编写一个游戏,将一艘飞船放在屏幕左边,并允许玩家上下移动飞船。在玩家按空格键时,让飞船发射一颗在屏幕中向右穿行的子弹,并在子弹离开 屏幕而消失后将其删除。
settings.py
class Settings():
def __init__(self):
# 屏幕设置
self.canvas_width = 1200
self.canvas_height = 900
self.canvas_bg_color = (60,179,113)
self.pilot_speed_factor = 0.8
# 子弹设置
self.bullet_width = 15
self.bullet_height = 3
self.bullet_color = 60, 60, 60
self.bullet_speed_factor = 1.3
self.bullet_max = 4
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, glob_setting, pilot, canvas):
super(Bullet, self).__init__()
# 代码super(Bullet, self).__init__() 使用了Python 2.7语法。
# 这种语法也适用于Python 3,但你也可以将这行代码简写为super().__init__() 。
self.canvas = canvas
# 创建一个位于0,0的子弹,再移到右边去
self.rect = pygame.Rect(0, 0, glob_setting.bullet_width,
glob_setting.bullet_height)
self.rect.left = pilot.rect.left
self.rect.centery = pilot.rect.centery
# 小数存储子弹飞行轨迹x,y位置
self.x = float(self.rect.x)
# self.y = float(self.rect.y)
# self.bullet_firing_ticker = False
# 子弹颜色和速度
self.color = glob_setting.bullet_color
self.speed_factor = glob_setting.bullet_speed_factor
def update(self): # 这个地方一定要用update,是pygame.sprites()的内建函数
# 子弹从右向左移动
self.x -= self.speed_factor
# 更新子弹位置
self.rect.x = self.x
def draw_bullet(self):
pygame.draw.rect(self.canvas, self.color, self.rect)
pilot.py
import pygame
# 创建飞机
class Pilot():
def __init__(self, canvas, glob_setting):
self.setting = glob_setting
self.canvas = canvas
self.image = pygame.image.load('images/pilot.svg')
self.image = pygame.transform.smoothscale(self.image, (100, 100))
# 获取飞机图像外框矩形
self.rect = self.image.get_rect()
# 获取画布矩形
self.canvas_rect = canvas.get_rect()
# 设置右边中间为初始位置
self.rect.right = self.canvas_rect.right
self.rect.centery = self.canvas_rect.centery
self.move_right_ticker = False
self.move_left_ticker = False
self.move_up_ticker = False
self.move_down_ticker = False
# centerx和centery不能是小数,过渡一下
self.center_x = float(self.rect.centerx)
self.center_y = float(self.rect.centery)
# 设置飞机四个方向移动位置
def refresh_pilot_position(self):
if self.move_right_ticker and self.rect.right < self.canvas_rect.right:
self.center_x += self.setting.pilot_speed_factor
if self.move_left_ticker and self.rect.left > self.canvas_rect.left:
self.center_x -= self.setting.pilot_speed_factor
if self.move_up_ticker and self.rect.top > self.canvas_rect.top:
self.center_y -= self.setting.pilot_speed_factor
if self.move_down_ticker and self.rect.bottom < self.canvas_rect.bottom:
self.center_y += self.setting.pilot_speed_factor
self.rect.centerx = self.center_x
self.rect.centery = self.center_y
# 显示飞机
def blitPilot(self):
self.canvas.blit(self.image, self.rect)
game_function.py
import pygame
import sys
from bullet import Bullet
# 聆听游戏鼠标键盘事件
def listen_events(pilot, glob_setting, canvas, bullets):
# event队列中如果有quit,则退出
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 按下键盘后的事件
elif event.type == pygame.KEYDOWN:
listen_events_KEYDOWN(event, pilot, glob_setting, canvas, bullets)
# 抬起键盘后的事件
elif event.type == pygame.KEYUP:
listen_events_KEYUP(event, pilot)
def listen_events_KEYDOWN(event, pilot, glob_setting, canvas, bullets):
if event.key == pygame.K_UP:
pilot.move_up_ticker = True
elif event.key == pygame.K_DOWN:
pilot.move_down_ticker = True
elif event.key == pygame.K_LEFT:
pilot.move_left_ticker = True
elif event.key == pygame.K_RIGHT:
pilot.move_right_ticker = True
# 按下空格生成子弹
elif event.key == pygame.K_SPACE:
if len(bullets) < glob_setting.bullet_max:
new_bullet = Bullet(glob_setting, pilot, canvas)
bullets.add(new_bullet)
def listen_events_KEYUP(event, pilot):
if event.key == pygame.K_UP:
pilot.move_up_ticker = False
elif event.key == pygame.K_DOWN:
pilot.move_down_ticker = False
elif event.key == pygame.K_LEFT:
pilot.move_left_ticker = False
elif event.key == pygame.K_RIGHT:
pilot.move_right_ticker = False
# 刷新画布
def refresh_canvas(canvas, glob_setting, pilot, bullets):
# 画布添加背景色
canvas.fill(glob_setting.canvas_bg_color)
# 在小图像上画子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
# 显示pilot
pilot.blitPilot()
# 方向键后刷新pilot位置
pilot.refresh_pilot_position()
# 显示最新绘制的canvas
pygame.display.flip()
# 子弹状态
def bullets_conditions(bullets):
# 删除屏幕外的子弹
for bullet in bullets.copy():
if bullet.rect.right < 0:
bullets.remove(bullet)
# print(len(bullets))
# 刷新画布上的子弹
bullets.update()
main.py
import pygame
import game_function as gf
from settings import Settings
from pilot import Pilot
from pygame.sprite import Group
def run_game():
# 初始化pygame
pygame.init()
# 根据setting的数值,创建对象
glob_setting = Settings()
# 创建屏幕画布
canvas = pygame.display.set_mode(
(glob_setting.canvas_width, glob_setting.canvas_height)
)
# 设置标题
pygame.display.set_caption('python从入门到提高 12-5')
# 根据class Pilot创建飞机pilot
pilot = Pilot(canvas, glob_setting)
# 创建子弹群
bullets = Group()
# 开始游戏循环
while True:
# 监听键盘鼠标事件
gf.listen_events(pilot, glob_setting, canvas, bullets)
# 刷新画布
gf.refresh_canvas(canvas, glob_setting, pilot, bullets)
# 子弹管理
gf.bullets_conditions(bullets)
run_game()
这一章做的有点慢,看代码,学,然后找bug。
昨天晚上写的,斜着移动只能先x,y方向移动(看按着哪个按键),然后再往另外一个方向移动,bug还没找出来呢。。。。囧。
搞了一天,找到问题所在了:
昨天晚上写的代码,在pilot.py
的def refresh_position()
这个地方,用的是if - elif - elif -elif
,这回导致第一个if
优先级最高,表现出来的就是按住右之后,只要不松手,一直往右走(这时候按上下无反应)导致我说的没有办法斜着走。这个地方要如本文代码一样,全部用if - if - if - if
,课本中说了,但是当时这一部分没仔细看,直接撸代码去了。。。
在方法update() 中,我们添加了一个if 代码块而不是elif 代码块,这样如果玩家同时按下了左右箭头 键,将先增大飞船的rect.centerx 值,再降低这个值,即飞船的位置保持不变。如果使用一个elif 代码块来处理向左移动的情况,右箭头键将始终处于优先地位。从向左移 动切换到向右移动时,玩家可能同时按住左右箭头键,在这种情况下,前面的做法让移动更准确。