动画精灵
动画精灵表示作为一个单位来移动和显示一组像素,它是一种图形对象。我们
可以把动画精灵想成一个小图片-----可以在屏幕上移动的图形对象,并且可以与其他
图形对象交互。
- 图像(image):为动画精灵显示的图片;
- 矩形区(rect):包含动画精灵的的矩形区域。
Pygame的sprite模块提供了一个动画精灵基类,名为Sprite。一般,我们不会直接使用基类,而是基于
pygame.sprite.Sprite来创建自己的子类。
下面,我们来完成一个例子,基于pygame.sprite.Sprite基类来创建我们的子类 ,命名为PlaneClass。
import sys
import pygame
from pygame.locals import *
#创建动画精灵的子类,命名为PlaneClass
class PlaneClass(pygame.sprite.Sprite):
def __init__(self, img_file, location):
pygame.sprite.Sprite.__init__(self) #初始化动画精灵
self.image = pygame.image.load(img_file) #加载图像
self.rect = self.image.get_rect() #获取图像的矩形数据
self.rect.left, self.rect.top = location #设置图像的初始位置
实例化PlaneClass
pygame.init()
screen_size = 480, 700
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption(“AirplaneSprite”)
background = pygame.image.load(“background.png”).convert()
screen.blit(background, (0, 0)) #只加载一次图片,因此先将背景加载好
img_airplane = "me1.png"
airplanes = []
for i in range(3):
location_img = [i *100, 10] #每次循环产生一个不同的位置
airplane = PlaneClass(img_airplane, location_img) #在这个位置创建一个airplane实例对象
airplanes.append(airplane) #把新创建的airplane添加到列表
#依次将airplane显示在窗口不同的位置
for each in airplanes:
screen.blit(each.image, each.rect)
程序退出
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.flip()
现在我们加载了多个动画精灵,怎样让动画精灵动起来呢?我们要给它们增加一个移动的技能,给PlaneClass类添加speed属性和move()方法。
class PlaneClass(pygame.sprite.Sprite):
def __init__(self, img_file, location, speed): #增加speed参数
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(img_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
self.speed = speed #为PlaneClass类创建一个speed属性
#定义move()方法来移动精灵
def move(self):
self.rect = self.rect.move(self.speed)
#如果遇到边界,则移动速度反向
if self.rect.left < 0 or self.rect.right > width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > height:
self.speed[1] = -self.speed[1]
动起来…
from random import *
……[省略代码]
screen_size = width, height = 480, 700
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("AirplaneSprite")
background = pygame.image.load("background.png").convert()
img_airplane = "me1.png"
airplanes = []
speed = [2, 2] #初始化speed
for i in range(3):
location_img = [i *100, 10]
speed = [choice([-2, 2]), choice([-2, 2])] #随机生成速度
airplane = PlaneClass(img_airplane, location_img, speed) #实例化对象
airplanes.append(airplane)
调用move()方法移动精灵
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.time.delay(10)
screen.blit(background, (0, 0))
for each in airplanes:
each.move() #调用move()方法,移动精灵
screen.blit(each.image, each.rect)
pygame.display.flip()
尝试在同一个程序中加载不同的精灵
……[省略代码]
#定义所有精灵的文件列表
imgs = [“me1.png”, “enemy1.png”, “enemy2.png”, “enemy3_n1.png”]
airplanes = []
speed = [2, 2] #初始化speed
for i in range(4):
location_img = [i *100, 10]
speed = [choice([-2, 2]), choice([-2, 2])] #随机生成速度
airplane = PlaneClass(imgs[i], location_img, speed) #加载所有的飞机对象
airplanes.append(airplane)
碰撞检测
碰撞检测指的是检测两个动画精灵接触或者重叠。
- pygame提供了一种方法对动画精灵分组。例如,在保龄球游戏中,所有的瓶在
一组,球在另一组。组与碰撞检测密切相关,我们可以检测球何时击倒某个瓶子,
因此需要寻找精灵(球)与球瓶组中所有精灵之间的碰撞;当然,还可以检测组内
部的碰撞(比如球瓶相互碰倒)。
碰撞检测分为两种:
- 某一动画精灵与自己所在组的动画精灵之间的碰撞;
- 某一动画精灵与其他组的动画精灵之间的碰撞;
那么怎么分组呢?Pygame提供了pygame.sprite.Group类,用来管理分组过个sprite对象。比如我们经常使用pygame.sprite.Group.add()方法添加动画精灵到组当中,使用pygame.sprite.Group.remove()方法从组当中移除某一个动画精灵。
pygame.sprite.spritecollide()方法用来检测某个动画精灵组当中的“精灵们”是否与其他的动画精灵碰撞。spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
#动画精灵,精灵们#是否要移除组中所有的精灵?True or False
#什么样的方式碰撞,默认为矩形碰撞
下面,我们完成一个小游戏,加载几个小飞机,互相碰撞以后 ,反向移动。
首先,定义PlaneClass,与上节课程序类似。
import sys
import pygame
from pygame.locals import *
from random import *
class PlaneClass(pygame.sprite.Sprite):
def __init__(self, img_file, location, speed):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(img_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
self.speed = speed
def move(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > height:
self.speed[1] = -self.speed[1]
定义一个函数collide_check()用来检测小飞机之间是否产生碰撞。
def collide_check(group): #传入参数group为小飞机动画精灵的组
#先移动所有的小飞机
for plane in group:
plane.move()
#分别检测每一个小飞机是否与其他小飞机碰撞
for plane in group:
group.remove(plane) #第一步,从组中删除该精灵(小飞机)
#第二步,调用spritecollide()方法检测该精灵是否与组中其他精灵碰撞
if pygame.sprite.spritecollide(plane, group, False):
#如果产生碰撞,则该精灵的运动方向反向
plane.speed[0] = -plane.speed[0]
plane.speed[1] = -plane.speed[1]
group.add(plane) #第三步,再将该精灵加入组
screen.blit(plane.image, plane.rect)
主程序中,创建精灵组,并且将所有的精灵添加到组中。
pygame.init()
screen_size = width, height = 480, 700
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption(“AirplaneSprite”)
background = pygame.image.load(“background.png”).convert()
imgs = [“me1.png”, “enemy1.png”, “enemy2.png”]
img_file = “me1.png”
group = pygame.sprite.Group() #创建精灵组
speed = [2, 2]
for i in range(3):
location_img = [i *120, 10]
speed = [choice([-2, 2]), choice([-2, 2])]
airplane = PlaneClass(imgs[i], location_img, speed)
group.add(airplane) #将各个精灵添加到组中
clock = pygame.time.Clock()
主程序中调用动画精灵碰撞检测的函数
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.blit(background, (0, 0))
collide_check(group) #调用collide_check()函数,检测并更新精灵的位置
pygame.display.flip()
clock.tick(30)
程序完成,但是,有一个问题,小飞机并不是完全碰撞,而是碰撞到了小飞机图像的矩形边(如下图),就认为碰撞了。总觉得不太理想,该怎样让小飞机看上去真的碰撞呢?
pygame提供了一个图像蒙版的模块pygame.mask,用来实现完美检测,该模块中Pygame.mask.from_surface()方法可以通过制定的图像返回一个蒙版(图像轮廓)。
我们我们使用spritecollide(sprite, group, dokill, collided = None) 方法检测碰撞时,只传入了前三个参数,现在我们就要用到第四个参数了,我们在第四个参数传入pygame.sprite.collide_mask
(实际是调用pygame.sprite.collide_mask ()方法),即实现蒙版检测碰撞,只有两个动画精灵的轮廓发生碰撞时,才被认为是碰撞,即完美碰撞。
除了使用蒙版外,还可以使用圆形范围检测。
修改部分代码,实现完美碰撞。
class PlaneClass(pygame.sprite.Sprite):
def __init__(self, img_file, location, speed):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(img_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
self.speed = speed
self.mask = pygame.mask.from_surface(self.image) #获取图像的蒙版
def collide_check(group):
for plane in group:
plane.move()
for plane in group:
group.remove(plane)
#使用图像的蒙版进行检测,从而实现完美碰撞检测
if pygame.sprite.spritecollide(plane, group, False, pygame.sprite.collide_mask):
plane.speed[0] = -plane.speed[0]
plane.speed[1] = -plane.speed[1]
group.add(plane)
screen.blit(plane.image, plane.rect)
播放声音
为了让作品更好玩,更有趣,游戏和其他很多程序都添加了声音(音乐),下面我们学习如何在程序中播放音乐或音效等声音
- pygame中有一个处理声音的模块,即pygam.mixer。该模块提供了一些方法来播放,暂停,停止声音
- 我们可以播放的声音或音乐的文件格式 有以下几种:war格式,mp3格式,wma格式,ogg格式
要播放一段声音,必须初始化pygame.mixer,如下:
pygame.mixer.init()
然后准备播放的声音,我们先试试比较短的声音,这样的声音通常保存为wav文件,对于这种类型的文件,pygame.mixer会使用一个Sound对象,如下:
voice=pygame.mixer.Sound("get_bomb.wav")
voice.play()
另一种大量使用的声音是音乐,音乐大多数以mp3、wma或ogg文件存储,而且文件较大,声音时间较长,要播放这些音乐,pygame会使用mixer中的music模块,如下:
pygame.mixer.music,load("game_music.ogg")
pygame.mixer.music.play
我们写一个小程序,测试一下这两种方法吧。
import sys
import pygame
from pygame.locals import *
pygame.init()
pygame.mixer.init()
screen_size = width, height = 320, 480
pygame.display.set_mode(screen_size)
pygame.display.set_caption(“testVoice”)
#加载wav文件格式的声音并播放
voice = pygame.mixer.Sound("get_bomb.wav")
voice.play()
#加载ogg文件格式的声音并播放
#pygame.mixer.music.load("game_music.ogg")
#pygame.mixer.music.play()
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
pygame.quit()
pygame.display.flip()
为上节课的游戏程序添加背景音乐,背景音乐文件名为”game_music.ogg”。
pygame.init()
#初始化pygame.mixer
pygame.mixer.init()
screen_size = width, height = 480, 700
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("AirplaneSprite")
background = pygame.image.load("background.png").convert()
#加载背景音乐文件”game_music.ogg”
pygame.mixer.music.load("game_music.ogg")
#播放音乐
pygame.mixer.music.play()
当两个小飞机碰撞时,播放碰撞的声音,声音文件为”get_bomb.wav”。
def collide_check(group):
for plane in group:
plane.move()
for plane in group:
group.remove(plane)
if pygame.sprite.spritecollide(plane, group, False, pygame.sprite.collide_mask):
plane.speed[0] = -plane.speed[0]
plane.speed[1] = -plane.speed[1]
#加载声音文件
collide_voice = pygame.mixer.Sound("get_bomb.wav")
#播放声音
collide_voice.play()
group.add(plane)
screen.blit(plane.image, plane.rect)
声音有点大?怎样调节音量呢?
- pygame.mixer.music.set_volume()方法可以调节音乐的音量大小;
- 如果加载的是声音,我们可以使用声音对象的set_volume()方法调节音量,比如本节第一个程序中voice.set_volume(0.2);
- 括号中传入的参数为从0到1的小数,0.5表示最大音量的50%
……[省略代码]
pygame.mixer.music.load(“game_music.ogg”)
pygame.mixer.music.set_volume(0.2) #调节音量
pygame.mixer.music.play()
……[省略代码]
collide_voice = pygame.mixer.Sound("get_bomb.wav")
collide_voice.set_volume(0.2) #调节音量
collide_voice.play()
按下键盘上/下方向键调节背景音乐的音量大小。
#定义调节音量大小的变量
volumeNum = 1.0
……[省略代码]
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
#通过上/下方向键调节背景音乐音量的大小
if event.type == KEYDOWN:
if event.key == K_UP and volumeNum < 1:
volumeNum += 0.1
if event.key == K_DOWN and volumeNum > 0:
volumeNum -= 0.1
pygame.mixer.music.set_volume(volumeNum)
screen.blit(background, (0, 0))
collide_check(group)
pygame.display.flip()
clock.tick(30)
我们发现背景音乐播放一段时间后,就播放结束,不再继续播放。该怎样让音乐重复播放呢?
我们可以在调用play()方法时,在括号内传入一个特殊值 -1,这样音乐就会永远重复下去,如下:
pygame.mixer.music.play(-1)
如果传入其他数值,可以让音乐播放一定的次数,比如 3,可以让音乐重复播放3次。
pygame.mixer.music.play(3)
试着添加更多的音乐。