第17章 创建动画应用
本章将讨论另一类应用——包含简单动画(会移动的物体)的应用。你将学习用App Inventor创建二维游戏的基本知识,包括熟练使用精灵组件,以及处理像两个物体碰撞这样的事件。
当在电脑屏幕上看到一个平滑移动的物体时,你实际上看到的是一连串快速移动的图片,图片每次只移动很短的距离。与手翻书(一种通过快速翻页来实现动画效果的书)一样,它利用了人类的视觉暂留现象,这也是那些精美绝伦的动画电影的本质。
App Inventor中的动画是这样实现的:将球或精灵放置在画布上,然后编写程序,让这些物体随时间做微小的移动或变换,时间间隔以毫秒计。本章将讲解画布的坐标系统,学习利用计时器的计时事件来触发运动,学会控制运动的速度以及处理两个物体的碰撞事件,等等。
在应用中添加画布组件
从组件面板的绘画动画分组中拖出画布组件,然后定义它的宽度及高度属性。通常我们希望画布与屏幕等宽,为此将宽度设为“充满”。
可以用同样的方式设定高度属性,但一般会将其设为一个具体的数字(如300像素),以使画布的上方或下方留有足够的空间容纳其他组件。
画布的坐标系统
画布上的图画实际上是一个由像素拼接而成的网格。像素是屏幕上能够显示的最小色块(一个宽高均为1个单位的小方格),每个像素的位置由网格系统的x-y坐标系定义,如图17-1所示。在这个坐标系中,x定义了像素在水平方向上的位置(从屏幕左侧的0开始,向右为增大方向),y定义了像素在垂直方向的位置(从屏幕顶部的0开始,向下为增大方向)。
位于画布左上角的方格是坐标系中两个坐标轴的原点,坐标值为0,因此这一点的坐标可以表示为(x=0, y=0)。向右移动时,x值增大;向下移动时,y值增大。紧邻左上角方格右侧的方格,其坐标为(x=0, y=0),右上角方格的x坐标等于画布的宽度减1。大部分手机屏幕的宽度在300(目前市场上的高端安卓手机屏幕宽度已达1440像素。——译者注)个像素左右,但图17-1中所示的屏幕宽度只有20个像素,因此右上角的像素坐标为(x=19, y=0)。
有两种方法可以改变画布外观:在画布上绘画,或者在画布中放置移动的物体(还可以设置画布的背景图片属性。——译者注)。本章主要关注后者,不过我们首先来讲解如何在画布上绘画,以及如何通过绘画来创建动画(这也是第2章中的主要内容)。
画布中的每一个方格(即像素)都呈现出单一一种颜色。画布组件提供了画线及画圆功能,可以用于在画布上绘制像素组成的图画。首先将画布的画笔颜色属性设为需要的颜色,然后调用画布的绘画功能来画出该颜色。其中的画圆功能可以绘制直径为任意大小的圆,但如果半径设为1,如图17-2所示,那么只能画出一个方形的像素点。(新版的App Inventor中画圆功能新增了一个参数“实心”,当实心设为真时,绘制实心圆,否则绘制空心圆。——译者注)
在App Inventor的编程视图中,点击内置块的颜色抽屉,可以看到13种常用颜色的块。可以选择其中的任意一种颜色,来绘制像素图(这些颜色同样可以用于设置组件的背景颜色、字体颜色等相关属性)。也可以使用三组0~255的数字来合成更为丰富的颜色,数字合成颜色的方法请参见相关App Inventor文档:http://appinventor.googlelabs.com/learn/reference/blocks/colors.html 。
改变画布外观的第二种方法是在画布上添加球或精灵组件。精灵是一个被放置在舞台上的图形对象,而App Inventor中的舞台就是画布组件。球与精灵实际上是同种类型的组件,只是外观不同而已。球的外形为圆形,只能通过改变颜色和半径来改变它的外观,而精灵的形状取决于图片属性中图片的形状(可以是任意形状)。两种精灵组件都只能添加到画布中,如果你试图将它们拖到用户界面中画布以外的区域,那是徒劳的。
用计时事件制作动画
在App Inventor中,为应用添加动画的方法之一就是让物体对计时器的计时事件做出响应。最常用的方法就是让精灵按照设定的时间间隔,在画布上改变位置。通过设定计时间隔,在计时事件中移动精灵,这是最常用的动画制作方法。稍后我们还将讨论另一种方法,即,利用精灵类组件特有的速度及方向属性,通过编程来实现动画效果。
我们很容易理解某些由用户引发的事件,如按钮点击事件。用户做了一个动作,应用就会执行一系列的操作来响应用户的行为。但是计时事件则不然:这类事件不是由最终用户发起的,而是靠时间的流逝来触发的。你需要对这两类事件加以区分,即手机时钟触发的事件不同于用户引发的事件。
为了定义计时事件,首先要在设计视图中将一个计时器组件拖到应用中。计时器组件用计时间隔属性来定义两次计时事件之间的时间间隔,用毫秒为单位来计量时间。如果将计时间隔设为500,就意味着每隔半秒钟触发一次计时事件。计时间隔越小,动画的帧频(每秒钟图像变化的次数)也就越高。
在设计视图中添加了计时器组件,并设定了它的计时间隔之后,就可以在编程视图中拖出“当计时器1到达计时点时”块,并在其中添加任何你需要的块,这些块将每隔一个计时间隔执行一次。
产生运动
要让精灵组件随时间移动,需要用到精灵组件的“移动到指定位置”功能,球及精灵都具备这一功能。例如,要使一个球在水平方向上穿越屏幕,需要使用图17-3中的块。
“移动到指定位置”的作用是将物体移动到画布上的某个绝对位置,而不是相对位置。因此,为了实现这样的移动,需要将“移动到指定位置”块的x、y坐标参数设定为当前位置与增量之和。这里我们要实现球的水平移动,只需要将参数x设定为当前球1的x坐标与增量20之和,而y值保持不变(球1的y坐标)。
如果想让物体向下移动,只需修改球1的y坐标,而x坐标保持不变。如果想让物体沿着对角线方向移动,就需要同时设定x、y坐标的增量,如图17-4所示。
控制速度
在前面的例子中,球的移动有多快呢?速度取决于两个因素:计时器组件的计时间隔,以及“移动到指定位置”块中的参数值。如果计时间隔设为1000毫秒,就意味着每秒钟触发一次计时事件,这样,在图17-3的例子中,球每秒钟移动20个像素。
1000毫秒这样的时间间隔无法产生流畅的动画效果。想象一下,球每秒钟才会移动一次,这会显得很突兀。为了得到更为平滑的运动效果,需要缩短计时间隔。如果将计时间隔设为100毫秒,则球每隔1/10秒移动20像素,即每秒移动200像素,对于应用的使用者来说,这个速度看起来会平滑得多。
碰撞检测
要制作游戏以及其他的动画类应用,不仅要展示出运动的效果,还要实现更为复杂的功能。幸运的是,App Inventor提供了几个高级块,可以处理与运动有关的事件,如精灵的碰到边界事件,或两个精灵之间的碰撞事件。
这里所说的高级块,意味着你不必关心精灵之间的碰撞事件是怎样发生的,也不必关心碰撞会在何时发生,因为App Inventor已经精心地设计并实现了这些底层的技术细节,你只需要考虑应用的逻辑就可以了。其实你也可以利用计时器的计时事件,手动地实现碰撞检测:通过检查精灵及画布的相关属性值,来判断是否发生了碰撞,不过这种类型的程序会涉及比较复杂的逻辑。幸运的是,App Inventor已经为你提供了这些现成的功能,这是动画类应用开发者的福音,因为在这类应用中都少不了对碰撞的检测及处理。
碰到边界
重新考虑图17-4中的动画,让物体从画布的左上角沿对角线方向向右下角移动。如果采用前面的程序,物体沿对角线方向移动并将停在画布的右侧或底部边缘(因为系统不允许精灵超出画布的边界)。
如果想让物体在到达边缘时重新回到画布的左上角,可以利用精灵的碰到边界事件,来编写一段程序,如图17-5所示。
当球或精灵碰到画布的任何一条边时,将触发碰到边界事件。这个事件,再加上前面提到的让球沿对角线移动的计时事件(图17-4),两个事件共同作用的结果就是,球从左上角向右下角移动,在到达某个边缘后再跳回到左上角,然后继续沿对角线移动,并再次跳回,循环往复,永不停止(直到接到其他指令)。
在这个例子中,我们没有区分球碰到的是哪条边。在碰到边界事件中有一个参数——边界代码,它代表球碰到的那条边,这里用数字来代表八个不同的方向:
- 北(顶边) = 1
- 东北(右上角) = 2
- 东(右边) = 3
- 东南(右下角) = 4
- 南(底边) = -1
- 西南(左下角) = -2
- 西(左边) = -3
- 西北(左上角) = -4
碰撞事件与分离事件
在游戏或其他类动画应用中,通常都会涉及两个或多个物体之间的碰撞(如球拍击中球)。
例如,考虑这样一个游戏,当其中的一个物体与其他物体发生碰撞时,会改变颜色并发出爆炸声。图17-6中给出了实现这一功能的事件处理程序。
分离事件是与碰撞相反的事件,当两个碰到一起的物体分开时,触发该事件。因此在游戏中,可能会用到图17-7中的代码块。
注意,碰撞事件和分离事件中都有一个参数——其他精灵,它代表了被撞(或正在与之分离)的那个精灵。这个参数可以用来实现与特定精灵之间的互动。如图17-8所示。
之前我们没有提到过图中的“球拍”块,这个块代表组件本身,而非组件的某个属性。如果需要比较两个组件(得知究竟是哪一个与之碰撞),就要用到这个块。每个组件都有一个指向它自己的块,这个块就在组件的代码块抽屉中(最后一个块),与组件同名。
交互动画
到目前为止,我们所讨论的动画行为都没有最终用户的参与。毫无疑问,游戏是个互动的过程,最终用户扮演着核心的角色,通常他们使用按钮或其他界面组件来控制物体的移动速度及方向。
作为例子,我们来修改沿对角线移动的动画,用户可以让球开始或停止移动。可以通过编写按钮的点击事件处理程序,设置计时器的启用属性,来实现这一功能。
在默认情况下,计时器组件的启用属性是被选中的(属性值为真),可以在事件处理程序中动态地设置该属性的值(真或假)。例如,在图17-9的事件处理程序中,当用户第一次点击按钮时,计时器停止运行。
在计时器的启用属性值被设为假之后,计时事件不再被触发,因此球停止移动。
当然,只是在第一次点击按钮时让球停止移动,这样的情节并不能让游戏有趣。需要在事件处理程序中添加一个“如果...则...否则”块来控制计时功能的启用与禁用,从而实现对运动的双向控制(运动及停止)。如图17-10所示。
在按钮的点击事件处理程序中,第一次点击按钮时,计时器停止计时,按钮上的文字由“停止”变为“开始”;第二次点击按钮,此时计时器的启用属性值为假,因此程序执行“否则”分支,于是计时器被置于启用状态,使得物体重新开始移动,按钮上的文字改回“停止”。关于“如果...则...否则”块的详细信息请参见第18章。另外,关于用方向传感器创建交互动画的例子,请参见第5章及第23章。
设置精灵的非计时器动画
至此,我们讲解的动画都离不开计时器组件——设置当计时事件发生时物体的移动方式。这也是最普遍的产生动画的方法。除了简单的位置移动,还可以随时间改变物体的颜色,动态输入文字(模拟打字输入过程),或者以适当的语速读出文字,等等。
如果只是要移动物体,App Inventor还提供了一种不需要计时器的动画制作方法。或许你已经注意到,球及精灵都有一些与运动相关的属性:方向、速度、间隔,因此无需借助计时器的计时事件,你也可以让精灵动起来,只要在设计视图或编程视图中做必要的设置即可。
为了便于描述,我们来重新考虑沿对角线移动的例子。先来说明一下球或精灵的方向属性,它的取值范围为0~360度,如图17-11所示。
如果方向属性设置为0,则球从左向右移动;如果设为90,则自下而上移动;如果设为180,则从右向左移动;如果设为270,则自上而下移动;当然,如果设为315,则从左上角向右下角移动。
此外,还需要设置精灵的速度属性,它可以是0以外的任何值。物体移动的速度取决于两个因素:速度属性及间隔属性。速度属性决定物体每次移动的距离(以像素为单位),而间隔属性决定物体隔多长时间移动一次。
为了测试这些属性对运动的影响,用画布及球组件创建一个测试应用,并连接测试设备或模拟器,准备实时测试。然后尝试修改方向、速* 间隔属性,看看球是如何运动的。
例如,假设你想让球在画布的左上角与右下角之间做连续的往复运动,可以在设计视图中,将球的速度属性设为5,间隔属性设为100,方向属性设为315,然后在编程视图中添加球1的碰到边界事件处理程序,当球到达画布边缘时,改变它的方向。如图17-12所示。
小结
利用画布组件,可以在设备屏幕范围内开辟出一块区域,使其成为物体移动及相互作用的场所。画布组件中可以容纳两类组件:球及精灵。
动画是事物随时间的变化,可能是位置的移动,也可能是某些外观属性的变化。你可以借助计时器组件的计时事件,通过编程来实现动画效果,包括位置的移动或其他图形变换效果;如果你只想让物体移动,也可以采用另一种方法,即通过设置精灵组件的方向、速度及间隔属性,来产生动画效果。
无论使用哪一种方法,你都可以利用App Inventor处理事件的高级功能,来处理物体之间的碰撞。