2D游戏和OpenCV的一点总结
寒假那段时间开始真正的接触2D游戏的开发,学的很少,只是看了看CSDN上的博客,不过有很多重要的是思想,知道了怎么去做,再加上自己的思考,以及最后的优化,就能很好的做好2D游戏。当然模仿别人的游戏就不说了,后来自己也花了点时间做了c++版的Flappy Bird,做的很粗糙,代码地址
(http://download.csdn.net/detail/zhoupeng39/6970365) ,不过有人说无法编译通过,这是不可能的,里面我放上了编译好的EXE文件,肯定是可以运行的,不过代码写的很乱,一次性写好,就没再去看它,说一下里面一些基本的方法。
1. 关于柱子的产生和移动
其实看得出界面上同时只有两个柱子,当左边的柱子移到窗口外面,就生成下一个柱子,然后移动,这样就产生了这样的效果(当然柱子之间的距离是确定的,上下柱子之间的距离可以用随机数来确定)。
2.小鸟的移动
点击鼠标左键小鸟上升(我是以匀速度上升的),到了一定的高度就会下降,下降的时候是以固定的加速度下降的,会越来越快,当然游戏的难度就体现在这些速度上面。
3.碰撞检测
小鸟飞的过高或者直接接触地面是肯定得死的,至于柱子,只要判断小鸟的四个点的坐标是否落在了柱子所在的矩形区域内就可以了,检测还是挺简单的
4.时间控制
这里面要设置几个定时器,后来直接导致我的程序在分数那块出现问题,当然解决还得调整计时器的时间。
主要就是上面几点,具体的可以看代码,当然看不懂的可以给我留言。
最近开始接触OpenCV,看到那些复杂的算法,我就在后悔,大一的时候矩阵咋学的,妈蛋,什么都不会,看到现在,更多的是看人家如何处理,调用什么函数,对于许多原理性的东西知之甚少,不过,这东西真的很难学,没有大量的时间也是不行的,我呢,只能每天翘课去学它了,不过忙里偷闲,没事的时候我就做做坦克大战这个小游戏,做到现在,也就完成了一小半,代码啥的就不沾了,说说从里面看到了什么。
1. 对于我这个新手,地图设计肯定是没做过的,一张地图里面往往分成很多块,有标志性的数据(人物移动出现上升的效果),这些都要涉及一些算法,那是我最不擅长的部分,坦克大战的游戏地图还是很好设计的,一共四种地形,在TXT文件里面分别写上标记,然后贴图就可以了,当然地图的数据是贯穿整个程序的,坦克的移动,子弹的射击,都要用到。
2. 坦克的移动,其实无非就是响应上下左右按键,但是当时的我改的很多,因为地图是分格的,所以每次都要移到指定的格子里面,不能出现移动到中间的现象(当然这里可以改,不过原来的游戏是这样设置的),问题就出现在换方向的时候,还没有移到指定位置,就换方向了(键盘的消息当然是第一时间响应的),这个问题我没有很好的解决,导致换方向的时候会出现卡的现象,我想过调整计时器的时间来改变,不过最近很忙,有时间要好好的考虑一下。
3.子弹 子弹还是挺好解决的,结合地图,综合子弹的等级,判断当前的地形被击中有什么效果,就可以了
4.下面我就要做敌人的部分,其实和英雄坦克差不多,再加上子弹的碰撞检测,得分系统和装备的升级就差不多了。游戏写完了之后,再认真的梳理一下思路
对于OpenCV嘛,这几天看了霍夫圆霍夫直线相关的就是矩形的检测方法,然后就是连通域,这两天看的就是PCA+SVM,说实话,原理真的没怎么清楚。 PCA 其实就是降维 的作用,给你一大堆要训练的人脸,每个人脸都有标志性的特征,而有些部分则可以忽略,PCA做的就是找出标志性的部分,达到降低维度的目的,进而减少下面的计算量(不过看到一些用PCA匹配人脸的,计算同一特征向量下的结果的方差,找出最小值,不过精度不高,当然也有后面使用SVM再训练数据的)。SVM就是一大堆数据,里面有错误的有正确的,做的就是找出他们的分割线,当然有些时候无法分割,就出现了奖惩机制,减少机器获得错误的训练信息的概率,具体的实现我看不懂,上面用SVM进行训练也就是将样本和测试数据分类,然后找出它属于哪一个,这里具体的代码我没看到,博客里的代码都编译不通过。下面粘出PCA 的部分代码
#include <opencv.hpp> #include <stdlib.h> #include <iostream> using namespace std; //本程序是对PCA的一个简单的使用 未涉及 SVM的相关知识 //PCA在这里 降低所有样本的维度 然后对照测试样本 通过求差的平方的和的方式 找出最小值 //最小值 就是最终配对的结果 这里用于人脸的配对 识别 还是使用分类器(具体的看 文档) // PCA+SVM 利用PCA降低维度 但是 用SVM进行最终的匹配 标记 训练样本和测试样本 分隔开 // 然后加载测试样本 利用SVM进行训练 得到最终的结果(具体的还要再分析) //第一步 识别出人脸 并画出显示 //第二步 生成灰度的样本训练库 //第三步 针对测试样本(理论上先 找出人脸部分 生成灰度图 用灰度图进行PCA匹配) const int GrayWidth=90; //宽度 const int GrayHeight=120; //高度 CvMat* Train_image; CvMat* Test_image; void main() { IplImage* srcPicture=cvLoadImage("picture.jpg"); IplImage* srcPhoto=cvLoadImage("photo.jpg"); CvHaarClassifierCascade* pCascade=0; //分类器 CvSeq* faceContour=0; CvMemStorage* storage=0; storage=cvCreateMemStorage(0); //加载分类器 pCascade=(CvHaarClassifierCascade*)cvLoad(("haarcascade_frontalface_default.xml"),0,0); //判断 if (!srcPicture||!srcPhoto||!pCascade) { printf("加载错误!!!"); return; } //开始匹配人脸 // 参数 图片 分类器 内存空间 比率 相邻的距离 是否做边缘检测 人脸最大的大小 faceContour=cvHaarDetectObjects(srcPicture,pCascade,storage,1.1,3,CV_HAAR_DO_CANNY_PRUNING,cvSize(20,20)); for(int i=0;i<faceContour->total;i++) { CvRect* tmp=(CvRect*)cvGetSeqElem(faceContour,i); CvPoint pt1=cvPoint(tmp->x,tmp->y); CvPoint pt2=cvPoint(tmp->x+tmp->width,tmp->y+tmp->height); cvRectangle(srcPicture,pt1,pt2,cvScalar(0,0,255),1,8,0); IplImage* grayTmp=cvCreateImage(cvSize(GrayWidth,GrayHeight),srcPicture->depth,1); IplImage* colorTmp=cvCreateImage(cvSize(GrayWidth,GrayHeight),srcPicture->depth,3); cvSetImageROI(srcPicture,*tmp); //大小变化 cvResize(srcPicture,colorTmp); cvResetImageROI(srcPicture); cvCvtColor(colorTmp,grayTmp,CV_BGR2GRAY); //直方图均衡化 处理灰度图 能有效的沿着X轴展开 灰度图更加的清晰 cvEqualizeHist(grayTmp,grayTmp); char filename[20]; sprintf(filename,"%d.jpg",i+1); cvSaveImage(filename,grayTmp); } faceContour=0; faceContour=cvHaarDetectObjects(srcPhoto,pCascade,storage,1.1,3,CV_HAAR_DO_CANNY_PRUNING,cvSize(20,20)); for(int i=0;i<faceContour->total;i++) { CvRect* tmp=(CvRect*)cvGetSeqElem(faceContour,i); CvPoint pt1=cvPoint(tmp->x,tmp->y); CvPoint pt2=cvPoint(tmp->x+tmp->width,tmp->y+tmp->height); cvRectangle(srcPhoto,pt1,pt2,cvScalar(0,0,255),1,8,0); IplImage* grayTmp=cvCreateImage(cvSize(GrayWidth,GrayHeight),srcPhoto->depth,1); IplImage* colorTmp=cvCreateImage(cvSize(GrayWidth,GrayHeight),srcPhoto->depth,3); cvSetImageROI(srcPhoto,*tmp); cvResize(srcPhoto,colorTmp); cvResetImageROI(srcPhoto); cvCvtColor(colorTmp,grayTmp,CV_BGR2GRAY); cvEqualizeHist(grayTmp,grayTmp); char filename[20]; sprintf(filename,"%d.jpg",i+20); cvSaveImage(filename,grayTmp); } //初始的将图片的所有数据加载到一行 所有的样本加载到一个矩阵里面 32位浮点型单通道 Train_image=cvCreateMat(5,GrayWidth*GrayHeight,CV_32FC1); for(int i=1;i<=5;i++) { char filename[20]; sprintf(filename,"%d.jpg",i); IplImage* tmp=cvLoadImage(filename,0); CvMat* mat=cvCreateMat(tmp->height,tmp->width,CV_32FC1); cvConvertScale(tmp,mat,1.0); //这里注意 CvMat header; CvMat* row; //该函数 是将 所有的数据变成一行 返回的是矩阵的指针 指向数据部分 row=cvReshape(mat,&header,0,1); //浮点型的 指针移动 占4位 float* pt1=(float*)(Train_image->data.fl+(i-1)*Train_image->step/4); float* pt2=(float*)row->data.fl; for(int j=0;j<mat->cols;j++) { *pt1=*pt2; pt1++; pt2++; } } Test_image=cvCreateMat(1,GrayWidth*GrayHeight,CV_32FC1); IplImage* tmp=cvLoadImage("20.jpg",0); CvMat* mat=cvCreateMat(tmp->height,tmp->width,CV_32FC1); cvConvertScale(tmp,mat,1.0); CvMat header; CvMat* row; row=cvReshape(mat,&header,0,1); float* pt1=(float*)Train_image->data.fl; float* pt2=(float*)row->data.fl; for(int j=0;j<mat->cols;j++) { *pt1=*pt2; pt1++; pt2++; } // 开始匹配 CvMat* pMean=cvCreateMat(1,Train_image->cols,CV_32FC1); //注意行和列的分配 CvMat* pEigVals=cvCreateMat(1,min(Train_image->rows,Train_image->cols),CV_32FC1); CvMat* pEigVecs=cvCreateMat(min(Train_image->rows,Train_image->cols),Train_image->cols,CV_32FC1); CvMat* result1=cvCreateMat(Train_image->rows,min(Train_image->rows,Train_image->cols),CV_32FC1); CvMat* result2=cvCreateMat(Test_image->rows,min(Train_image->rows,Train_image->cols),CV_32FC1); // cvCalcPCA(Train_image,pMean,pEigVals,pEigVecs,CV_PCA_DATA_AS_ROW); cvProjectPCA(Train_image,pMean,pEigVecs,result1); cvProjectPCA(Test_image,pMean,pEigVecs,result2); //对结果开始计算 for(int i=0;i<result1->rows;i++) { float sum=0.0; float* ptr1=(float*)result1->data.fl+i*result1->step/4; float* ptr2=(float*)result2->data.fl; for(int j=0;j<result1->cols;j++) { sum+=(*ptr1-*ptr2)*(*ptr1-*ptr2); ptr1++; ptr2++; } //这里的准确率并不高 上面的数字算出来很大 // 可以考虑用SVM printf("%f\n",sum); } cvNamedWindow("Picture"); cvShowImage("Picture",srcPicture); cvNamedWindow("Photo"); cvShowImage("Photo",srcPhoto); cvWaitKey(0); cvDestroyWindow("Picture"); cvDestroyWindow("Photo"); cvReleaseImage(&srcPicture); cvReleaseImage(&srcPhoto); cvReleaseMemStorage(&storage); }