ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### 5.3.2 计算机动画 顾名思义,动画就是运动的画面,计算机动画就是通过计算机编程来实现运动的画面。计算机动画在很多领域中都有应用,例如游戏开发、电影电视制作、教学演示等。计算机动 画并不神秘,只要掌握了静止图形的绘制方法,就很容易学会活动画面的制作。 现实世界中运动是连续的,而数字计算机只能处理离散量,因此计算机动画本质上只能 是对连续运动的近似和模拟。具体来说,动画是通过在屏幕上快速地交替显示一组静止图形(图像),或者让一幅图形(图像)快速地移动而实现的。每一幅静止图形(图像)称为一 帧,帧与帧之间在画面上只有小部分的不同,于是人眼的视觉暂留现象会使我们产生运动的 感觉。实验表明,每秒显示 24 帧画面能使人眼感觉到最佳的连续运动效果,所以在连续两帧画面之间应该停顿 0.04 秒左右。 动画制作有很多现成软件工具可用,例如在网页和多媒体教学中常用的 Flash。而我们在此介绍的是直接编程实现动画。 下面我们利用 Tkinter 来实现一个简单的动画程序。程序的功能是演示太阳、地球和月 球三个天体之间的运动情况,即月球绕地球运动,并且和地球一起绕太阳运动。 程序规格 输入:没有输入。 输出:演示太阳、地球和月球之间相对运动的动画。 算法设计 首先当然是建立窗口和画布,然后画出太阳、地球和月球三个天体,具体做法与 5.2.3 节中绘制椭圆的例子相似(图 5.9),当然现在需要多画一个月球,并且需要移动地球和月球。 本程序最关键的部分是解决地球和月球沿椭圆轨道移动的计算(假设太阳位置固定不动),下面先解决地球的运动问题。中学数学告诉我们,椭圆可以用如下方程来刻划: ``` x = a cos t y = b sin t ``` 因此,地球在轨道上自西向东旋转时的每一个位置(x,y)都可利用此方程算出,其中椭圆轨 道的 a、b 值是固定不变的,位置只由旋转角度 t 决定(参见图 5.20)。假设地球每次旋转 0.01p 弧度(这就是连续运动的离散化!),则地球的下一位置就是 ``` x' = a cos(t + 0.01 pi) y' = b sin(t + 0.01 pi) ``` 由此可算出 ``` dx = x' - x dy = y' - y ``` 于是可利用画布对象的 move()方法来移动地球到新的位置。 再看图 5.20,由于画布的坐标系原点不是椭圆轨道的中心,椭圆中心在画布坐标系中是(150,100),故地球在 t 角度时的位置应该做个变换: ``` x = 150 + a cos t y = 100 - b sin t ``` 注意画布坐标系中 y 轴是向下的,因此上式计算 x 和 y 坐标时有加减的不同。 ![](https://box.kancloud.cn/2016-02-22_56cafce1ac4c2.png) 图 5.20 地球沿椭圆轨道旋转位置的计算 接下来解决月球的运动问题。首先月球是与地球一起沿椭圆轨道绕太阳运动的,因此月球相对于太阳的位置变化与地球一样由上述 dx 和 dy 决定,即程序 5.3 中的 edx 和 edy。 此外,月球又在绕地球旋转,利用同样方法可算出月球沿绕地球椭圆轨道(设 a、b 的值分 别为 20 和 15)运动时相对于地球的位置变化,即程序 5.3 中的 mdx 和 mdy。最终月球的位 置变化为 edx+mdx 和 edy+mdy。注意,月球绕地球的旋转速度大约是地球绕太阳的旋转 速度的 12 倍(一年有十二个月)。 解决了关键的地球月球的位置计算问题,本程序的算法就明确了,伪代码如下: ``` 算法: 创建窗口和画布; 在画布上绘制太阳、地球和月球,以及地球的绕日椭圆轨道; 设置地球和月球的当前位置; 进入动画循环: 旋转 0.01p; 计算地球和月球的新位置; 移动地球和月球到新位置 更新地球和月球的当前位置; 停顿一会 ``` 代码实现 上面的算法很容易翻译成如程序 5.3 所示的 Python 代码。代码中有两处需要说明一下: 第一,每次循环中修改图形位置后都必须执行一个更新画布显示的方法 c.update(),以 使新画面显示出来;第二,两个画面之间的停顿可以用 time 模块中的 sleep()函数来实 现,该函数的作用就是让程序休眠一会(参数以秒为单位)①。 【程序 5.3】animation.py ``` from Tkinter import * from math import sin,cos,pi from time import sleep def main(): root = Tk() c = Canvas(root,width=300,height=200,bg='white') c.pack() orbit = c.create_oval(50,50,250,150) sun = c.create_oval(110,85,140,115,fill='red') earth = c.create_oval(245,95,255,105,fill='blue') moon = c.create_oval(265,98,270,103) eX = 250 # earth's X eY = 100 # earth's Y m2eX = 20 # moon's X relative to earth m2eY = 0 # moon's Y relative to earth t = 0 while True: t = t + 0.01*pi new_eX = 150 + 100 * cos(t) new_eY = 100 - 50 * sin(t) new_m2eX = 20 * cos(12*t) new_m2eY = -15 * sin(12*t) edx = new_eX - eX edy = new_eY - eY mdx = new_m2eX - m2eX mdy = new_m2eY - m2eY c.move(earth,edx,edy) c.move(moon,mdx+edx,mdy+edy) c.update() eX = new_eX eY = new_eY m2eX = new_m2eX m2eY = new_m2eY sleep(0.04) main() ``` > ① 如果不知道这个 sleep 函数,也可以自己写一个纯粹消磨时间的循环语句,例如循环 1 百万次,每次 都执行无用语句。同样能起到让画面停顿的效果。 图 5.21 是程序 5.3 执行过程中的一个屏幕截图。 ![](https://box.kancloud.cn/2016-02-22_56cafce1bddb3.png) 图 5.21 程序 5.3 的屏幕截图