You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
10 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 第13讲 | 如何设置淡入淡出和碰撞检测?
我们在前一节,学习了精灵的变形、放大和缩小,并且学习了如何使用精灵类和组的概念来管理精灵,制成动画。今天,我将带你学习淡入淡出和碰撞热点的判断。
所谓的**淡入淡出**,就是英文的**fade-in**和**fade-out**。淡入淡出在电影、游戏、CG、操作系统、手机UI、应用等等各种地方随处可见。那究竟什么是淡入淡出呢它在游戏中究竟如何实现呢在我们的打飞机游戏中什么时候会用到这个操作呢
## 什么是淡入淡出?
不知道你有没有注意,在我们玩过的打飞机游戏中,当每一关游戏开始的时候,都会有个游戏画面逐渐出现的过程。短短几秒,从无到有,整个画面就呈现在你眼前了。同样,每一关结束的时候,也会有个画面逐渐消失的过程。
从**画面效果**讲,这个画面从有到逐渐屏幕变暗,直到消失,或者反过来,由暗逐渐变亮,到完全进入画面的过程,就叫做淡入淡出。从**声音**角度讲,也存在淡入淡出,比如音乐从无声到逐渐有声,或者从有声到逐渐无声。
**在Pygame中并不存在“画面的淡入淡出”这样的函数需要我们自己去实现这样的功能。**
首先如果我们想给这张图片进行淡入淡出的处理的话就需要对它进行alpha混合处理。我们在前面谈到过alpha混合你可以理解成半透明但是alpha混合究竟是什么呢
**alpha混合**就是将一部分被遮盖的图像进行半透明处理。在游戏引擎或者游戏库中图像的alpha值是可以被修改的。每动态修改一次alpha值就会让图像更透明或者更不透明。通过制作出alpha效果我们可以在游戏中实现各种绚丽的效果。
一般来讲底层图形接口的颜色为32位的值包含RGB以及Aalpha其中红色R、绿色G和蓝色B各为8位alpha也为8位所以合起来是32位的颜色值。
但是如果不存在A通道那么就是24位的颜色值。每个颜色值都有256个级别的值从程序角度是从0到255而支持alpha通道的图片格式有png、tiff等。但是如果没有带alpha透明通道的图我们也可以在程序中设置它的alpha值来做透明。
如果是Pygame在load image函数的时候不要处理alpha也就是不要调用convert\_alpha函数。具体为什么呢我后面给你揭晓。
## 如何做出淡入淡出效果?
我们在没有背景图片载入的时候做淡入淡出效果就不是使用alpha通道了而是需要用**fill函数**来填充背景色。
如果背景色是0,0,0也就是纯黑的话那么就需要将0,0,0逐渐变成255,255,255来变成纯白或者你自己定义一个RGB值来完成最终淡出后的背景色。
我们现在来看一下这段代码。
```
pln = pygame.image.load(plane).convert()
a=0
while True
pln.set_alpha(a)
screen.blit(pln, (20, 150))
if a > 255:
a=0
screen.fill([a,a,a])
a += 1
```
这段代码中我们开始载入飞机图片。注意一下我们没有用convert\_alpha。如果我们用了convert\_alpha就会出现设置的alpha值没有任何作用。因为在载入的时候已经处理了alpha值了。
随后我们定义一个变量a这个a既作用在screen.fill上将fill的RGB值进行变换也作用在set\_alpha这个函数里这个函数将图片的surface进行alpha值的设置最后blit出来呈现在屏幕上。
我们呈现的效果就是这样。
![](https://static001.geekbang.org/resource/image/2b/1c/2b6bc9023c281fa5ae7f44bbe51ef01c.jpg)
其他图片也可以做alpha混合我们将最早的背景jpg图片传入进行alpha半透明调整效果是这样的。
![](https://static001.geekbang.org/resource/image/9c/ac/9c71cf0a87d6ebb9c2fd6e7f323d09ac.jpg)
## 如何设置碰撞检测?
说完了alpha混合我们现在要来学习一下碰撞相关的内容。这个很好理解飞机相撞了就要用到碰撞。
事实上在游戏中碰撞属于物理引擎的一部分。特别是在3D游戏当中物理引擎是独立于图形引擎的一个模块。程序员需要将图形引擎的对象填入到物理引擎中计算出碰撞的结果然后再返回给图形引擎给出画面效果。做得精致的2D游戏也有独立的物理引擎专门检测碰撞、计算重力等等。
但是在今天我们的课程中,我将使用浅显易懂,用你最能看懂的代码来解释碰撞是怎么回事。
**事实上,我们今天要讲到的碰撞是两个图片相交之间的碰撞检测,这并不算物理检测,而是图片检测。**
既然我们要检测的是图片,那么哪些前置信息是我们需要知道的呢?
首先,我们肯定要知道这两张需要碰撞图片的长宽,才能计算图片是否相交。在计算图片相交的时候,我们首先要知道它**所在位置的x轴的起点**,然后要知道它的**图片宽度**,然后我们要知道**图片位置的y起点**,以及它的**图片长度**,这样我们就得到了图片的长宽。
我们用上面的主角飞机图片和敌人飞机图片来做演示。
![](https://static001.geekbang.org/resource/image/86/68/861154a6a7403ef7113c259f65425d68.jpg)
让两架飞机面对面,敌人的飞机从上往下飞,主角飞机从下往上飞。如果两架飞机碰到,我将在后台的命令行窗口显示一些字符串。
### 定义碰撞函数
接下来,我们来看一下,如何定义这个碰撞函数。
```
def collide(a, axy, b, bxy):
a_x1, a_x2 = axy[0], axy[0]+a.get_width()
a_y1, a_y2 = axy[1], axy[1]+a.get_height()
b_x1, b_x2 = bxy[0], bxy[0]+b.get_width()
b_y1, b_y2 = bxy[1], bxy[1]+b.get_height()
a1, a2 = range(a_x1, a_x2) , range(a_y1, a_y2)
b1, b2 = range(b_x1, b_x2) , range(b_y1, b_y2)
ct = 0
for a in a1:
if a in b1:
ct = 1
break
for a in a2:
if a in b2:
if ct == 1:
return True
return False
```
我们来仔细地看一下这段函数。
首先,**collide函数**拥有四个参数。第一个参数是第一幅图片的对象第二个参数接收一个元组接收第一幅图片所在的x轴和y轴第三个参数是第二幅图片的对象第四个参数接收一个元组接收第二幅图片所在的x轴和y轴。
随后,代码进入一个**得到长宽**的过程。
a\_x1获取a图片对象所在屏幕的x点的起始位置这个位置由第二个参数的元组下标0提供a\_x2获取a图片对象所在屏幕的x点的终止位置事实上是它的宽度由于有x轴的起始坐标的关系所以需要起始坐标加上图片宽度才是它真实的x坐标结束点。
a\_y2获取a图片对象所在屏幕的y点的起始位置这个由第二个参数的元组下标1提供a\_y2获取a图片对象所在屏幕y点的终止位置其实是它的长度和前面的x轴一样需要加上y轴所在屏幕的位置才是真正的y轴的结束点。
和a图片是一个道理b图片我就不作具体阐述了。
接下来,我们需要知道整个图片所在的屏幕点,那么我们就需要用到**range函数**。
Python的range函数是自动形成的一串整数列表。它的函数原型是这样的。
```
range(start, stop, [step])
```
其中步长step可以省略。因为默认是1所以如果在range中输入了开始和结束就会形成一个列表。如果省略了stop就会从0开始计数形成一串列表。比如range(5)那就会形成01234。
我们在range中形成了一串列表其中a1对应的是a图片x值的起始点到终止点的列表a2对应的是a图片y值的起始点到终止点的列表。接下来的b1和b2就不做阐述了和a1是相同的代码逻辑。
### 碰撞的检测
随后,我们就需要进行碰撞的检测了。
首先我们先要判断a图片x轴的列表数字里面是不是存在b图片的x轴的数字。如果存在那么就把计数加1跳出循环。
接下来我们再判断a图片的y轴的列表数字里面是不是存在b图片的y轴的数字。如果存在那么就返回为真True就说明碰撞检测成功了。如果计数等于0或者计数等于1但是并没有通过y轴的列表检测那么就返回假False
我们来看一下传入参数的代码。
```
y1, y2 = 1, 1
screen.blit(pln, (100, 300 + y1))
screen.blit(enm, (100, 20 + y2))
print collide(pln, (100,300+y1), enm, (100,20+y2))
y1-=1
y2+=1
```
我们在blit绘制的时候y轴加入了一个变量就是y1和y2。其中主角的飞机pln对象y轴始终减1敌人的飞机enm始终加1为的就是让两架飞机对向飞过来并且检测碰撞。
我们将pln和 enm以及它们所在的位置分别传入collide函数进行检测。我们将在命令行后台打印True或者False。如果是False就是没有碰撞如果是True就是碰撞了。
![](https://static001.geekbang.org/resource/image/bb/35/bbc8171fa91b1cc49bede8ba38f2ea35.jpg)
当两架飞机碰到的时候True就出现了那是因为x轴和y轴都有不同程度的重叠。所以在collide函数里面就返回了True。
另外在Pygame里精灵类当中也有碰撞检测的类可以提供使用但是**使用碰撞检测类可以用来进行球形的判断,而不能用于矩形图片之间的判断**。 这是更为高级和复杂的用法,在这里不做更深的阐述了。
## 小结
今天我和你讲解了淡入淡出以及碰撞的热点检测。我们需要设置Alpha混合和背景填充来实现淡入淡出而普通图像碰撞的检测则是通过判断图像x轴和y轴是否重叠来实现。
给你留个小问题吧。
如果给你一张图片,需要判断精准的碰撞,比如碰到机翼,或者碰到某一个非矩形的位置,你该如何判断碰撞结果?
欢迎留言说出你的看法。我在下一节的挑战中等你!