本文内容包括:
1、mac中新建openGL工程
2、绘制一个简单图形
3、键盘控制
4、定时器
5、文字绘制这次是初次学习openGL,所以用了第一版的OpenGL,用的是固定片元着色器。
代码仓库:贪吃蛇
游戏截图:
游戏截图一、工程新建
1、首先是新建一个mac OS的APP工程
新建工程2、添加三个库,GLUT和OpenGL是系统库,可直接添加,libGLTools是第三方库,可以从我的项目中获取并拖拽到新建的项目中,注意需要把include目录下的头文件也一并添加进去
引入三个库 头文件引入3、这次是使用C++来编写,所以需要把新建项目中的AppDelegate.h,AppDelegate.m,main.m,ViewController.m,ViewController.h删除,并新建main. cpp文件
代码目录结构到此为止,一个空项目创建完成,可以试试是否能编译成功。
二、OpenGL绘制简单方形
1、绘制一个简单图形需要哪些元素?比如一个三角形
a、顶点,要确定一个三角形,必须得知道三角形的三个顶点坐标
b、颜色,以什么颜色来绘制
2、这次项目使用的是GLUT库来绘制,GLUT是OpenGL Utility Toolkit,是一个处理openGL程序的工具库。
//设置当前工作目录,针对MAC OS X
gltSetWorkingDirectory(argv[0]);
//初始化GLUT库
glutInit(&argc, argv);
/*初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指
双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区*/
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
//GLUT窗口大小,标题窗口
glutInitWindowSize(800,600);
glutCreateWindow("贪吃蛇");
//注册回调函数
glutReshapeFunc(ChangeSize);//窗口改变大小
glutDisplayFunc(RenderScene);//绘制刷新
glutKeyboardFunc(OnKeyTap);//键盘监听
glutTimerFunc(speed,timerFunc,1);//定时回调,第一个参数是定时间隔,单位毫秒,第二个回调方法指针,第三个是回调ID,会透传给回调方法的参数
//驱动程序的初始化中没有出现任何问题。
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
//调用SetupRC
SetupRC();
glutMainLoop(); //启动runloop
代码比较简单,思路就是初始化GLUT库,创建窗口,设置背景色,绘制图形,事件监听,看代码都有注释,不多说。
三、贪吃蛇的思路
1、以链表存放蛇块,每吃一个食物方块,链表尾部加1
//新建一个蛇块,并添加到链表后面
void newASnake(Point point, GLfloat *color) {
SnakeObject snake = SnakeObject();
snake.points = (GLfloat*)malloc(sizeof(GLfloat) * 12);
snake.color = (GLfloat*)malloc(sizeof(GLfloat) * 4);
for (int i=0; i< 4; i++) {
snake.color[i] = color[i];
}
createBlock(snake.points, blockSize);
moveTo(snake.points, point);
snake.triangleBatch = new GLBatch();
snake.triangleBatch->Begin(GL_TRIANGLE_FAN,4);
snake.triangleBatch->CopyVertexData3f(snake.points);
snake.triangleBatch->End();
SnakeNode *newNode = (SnakeNode *)malloc(sizeof(SnakeNode));
if (objectCount == 0) {
SnakeHead = newNode;
}else {
SnakeNode *lastNode = SnakeHead;
while (lastNode->next) {
lastNode = lastNode->next;
}
lastNode->next = newNode;
}
newNode->snake = snake;
newNode->next = NULL;
objectCount++;
}
2、移动:以复杂度最低的方式移动,即把尾块移动到即将移动的点,这样只需移动一个块就可以达到移动的效果,而不需要把所有的块都绘制一遍。这也是为什么选用单向链表做数据结构的原因。
向右移动 向下移动
要想自动移动,还要配合定时器的使用,改方法只会调用一次,所以再执行完之后需要继续调用,达到循环调用的效果
glutTimerFunc(speed,timerFunc,1);
//定时回调,第一个参数是定时间隔,单位毫秒,第二个回调方法指针,第三个是回调ID,会透传给回调方法的参数
//定时方法
void timerFunc(int nTimerID) {
move();
glutTimerFunc(speed,timerFunc,1);//继续调用
}
3、碰撞检测:主要是检测两个块的顶点是否有相交
碰撞检测
//两个点是否碰到
bool isTouch(GLfloat *v, GLfloat *t) {
Point vxy = getPoint(v);
Point txy = getPoint(t);
GLfloat size = blockSize*ScreenWidth*2;
bool hTouch = vxy.x > txy.x && vxy.x < txy.x + size || vxy.x + size > txy.x && vxy.x+ size < txy.x + size;
bool vTouch = vxy.y > txy.y && vxy.y < txy.y + size || vxy.y + size > txy.y && vxy.y+ size < txy.y + size;
return hTouch && vTouch;
}
4、键盘监听
glutKeyboardFunc(OnKeyTap);//键盘监听
//键盘监听
void OnKeyTap(unsigned char key, int x, int y) {
Direction nextDirection = currentDirection;
if (key == 'w') {
nextDirection = Up;
}else if (key == 'd') {
nextDirection = Right;
}else if (key == 's') {
nextDirection = Down;
} else if (key == 'a') {
nextDirection = Left;
}
//方向相反无法操作
if (nextDirection == Left && currentDirection == Right || nextDirection == Right && currentDirection ==Left
|| nextDirection == Up && currentDirection == Down || nextDirection == Down && currentDirection == Up) {
return;
}
currentDirection = nextDirection;
}
5、文字绘制
//打印文字
void glutPrint(float x, float y, char *text)
{
glRasterPos2f(x,y);
glColor3f(0, 0, 0);
for (int i=0; text[i]; i++){
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, text[i]);
}
}
网友评论