日更第3期-2015-1-18-openFrameworks系列第二讲-规范学习函数式编程!

昨天周六,我休息了一次。今天感觉心情还不错,就干脆多更一点。恩,同学们,注意啦,今天的课程可是不仅长,

还非常的不轻松哦!我们主要要讲一下C++中继承于C的函数式编程,还有判断这种特殊结构;除此之外,

OpenFrameworks的一些基础函数、概念,我也会悉数讲解。

总之,做好觉悟再上吧!

有问题可以发到我的邮箱:[email protected]

于是,开始今天的课程!

C++中的函数式编程

于是,先把我下面的这些代码敲到你的编辑器里吧。

  1 //ofApp.cpp
  2
  3 #include "ofApp.h"
  4
  5 int posX;
  6 int posY;
  7 //球的位置
  8 double speedX;
  9 double speedY;
 10 //球的速度
 11 int windX;
 12 int windY;
 13 //程序窗口大小
 14 double lossRate;
 15 //速度减慢的速率(即加速度的百分比形式)
 16 float hue;
 17 //球的色相
 18 float ballRadius;
 19 //球的半径
 20
 21 bool isShot = false;
 22 //是否截图
 23 bool isSmalling = true;
 24 //是否球在变小
 25 //以上代码请放在最上面
 26
 27 void changeDirection(int x, int y);
 28 //改变方向的函数
 29 void outRange();
 30 //检查是否出界的函数
 31 void slow();
 32 //减慢小球速度的函数
 33 void changeSize();
 34 //改变小球大小的函数
 35
 36 //--------------------------------------------------------------
 37 void ofApp::setup(){
 38     posX=200;
 39     posY=180;
 40     //赋予小球初始位置
 41     speedX=1;
 42     speedY=-1;
 43     //赋予小球初始速度
 44     lossRate = 0.999;
 45     //赋予小球损失速率
 46     ballRadius = 60;
 47     //赋予小球初始半径
 48     ofSetCircleResolution(30);
 49     //设置显示小球的为 30边形
 50     hue = fmodf(ofGetElapsedTimef()*10,255);
 51     //从系统时间得到色相值
 52     ofColor c;
 53     c.setHsb( hue,200,200);
 54     //把色相值转化为颜色
 55     ofSetColor(c);
 56     //设置颜色
 57 }
 58
 59 //--------------------------------------------------------------
 60 void ofApp::update(){
 61     windX = ofGetWindowWidth();
 62     windY = ofGetWindowHeight();
 63     //获取当前窗口宽高
 64     hue++;
 65     //色相增加
 66     if(hue > 255)
 67     {
 68         hue = 0;
 69     }
 70     //如果色相值越界,重新取0
 71
 72     outRange();
 73     //检查小球是否越界
 74
 75     posX += speedX;
 76     posY += speedY;
 77     //小球更新坐标
 78
 79     slow();
 80     //小球减慢速度
 81     changeSize();
 82     //改变小球尺寸
 83 }
 84
 85
 86 //--------------------------------------------------------------
 87 void ofApp::draw(){
 88     ofBackgroundGradient(ofColor::white,ofColor(100,100,100), OF_GRADIENT_CIRCULAR);
 89     //画有渐变的背景
 90     if(isShot)
 91     {
 92         ofBeginSaveScreenAsPDF("screenshot-"+ofGetTimestampString()+".pdf", false);
 93     }
 94     //开始准备截屏
 95     ofFill();
 96     //确认填充状态为填充
 97     ofColor c;
 98     c.setHsb( hue,200,200);
 99     ofSetColor(c);
100     //确认现在填充颜色状态为c
101     ofCircle(posX, posY, ballRadius);
102     //画出小球
103     if(isShot)
104     {
105         ofEndSaveScreenAsPDF();
106         isShot = false;
107     }
108     //截图
109 }
110
111 //--------------------------------------------------------------
112 void ofApp::keyPressed(int key){
113     if(speedX>2000 || speedX < -2000 || speedY >2000 || speedY <  -2000)
114     {
115         return;
116     }
117     //检查小球是否超速;如果超速,不再增加
118     if(key == ‘W‘ || key == ‘w‘ || key ==OF_KEY_UP)
119     {
120         speedY -= 5;
121     }
122     if(key == ‘S‘ || key == ‘s‘ || key ==OF_KEY_DOWN)
123     {
124         speedY += 5;
125     }
126     if(key == ‘A‘ || key == ‘a‘ || key ==OF_KEY_LEFT)
127     {
128         speedX -= 5;
129     }
130     if(key == ‘D‘ || key == ‘d‘ || key ==OF_KEY_RIGHT)
131     {
132         speedX += 5;
133     }
134     if(key ==‘ ‘)
135     {
136         speedX = 0;
137         speedY = 0;
138         //强制停止小球
139     }
140     //根据不同键位确定对小球的指令
141 }
142
143 //--------------------------------------------------------------
144 void ofApp::keyReleased(int key){
145
146     if(key ==‘p‘|| key ==‘P‘)
147     {
148         isShot = true;
149     }
150     //确认截图
151 }
152
153 //--------------------------------------------------------------
154 void ofApp::mouseMoved(int x, int y ){
155
156 }
157
158 //--------------------------------------------------------------
159 void ofApp::mouseDragged(int x, int y, int button){
160
161 }
162
163 //--------------------------------------------------------------
164 void ofApp::mousePressed(int x, int y, int button){
165
166 }
167
168 //--------------------------------------------------------------
169 void ofApp::mouseReleased(int x, int y, int button){
170
171 }
172
173 //--------------------------------------------------------------
174 void ofApp::windowResized(int w, int h){
175
176 }
177
178 //--------------------------------------------------------------
179 void ofApp::gotMessage(ofMessage msg){
180
181 }
182
183 //--------------------------------------------------------------
184 void ofApp::dragEvent(ofDragInfo dragInfo){
185
186 }
187
188 void outRange()
189 {
190     if(posX>windX-ballRadius)
191     {
192         posX = windX-ballRadius;
193         changeDirection(1,0);
194     }
195     if(posX<0+ballRadius)
196     {
197         posX = 0+ballRadius;
198         changeDirection(1,0);
199     }
200     if(posY>windY-ballRadius)
201     {
202         posY = windY-ballRadius;
203         changeDirection(0,1);
204     }
205     if(posY<0+ballRadius)
206     {
207         posY = 0+ballRadius;
208         changeDirection(0,1);
209     }
210     //检查是否出界
211 }
212
213 void slow()
214 {
215     speedX *= lossRate;
216     speedY *= lossRate;
217     //减慢小球速度
218 }
219
220 void changeDirection(int x, int y)
221 {
222     if(x==1)
223     {
224         speedX *= -1;
225     }
226     if(y==1)
227     {
228         speedY *= -1;
229     }
230     //改变小球方向
231 }
232
233 void changeSize()
234 {
235     if(isSmalling == true)
236     {
237         ballRadius -= 0.1;
238     }
239     else
240     {
241         ballRadius += 0.1;
242     }
243
244     if(ballRadius <= 30)
245     {
246         isSmalling = false;
247     }
248     if(ballRadius >= 90)
249     {
250         isSmalling = true;
251     }
252     //改变小球大小
253 }
 1 //main.cpp
 2 #include "ofMain.h"
 3 #include "ofApp.h"
 4
 5 //========================================================================
 6 int main( ){
 7     ofSetupOpenGL(800,600,OF_WINDOW);            // <-------- setup the GL context
 8
 9     // this kicks off the running of my app
10     // can be OF_WINDOW or OF_FULLSCREEN
11     // pass in width and height too:
12     ofRunApp(new ofApp());
13
14 }

我在这里先声明一件事:我知道带着行号的话不好粘贴复制;我这么做就是因为不想让你们粘贴复制

为什么呢?因为只是粘贴复制的话,是根本没有任何意义的——但即便仅仅是用手打一遍,看着我的注释,也能大概

理解我到底想做些什么。这就是区别。所以,请不要粘贴复制了,请用自己的手把相应的代码打到相应的位置上去吧!

然后我来说一下今天怎么展开我的教程:第一步,讲解OpenFrameworks程序的编程思路,并介绍这是如何实现的;

第二步,讲解C++的判断结构 与 函数式编程;第三步,讲解这个程序里使用的OpenFrameworks函数接口;第四步,

就某些特殊步骤重点讲解;第五步,提出问题和建议,引导读者自行修改程序

那么,我先从第一步开始

OpenFrameworks编程思路

在正式开始讲解之前,我先讲解一个概念:程序其实是多种多样的,常见的有独占型应用、响应型应用、辅助型应用、

精灵型应用等等。(应用和程序指代的东西相同,只不过分类源头不同)什么是独占型应用呢?就是它会默认你在

打开这个应用的时候,就不会打开别的应用;或者,只是某些功能可以独占。比如,游戏,就是一种典型的独占型应用,

它会占据大量内存,也需要CPU的大量运算——你也知道这一点,所以玩游戏的时候就肯定会关上无关的程序,不然

游戏就要卡;当然,所谓独占不一定是完全独占,他可能只独占了显卡、内存,可能不会妨碍你的网络通信,所以你

还可以下载东西之类的。然后,响应型应用就不同了——你如果没有操作,不需要它响应的话,它就不会耗费额外的资源。

(不过有的软件只要打开就会占用很大资源,比如word,但是也可以算为响应型应用,因为它不会自己增加消耗)网页

浏览器是一个比较合适的例子(只要不在上面玩游戏或者播放流媒体的话;不过现在的情况又有了点变化,比如Ajax......

),而一般Windows程序都是以相应型应用的标准做出来的——不然你就不可能同时开着那么多进程了。辅助型应用是

占资源比较低,但是一般一直打开的应用;比如输入法;精灵型应用其实是个老概念,你可以认为,凡是悬浮窗的部分,

就是精灵型应用。

那么,终于到重点了,OpenFrameworks上的应用是什么应用呢?答案就是:独占型应用

不知道大家熟不熟悉电影的原理——利用视觉暂留现象,快速的切换画面,使人误以为静止的画面是动起来的 那么一种

现象。而其中,每一幅画面,叫做一帧。在动画、游戏的领域,也同样有“帧”这个称呼。实际上,OpenFrameworks

产生的程序,大部分都是以交互为目标的,那么必然就需要动起来,于是OpenFrameworks就采取了每隔一段时间,

进行计算,更新图形的相应信息,然后整个窗口重新绘制的方法。而这间隔的时间,一般是1/60秒,即16毫秒左右,以

人类的观点来看,是很小的数字;但对于计算机来说,已经足够大了。

(这些图片是用并排放着的好几个摄像机拍出来的,后来Muybridge根据这些赛马的图片,创作了人类历史上的第一个小电影。)

那么,说了这些知识,无非就是想说明:OpenFrameworks程序每秒钟都会绘制非常多次,而编程的重点,就是把连续

的动作,分为每一段每一段的循环往复的操作。OpenFrameworks现在提供了三个接口函数用于每秒调用:setup(),

update(),draw()。其中setup()是只有开始程序时调用,一般用于初始化;update()和draw()都是每秒调用多次,但是,

update()中一般会写那些数值如何变化的部分;而draw()只负责画。这样的好处是,如果程序很卡,那么会出现掉帧的

现象,但不会是整个程序变慢(具体来讲的话,可以想象竞速类游戏——如果都在draw()里更新的话,那么结果就是,

不卡顿的情况下很正常,卡顿的话,整个车的速度都会变慢;而分开写的话,车的速度不会变,只不过变成一下一下的

瞬移了)所以,我们设计的时候就要考虑怎么合理划分我们的程序内容,怎么把连续的动作化为分解动作。

C++的判断结构 与 函数式编程

我这里以大家完全没有过编程经验为前提——虽然我强烈建议至少学过C,所以会从很基础讲起;但是,我这里并不是

基础课教程,所以也会只讲我用到的部分,相要学的更好,你就还是需要自己另外的学习的。

首先我把程序中的某个句子拿出来:

1 hue++;
2     //色相增加
3     if(hue > 255)
4     {
5         hue = 0;
6     }
7     //如果色相值越界,重新取0

这里的几句话我都会讲的。

首先,我说说色相是什么——我会在后面的色彩一节具体的讲解的,但是此处我也粗略的讲一讲。你可以将之理解为

颜色的特征——也就是说只要色相不同,看起来就不一样。其取值范围是0~255。那么,我想要我显示的那个小球的

颜色不停变化的话——我只有把这个色相的值不停地改变,然后每次画的时候,都用新的颜色来画,不就可以了吗?

hue++;

这句话就说hue这个”变量“的值增加1。

变量:存储数据的容器

++:自增符号,表示取到自己的值加上1再赋给自己

if(hue > 255)
{     hue = 0;    }

上面这段代码的意思是 如果 hue 大于 255 的话, 把0这个数赋给255

这里面包含了一个判断if(){},就是说当()内的内容被判断为真的时候,运行{}内的内容(也可以不加{},那样默认

下面的第一行是运行内容)这样,通过判断,我们就保证了hue这个变量的取值一定在0~255之间了。

我的程序中还有很多用到了判断的地方,大部分都用相应的注释,请自行消化

那么我接下来讲一讲函数

(一张三角函数的图,有没有回忆起初高中)

函数是一个数学上的概念,当然不止是上图所示的三角函数。真正的定义是什么呢:对于给定的某个输入值,一定有唯一

确定的输出。就是说,只要你的输入不变,输出也绝对不变。你可以利用函数来计算或者做某些变化,但是它具体怎么做的,

你在使用它的时候你并不需要关心。

实际上,我们在写OpenFrameworks上的程序时,不可能不用OpenFrameworks自带的函数;但是仅有那些还是不够的,

我们有的时候为了简化结构,就需要自己写一些函数。如果你认真看我上面的代码了的话,你就会发现,有这种结构:

void changeDirection(int x, int y);
//改变方向的函数

void changeDirection(int x, int y)
{
    if(x==1)
    {
        speedX *= -1;
    }
    if(y==1)
    {
        speedY *= -1;
    }
    //改变小球方向
}

上面的部分叫做声明,下面是实现。

那么为什么要声明?因为程序在编译的时候,是从上到下的,所以如果你的函数在下面的话,使用的时候可能看不见,

那么就需要提前声明一下。

根据上面这个函数,我们可以得到这个规律:

void 函数名 (...){...}

不过其实准确来说,是这样:

类型名 函数名(...){...}

什么是类型?就是变量的结构(变量是存储数据的地方),即这个变量是存储什么类型的数据的。void是空类型,即什么

也没有,就是说,我上面那个函数是什么都不会返回的。但是,你也可以让它返回——只要你需要的话。数据类型有很多,

比如上面的int、double。分别代表整型和双精度浮点型。如果展开来说实在太宏大了,所以还仰仗你们自己的学习。

我在程序里定义了好几个函数,这样,就缩短了update()这个函数的长度,加强了可读性。

当然,把函数里的东西,都放回到调用它的地方,是完全可行的;但那样很明显不清晰。所以,为了简化结构(从而写出

更负责的程序),人们发明了函数式编程这是必须掌握的。

我用到的一些OpenFrameworks程序接口

我只介绍几个,因为大部分在代码里看着已经很清晰了

ofFill();
ofSetColor(c);
ofCircle(posX, posY, ballRadius);

以上三个函数是绘图时的核心函数,第一个,用于确认现在的填充状态为填充(如果写ofNoFill()的话就没有填充),第二个,

是设置现在的填充颜色为c这个变量里储存的颜色,第三个,就是在(posX,posY)这个点上画一个半径为ballRadius的圆。

现在,你可以运行一下程序了,然后你就可以发现一个可以到处乱动的、不断变着颜色、变着大小的小球就出现了。你按下wasd

或者方向键的话,它就会按你的要求加速减速;按空格就会马上停在原地;按p键就可以截图。

你可以试着以自己的想法改变一下我的代码,然后观察现象是不是符合你的推测。毕竟,实践是学习的最好方法。

时间: 2024-10-17 15:35:15

日更第3期-2015-1-18-openFrameworks系列第二讲-规范学习函数式编程!的相关文章

日更第2期-2015-1-15-openFrameworks系列第一讲-手把手制作openFrameworks上的第一个程序!

恩,今天和朋友打球来着,于是今天的案例程序就做一个球吧!O(∩_∩)O哈哈~ 首先,没有看过上一篇教程的同学,还有还没有下载好VS和OpenFrameworks的同学,都去下一下. 传送地址:http://www.cnblogs.com/linongbo/p/4227552.html 那么,开始今天的日更啦! Hello OpenFrameworks! VS的安装部分我就不说了,不过我个人建议——默认是安装在C盘的,不过你要是手动改到别的盘上的话,C盘上 依然会有6G左右的内容.......Σ(

日更第4期-2015-1-19-openFrameworks系列第三讲-面向对象的小球们

昨天的教程里,实在有太多想讲的东西了,所以反而什么都没有讲.一个很大的原因就是——我妄想让所有 水平的读者都能读懂.这其实是绝对不可能的.因为,每个人知识面不同,已经掌握的技能也不同,那么所 适应的学习轨迹其实也该不同. 以我个人来说,我其实是一个谨慎型的人,在学习的过程中一般不会冒进,这意味着我的基础知识会更加 扎实,但是进步的速度就会偏慢.我在这里写博客,其实就是一种学习方式,但无疑,这种学习方式其实很 效率低下(而且还没有人看).可是,对于我就是一个很好的补充......感觉有点跑题了,其

日更第6期-2015-1-29-如何科学地使用因特网-第一讲-总之先爬墙

哟哟,我又来日更了啊!O(∩_∩)O哈哈~不过,其实是隔了两天,不过比起上次,是要好了不少. 先说个好消息吧!我1月31日就要放假啦,然后就可以回家啦. 然后,回家之后,我就要正式的日更啦!现在为止我可是一直在隐藏实力哦.我要从几天一更进化为一天几更. 先试试一夜七次...... 恩,说点正经的:我接下来会更新些什么内容. 第一部分,OpenFrameworks的使用,即OpenFrameworks系列教程,里面包括example的解释,tutorial的 翻译,api的详解以及最新例子的展示.

日更第11期-2015-3-27-processing教程-API篇-第一讲-map(),Table,loadTable(),norm(),lerp()

hI!!今天上线发现我多了一个粉丝!!哇,好高兴! 不过我昨天食言了,没有继续日更......希望不会掉粉..... 不过那是有原因的,我昨天一直在找数据,终于今天给整理好了,我打算这个周末整一整.然后就可以出真正厉害的教程啦!! 我先说一下我接下来会出的教程,然后说说今天发的这个到底是什么. 接下来: 1,美国失业数据可视化 2,地图数据可视化案例教学(案例来自processing教学书visualizing data) 3,中国高考分地域分析 4,API教程 然后说说今天这是干啥. 简单来说

日更第13期-2015-4-9-processing教程-API篇-第二讲-lerpColor()、nfp()、

于是隔了十天来更新了.然后今天更新的是Processing的api,其实这算是我学习Processing途中的一种副产品:反正凑够数了, 我今天就拿出来看看.其实只是api文档的话,并没有什么存在的必要性.但如果只是讨论存在的必要性的话,我的博客本身也没有 存在的必要性.比较也没有记载什么高深或者特别有用的东西.我之前写博客的目的只是因为想写,而因为想写而写,自然就会导致 中断,毕竟人是会善变的动物. 那么,我接下来要做的事就是改变. 我打算写些真正有用的文章. 下一篇进入Processing实

【算法?日更?第八期】区间动态规划:1572:括号配对题解

废话不多说,直接上题: 题目测评链接:戳这里 其实什么GBE都没用,小编最开始看了半天不懂,看了看别人的博客才知道这段话没什么用处.其实就是给一段字符串,判断是否括号是配对的. 这道题一看就会想到区间动态规划(不会戳这里临时补一补),最开始先老老实实地写了一遍区间动态规划,后来觉得用栈也可以,于是写了一遍,代码如下: 1 #include<iostream> 2 #include<cstring> 3 #include<stack> 4 using namespace

【算法?日更?第十七期】信息奥赛一本通1598:【 例 2】最大连续和题解

废话不多说,直接上题: 1598:[ 例 2]最大连续和 时间限制: 1000 ms         内存限制: 524288 KB提交数: 303     通过数: 91 [题目描述] 给你一个长度为 n 的整数序列 {A1,A2,?,An},要求从中找出一段连续的长度不超过 m 的子序列,使得这个序列的和最大. [输入] 第一行为两个整数 n,m: 第二行为 n 个用空格分开的整数序列,每个数的绝对值都小于 1000. [输出] 仅一个整数,表示连续长度不超过 m 的最大子序列和. [输入样

《软件测试管理公开课》2015.8.7~8 深圳 2015.8.11~12 北京 2015.8.18~19上海,欢迎报名!

课时:13小时(2天) 在软件开发流程中构筑软件质量 --软件测试管理     2015.8.7~8 深圳 2015.8.11~12 北京 2015.8.18~19上海   [课程背景] 据中国软件行业协会研究报告显示,2010年1-11月,我国软件业呈快速增长态势,同比增长30%,增速比去年同期提高8.6个百分点,软件产业已成为中国高科技发展重要支柱之一,但中国软件产品质量保证手段以及测试流程和管理的规范性,与国外同行(美国.印度等)存在较大的的差距.      在软件业较发达的国家, 软件测

《高性能JavaScript》学习笔记(2)——日更中

我说日更就日更,接着....今天从缓冲布局信息开始啦! -------------------2016-7-22 21:09:12--------------------------- 14.减少对布局信息的查询次数,查询时将他赋值给局部变量参与计算. 例子,在元素网右下方不断平移时,在timeout中可以写: 1 var current = myElement.offsetLeft; 2 current++; 3 myElement.style.left = current + 'px'; 4