日更第4期-2015-1-19-openFrameworks系列第三讲-面向对象的小球们
昨天的教程里,实在有太多想讲的东西了,所以反而什么都没有讲。一个很大的原因就是——我妄想让所有
水平的读者都能读懂。这其实是绝对不可能的。因为,每个人知识面不同,已经掌握的技能也不同,那么所
适应的学习轨迹其实也该不同。
以我个人来说,我其实是一个谨慎型的人,在学习的过程中一般不会冒进,这意味着我的基础知识会更加
扎实,但是进步的速度就会偏慢。我在这里写博客,其实就是一种学习方式,但无疑,这种学习方式其实很
效率低下(而且还没有人看)。可是,对于我就是一个很好的补充......感觉有点跑题了,其实我是想表达
这么一个意思:因材施教是一种很好的学习方式,但是想要在这种没法看见读者,并且不知道他已经知道了
多少内容的情况下,这就是完全不可能的事。但如果想把教学内容适合于所有人,那其实是更高傲的想法,
是更加难以实现的——所以,我只能以我的某个标准来教学(并尽量保证这种标准偏低但是不会过于影响教学)
然后大家就各取所需好了。
今天说了很多无关主题的话,主要就是想说:我会努力提高这些教程的质量的!
那么,今天的日更,开始啦!
面向对象的小球
什么是面向对象
简单来说,面向对象就是把某些数据和对于这些数据的操作放在一起的做法。那么,为什么要这样?为什么要把
数据和操作放在一起?答案就是 这样做有利于减轻思考负担。
很多人在讲解面向对象时,喜欢说,这是一种对于现实世界的仿真,是模拟现实中实际存在的东西的一种抽象手法。
这么说当然没错,但是我不喜欢——这样的话确实会让听到的人感觉很厉害,但并不利于理解这个概念,甚至会妨碍
他们思考如何写出一个对象。拿神经网络来说,如果说它是仿照人的神经元制作的系统,那么确实可以忽悠一大波人,
但是想简单说也不是不可以——它就是一个不知道真实函数的情况下的暴力求解法。通过一层层的判断,逐渐生成更
近似真实情况的函数,从而得到几乎正确的近似解。
那么我们来说说这个方法怎么减少负担。
拿上一次的代码作为栗子:
1 void ofApp::update(){ 2 windX = ofGetWindowWidth(); 3 windY = ofGetWindowHeight(); 4 //获取当前窗口宽高 5 hue++; 6 //色相增加 7 if(hue > 255) 8 { 9 hue = 0; 10 } 11 //如果色相值越界,重新取0 12 13 outRange(); 14 //检查小球是否越界 15 16 posX += speedX; 17 posY += speedY; 18 //小球更新坐标 19 20 slow(); 21 //小球减慢速度 22 changeSize(); 23 //改变小球尺寸 24 }
这个函数是来控制小球变化情况的一个函数。它要做什么呢?首先获取窗口宽高那里,其实是在改变两个全局变量的值,
通过这种方式来减少不必要的别的地方的开支(比如在判断是否越界时,你就不用再去获取宽高了)。而outRange()
其实是检查这个小球是否越界,posX,posY的两句是用来更新小球位置。slow()是用来减慢小球速度,changeSize()
是用来改变小球的大小。
可能你会说,这不是挺好的吗?对,以目前的情况来看是没有什么问题。那么,如果你想做很多个小球呢?有人可能知道
数组,他就说,干脆把每一个数值都改成一个数组,然后呢,就可以通过数组来操纵所有小球了。当然还需要把函数进行
一些改造,使之变成对于数组的操作而不是对于某一个数的操作。
这确实是一种解决方法。但是,这种方法也有一些问题——比如,我想单独操作某个小球,那么这个时候应该怎么做呢?
一种解决方式是——增加函数的数量,然后通过循环把每个小球的对应的函数调用即可。
这已经有了些方面对象思想的雏形,但是还是不行——如果我现在有成百上千个小球,那么你就要写个成百上千行吗?
当然这不是一种好的解决方案。
所以我们提出了“类”,它代表着同一类型的对象。比如我们有很多小球,它们每一个都是一个对象,都有相同的数据类型,
都有相同的操作方式——但是存储的内容不同。这样,我们就可以建立一个类来描述这些小球的共有特征,然后提供相应
的函数来对于这些数据进行操作。这样我们就可以用循环而不是一大长串的代码来做我们想做的内容了。
是不是感觉这种要好一点?
当然,面向对象编程还有很多好处,比如你要动态生成一个结构,那么其实在它所在的类的析构函数中释放就好了。不过,
要注意的一点是——不是说面向对象的越彻底越好——那样的话有时候反而会丧失一定的灵活性。具体怎么使用,还是要看
个人习惯以及具体的内容来定
(左边是类,右边是对象)
设计类
那么就开始设计我们的小球类吧!
由于C++中的对象都是由类来生成的,所以如果我们想要建立小球的对象,就需要先建立小球的类。
那么,我们先想一下,我们需要这个小球干什么?
- 属性:大小
- 属性:颜色
- 属性:位置
- 属性:速度
- 方法:获得随机的初始大小、颜色、位置、速度
- 方法:根据速度更新位置
- 方法:碰撞检测(在后面的考虑中可以发现,从小球角度来实现比较麻烦)
- 方法:越界检测
恩,那我们就按照这个来编写我们的类好了。(这一次我们就不变颜色大小了)
对了,VS中有一个很方便的添加类的方式(有很多工程中使用这种方式会更加轻松),我来演示一遍。
(如图选择)
(点击创建之后就会出现这个向导,取好名字就好,一般默认首字母大写)
于是开始写啦!~\(≧▽≦)/~啦啦啦
这里我就不管你们懂不懂啦,我就直接写了,请看代码!
#pragma once #include "ofMain.h"class Ball { public:Ball(int windowX, int windowY);~Ball(void);void update(int windowX, int windowY);void draw();void changeDirection(bool a,bool b);void setSpeed(double x, double y);void setPos(double x, double y);double getSize();int getX();int getY();double getSpeedX();double getSpeedY();private:int posX;int posY;//位置double speedX;double speedY;//速度float hue;float size;//颜色、大小void slowDown();void move();void checkRange(int windowX, int windowY); };
#include "Ball.h"Ball::Ball(int windowX, int windowY) {this->posX = ofRandom(100,windowX);this->posY = ofRandom(0,windowY);this->speedX = ofRandom(0,100);this->speedY = ofRandom(0,100);this->hue = ofRandom(0,255);this->size = ofRandom(20,40); }Ball::~Ball(void) { }void Ball::update(int windowX, int windowY) {this->slowDown();this->move();this->checkRange(windowX,windowY); }void Ball::draw() {ofFill();//确认填充状态为填充 ofColor c;c.setHsb( this->hue,200,200);ofSetColor(c);//确认现在填充颜色状态为cofCircle(posX, posY, this->size);//画出小球 }void Ball::changeDirection(bool a, bool b) {if(a){this->speedX *= -1;}if(b){this->speedY *= -1;}}void Ball::setSpeed(double x, double y) {this->speedX = x;this->speedY = y; }void Ball::setPos(double x, double y) {this->posX = x;this->posY = y; }double Ball::getSize() {return this->size; }int Ball::getX() {return this->posX; }int Ball::getY() {return this->posY; }double Ball::getSpeedX() {return this->speedX; }double Ball::getSpeedY() {return this->speedY; }void Ball::slowDown() {this->speedX *= 0.99;this->speedY *= 0.99; }void Ball::move() {this->posX += this->speedX;this->posY += this->speedY;}void Ball::checkRange(int windowX, int windowY) {if(this->posX < this->size){this->posX = this->size+1;this->changeDirection(true,false);return;}if(this->posX > windowX - this->size){this->posX = windowX - this->size -1;this->changeDirection(true,false);return;}if(this->posY < this->size){this->posY = this->size + 1;this->changeDirection(false,true);return;}if(this->posY > windowY - this->size){this->posY = windowY- this->size - 1;this->changeDirection(false,true);return;}};
一般来说,刚写好的代码都是用不了的,因为它还没有经历实践的检验。所以我在这里写的代码,其实是我已经
修改之后的代码——不要觉得自己一下子就可以完全写好哦。
然后我们来想想我们想要什么效果?我们这一次预计的效果是这样的:我们有10个小球,大小颜色各不相同;它们
拥有自己的颜色、大小、速度、位置,并且可以相互碰撞。我们点击鼠标左键的话,就可以出现一个球,然后这个球
也可以和那些小球进行碰撞处理,并且有一个越快拖动加速就越快的设定。
那么我们还需要鼠标那个球的类以及整个游戏的类(出于个人习惯,我在总结一些全局控制的东西时,喜欢加上一个
管理它们的类,如不加,用函数实现亦可)
于是我们想想怎么写鼠标产生的球的类:
- 属性:颜色
- 属性:大小
- 属性:位置
- 属性:速度
- 方法:碰撞小球
- 方法:隐形
- 方法:显露
可以看出,有很多的部分和上面的那个球的类的部分十分相近——有的人可能知道继承,但是我这一次是不会使用的,
因为没有必要。
那么,代码如下:
#pragma once class MouseBall { public:MouseBall(void);~MouseBall(void);void show(int x, int y);void update(int x,int y);void draw();void hide();double getX();double getY();double getSpeedX();double getSpeedY();int getSize();bool isShow();private:bool isShowing;double pastPosX;double pastPosY;double currentPosX;double currentPosY;int size; };
1 #include "MouseBall.h" 2 #include "ofMain.h" 3 4 MouseBall::MouseBall(void) 5 { 6 this->size = 10; 7 this->currentPosX = 0; 8 this->currentPosY = 0; 9 this->pastPosX = 0; 10 this->pastPosY = 0; 11 } 12 13 14 MouseBall::~MouseBall(void) 15 { 16 } 17 18 19 void MouseBall::show(int x, int y) 20 { 21 this->isShowing = true; 22 this->update(x,y); 23 this->update(x,y); 24 } 25 26 void MouseBall::update(int x, int y) 27 { 28 if(this->isShowing == false) 29 { 30 return; 31 } 32 this->pastPosX = this->currentPosX; 33 this->pastPosY = this->currentPosY; 34 this->currentPosX = x; 35 this->currentPosY = y; 36 } 37 38 void MouseBall::draw() 39 { 40 if(this->isShowing == false) 41 { 42 return; 43 } 44 ofFill(); 45 //确认填充状态为填充 46 ofColor c; 47 c.set(255,50,50); 48 ofSetColor(c); 49 //确认现在填充颜色状态为c 50 ofCircle(this->currentPosX, this->currentPosY, this->size); 51 //画出小球 52 } 53 54 void MouseBall::hide() 55 { 56 this->isShowing = false; 57 } 58 59 60 double MouseBall::getX() 61 { 62 return this->currentPosX; 63 } 64 65 double MouseBall::getY() 66 { 67 return this->currentPosY; 68 } 69 70 71 double MouseBall::getSpeedX() 72 { 73 return (this->currentPosX - this->pastPosX); 74 } 75 76 double MouseBall::getSpeedY() 77 { 78 return (this->currentPosY - this->pastPosY); 79 } 80 81 int MouseBall::getSize() 82 { 83 return this->size; 84 } 85 86 bool MouseBall::isShow() 87 { 88 return isShowing; 89 } 90 91
之后,我们就要想想整个游戏的类了,它需要什么呢?
- 初始化函数
- 结束函数
- 小球们的操作
- 对于鼠标和小球的碰撞处理
- 对于小球们自己的碰撞处理
然后,代码如下。
#include "ofMain.h" #include "Ball.h" #include "MouseBall.h" #pragma once class Table { public:Table(void);~Table(void);void setupBalls(int windowX, int windowY);void moveBalls(int windowX, int windowY);void drawBalls();void showMouseBall(int windowX, int windowY);void hideMouseBall();void updateMouseBall(int windowX, int windowY);void drawMouseBall();bool isMousBallShow();void checkMouseBall(int windowX, int windowY);private:void checkBump(int a, int b);Ball* smallBall[30];MouseBall mouseball;int ballNum; };
#include "Table.h"Table::Table(void) {this->ballNum = 30; }Table::~Table(void) {for(int i=0; i<this->ballNum; i++){delete this->smallBall[i];} }void Table::setupBalls(int windowX, int windowY) {for(int i=0; i<this->ballNum; i++){this->smallBall[i] = new Ball(windowX,windowY);for(int j =0; j<i; j++){int Aposx = this->smallBall[j]->getX();int Aposy = this->smallBall[j]->getY();int Bposx = this->smallBall[i]->getX();int Bposy = this->smallBall[i]->getY();double distance = ofDist(Aposx,Aposy,Bposx,Bposy);if(distance < (this->smallBall[i]->getSize()+this->smallBall[j]->getSize()) + 10 ){delete this->smallBall[i];i--;break;}}}for(int i=0; i<this->ballNum; i++){this->smallBall[i] = new Ball(windowX,windowY);} }void Table::moveBalls(int windowX, int windowY) {for(int j=0; j<this->ballNum; j++){for(int k=j; k<this->ballNum; k++){checkBump(j,k);}}for(int i=0; i<this->ballNum; i++){this->smallBall[i]->update(windowX,windowY);}for(int j=0; j<this->ballNum; j++){for(int k=j; k<this->ballNum; k++){checkBump(j,k);}} }void Table::drawBalls() {for(int i=0; i<this->ballNum; i++){this->smallBall[i]->draw();} }void Table::checkBump(int a, int b) {int Aposx = this->smallBall[a]->getX()+this->smallBall[a]->getSpeedX();int Aposy = this->smallBall[a]->getY()+this->smallBall[a]->getSpeedY();int Bposx = this->smallBall[b]->getX()+this->smallBall[b]->getSpeedX();int Bposy = this->smallBall[b]->getY()+this->smallBall[b]->getSpeedY();int sizeA = this->smallBall[a]->getSize();int sizeB = this->smallBall[b]->getSize();double distance = ofDist(Aposx,Aposy,Bposx,Bposy);if(distance > (this->smallBall[a]->getSize()+this->smallBall[b]->getSize()) ){return;}else{double ma = sizeA*sizeA;double mb = sizeB*sizeB;double vaX = this->smallBall[a]->getSpeedX();double vbX = this->smallBall[b]->getSpeedX();double vaY = this->smallBall[a]->getSpeedY();double vbY = this->smallBall[b]->getSpeedY();double vBBX = ( ma*(2*vaX-vbX)+mb*vbX ) / (ma+mb);double vBBY = ( ma*(2*vaY-vbY)+mb*vbY ) / (ma+mb);double vAAX = vbX + vBBX - vaX;double vAAY = vbY + vBBY - vaY;if(Aposx-sizeA < 2){if(vBBX < 0){vBBX *= -1;}}if(Aposy-sizeA < 2){if(vBBY < 0){vBBY *= -1;}}int xxx = ofGetWindowWidth();int yyy = ofGetWindowHeight();if(Aposx > xxx-2-sizeA){if(vBBX > 0){vBBX *= -1;}}if(Aposy > yyy-2-sizeA){if(vBBY > 0){vBBY *= -1;}}if(Bposx-sizeB < 2){if(vAAX < 0){vAAX *= -1;}}if(Bposy-sizeB < 2){if(vAAY < 0){vAAY *= -1;}}if(Bposx > xxx-2-sizeB){if(vAAX > 0){vAAX *= -1;}}if(Bposy > yyy-2-sizeB){if(vAAY > 0){vAAY *= -1;}}this->smallBall[a]->setSpeed(vAAX,vAAY);this->smallBall[b]->setSpeed(vBBX,vBBY);} }void Table::showMouseBall(int windowX, int windowY) {this->mouseball.show(windowX,windowY); }void Table::hideMouseBall() {this->mouseball.hide(); }void Table::updateMouseBall(int windowX, int windowY) {this->mouseball.update(windowX,windowY);this->checkMouseBall(windowX,windowY); }void Table::drawMouseBall() {this->mouseball.draw(); }bool Table::isMousBallShow() {return this->mouseball.isShow(); }void Table::checkMouseBall(int windowX, int windowY) {for(int i=0; i<this->ballNum; i++){int Aposx = this->smallBall[i]->getX();int Aposy = this->smallBall[i]->getY();int size = this->smallBall[i]->getSize();if(ofDist(Aposx,Aposy,this->mouseball.getX(), this->mouseball.getY()) < size + this->mouseball.getSize() ){double spx = this->smallBall[i]->getSpeedX();double spy = this->smallBall[i]->getSpeedY();this->smallBall[i]->setSpeed(spx+this->mouseball.getSpeedX(),spy+this->mouseball.getSpeedY());return;}} }
之后,把它的代码放到主程序里,你就可以看到运行的结果了。
你可以试试把球的数目更改一下,看看是不是依然可以。
以上,通过这三期教程,我们初步了解了C++的编程方式、OpenFrameworks的编程思想还有一些十分基础的
图形接口。可以算是对于OpenFrameworks有了一个入门。那么,接下来,我们想要用好这个框架的话,就需要
不断地钻研与探索。其中的方法之一是研读 文档, 其二则是学习程序例子。在接下来的日更中,我们就要结合这
两方面来做。可能行文会比较混乱一些,但是之后集合成为有意义的内容之后,我就会整理为系列的内容,单独
发布。觉得日更吸引力比较低的读者,可以直接去看我整理好的资料——但是强烈建议初学者继续看我的日更部分,
因为这里面有很多的编程思想。
不过,为了能够更好地学习OpenFrameworks,我们要先学习一些无关的知识。也就是什么是FQ、如何FQ,
还有github是什么,怎么用好github的问题。估计要花一个星期左右。在这期间,我会尽量简单、高效的介绍
各种知识与技术的,敬请期待。
另外,由于今天内容较多,编辑时间长,故未能完全编写结束,一部分内容是在1月20日补充上的。而1月20日
则因为一些私事,不能完成一篇完整的日更,故休息一天。另,当网络部分知识教程解释之后,我可能会考虑
开始连载新的不同性质的博客(比如翻译、转载、新闻),具体内容当日声明。
以上
转载于:https://www.cnblogs.com/linongbo/p/4235030.html
日更第4期-2015-1-19-openFrameworks系列第三讲-面向对象的小球们相关推荐
- 日更第8期-2015-3-23-如何科学地使用因特网-第三讲-为什么要用Git Bash?咱们用Github for Windows吧!(上)...
Hi!我又回来了!这次我保证,真的真的要开始日更了,绝对不会再断了!再断我就......我就再发次誓呗...... 不过说实话,最近确实有些忙,主要是大创的申请工作,弄得我焦头烂额的.不过我最近确实编 ...
- 【算法•日更•第七期】区间动态规划详解+一本通1570能量项链题解
▎前置基础 啥也不用说,不会动态规划绝对看不懂,请不会动态规划的同志们先戳这里了解基础动态规划. ▎什么是区间动态规划? 区间动态规划可以理解为用了分治的动态规划. 顾名思义,动态规划中涉及了区间,那 ...
- SEO优化倦怠期?SEO排名和收录都不错时,到底还需不需要日更?
众所周知,网站优化是一件需要坚持不懈.有耐心的工作,但长时间以来,优化专员们难免会有些疲惫,甚至当网站有了一定的好排名和收录时,就想着要不要歇一歇.答案是,不能.要知道在互联网发展更快速的时代,每天都 ...
- 20级、19级 | 一天一瞬间!【日更】
点击上方蓝色关注我们! 欢迎来到今天的一天一瞬间专栏 今天是2020年09月08日,天气状况:炎热. 19级目前还是上午上课下午补课,补课私下里和我说,班内学生表现都不错,听罢,甚是开心,不管是哪位老 ...
- 省选前的计划(日更,然而你们天天吊打我)
还有17天就省选了,不能再颓废了! 总结篇(不定期更新) 网络流基本 计算几何基本 字符串入门+基础 斜率优化dp 日记篇(日更) 3.18 今天做了一些题目吧. 考试的题目也做了一道题目. 方格取数 ...
- 【 零 】 Java 随 笔 ( 2 月 9 日 更 新 ---- 贪 吃 蛇 雏 形 )
[ 零 ] Java 随 笔 ( 2 月 9 日 更 新 ---- 贪 吃 蛇 雏 形 ) 2009年08月18日 07年的7,8月份从这个论坛开始了我的Java学习之路!历时一年多了,自我感觉良好! ...
- agentzh 的 Nginx 教程(版本 2015.03.19) 第一篇
转载:http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html#02-NginxDirectiveExecOrder10 目录 缘 ...
- 获香港证监会颁发牌照的弘量研究,正用智能投顾帮助金融机构降低成本,提升资产管理能力 By 藤子2017年10月09日 17:16 撰文 | 藤子 2015 年,雷春然和黄耀东都是在香港科技大学的
获香港证监会颁发牌照的弘量研究,正用智能投顾帮助金融机构降低成本,提升资产管理能力 By 藤子2017年10月09日 17:16 撰文 | 藤子 2015 年,雷春然和黄耀东都是在香港科技大学的图书馆 ...
- 灯塔上线网络电影日分账票房数据,网络电影进入票房日更时代
昨日,优酷首次向灯塔专业版公布网络电影日票房数据,使灯塔进一步深耕影视精细化数据的同时,也标志着网络电影正式进入票房日更时代. 目前在灯塔专业版上,用户不仅可以查看优酷网络电影日分账票房,还能够查询到 ...
- 黄宇清 java_二面笔记 2015.9.19
二面笔记 2015.9.19 总结 1.杜佰超(x) 不太清楚目标,按兴趣来,态度比较诚恳. 怕困难? 没办法快速想到一件事情自己一直坚持的 有说大话的嫌疑(我不敢说自己能付出别人百倍的努力...) ...
最新文章
- DevOps时代测试应该如何应对?
- 数据库学习day_01:SQL的发展和数据库操作相关sql语句
- poj1655Multiplication Puzzle
- python 文件和路径操作函数小结
- react学习(57)--map赋值
- 【LeetCode笔记】剑指 Offer 68 - I. 二叉搜索树的最近公共祖先(Java、二叉树、dfs)
- 为iOS7重新设计你的App
- VUE3.0引入本地js文件
- 离散数学期末复习笔记【精华版】
- 电力前沿:Hightopo 助力贵州院打造智慧能源生态系统
- xise菜刀千万不要随便下载!(警惕!)
- 微信开发-JS接口微信定位
- LMS Virtual.Lab二次开发:场点网格编辑(VBScript)
- 虚拟机中安装配置Windows server 2003和iis6
- 启动定时器t0的工作指令是_启动定时器 0 工作的指令是使 TCON 的 ( )_学小易找答案...
- Android Studio获取数字签名(SHA1)
- 姓张信息mysql_MySQL_数据查询
- 初学者-----HTTP协议的基本格式
- WebLogic启动服务卡住报错 :Server started in RUNNING mode
- 数据结构大致包含以下几种存储结构: