用Python和Pygame写游戏-从入门到精通(20)
紧接着上一次,我们继续来看如何在Pygame中使用声音。
Sound对象
在初始化声音系统之后,我们就可以读取一个音乐文件到一个Sound对象中了。pygame.mixer.Sound()接受一个文件名,或者也可以使一个文件对象,不过这个文件必须是WAV或者OGG,切记!
1
|
hello_sound
=
Pygame
.
mixer
.
Sound
(
"hello.ogg"
)
|
一旦这个Sound对象出来了,你可以使用play方法来播放它。play(loop, maxtime)可以接受两个参数,loop自然就是重复的次数,-1意味着无限循环,1呢?是两次,记住是重复的次数而不是播放的次数;maxtime是指多少毫秒后结束,这个很简单。当你不使用任何参数调用的时候,意味着把这个声音播放一次。一旦play方法调用成功,就会返回一个Channel对象,否则返回一个None。
Channel对象
Channel,也就是声道,可以被声卡混合(共同)播放的数据流。游戏中可以同时播放的声音应该是有限的,pygame中默认是8个,你可以通过pygame.mixer.get_num_channels()来得知当前系统可以同时播放的声道数,而一旦超过,调用sound对象的play方法就会返回一个None,如果你确定自己要同时播放很多声音,可以用set_num_channels()来设定一下,最好一开始就设,因为重新设定会停止当前播放的声音。
那么Channel对象有什么用呢?如果你只是想简单的播放一下声音,那么根本不用管这个东西,不过如果你想创造出一点有意境的声音,比如说一辆火车从左到右呼啸而过,那么应该是一开始左声道比较响,然后相当,最后右声道比较响,直至慢慢消失。这就是Channel能做到的事情。Channel的set_volume(left, right)方法接受两个参数,分别是代表左声道和右声道的音量的小数,从0.0~1.0。
竖起我们的耳朵
OK,来个例子试试吧~
这个例子里我们通过释放金属小球并让它们自由落体和弹跳,听碰撞的声音。这里面不仅仅有这次学习的声音,还有几个一起没有接触到的技术,最重要的一个就是重力的模拟,我们可以设置一个重力因子来影响小球下落的速度,还有一个弹力系数,来决定每次弹跳损失的能量,虽然不难,但是游戏中引入这个东西能让我们的游戏仿真度大大提高。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
SCREEN_SIZE
=
(
640
,
480
)
# 重力因子,实际上是单位 像素/平方秒
GRAVITY
=
250.0
# 弹力系数,不要超过1!
BOUNCINESS
=
0.7
import
pygame
from
pygame
.
locals
import
*
from
random
import
randint
from
gameobjects
.
vector2
import
Vector2
def
stero_pan
(
x_coord
,
screen_width
)
:
"""这个函数根据位置决定要播放声音左右声道的音量"""
right_volume
=
float
(
x_coord
)
/
screen_width
left_volume
=
1.0
-
right_volume
return
(
left_volume
,
right_volume
)
class
Ball
(
)
:
"""小球类,实际上我们可以使用Sprite类来简化"""
def
__init__
(
self
,
position
,
speed
,
image
,
bounce_sound
)
:
self
.
position
=
Vector2
(
position
)
self
.
speed
=
Vector2
(
speed
)
self
.
image
=
image
self
.
bounce_sound
=
bounce_sound
self
.
age
=
0.0
def
update
(
self
,
time_passed
)
:
w
,
h
=
self
.
image
.
get_size
(
)
screen_width
,
screen_height
=
SCREEN_SIZE
x
,
y
=
self
.
position
x
-
=
w
/
2
y
-
=
h
/
2
# 是不是要反弹了
bounce
=
False
# 小球碰壁了么?
if
y
+
h
>=
screen_height
:
self
.
speed
.
y
=
-
self
.
speed
.
y
*
BOUNCINESS
self
.
position
.
y
=
screen_height
-
h
/
2.0
-
1.0
bounce
=
True
if
x
<=
0
:
self
.
speed
.
x
=
-
self
.
speed
.
x
*
BOUNCINESS
self
.
position
.
x
=
w
/
2.0
+
1
bounce
=
True
elif
x
+
w
>=
screen_width
:
self
.
speed
.
x
=
-
self
.
speed
.
x
*
BOUNCINESS
self
.
position
.
x
=
screen_width
-
w
/
2.0
-
1
bounce
=
True
# 根据时间计算现在的位置,物理好的立刻发现这其实不标准,
# 正规的应该是“s = 1/2*g*t*t”,不过这样省事省时一点,咱只是模拟~
self
.
position
+
=
self
.
speed
*
time_passed
# 根据重力计算速度
self
.
speed
.
y
+
=
time_passed
*
GRAVITY
if
bounce
:
self
.
play_bounce_sound
(
)
self
.
age
+
=
time_passed
def
play_bounce_sound
(
self
)
:
"""这个就是播放声音的函数"""
channel
=
self
.
bounce_sound
.
play
(
)
if
channel
is
not
None
:
# 设置左右声道的音量
left
,
right
=
stero_pan
(
self
.
position
.
x
,
SCREEN_SIZE
[
0
]
)
channel
.
set_volume
(
left
,
right
)
def
render
(
self
,
surface
)
:
# 真有点麻烦了,有爱的,自己用Sprite改写下吧……
w
,
h
=
self
.
image
.
get_size
(
)
x
,
y
=
self
.
position
x
-
=
w
/
2
y
-
=
h
/
2
surface
.
blit
(
self
.
image
,
(
x
,
y
)
)
def
run
(
)
:
# 上一次的内容
pygame
.
mixer
.
pre_init
(
44100
,
16
,
2
,
1024
*
4
)
pygame
.
init
(
)
pygame
.
mixer
.
set_num_channels
(
8
)
screen
=
pygame
.
display
.
set_mode
(
SCREEN_SIZE
,
0
)
pygame
.
mouse
.
set_visible
(
False
)
clock
=
pygame
.
time
.
Clock
(
)
ball_image
=
pygame
.
image
.
load
(
"ball.png"
)
.
convert_alpha
(
)
mouse_image
=
pygame
.
image
.
load
(
"mousecursor.png"
)
.
convert_alpha
(
)
# 加载声音文件
bounce_sound
=
pygame
.
mixer
.
Sound
(
"bounce.ogg"
)
balls
=
[
]
while
True
:
for
event
in
pygame
.
event
.
get
(
)
:
if
event
.
type
==
QUIT
:
return
if
event
.
type
==
MOUSEBUTTONDOWN
:
# 刚刚出来的小球,给一个随机的速度
random_speed
=
(
randint
(
-
400
,
400
)
,
randint
(
-
300
,
0
)
)
new_ball
=
Ball
(
event
.
pos
,
random_speed
,
ball_image
,
bounce_sound
)
balls
.
append
(
new_ball
)
time_passed_seconds
=
clock
.
tick
(
)
/
1000.
screen
.
fill
(
(
255
,
255
,
255
)
)
# 为防止小球太多,把超过寿命的小球加入这个“死亡名单”
dead_balls
=
[
]
for
ball
in
balls
:
ball
.
update
(
time_passed_seconds
)
ball
.
render
(
screen
)
# 每个小球的的寿命是10秒
if
ball
.
age
>
10.0
:
dead_balls
.
append
(
ball
)
for
ball
in
dead_balls
:
balls
.
remove
(
ball
)
mouse_pos
=
pygame
.
mouse
.
get_pos
(
)
screen
.
blit
(
mouse_image
,
mouse_pos
)
pygame
.
display
.
update
(
)
if
__name__
==
"__main__"
:
run
(
)
|
这么久了,咱们的游戏终于有了声音,太棒了!不过,是不是游戏的背景音乐也是用mixer来播放的呢?这不是一个好主意,因为背景音乐往往很长,比较占资源,pygame中另外提供了一个pygame.mixer.music类来控制背景音乐的播放,这也是我们下一次要讲的内容。