查看原文
其他

写给设计师的OF编程指南(3)- 图形的运动(上)

2016-05-16 Wenzy InsLab

在开始看这节之前,希望你已经熟悉了基本的函数绘图方法。

前面的有关绘图部分,我们的代码都只写在 draw 函数中,这节会开始介绍 setup 函数和 update 函数。

在开始做运动图形之前,我们需要了解“动画”是如何产生的。


上图相当吸引人,而且非常直观地揭示了动画的实现原理。

动画是魔法,是关于视觉欺骗的魔法。只是在这个信息爆炸,视频满天飞的年代,我们已经对它习以为常了。也很少有人会去惊叹,能看到动画,本身就是一件神奇的事。

在程序中制作动画,原理是相通的。我们只要考虑怎么在每页上画上不同的图形,程序就会自动去翻页,而大脑会将它脑补成动画。

下面会聊聊如何实现基本的动画运动,在此之前需要我们了解一些变量的基础知识。

变量

变量是数据的容器,它可以在程序中反复使用。

代码示例(2-1):(窗口大小为 500 X 500)

void ofApp::draw(){   ofDrawCircle(100, 250, 25);   ofDrawCircle(200, 250, 25);   ofDrawCircle(300, 250, 25);   ofDrawCircle(400, 250, 25); }


这段代码没有用到变量,在屏幕上画了四个圆。仔细观察,会发现这四个圆的宽高都是一样的。既然一样,为了减少数值的重复输入,我们其实可以定义一个符号来表示,这个符号就是变量。

使用变量后的代码:

代码示例(2-2):(窗口大小为 500 X 500)

void ofApp::draw(){    int a = 25;   ofDrawCircle(100, 250, a);   ofDrawCircle(200, 250, a);   ofDrawCircle(300, 250, a);   ofDrawCircle(400, 250, a); }


绘图的结果与原来是完全一样的。

定义了变量 a ,我们就可以很方便地改变数值。如果将 a = 25,改成a = 50。那所有圆的半径都会统一变成 50,这样就无需再逐一修改数值了。变量是个相当好的发明~

变量的创建

变量使用前需要先声明,并且需要指定它的数据类型。

int i;     i = 50;

第一句代码声明了一个变量 i。int 是一个专门用来声明变量的符号。声明时,会在电脑内存中开辟一个空间,相当于生成了一个“箱子”,专门用来存放整数数据。

第二句代表把 50 赋值给变量 i。执行这句以后,数据就会稳稳地存放在了 i 这个变量上了。

你也可以用更便捷的方法,两步并作一步,声明的同时完成赋值。

int i = 50;

变量的命名规则

变量名的命名比较自由,但也有讲究的地方。

  • 必须是字母和数字或下划线的组合。可以用一个字符,也可以用一个单词,

  • 区分大小写,name 和 Name 代表不同的变量

  • 名字尽量简洁易懂,首字符只能以字母开头,不要用数字和特殊字符。

  • 不能用已有的关键字作为变量名,比如 int,float

以下,都是错误的声明方式

int $a; int 89b;

以下是合法的声明

int r; int super_24; int openTheDoor;

变量的类型

除了可以声明为整数数据,还可以声明为小数数据(也叫浮点数据),用关键字float。

float b = 0.5

这里要牢记声明数据时,用了变量类型。若是用了关键字 int ,后面赋值时就不能写 i = 0.5 之类,这样程序会出错。但反过来是可以的,比如 float i = 5 是正确的语法,程序仍会将它识别为小数。

运算符

Openframeworks 中的常用运算符有几种:

+

-

*

\

取模,得出余数

  • 加减乘除大家都不陌生,% 可能是第一次看到,它得出的数是余数。9 % 3的结果是 0 。而 9 % 5 的结果会是 4。

  • 运算符可以在数值以及变量间使用。

代码示例(2-3):

void ofApp::draw(){    int a = 1;         //声明整数变量a,赋值为1    int b = 2;         //声明了整数变量b,赋值为2    int c;                  //声明了整数变量c    c = a + b;         //将两变量相加,并赋值给c    cout << c << endl;          //输出变量c }    


输出的结果不会显示在窗口,而是在下方的控制台。

  • 第四行的写法看上去很特别。这是计算机赋值操作的常用格式。等号的左边要写最终赋值的变量,等号右边写上运算的过程。若写成 a + b = c; 则是无效的

  • 第五行的 cout << ? << endl 可以先理解为一个固定写法,? 中可以填写数值,变量又或是表达式,通过它就可以在控制台输出相应的结果,这同时是检查程序有否出现异常的常用方法。

  • 实时运行程序时,下面的数字 3 则为输出的计算结果。你会发现控制台上的数字会不断往下刷新。这是由于我们将 cout 句式写在 draw 函数中导致的。后面会提到具体原因。

运算规则

Openframeworks 中进行运算,需要搞清变量的类型。其中特别要留意的,是浮点数与整数类型之间的转换。

代码示例(2-4):

cout << 9 / 5 << endl;     //输出结果 1

在程序中,整数与整数进行运算只会得到整数,9 除以 5 的实际结果是 1.8 ,但程序中的输出结果却会是 1 。这是由于程序对结果不进行四舍五入处理,而是直接舍去小数点之后的数。

代码示例(2-5):

cout << 9.0 / 5.0 << endl ;   //输出结果 1.8

浮点数与浮点数之间运算会得出浮点数,实际结果是 1.8 ,程序输出结果也会是 1.8。

代码示例(2-6):

cout << 9 / 5.0 << endl ;     //输出结果 1.8 cout << 9.0 / 5 << endl ;     //输出结果 1.8

最后是整数和浮点数混用,最终输出的结果会是浮点数 1.8。

  • 其实只需记住一点,规则这样设计是为了不损失数据的精度,所以只要有一方是浮点数,结果都会是浮点数。

Setup函数,Update 函数 与 Draw函数

铺垫完基础的知识,离制作动画越来越接近了。

setup,update 以及 draw函数,相当于是 Openframeworks 里面的主函数。这三个函数非常特殊,它们是程序的基本框架,会控制程序的流程。

基本格式:

void ofApp::setup(){ } void ofApp::update(){ } void ofApp::draw(){ }

程序看似能灵活地响应多个任务,实现丰富复杂的功能。但它内部是其实有一个非常严谨的运行流程。通过前面提到的 cout 命令,我们可以通过控制台的输出直观地了解过程。

示例代码:

void ofApp::setup(){   cout << 1 << endl; } void ofApp::update(){    cout << 2 << endl; } void ofApp::draw(){    cout << 3 << endl; }

我们先试着在三个函数中输入以上代码。点击三角符运行一段时间再关闭程序。以下是运行结果:


控制台输出的第一个结果为数字1,后面则是 2 3 之间来回交替。

cout 命令每执行一遍,就会输出一个结果。setup 函数在程序中是第一个执行的,同时只会运行一次。所以你会发现“1”只出现了一次。而 update 函数和 draw 函数则是循环交替执行的,并且先运行 update 函数再运行 draw函数。所以你会看到先输出的是数字“2”,后面出现的才是数字“3”。

由于这种特性,setup函数往往用于初始化环境属性,如设置屏幕宽高,背景颜色,进行变量的赋值等等。而涉及大量数据运算的,则放在 update 函数内。而 draw函数 ,则用于放绘图函数,以此产生不断变化的图像。

update与draw的运行频率

相信现在你能明白,实例(2-3)中为会不断地输出 3了。因为我们将代码写在 draw 函数内,所以 draw 函数内的代码都会从上至下,不断循环执行,重复输出结果。


由于 update 函数和 draw 函数在 OF 中的运行频率默认为每秒 60 次(60 FPS)。所以在一秒之内,draw 函数的每一行代码都会执行 60 次。如果你仅仅是为了获得计算结果,多余的计算是没有意义的。所以更好的办法应该是直接放在 setup 函数里面。代码同样会被执行,并且只运行一次。

代码示例(2-7):

void ofApp::setup(){    int a = 1;         //声明整数变量a,赋值为1    int b = 2;         //声明了整数变量b,赋值为2    int c;                  //声明了整数变量c    c = a + b;         //将两变量相加,并赋值给c    cout << c << endl;          //输出变量c }    


与帧率相关的函数:

设置帧率

OF程序默认每秒 60 帧的频率是可以被改变的,通过 ofSetFrameRate() 函数就能进行设置

格式:

ofSetFrameRate(x)

x 代表设置后的帧速率

--代码示例(2-9):

void ofApp::setup(){    ofSetFrameRate(1); } void ofApp::update(){    cout <<  1 << endl; }

程序运行后,现在就变成每隔一秒,才从控制台输出一个数字1

获取帧率

尽管设置了程序的帧率,在程序中未必就是按预置的帧率来运行的。特别对于运算量大的程序,往往会有卡帧的情况出现。我们可以通过 ofGetFrameRate() 获取程序当前的实时帧数。

--代码示例(2-10):

void ofApp::setup(){    ofSetFrameRate(10); } void ofApp::update(){    cout <<  ofGetFrameRate() << endl; }


通过这种方法,你可以了解你的程序是否运行流畅。

平移的圆

为了加深这三个函数的理解,最好的办法莫过于做一个动画。

Openframeworks 中写动画效果的方式是“很笨拙”的。没有我们想象中的智能。不存在某种现成的指令,比如指定某图形做曲线运动,再设置速度,移动距离等产生,程序便自动生成动画。

既然你是程序世界的造物主,你就需要亲力亲为,去定义这些细节。电脑需要非常明确的指令,每帧要画怎样的图形。

我们先从一个平移运动的动画开始去了解。

代码示例(2-11):

—- ofApp.cpp内  —-

void ofApp::setup(){    ofSetWindowShape(300,300);    x = 0;    y = ofGetHeight()/2; } void ofApp::update(){    x = x+1; } void ofApp::draw(){    ofBackground(234, 113, 107);    ofDrawCircle(x, y, 25, 25); }


前面都只在 ofApp.cpp 中写代码,到这里我们还需要在 ofApp.h 中写上

—- ofApp.h内  —-

int x; int y;


运行效果:


一段纯用代码实现的动画就出来了

代码解释

我们先来看最奇怪的部分。现在是第一次在 ofApp.h 里面写代码。写了两个声明整形变量 x 与 y 的语句。细心的人会发现,前面的例子 2-2 中,变量的声明是直接写在 draw 函数中的,这与在 ofApp.h 里有何区别?

在 ofApp.h 中声明的变量,为全局变量,它的好处是只要声明过一次,这个变量在 setup ,update 或是 draw 函数中都能使用。
而如果仅在某个函数中声明的变量,则为局部变量。它只能在这个函数中使用。

所以若是在程序中这么写,是无法运行的,会出现报错提示。

理解 ofApp.h 部分后,我们再来看 ofApp.cpp 中的代码。前面声明了两个变量x,y。用于储存圆的坐标位置。变量的初值设定,在setup函数中也实现了,这代表的就是圆的起始坐标。

setup 里面有一句

ofSetWindowShape(300,300);

它的作用是设置窗口的大小,与通过前面的 main.cpp 中的 ofSetupOpenGL() 语句去设置,效果是一样的。

现在关键代码其实是 update 函数中的这个:

x = x + 1;

我们不要将它看成数学等式,否则会很奇怪。这里的“=”号是一个赋值符号,代表把右边的数值放到左边的变量里。假设 x 为 50,代码运行后。等号右边就等于 50 + 1,即 51。最终这个结果会赋到变量 x 里,于是x的值就变更为 51 了。

顺着程序的流程走,update 函数每运行一次,x的数值就会增加1。于是每次绘图,圆形都会比上一帧多向右平移一个像素,图形因此就动起来了。

如果你之前有使用过 Processing,就会发觉 Openframeworks 中多了一个 update 函数,那 update 函数与 draw 函数具体有何区别?什么代码应该写在 update 中,什么代码应该写在 draw 里?

可以简记为一点

  • 数据部分放 update ,绘图部分放 draw

与图形运动相关的数据运算,尽量写在 update 函数中,而实现绘图部分的代码,则写在 draw 函数内。这样将数据部分与视觉部分加以区分,可以更好地管理,Debug时也更容易发现错误。另外绘图函数只有写在 draw 里面,才能在画布上显示。

控制运动方向

图形往哪个方向运动,取决于你怎么变化你的坐标。如果改成 y=y+1,圆就会往下运动,如果 x,y 都同时增加 1,圆就会往右下方运动。写成减号会朝相反的方向。

--代码示例(2-12):

—- ofApp.cpp内  —-

void ofApp::setup(){    ofSetWindowShape(300,300);    x = 0;    y = 0; } void ofApp::update(){    x = x + 1;    y = y + 1; } void ofApp::draw(){    ofBackground(234, 113, 107);    ofDrawCircle(x, y, 25, 25); }

—- ofApp.h内  —-

int x; int y;


控制运动速率

由于 update 函数和 draw 函数默认都是每秒运行60帧的。因此按这个速率去推算,上面的圆,每秒就会向右移动60像素。

想改变图形的速率,有两个办法。一个就是增大每次 x 的变化值。

x=x+10;

这样速度就会比原来提升10倍!

另一个就是改变画布的帧速率,用前面提到的 ofSetFrameRate()。假如你在setup函数内写上ofSetFrameRate(10),就会将播放速度设置成 10 帧每秒。这样较原来默认的 60 帧每秒就变慢了6倍。

但第二种方法不推荐,因为会降低画面的流畅度。

OF 中的背景

前面的例子,都是将 background 函数写在draw里的,有没有想过,写在setup里,会有何不同?现在在平移运动的例子上作些改动。

--代码示例(2-13):

—- ofApp.cpp内  —-

void ofApp::setup(){    ofSetWindowShape(300,300);    ofBackground(234, 113, 107);    x = 0;    y = ofGetHeight()/2; } void ofApp::update(){    x = x+1; } void ofApp::draw(){          ofDrawCircle(x, y, 25, 25); }

—- ofApp.h内  —-

int x; int y;


从结果上看其实是没有区别的。这点与 Processing 完全不同。这是因为 OF 中会默认开启自动刷新背景的功能。我们需要关闭它才能看到效果。

在 setup 中加入一行代码。

ofSetBackgroundAuto(false);

--代码示例(2-14):

—- ofApp.cpp内  —-

void ofApp::setup(){    ofSetWindowShape(300,300);    ofSetBackgroundAuto(false);    ofBackground(234, 113, 107);    x = 0;    y = ofGetHeight()/2; } void ofApp::update(){    x = x+1; } void ofApp::draw(){          ofDrawCircle(x, y, 25, 25); }

—- ofApp.h内  —-

int x; int y;

这时会发现动画变得有所不同了,画面上运动的圆形似乎没有被清除。若是将自动刷新的功能关闭,draw 函数里面的图形都会绘制到同一个画布上。


这种不重绘的方式,适当运用可以做出很特别的效果,复制下面代码体验一番

--代码示例(2-14):

void ofApp::setup(){    ofSetWindowShape(300,300);    ofSetBackgroundAuto(false); } void ofApp::update(){ } void ofApp::draw(){    ofSetColor(0);    ofDrawCircle(mouseX, mouseY, mouseX/5.0,mouseY/5.0);    ofSetColor(0);    ofDrawCircle(ofGetWidth() - mouseX, mouseY, mouseX/5.0,mouseY/5.0);    ofSetColor(255);    ofDrawCircle(mouseX, ofGetHeight() - mouseY, mouseX/5.0,mouseY/5.0);    ofSetColor(255);    ofDrawCircle(ofGetWidth() - mouseX, ofGetHeight() - mouseY, mouseX/5.0,mouseY/5.0);   }


  • mouseX,mouseY 可以获取鼠标的横纵坐标

  • 在程序中实现对称非常简单。若已知某点的横纵坐标为(x,y)。则与它左右对称的坐标则为 (ofGetWidth() - x,y),上下对称的坐标则为 (x,ofGetHeight() - y),中心对称的坐标则为 (ofGetWidth() - x, ofGetHeight() - y)。

实现拖尾效果

通过命令 ofSetBackgroundAuto(false) ,OF 的画布就有了不清除的特性。我们可以结合已知的绘图函数实现一些有趣的效果。

--代码示例(2-15):

void ofApp::setup(){    ofSetWindowShape(300,300);    ofSetBackgroundAuto(false);    ofBackground(234, 113, 107);    x = 0;    y = ofGetHeight()/2; } void ofApp::update(){    x = x+1; } void ofApp::draw(){    ofSetColor(234, 113, 107,15);    ofDrawRectangle(0,0,ofGetWidth(),ofGetHeight());    ofSetColor(255);    ofDrawCircle(x, y, 25, 25); }


上节提到过,写在 draw 开头的 

ofSetColor(x); ofDrawRectangle(0,0,ofGetWidth(),ofGetHeight());

组合起来使用,结果与

ofBackground();

实现的效果是一样的。

但若是结合 ofSetBackgroundAuto() 函数,则可以很巧妙地实现特殊效果。其中的关键在于填充矩形时,添加的是带透明度的色彩。每刷新一帧,在每次绘制前都会覆盖一层和背景色相同且透明的矩形,这就使得前一帧的所有图形都变淡了。随着时间的推移,越是在前面的图形,就会覆盖数量更多透明矩形,也就逐渐变淡直至消失。这个透明度的值,就会影响拖尾效果的强弱。透明度越小则遮盖得越少,留下的残影持续的时间也越长。

抖动的圆

现在再回到运动部分。如果我希望圆的运动方向是不规则的怎么办?random函数可以做到这点。 random是一个高频使用的函数,可以用来生成随机数。它像神出鬼没的精灵,一旦变量和它扯上关系,你完全无法预测下一步会发生什么。通过它,你可以感受程序中不确定性所带来的魅力。

调用形式:

ofRandom(high);

high 可以填整数,也可以填小数。它代表随机的上限,默认下限是 0。比如 ofRandom(10)。就会从 0 到 10 之间,随机生成一个小数值(包括0,但不包括10)。

ofRandom(low,high);

如果设置两个参数,那就会返回这两个数之间的随机值。比如random(5,10)。就会从 5 到 10 之间,随机生成一个小数值(包括5,但不包括10)。

--代码示例(2-16):

void ofApp::draw(){    float x;    x = ofRandom(50,100);    cout << x << endl; }

每次运行程序,会在控制台上输出不同的数值。

  • 值得注意的是,通过 ofRandom 函数生成的数值为浮点类型(小数类型),如果要赋值给整型变量,要通过函数int()进行转换,转换的过程不遵循四舍五入,而是直接将小数部分舍去。因此 int(ofRandom(5)),输出的数值只有五种可能,0,1,2,3,4。

熟悉 random 函数的用法之后,可以直接看下面的例子。

--代码示例(2-17):

--- ofApp.cpp内 ---

void ofApp::setup(){    ofSetWindowShape(300,300);    x = ofGetWidth()/2;    y = ofGetHeight()/2; } void ofApp::update(){    x += int(ofRandom(-5, 5));    y += int(ofRandom(-5, 5)); } void ofApp::draw(){    ofBackground(234, 113, 107);    ofDrawCircle(x, y, 25, 25); }

--- ofApp.h内 ---

int x; int y;



之前坐标增加的数值都是固定的。所以只要增加一个随机值,圆就会往不确定的方向移动。而随机的范围越大,它就“抖动”得越厉害。因为数值变化是跳跃的,所以运动就不平滑。好比前一帧还是在(150,150),后一帧就有可能“瞬移”到(170,170)的位置了。

游走的圆

理解ofNoise

那有没有可能产生平滑的运动?ofNoise 函数可以做到这点。它比标准的 ofRandom 函数更有韵律,生成的数值是连续的,但又带有变化。

调用形式:

ofNoise(t);

与 ofRandom 不同,ofNoise 无法定义它的输出范围。程序中规定它只能生成从 0 到 1 的浮点数。并且固定的输入只能产生固定的输出。

--代码示例(2-18):

void ofApp::setup(){    float x = ofNoise(0.33);    float y = ofNoise(0.33);    cout << x << endl;    cout << y << endl; }


由于上面输入的参数都是 5,所以输出的两个结果都是相同的。那如何才能使结果产生变化呢?答案就是动态地改变输入的参数。ofNoise 可以将它理解为一个无限长的音轨。输入的参数就好比是“当前时间”,如果我们的参数输入是连续的,输出也会是连续的。

以下例子绘制了 y 值的变化轨迹。便于我们理解 ofNoise 函数。

--代码示例(2-17):

--- ofApp.cpp内 ---

void ofApp::setup(){    ofSetWindowShape(700, 100);    ofSetBackgroundAuto(false);    ofBackground(0);    x = 0; } void ofApp::update(){    x += 1;    y = ofNoise(ofGetFrameNum()/100.0)*100; } void ofApp::draw(){    ofDrawCircle(x, y, 1); }

--- ofApp.h内 ---

int x; int y;



  • 其中 ofGetFrameNum 会获取程序从开始运行到当前的总帧数。和前面介绍的 ofGetWidth ,ofGetHeight 不一样。它返回的不是一个定值。而是会从 0 开始,不断递增,如果用开头演示的动图去理解,就代表翻到了第几页。

  • ofGetFrameNum 返回的是整数,整数除以整数,程序中会默认将结果处理成整数。所以为了提高结果精度,需要将 100 写成 100.0 。整数除以浮点数,会得到浮点数。

  • 为了使 y 轴坐标在 0 到 100 里变化,需将 ofNoise 返回的结果乘以 100。以此控制随机值的范围。

但为何 ofGetFrameNum 要除以 100 ?不能直接写 ofGetFrameNum ?实际上是可以的,但这里为了更好地展示 ofNoise 函数的特性,所以放慢了“播放速率”。下面的例子就展示了不同变化速率下,输出值的变化。

--代码示例(2-18):

--- ofApp.cpp内 ---

void ofApp::setup(){    ofSetWindowShape(700, 500);    ofSetBackgroundAuto(false);    ofBackground(0);    x = 0; } void ofApp::update(){    x += 1;    y1 = ofNoise(ofGetFrameNum() + 0.32)*100;    y2 = ofNoise(ofGetFrameNum()/10.0)*100;    y3 = ofNoise(ofGetFrameNum()/100.0)*100;    y4 = ofNoise(ofGetFrameNum()/1000.0)*100;    y5 = ofNoise(ofGetFrameNum()/10000.0)*100; } void ofApp::draw(){    ofSetColor(255);    ofDrawCircle(x, y1, 1);    ofDrawCircle(x, y2+100, 1);    ofDrawCircle(x, y3+200, 1);    ofDrawCircle(x, y4+300, 1);    ofDrawCircle(x, y5+400, 1);    ofSetColor(80);    ofDrawLine(0,100,ofGetWidth(),100);    ofDrawLine(0,200,ofGetWidth(),200);    ofDrawLine(0,300,ofGetWidth(),300);    ofDrawLine(0,400,ofGetWidth(),400); }

--- ofApp.h内 ---

int x,y1,y2,y3,y4,y5;


  • 可以将 ofNoise 里面变化的参数理解成进度条,变化这个参数,就相当于拨动进度条。所以在这个“音轨”上,当输入参数的变化幅度越大,输出值前后的连续性也会越弱(可以想象2倍速,5倍速,20倍速播放一段音乐或是视频)。当参数的变化幅度大于某个值,可能就与 ofRandom 函数生成的值没有太大差别了。

  • ofNoise 与 P5 中的 noise 函数是相对应的。但又略有不同。ofNoise 中参数输入为整数时,返回的数值必定为 0.5,所以我们需要避免整数,在 ofNoise 的参数输入上添加一个小数来错开。

若不加上小数,y1的纵坐标则是固定不变的。图形也会变成一条直线。下面的红点标示了当输入参数为整数时的点坐标,会看到得到的都是定值。


ofNoise 控制运动效果

如果前面的例子都能理解。那画一个游走的圆,是再简单不过的事。你也会明白其中的道理。

--代码示例(2-19):

--- ofApp.cpp内 ---

void ofApp::setup(){    ofSetWindowShape(300,300); } void ofApp::update(){    x = ofNoise(ofGetFrameNum()/100.0 + 2.32)*300;    y = ofNoise(ofGetFrameNum()/100.0) * 300; } void ofApp::draw(){    ofBackground(234, 113, 107);    ofDrawCircle(x, y, 25, 25); }

--- ofApp.h内 ---

int x,y;


现在的运动就比较有意思了,像一个旋转摇摆的陀螺。

  • 其中变量 x 里的 ofNoise 中的参数之所以要加上 2.32 ,是为了错开一段距离,让运动更有随机性。若是 x,y 里面 ofNoise 函数输入的参数完全一样,又或是过于接近。x,y坐标的变化就会趋近相同。


用鼠标移动圆

并不是所有运动都必须通过函数计算出来,还有一种最原始的方法,那就是用鼠标。mouseX 和 mouseY 在前面一个实例里有提到过,可以实时获取鼠标的横纵坐标。当初在看到这两个概念的时候,就让人两眼发光。因为这是与图像发生交互最直接的方式。灵活运用它,可以写出很多有趣的程序。

--代码示例(2-20):

void ofApp::setup(){    ofSetWindowShape(300,300); } void ofApp::update(){ } void ofApp::draw(){    ofBackground(234, 113, 107);    ofDrawCircle(mouseX, mouseY, 25); }


  • 可以尝试改变正负号,又或是交换 mouseX 和 mouseY


Tips-代码格式

代码书写是有一定规范的。

  • 为了代码有更好的可读性,大括号里面的每行代码前面应该留出一定的空间,并且尽量对齐。键入 TAB 或者若干空格可以进行缩进。

  • 程序中空格符与换行符不会影响程序本身,多打一个少打一个往往没有关系。

End

通过熟悉这些命令,你已经可以简单地指挥图形的运动了。结合上节内容,就可以做出很多有意思的动效。

下一节会展示更多丰富的实例,同时也会用上数学函数,将它和图形的运动结合起来。

Processing 版:

写给设计师的编程趣味指南-(3)图形的运动(上篇)



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存