写给设计师的编程趣味指南-(2)创造第一个PROCESSING程序
这一节,我们就来玩转 processing。
其他编程语言的经典入门大多从输出 “HelloWorld” 开始讲起。
但属于设计师的 “HelloWorld”,不应该是字符。咱们从画图开始!
画布与函数
在开始前,首先聊聊 Processing 中的画布—— Sketch 。Sketch 的字面含义就是素描,草图。程序设计者把工程文件的名字起得太优雅了,就是想告诉我们,这就是给设计师玩的Sketch!
打开Processing,它会自动新建一个叫名叫 sketch_xxxx 的文件。我们可以直接开始写代码。完成后点 File-save 就可以保存自己的作品。
第一步,我们先写下这行命令:
size(500,500);
接着点击左上方的三角按钮运行程序。它会弹出一个灰色的窗口,这就是运行后的样子。
聪明的读者是不是已经能猜到这段代码的含义呢?
没错。size()就是用来设置画布大小的函数。以后如果看到这样的格式:一串英文字符,后面再跟一对括号,那在程序中就被称为函数。
格式:
函数名()
与数学函数的定义不一样。程序中的函数,其实是一些代码块的封装。我们可以无需搞清函数内部的构造和原理,只需要知道它究竟能做什么。正如要开汽车不需要懂引擎的运作原理。
现在再回到size函数。它后面的括号里还有两个参数,中间用逗号作了区分。前一个代表窗口的宽度,后一个代表窗口的高度,完整的调用形式如下。
size(x,y) // x设置运行窗口宽度,y设置高度
表达式写完后,请千万要记得在指令后面加一个;号,代表结束。这是初学者最容易踩的坑,请记得保持这个习惯,否则程序会出BUG(错误)。
这样具有历史意义的第一行代码就被我们敲出来了。后面介绍的绘图函数也会同样简单。函数相当神奇,使用它可以大大提高效率,它生来就是为懒人服务的。只要懂得正确地组装,就能写出功能丰富的程序。
坐标系统
在使用绘图函数之前,需要了解 Procssing 的坐标系统。这与我们中学阶段接触到的笛卡尔坐标系很不一样,它的 Y 轴方向是反的。假如我们定义了一个 150 X 100 的画布,那左上角的第一个像素坐标就为(0,0),右下角的最后一个像素坐标则为(149,99)。
觉得难记可以这么去理解,坐标数其实代表了偏移量。起始坐标点序号为 0,与它相邻的坐标就距离初始坐标一个单位,因此序号为 1 。第 150 个坐标距离与第 1 个坐标相比,偏移了 149 个单位,所以序号就为 149。
但为什么要从0计算到149而不是从1计算到150呢?看上去多麻烦。这是程序中约定俗成的,很多概念都是从0开始计数。
我尝试从另一个设计角度去解释,以理解它的一些优点。假如我们要在画布中确立一个中心点。
如果是从 0 开始计数,要寻找中心像素就会很方便。只需将屏幕的宽高各自除以 2 (除了像素点为偶数的情况不存在绝对的中心)。而如果从 1 开始计数,除以 2 之后横纵坐标还要再加 1,才能得到准确的中心位置。(程序中两数相除,不会进行四舍五入,会直接舍去小数点后的数字)
现在应该对这种计数方式印象更深了吧,其实如果你是ps老手,就会发现在ps的信息面板上早就是按这样的方式来标记图片像素的位置。
改变坐标系
这个坐标系可不是一成不变的,有一个函数 translate 可以改变它。
调用形式:
translate(x,y);
它可以将整个坐标系进行平移,xy 可以取正数或者负数,数值的正负决定移动的方向,数值的大小决定移动距离。以 x 值为例,如果取正数,则会朝 x 轴的正方向平移 x 个单位。负数则往负方向平移 x 个单位。
例如:
translate(50, 50)
绘图函数
坐标系统就像地图一样。通过它,所有绘制的图形都有了立足之地。下面开始介绍各种绘图函数。
画点
在 size 后面写下这段代码。
point(250,250);
点三角符号运行。
嗯?什么也没看到?再凑近看看。你会发现中央出现了一个小黑点。
这就是用于画点的函数。在 processing 里,这个点刚好就是一个像素的大小。第一个参数代表横坐标,第二个参数代表纵坐标。由于屏幕宽高都为 500,所以这个点刚好处于中间的位置。
但若想在画布中心画点,可以用这种便捷的方式去表示。
point(width/2, height/2);
其中 width 会获取屏幕的宽,height 会获取屏幕的高,“/”在程序中代表除号。这样写程序可以自动计算结果,而无需给出具体的坐标。
你甚至可以在里面写一个超复杂的表达式进去
point(1 + 2 + 3 + 4 + 5, 100 - 50 - 20 - 10)
当然,没事可不要这么去写,除非是想研究什么古怪的数列规律~~
画圆
接着来看画圆函数,ellipse。
调用形式:
ellipse(x, y, a, b)
括号中的前两个参数与point函数类似,后两个参数分别代表圆的宽和高。如果宽高值不一样,就能画出椭圆。
可以加在原代码后面的效果:
整体代码:
size(500, 500);
point(250, 250);
ellipse(250, 250, 20, 20);
终于出现了一个大大的圆点。但有没有发现,之前绘制的点消失不见了!
其实,它只是被ellipse绘制的圆形覆盖了。在程序中,代码是由上到下,逐行执行的。先执行的先绘制,后执行的会覆盖前面的。有没有觉得和ps的图层很像?
画线,画三角,画方形
这几个函数的用法也很简单。
画线:
line(x1, y1, x2, y2)
x1, y1代表线条的一个端点坐标,x2,y2则代表另一个。函数会将两个坐标连起来。
画三角:
triangle(x1, y1, x2, y2, x3, y3)
这里会用到三对坐标参数,分别代表3个顶点坐标。
画方形:
rect(x,y,a,b)
前两个参数代表方形左上方的顶点坐标,a,b 分别代表方形的长和宽。
你可以单独地去试验这些函数,熟悉它们的用法。之后可以复制下面的代码:
size(500, 500);
ellipse(175, 220, 183, 183);
ellipse(width-175, 220, 183, 183);
rect(100, 100, 300, 300);
line(150, 160, 220, 240);
line(width-150, 160, width-220, 240);
ellipse(175, 220, 60, 60);
ellipse(width-175, 220, 60, 60);
point(width/2, height/2);
triangle(170, 300, width-170, 300, 250, 350);
这个例子将前面介绍的函数都一并用上了,还画了一个表情图形(姑且算吧),你也可以尝试自己画一些稍微复杂的图案。同时琢磨两个问题。
每行函数对应的图形部件是哪一个?
width-x代表的含义?作用是什么?这种写法有什么优点?
上色
只有形状,不免无趣。我们还要学会上色。和我们预想的上色方式不一样,不是每个形状都会有一个对应的色彩属性,来让你去定义它的颜色。比如ellipse(200,200,20,20,?)后面再加一个参数之类的。
程序中上色要使用 fill 函数,这是一种通用的上色方法,并且要写在绘图函数前面,
fill 的输入参数很特别,允许几种输入方式。
可以这样
fill(x)
x代表灰度值,最小为0(代表黑色),最大为255(代表白色)
可以这样
fill(x,a)
x代表灰度值,a代表透明度(最小值0,最大值255)
也可以这样
fill(r,g,b)
r代表red(红),g代表green(绿),b代表blue(蓝)。设计师对RGB应该是相当熟悉了,代表的就是三原色。
还能这样
fill(r,g,b,a)
rgb就代表三原色,a代表alpha(透明度)
实例效果:
size(500, 500);
fill(125);
ellipse(100, 250, 100, 100);
fill(125, 100);
ellipse(200, 250, 100, 100);
fill(255, 0, 0);
ellipse(300, 250, 100, 100);
fill(255, 0, 0, 50);
ellipse(400, 250, 100, 100);
但如果前面只写一个fill呢?
size(500, 500);
fill(125);
ellipse(100, 250, 100, 100);
ellipse(200, 250, 100, 100);
ellipse(300, 250, 100, 100);
ellipse(400, 250, 100, 100);
那之后绘制的图像,都会填充同一个颜色。如果将 fill 放在中间,前面的依然会是默认的白色,只有后面的代码会产生影响。(代码由上至下执行的)
size(500,500);
ellipse(100, 250, 100, 100);
fill(125);
ellipse(200, 250, 100, 100);
ellipse(300, 250, 100, 100);
ellipse(400, 250, 100, 100);
如果希望绘制的每个形状都是不同的颜色,就得在每个绘图函数的前面加上fill。
与fill相关的还有noFill函数。
noFill()
它不需要输入任何参数,写在绘图函数前面,之后绘制的图形都不会填充颜色,而只显示边线。
size(500,500);
noFill();
ellipse(100,250,100,100);
ellipse(200,250,100,100);
ellipse(300,250,100,100);
ellipse(400,250,100,100);
除非你后面又写上fill(),那才会开启填充功能。
边线
除了能处理形状的填充色,还可以单独对边线的颜色和宽度进行处理。
边线颜色
stroke(x)
stroke 函数可以改变边线的颜色。括号内参数与 fill 函数的用法完全一样。代表边线的颜色。可以选择四种不同的参数输入方法。
stroke(x)
x代表灰度值,最小为0(代表黑色),最大为255(代表白色)
stroke(x,a)
x代表灰度值,a代表透明度alpha
stroke(r,g,b)
rgb代表三原色RGB
stroke(r,g,b,a)
rgb代表三原色RGB,a代表透明度alpha
边线宽度
strokeWeight(x)
控制边线的粗细(还可以控制点的大小)
noStroke()
无需输入参数,代表不显示边线
stroke的用法就留待大家去探索了。如果把下面这行代码写在之前表情图案的前面,就会出现这个效果。
strokeWeight(8);
所有的边线都被加粗了。
最后还有一个与上色相关的background函数,它可以用来填充背景,相当于油漆桶。括号中填的也是色彩参数,和前面的完全一致,有四种不同的参数输入方式。如果把background(200,0,0)写在前面可以得到。
size(500, 500);
strokeWeight(8);
background(200, 0, 0);
ellipse(175, 220, 183, 183);
ellipse(width-175, 220, 183, 183);
rect(100, 100, 300, 300);
line(150, 160, 220, 240);
line(width-150, 160, width-220, 240);
ellipse(175, 220, 60, 60);
ellipse(width-175, 220, 60, 60);
point(width/2, height/2);
triangle(170, 300, width-170, 300, 250, 350);
是不是开始像个“作品”了:)
End
学程序不要单纯去背知识点,而是要将它们灵活运用起来。除了一些特别常用的函数要记住以外,其余的在用的时候再去查也是没有问题的。
在 Processing 官网中可以查到各种函数的用法。(https://processing.org/reference/)
说个小秘诀,如果在写程序的时候忘记怎么用某个函数。只要你的函数名书写正确,就可以查询该函数的详细用法。先双击函数名,等变成黄色背景时再点右键,选择Find in reference。
函数介绍:
通过这节的介绍。是不是觉得代码没有以前那么神秘了?
在程序中画图,远没有用画笔或者ps工具那么形象和自由。里面的任何一个图形,都需要用数据来描述它的位置,大小或角度。而对于复杂的图形,是很难纯靠想象,直接写出代码的。一般是先绘制好草图,再用ps的信息面板查看关键点的坐标,最后选择合适的函数去表现。
就像先前的例子
看到这里,有怀疑精神的设计师估计会跳出来。我在PS里面随便拉几下就能画出来的图形,为什么非要在用程序里画,过程还这么麻烦?这完全体现不出程序的优势呀?
说得很有道理,请保持好质疑的心态,答案在下节揭晓。之后还会聊聊两个“大头”函数,setup函数和draw函数。只要灵活运用它们,就可以让图片“动”起来。而我们在学校里曾经学习的那些有关数学函数的知识,也开始有了用武之地,过往那些看似枯燥的数学公式,将会成为我们控制图形的利器。