2.3 让精灵表单动起来
学习如何制作精灵表单动画对于我们是很有帮助的。它可以用于粒子特效,或翻书的效果,不过它最常用于2D侧卷轴游戏。
精灵表单,也可以称为精灵图集(sprite atlas),如果你不熟悉这个术语,你可以把它理解为一个巨大的图片,里面镶嵌了很多小图片,有时也称为图片序列。
当你逐个遍历表中的每个小图片时,你会得到一个类似动画的效果。这个概念如同一个便签的翻页效果或者一部电影胶卷的帧图像一样。如果我们循环精灵表单的每一帧,我们将创建一个动画效果。
本节在编写代码时将使用一点点数学公式,不过不用担心,我们会逐行解释所编写的代码。
2.3.1 准备工作
为了能够测试我们的着色器代码,还应该做一些美术方面的工作。我们需要自己制作一个精灵表单或者在网上找一个。精灵表单并不复杂,它只需提供一个能够浏览的图像序列。
创建一个新的材质和新的着色器,接着在场景视图中将你创建的材质设置为一个平面的材质。然后将精灵表单拖到材质的纹理上。
2.3.2 如何操作
输入如下代码使精灵动画着色器开始工作:
1.在着色器的Properties块中创建三个新属性。这样我们可以直接从材质Inspector面板中修改系统,而不必涉及代码:
2.然后将输入的UV存储到独立变量,以便在代码中使用它们的值:
3.下面,我们需要得到每个单元格的宽度。在精灵表单中,每个网格占用范围为0到1之间的值,所以我们需要计算每个网格的百分比值:
4.接下来,我们必须使用系统自带的时间组件,这样我们才能使网格一个个地运动或者偏移起来:
5.最后,我们计算出了精灵表单x方向上的UV偏移量。现在,你应该拥有了一个可以创造翻书效果的着色器。
下面就是表面着色器使用物体的UV偏移实现的结果。同样,你应该相信下图其实是可以动起来的。
2.3.3 实现原理
首先,我们从Input结构体中得到UV值,将其存到一个单独变量中。不过也不一定必须这样做,因为它只是一种相对较好的选择,而不是一个硬性的要求,这只是我们阅读代码的方式而已。在这里,我们将该变量命名为spriteUV,并且声明为float2变量类型。因为我们需要将网格UV的x和y坐标都存到一个变量中。
接下来,我们得到当前纹理的宽度大小,然后根据Properties块中的_CellAmount值将其划分成相同大小的小图片。假如我们有一个宽度是512的纹理,我们把它分为16个单元格,就会得到值32。这就是每个单元格的宽度,但是我们还需要得到每个单元格占整个图片的百分比,这是因为UV值的范围是在0~1之间或者0%~100%之间的。这样我们得到了cellPixelWidth变量(小单元格的宽度),然后除以纹理图片的总宽度。如果我们将32像素除以512像素,可以得到0.06或者纹理图片整个宽度的6%的值。这个值就表示我们从一个单元格到另一个单元格的UV偏移量。
然后,我们需要计算随时间增大的偏移值,这些值都是整数。 例如,一个整数值依次从0,1,2,3,4开始增大,直到增大到精灵表单中单元格的数目。为了做到这些,我们可以使用CGFX的内置函数fmod()。
如果我们对x值使用fmod()函数并将它除以y值,会得到相除的余数。所以,当我们使用_Time代替x值,使用_CellAmount值代替y值后,就会得到一个随时间增加的返回值。并且当它的值等于_CellAmount值,它会重新从0开始增长。
得到随时间增长的值后,我们需要使用ceil()函数来确保该值是个整数而不是一个浮点数。该函数的基本功能就是向上取整,比如将1.5取整得到2。这样就可以得到整数0,1,2,3,4,...直到等于属性值_CellAmount。一旦达到单元格的数目大小时,它就重新归为0。
最后,我们得到Input?结构体中当前的UV值,然后计算百分比值、时间值还有单元格总数三者相乘的值,将得到的值加到刚才的UV值上。这样,我们就实现了从一个单元格到下个单元格的偏移,但是我们必须对UV值进行缩放使一个时间点只能看到一个单元格,要达到这种效果,我们将UV偏移量乘以百分比,得到最终的UV值。所以这些做完之后就可以将UV值放入tex2D函数中用于纹理渲染了。
2.3.4 更多内容
也许你已经注意到了,你不必只对一个方向进行偏移。就像我们在前面的章节中实现滚动纹理时使用了两个方向上的UV偏移,我们也可以实现一个2D的精灵表单动画。你只需在最后的偏移量上添加一个y方向上的偏移值。
这些步骤跟创建水平滚动一样,但是你可以在多维度里循环一个更大的精灵表单。虽然你做的只是增加了着色器的边数,但是最后可能会加入大量的着色器指令。这意味着它会影响应用程序的性能。
为了改善这一情况,我们可以将帧偏移选择的代码转移到C#脚本中,从而让CPU来分担GPU的部分负担。这其实要求我们在需要优化的时候对我们的应用程序进行一个平衡,在不损失后面元素的情况下,设计我们的应用程序。本书中包含了一个C#脚本,它演示了一个简单的精灵动画系统如何使用脚本将数据传递到着色器。它基本上是使用了时间计算,只是通过下面的代码将时间值传递给我们的着色器: