一、对于完整模式setup和draw函数是必须的
经过第一次的摸索,已经基本掌握了他的大体框架,以及编写一个简单到极点的可以运行的程序。但是即使这么简单的程序,我在摸索的时候都遇到了一个问题。
这个问题是这样的,我们来看看代码:
void setup()
{
size(400,300);
background(255);
}
void keyPressed()
{
println("hello this is key pressed!");
}
上述代码写好后,我并未感觉哪里有错,编译器也没有指出哪里有错。但是运行的结果却让我隐隐的感觉一定是哪里出错了,否则运行后在console输出中一定会有“hello this is mouse pressed!”。
我将上面的输出部分移动到draw函数中去,于是添加了draw函数,移入draw函数后代码运行的结果显示在了console中。
很奇怪,为何在keypress中就不行呢?我不信我的代码有问题,再次将draw中的这行输出代码剪切到keypressed函数中,非常惊奇的发现,这次keypressed函数中的代码运行了,一模一样没有改变。
如果要说改变,那么就是在整个代码中增加了draw函数。难道说,draw函数是keypressed函数赖以运行的基础???抱着试试的心态,我把draw函数删除,果然,程序运行后keypressed中的代码没有运行,console中没有输出hello this is mouse pressed!
综上所述,对于完整模式来说,setup和draw函数是必须的。而且,我们必须知道setup只运行一次,draw会按照某种频率来不停循环运行。
二、图形的刷新与绘制
1、图形刷新
在完整模式下,绘图的语句一般都放置在draw函数中,而且draw函数是反复循环的,当然,这个循环的频率是可以控制的,控制的方法是在setup中调用frameRate函数。我们来看看例子,我们按住ctrl+shift+O,打开processing的实例库,并在库中找到余弦波的动态图列子,如下图:
双击即可调入代码,通过运行我们发现这个图是动态的,屏幕会显示一个波动的余弦波。如下图:
我们为了满足某种需要,可能需要这个波动更慢或者更快,那么怎么办呢?
我们可以在setup中加入如frameRate的调用,代码如下:
frameRate默认的频率是60,这里我们设置为400,图形会运动的更快。
2、图形的绘制
a、绘图准备
绘图之前,我们必须准备一个画布,那么和我们在VC,QT,java中都一样,不同的是VC中可能需要绘图设备的准备,QT及java就会要求准备一个Qpainter。我们这里会更简单,直接进入绘图画布的准备,方法是直接调用一个size函数,设置高和宽即可(这个画布和VC 及QT中一样都是原点坐标在左上角)。比如我们设置一个400X300的画布,并且绘制一条线,可以在setup中这么简单的实现:
void setup()
{
/绘制一条线/
size(400,300); //设置画布大小
background(0); //设置背景颜色
stroke(255); //设置线条的颜色(这里是灰度),颜色可用RGB三个参数来设置
line(0,0,200,150);
}
运行效果如下:
需要注意的是,这里我们只是在setup中绘制了一条线,这条线只会被绘制一次。后面如果draw中的语句也在这块区域绘制图形,则这个图形就会被覆盖。如果要保证不覆盖,我们还是应该在draw中绘制。
b、draw函数中语句以及draw的作用
前面提到过,如果没有draw函数,我们的keypressed和mousePressed是不能正常运行的。特别需要指出的是draw函数中的语句是会按照frameRate所设置的频率来循环绘制的,所以draw函数中适合绘制动态图形。
比如,我们绘制一个跟随鼠标运动的原型,这个时候就必须把绘制的语句放置在draw函数中了。代码如下:
void setup()
{
/*绘制一条线*/
size(400,300); //设置画布大小
background(0); //设置背景颜色
// stroke(255); //设置线条的颜色(这里是灰度),颜色可用RGB三个参数来设置
// line(0,0,200,150);
}
void draw()
{
ellipse(mouseX,mouseY,50,50); //绘制一个圆,原点坐标随鼠标位置变化
}
我们在上面看到了一条重影(残影)的长管子,这就是因为我们没有及时擦去前面一帧留下的图形,我们将frameRate设置为2,这个时候会得到零星分布在画布上的圆:
当然,这里的终点不是讨论frameRate,我们要解决重影,需要及时擦去背景,那么在代码中加入background的设置即可。代码如下:
void setup()
{
/*绘制一条线*/
size(400,300); //设置画布大小
background(0); //设置背景颜色
// stroke(255); //设置线条的颜色(这里是灰度),颜色可用RGB三个参数来设置
// line(0,0,200,150);
//frameRate(2);
}
void draw()
{
background(0);
ellipse(mouseX,mouseY,50,50); //绘制一个圆,原点坐标随鼠标位置变化
}
b、2D与3D绘图的模式设定
在processing中绘制的图形分为2D和3D两种,在我写的processing摸索前行(1)中曾绘制过一个三维图形就是3d模式,那么在我们需要使用3d模式绘图时,需要在准备画布的时候就设定好,如我们要绘制一个圆球:
void setup()
{
/*绘制一条线*/
size(400,300,P3D); //设置3D画布大小
background(0); //设置背景颜色
}
void draw()
{
background(0);
translate(200,150,0); //设置三维图形的空间位置
sphere(50);
}
运行效果如下:
c、一些常用的绘制功能
除了在绘图时要绘制一些典型的几何图形外,我们有时候还需要用到绘制文字绘制图片,这里也来试试,首先我们来加载一个图片:
PImage img; //声明一个图形变量
void setup()
{
/*绘制一条线*/
size(400,300); //设置3D画布大小
background(0); //设置背景颜色
img=loadImage("face.jpg");//加载图形
}
void draw()
{
background(0);
image(img, 0, 0); //从原点(0,0)处以图形原大小加载
}
运行效果:
当如,如果我们要在上面写几个字的话,就要用到text函数:
PImage img; //声明一个图形变量
void setup()
{
/*绘制一条线*/
size(400,300); //设置3D画布大小
background(0); //设置背景颜色
img=loadImage("face.jpg");//加载图形
}
void draw()
{
background(0);
image(img, 0, 0); //从原点(0,0)处以图形原大小加载
textFont(20) //设置字体大小
text("显示图片案例",20,20); //显示文字,从20,20位置开始输出文字
}
运行效果如下:
当然,还有很多的具体的绘图函数,我们不做一一列举,我们可以在Reference中查看具体的用法。
三、事件函数调用
在Reference中我们可以找到很多的事件,这里我们最关心的莫过于有互动效果的两个,即按键事件和鼠标事件。这里须重点强调的是在调用这些事件函数前须将draw函数实现,也就是说,一定要在代码中有draw的函数体。
1、按键事件
我们可以通过键值来判断具体按了哪个键,如果我们不加判断,那么任意按键都会在这个函数中被响应。这里需要介绍的是 keyPressed函数和keyType函数。
首先我们来看看,keyPressed函数和key以及keyCode的使用,这里我们来实现一个小游戏,按上下左右键,白球向上下左右运动,按任意其他键,小球停止运动,代码如下:
int direction;
float px,py;
void setup()
{
size(600,300);
px=width/2;
py=height/2;
}
void draw()
{
background(0);
switch(direction)
{
case UP:
--py;
drawCircle(px,py);
break;
case DOWN:
++py;
drawCircle(px,py);
break;
case LEFT:
--px;
drawCircle(px,py);
break;
case RIGHT:
++px;
drawCircle(px,py);
break;
default:
py=py;
px=px;
drawCircle(px,py);
break;
}
}
void keyPressed()
{
if (key == CODED) {
print("key pressing now:");
switch(keyCode){
case UP:
direction=keyCode;
println("UP key is pressed!");
break;
case DOWN:
direction=keyCode;
println("DOWN key is pressed!");
break;
case LEFT:
direction=keyCode;
println("LEFT key is pressed!");
break;
case RIGHT:
direction=keyCode;
println("RIGHT key is pressed!");
break;
default:
break;
}
}
else
{
direction=0;
println("PAUSE key is pressed!");
}
}
void drawCircle(float x,float y)
{
ellipse(x,y,50,50);
}
运行效果如下图(这里是静态图,运行程序可以得到动态图)
这里需要注意的是,keyCode只包含了UP, DOWN, LEFT, RIGHT ALT, CONTROL, SHIFT几个键;其余的需要通过key的值,即ASCII来判断;
keyReleased函数的用法和keyPressed没有什么差别,这里不再赘述;
keyType函数与keyPressed的区别在于,它屏蔽了几个功能键 Ctrl, Shift, Alt,CapsLock,以及四个方向键,也就是说,他们几个的按键事件不会在keyTyped中有任何反应。
2、鼠标事件
和按键事件一样,我们可以通过键值来判断具体按了哪个鼠标键,如果我们不加判断,那么任意鼠标按键都会在这个函数中被响应。典型的鼠标事件有mousePressed,mouseReleased,mouseWheel ,mouseDragged,mouseMoved。鼠标事件主要依靠mouseX,mouseY,以及mouseButton(这是个枚举值,有LEFT, RIGHT, CENTER三个选项)
下面我们通过鼠标在屏幕上画直线的小程序来演示他们的使用(点击开始画线,再次点击结束画线),代码如下:
float startX,startY,endX,endY;
int mouseState=0,endraw=0;
void setup()
{
size(600,400);
background(255);
}
void draw()
{
background(255);
if(endraw==1)
drawline(startX,startY,endX,endY);
}
//获得起点坐标mouseState设置为1,按下
void mousePressed()
{
if(mouseButton==LEFT && mouseState==0 )
{
println("begin drawing line.");
startX=mouseX;
startY=mouseY;
mouseState=1; //它使mouseMoved能够更新终点坐标
endraw=0;
}
else
mouseState=0; //使终点坐标不再更新(起点坐标没有LEFT的配合也不能更新)
//(这里可以考虑添加存储已有线条的数据的代码)
}
//确认拖拽是否激活
void mouseMoved()
{
if( mouseState>0 )
{
endraw=1; //它使画线能够更新
endX=mouseX;
endY=mouseY;
}
}
void drawline(float sx,float sy,float ex,float ey)
{
line(sx,sy,ex,ey);
println(mouseState+"start:"+startX+":"+startY+"end:"+endX+":"+endY);
}
运行效果如下:
以上程序仅仅实现了绘制单根线条,如果想在屏幕上同时显示多根线条,须将绘制的线条数据保存起来,每次在绘制的时候都将他们绘制出来即可。我们在以后的摸索中将会展示多线条的绘制。
既然是摸索,可能很多问题在Processing的官网上或者在他的documents中都已经详细说明了,但我必须说明的是,我这里是摸索,而不是按照既定的说明和教程在学习。所以出现了文章一开始遇到的问题。
如果您是通过搜索引擎来到了本页而没有了解前面的内容,可以点击下面的链接回顾前面的内容:
Processing摸索前行(1)
已经继续一起摸索后续的内容,
Processing摸索前行(3)