写在前面
关于编程语言,江湖上曾经流转着一句话,“真正的程序员用C++”。好吧,可不论怎么说我也不想为了成为了一个真正的程序员跳进C++这个大坑。家里翻了大半本之后闲置的C++ Primer,我实在是不知道什么时候能有勇气翻完。完全没什么动力啊,翻完了它短时间内我也不知道能干啥。--!
这里可以引出processing了。这个语言把常用的图形相关的操作都抽象封装好了,你只需要简单的调用它们就行了。size(320,240)就可以建立一个320x240大小的画布,现在想在画布上画一个点,调用point(60,80),就可以在(60,80)的位置画一个点了。想画一条线只需调用line(0,0,width,height),就可以画一条从左上角到右下角的线。width和height就是画布的宽和高的尺寸,processing会自动把它转换成实际的画布尺寸。
看看它的官网介绍吧。processing就是设计出来用来学习视觉艺术和可视化编程用的,用它的大都是些搞艺术的或者业余爱好者们。正因为如此,它被设计的非常简单和易学,上手它真的不需要多长时间。有编程基础的,特别是有学过java的,可能都不需要几分钟就能上手了--它默认使用的就是个简化版的java,所以它也是跨平台的,不管你用的是windows,mac os 还是linux,都能用。
另外,processing最大的优点是它还是免费的。你可以点这里选择No Donation免费下载到它。当然了,你也可以根据你的能力选择其他的选项给予捐助。
图形基础
首先要说明的是processing支持android mode,java mode,python mode...等等好几个模式,我选择的是python 。不是为了什么高尚的理由,只是因为我懒,想少敲几个括号少写几个分号而已。顺便安利一下:python也是挺简单方便的,貌似我也没看几天教程。反正我不是高手,有时候自己太笨了那就用笨一点的方法呗,能够解决问题就行了。
不啰嗦了,不论我们要画什么,都得有个地方去画吧。
所以,第一句得从一块画布开始:
size(320,240)
这是建立一个320像素宽,240像素高的画布的命令。
Ctrl+R,然后就可以看到相应大小的画布了。
你当然可以改成你想要的大小,如size(800,600)
有了画布,点、线、面走起:
1.从一个点开始:
size(320,240)
point(160,120)
320x240px的画布,在(160,120)的位置画了一个小黑点。
不太明显是吧,没事,我们可以加上这两句:
size(320,240)
background(255)
strokeWeight(10)
point(160,120)
background(255)表示把背景色改成白色。这里顺便提一下,background()一个参数时取值范围是[0,255],表示从0(黑色)到255(白色)的灰度值。
这个说到颜色动画的时候再说,具体的参数可以查看:background()
上面好像忘了说坐标系了,直接画张图吧:
官方的坐标系介绍配图:
角度与弧度,不想画图了,截张《Getting Started with Processing》书里的吧:
2.processing里有一些常用的系统变量,像width,height,mouseX,mouseY...具体的可以查文档。
像上面的例子在画布中间画个小点,可以改成:
size(320,240)
background(255)
strokeWeight(10)
point(width/2,height/2)
执行效果是一样的,但是不论你在size()把画布改成多少,这个点都可以画在画布中间,如图:
另外值得一提的两个特殊方法是setup()和draw():
*setup()在processing执行时被执行一次,可以用来初始化一些指令。
*draw()在processing重绘画布时由processing自动调用。
好了,关于processing的有时间再补充吧。标题写的动画基础,结果写了半天我还没有让它动起来。再拖下去有坑的危险。就让我从这个点开始动起来吧。
动画基础
1.匀速运动
1.1 直线运动
首先考虑的简单一些的匀速运动,让点沿x轴从左运动到右,假定x轴上的速度是vx,代码如下:
x, y, vx = 0, 120, 2
def setup():
size(320, 240)
strokeWeight(10)
background(255)
def draw():
global x, y, vx
x += vx
background(255)
point(x, y)
OK,点动起来了,从左边(0,120)的位置开始,每一帧点都向右移动vx(代码中设定为2px)的距离,然后从画布右边消失了,并且黄鹤一去不复返...OTL
没事,我们可以加上一个检测语句,如果点从右边移动出了画布(x>width),我们就让它回到起点(x=0)好了.(弹性和碰撞我们后面再说)
# vx是点在x轴上的速度
def setup():
global x, y, vx
x, y = 0, height / 2
vx=2
size(320, 240)
strokeWeight(10)
background(255)
def draw():
global x, y, vx
x += vx
if x > width:
x = 0
background(255)
point(x, y)
这样,小球超出范围就会从起点重新开始了。
同理y轴上的运动可以增加一个vy,下面的例子点在x,y两个轴上都有速度:
# x,y是点的坐标(x,y)
# vx,vy是点在x轴和y轴上的速度
def setup():
global x, y, vx, vy
x, y, vx, vy = 0, 0, 2, 2
size(240, 240)
strokeWeight(10)
background(255)
def draw():
global x, y, vx, vy
x += vx
y += vy
if x > width:
x = 0
if y > height:
y = 0
background(255)
point(x, y)
可以看到圆点的实际速度v是两个轴上的的速度vx和vy的向量和。如图:
也就是根据实际运动速度值v和任意方向a,我们同样可以算出来圆点在x轴上和y轴上的速度vx,vy。
很明显:
vx=v*cos(a)
vy=v*sin(a)
现在可以以速度v往任意方向a运动了:
# 点坐标(x,y),速度v,方向a
def setup():
global x, y, v, a
x, y, v, a = width / 2, height / 2, 2, radians(10)
size(320, 240)
strokeWeight(10)
background(255)
def draw():
global x, y, v, a
vx = v * cos(a)
vy = v * sin(a)
x += vx
y += vy
# x,y超出范围返回起始位置
if x < 0 or x > width:
x = width / 2
y = height / 2
if y < 0 or y > height:
x = width / 2
y = height / 2
background(255)
point(x, y)
这里要注意一下cos()和sin()的参数是弧度值,如果给的是角度,可以使用radians()转换成弧度。
另外,参见之前的角度与弧度的图,processing的值是顺时针增加的,不是逆时针。
这个例子里的vx,vy其实是固定值,可以放到setup()里计算一次就够了,不用在draw()里每次计算。没错,这个例子里确实可以这样改,这样写的原因是还有接下来的圆周运动呢。
哎,不知不觉码字码到好晚了。头一次用Markdown,还不太熟悉,圆周就留着和变速运动一起慢慢写吧。
1.2 圆周运动
接着来写圆周运动吧。
为了看起来方便,用AI画了张示意图:
这个例子如上图:(x0,y0)为圆心,半径为r,起始角度为start_a,速度为v。那么很显然,点的坐标(x,y)应该是:
a=start_a
x=x0+rcos(a)
y=y0+rsin(a)
现在要让点绕圆心(x0,y0)作以恒定速度v作圆周运动,很明显只需要知道一个变量角速度va。这个v是线速度且值恒定,那么每帧这个角度a的变化值va(也就是角速度)也很简单算出来:以线速度和以角速度分别算出来的转一圈的时间t应该是相等的:
t=TWO_PI*r/v=TWO_PI/va
得到:
va=v/r
ok,现在足够完成圆周运动的动画了:
def setup():
global x0, y0, v, r, a
# 以点x0,y0为圆心,v是线速度,r是圆半径
x0, y0, v, r = width / 2, height / 2, 3, 30
# start_a是起始角度,单位是度
start_a = 30
a = radians(start_a)
size(320, 240)
strokeWeight(10)
background(255)
def draw():
global x0, y0, v, a, r
va = float(v) / r
x = x0 + r * cos(a)
y = y0 + r * sin(a)
a += va
background(255)
point(x, y)
可以在圆心加个十字线更明显一些:
def setup():
global x0, y0, v, r, a
# 以点x0,y0为圆心,v是线速度,r是圆半径
x0, y0, v, r = width / 2, height / 2, 3, 30
# start_a是起始角度,单位是度
start_a = 30
a = radians(start_a)
size(320, 240)
strokeWeight(10)
background(255)
frameRate(24)
frm=0
def draw():
global x0, y0, v, a, r
va = float(v) / r
x = x0 + r * cos(a)
y = y0 + r * sin(a)
a += va
background(255)
point(x, y)
# 在圆心画一个十字线
strokeWeight(1)
line(width / 2 - 5, height / 2, width / 2 + 5, height / 2)
line(width / 2, height / 2 - 5, width / 2, height / 2 + 5)
strokeWeight(10)
这些基本的运动是基础,你可以在基础之上加上一些自己的想法。试着改动一下初始参数,或者干脆加上自己想要的效果:比如改变颜色、透明度或大小,或者弄成阵列。比如可以试着让圆慢慢消失,代码如下:
def setup():
global x0, y0, v, r, a
# 以点x0,y0为圆心,v是线速度,r是圆半径
x0, y0, v, r = width / 2, height / 2, 10, 30
# start_a是起始角度,单位是度
start_a = 30
a = radians(start_a)
size(320, 240)
strokeWeight(10)
background(255)
frameRate(24)
def draw():
global x0, y0, v, a, r
va = float(v) / r
x = x0 + r * cos(a)
y = y0 + r * sin(a)
a += va
# background(255)
fill(0x33ffffff)
noStroke()
rect(0,0,width,height)
stroke(0,0,0)
strokeWeight(10)
point(x, y)