[OpenGL] 二维游戏中的卷动地图
2016-07-26 20:17
316 查看
卷动地图,也就是显示屏上只显示地图的一部分,随着玩家的走动,地图自动向走动方向卷动。在设计地图较大的二维游戏时,我们都不可避免地需要面对地图卷动这个问题。在切换场景方面,卷动地图和动态加载地图两者的结合,让整个游戏世界得以完整地呈现在玩家面前。
在这一方面,比较高效的算法是卡马克卷轴算法,但是在这里,出于了解卷动地图的目的,我们只实现了最为朴素的卷动地图算法。也就是说,我们一次性绘制了整个地图,然后通过调整观察位置和视口大小,来实现卷动的效果。
给出的样例中使用的地图是一张完整的图片。如果我们的地图不是一张完整的图片,而是网格布局的话,我们还可以考虑只绘制当前屏幕图片提高效率,这个优化会在之后的网格布局与碰撞检测中介绍。
函数回顾
首先,我们需要回顾一下OpenGL中几个重要函数的用法:
1.glOrtho。该函数实现正投影。也就是,我们定义一个当前摄像机下的取景框大小,只有落在这个框里的物体才会显示。注意我们在glOrtho设置的参数非常重要,比如在这个例子中,我们设置的参数是glOrtho(-4, 4, -4, 4, -100, 100)。这也就意味着我们的屏幕坐标左下角是(-4.0,-4.0),右上角是(4.0,4.0),(注:正视时)我们需要利用这个数据来进行图形位置的摆放。
2.glViewport。视口大小。在这里我们定义了视口的左下角坐标(一般为0,0)和视口长宽。可以这样理解,我们在此定义了取景框投影后的长与宽,然后屏幕也有长与宽,最终屏幕将截取屏幕大小的场景显示。一般而言,我们把这个视口大小设置的和窗体大小一样,就能恰好把所有物体包围。
进一步而言,如果我们把视口大小设置的比窗体大,那么窗体只显示取景框中的一部分物体,利用这个,我们可以实现卷动地图效果:
反言之,如果取景框大小比窗体小,那么就可以实现一个屏幕显示多个场景的效果:
3.gluLookAt,定义了摄像机的位置以及朝向等。在卷动中,我们就是在不断移动这个摄像机的位置。
自动卷动的原理很简单,但是,问题在于,什么是屏幕的3/4处,什么又是场景快要走出视野的状态?我们只讨论水平方向的移动来解释这个问题,竖直方向的移动是同理的。
首先,我们定义glOrtho参数为-size_x, size_x, -size_y, size_y,-100, 100; 卷动缩小的水平比例是x,竖直比例是y,也就是说,glViewport的参数是0,0,x*width,y*height,它将物体按这样的比例相对于窗体大小放大。
在上图中,x = 3,也就是说,对于一张地图的水平方向,窗口只显示了它的1/3大小。当人物移动到红线的时候,触发卷动事件。
在这里,首先要指出,中心坐标点 x = 0。图中一个方块的大小是2*size_x / x, 那么3/4个方块的大小就是3 / 2 * size_x / x。由于中心坐标x = 0,所以红线的坐标x = -size + 1.5*size/x。
那么,什么时候卷动结束呢?或者说,什么时候我们不能继续移动摄像机了?
我们用一些具体的例子来解释。
① x = 2。
整个场景占了两个格子,而屏幕只显示其中一个格子。
一开始,我们的摄像机位于黄色的位置,它把整个场景都包在取景框内,但是,由于glViewport的参数,初始状态下,我们在屏幕上看到的只是第一块格子。
那么,场景即将离开的时候,我们希望在屏幕上恰好显示完整的第二块格子。这个时候,摄像机应该处在绿色的位置。
一个格子的大小为2*size_/2 = size_x, 黄色处坐标为0,所以,摄像机的临界x值为:size_x。
②x = 3
整个场景占了3个格子,而屏幕只显示1个格子。
一个格子的大小为2*size_x/3 , 黄色处坐标为0,所以,摄像机的临界x值为:2*2*size_x/3 = 4*size_x/3。
③x = 4
整个场景占了4个格子,而屏幕只显示1个格子。
一个格子的大小为2*size_x/4 = size_x/2,黄色处坐标为0,所以,摄像机的临界x值为:3*size_x/2 .
……
最终,得到对于任意的x,摄像机的临界x值为 2 * size_x - 2 * size_x / x。
分辨率也会影响临界x值,在给出的例子中,我们考虑了分辨率的影响,涉及到的函数语句为wHeight = wWidth = min(width, height); 在相应的判断语句中也乘上了w/h系数,如果不希望考虑这一因素,直接把所有出现的w,h(宽度和高度)以及和它相关的语句删掉即可。两个卷动函数也可以合并为一个。
代码
test.h
texture.cpp
sprite.cpp
main.cpp
图片资源来自网络:
在这一方面,比较高效的算法是卡马克卷轴算法,但是在这里,出于了解卷动地图的目的,我们只实现了最为朴素的卷动地图算法。也就是说,我们一次性绘制了整个地图,然后通过调整观察位置和视口大小,来实现卷动的效果。
给出的样例中使用的地图是一张完整的图片。如果我们的地图不是一张完整的图片,而是网格布局的话,我们还可以考虑只绘制当前屏幕图片提高效率,这个优化会在之后的网格布局与碰撞检测中介绍。
函数回顾
首先,我们需要回顾一下OpenGL中几个重要函数的用法:1.glOrtho。该函数实现正投影。也就是,我们定义一个当前摄像机下的取景框大小,只有落在这个框里的物体才会显示。注意我们在glOrtho设置的参数非常重要,比如在这个例子中,我们设置的参数是glOrtho(-4, 4, -4, 4, -100, 100)。这也就意味着我们的屏幕坐标左下角是(-4.0,-4.0),右上角是(4.0,4.0),(注:正视时)我们需要利用这个数据来进行图形位置的摆放。
2.glViewport。视口大小。在这里我们定义了视口的左下角坐标(一般为0,0)和视口长宽。可以这样理解,我们在此定义了取景框投影后的长与宽,然后屏幕也有长与宽,最终屏幕将截取屏幕大小的场景显示。一般而言,我们把这个视口大小设置的和窗体大小一样,就能恰好把所有物体包围。
进一步而言,如果我们把视口大小设置的比窗体大,那么窗体只显示取景框中的一部分物体,利用这个,我们可以实现卷动地图效果:
反言之,如果取景框大小比窗体小,那么就可以实现一个屏幕显示多个场景的效果:
3.gluLookAt,定义了摄像机的位置以及朝向等。在卷动中,我们就是在不断移动这个摄像机的位置。
自动卷动
我们定义当人物走到屏幕3/4处时,触发卷动事件。在触发后,摄像机和人物一起向一个方向移动,注意移动速度最好相同。当场景快要走出视野后,我们限制摄像机不再移动。当然,人物还是可以继续走动的,直到它走到地图边缘。自动卷动的原理很简单,但是,问题在于,什么是屏幕的3/4处,什么又是场景快要走出视野的状态?我们只讨论水平方向的移动来解释这个问题,竖直方向的移动是同理的。
首先,我们定义glOrtho参数为-size_x, size_x, -size_y, size_y,-100, 100; 卷动缩小的水平比例是x,竖直比例是y,也就是说,glViewport的参数是0,0,x*width,y*height,它将物体按这样的比例相对于窗体大小放大。
在上图中,x = 3,也就是说,对于一张地图的水平方向,窗口只显示了它的1/3大小。当人物移动到红线的时候,触发卷动事件。
在这里,首先要指出,中心坐标点 x = 0。图中一个方块的大小是2*size_x / x, 那么3/4个方块的大小就是3 / 2 * size_x / x。由于中心坐标x = 0,所以红线的坐标x = -size + 1.5*size/x。
那么,什么时候卷动结束呢?或者说,什么时候我们不能继续移动摄像机了?
我们用一些具体的例子来解释。
① x = 2。
整个场景占了两个格子,而屏幕只显示其中一个格子。
一开始,我们的摄像机位于黄色的位置,它把整个场景都包在取景框内,但是,由于glViewport的参数,初始状态下,我们在屏幕上看到的只是第一块格子。
那么,场景即将离开的时候,我们希望在屏幕上恰好显示完整的第二块格子。这个时候,摄像机应该处在绿色的位置。
一个格子的大小为2*size_/2 = size_x, 黄色处坐标为0,所以,摄像机的临界x值为:size_x。
②x = 3
整个场景占了3个格子,而屏幕只显示1个格子。
一个格子的大小为2*size_x/3 , 黄色处坐标为0,所以,摄像机的临界x值为:2*2*size_x/3 = 4*size_x/3。
③x = 4
整个场景占了4个格子,而屏幕只显示1个格子。
一个格子的大小为2*size_x/4 = size_x/2,黄色处坐标为0,所以,摄像机的临界x值为:3*size_x/2 .
……
最终,得到对于任意的x,摄像机的临界x值为 2 * size_x - 2 * size_x / x。
分辨率也会影响临界x值,在给出的例子中,我们考虑了分辨率的影响,涉及到的函数语句为wHeight = wWidth = min(width, height); 在相应的判断语句中也乘上了w/h系数,如果不希望考虑这一因素,直接把所有出现的w,h(宽度和高度)以及和它相关的语句删掉即可。两个卷动函数也可以合并为一个。
代码
test.h#pragma once #define GLUT_DISABLE_ATEXIT_HACK #include "GL/GLUT.H" void loadTex(int i, char *filename, GLuint* texture);//一般纹理 void loadTex(int i, char *filename, GLuint* texture, unsigned char* backgroundColor);//透明纹理 class sprite { public: //精灵位置(中心位置) float pos_x; float pos_y; //帧动画参数 int num = 96;//一共多少帧 int col = 12;//一行有多少帧 //精灵索引下标 //前、左、右、后 int index[4][3][2]; //步长 float step; //用于计数 int count; int count2; int count3; //精灵贴图 GLuint texture; //行走方向(枚举量) typedef enum { left, right, front, back }direction; //是否停止 bool isStop = true; //行走方向 direction dir = front; sprite(int _col,int _num,float x,float y,GLuint _texture,int* index,float _step); //快速索引绘制精灵 void drawRect(GLuint texture, int i, int j); //绘制精灵 void drawSprite(); };
texture.cpp
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<windows.h> #include"test.h" #define BITMAP_ID 0x4D42 //读纹理图片 static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader) { FILE *filePtr; // 文件指针 BITMAPFILEHEADER bitmapFileHeader; // bitmap文件头 unsigned char *bitmapImage; // bitmap图像数据 int imageIdx = 0; // 图像位置索引 unsigned char tempRGB; // 交换变量 // 以“二进制+读”模式打开文件filename filePtr = fopen(filename, "rb"); if (filePtr == NULL) { printf("file not open\n"); return NULL; } // 读入bitmap文件图 fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr); // 验证是否为bitmap文件 if (bitmapFileHeader.bfType != BITMAP_ID) { fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n"); return NULL; } // 读入bitmap信息头 fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr); // 将文件指针移至bitmap数据 fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); // 为装载图像数据创建足够的内存 bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage]; // 验证内存是否创建成功 if (!bitmapImage) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } // 读入bitmap图像数据 fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr); // 确认读入成功 if (bitmapImage == NULL) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } //由于bitmap中保存的格式是BGR,下面交换R和B的值,得到RGB格式 for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) { tempRGB = bitmapImage[imageIdx]; bitmapImage[imageIdx] = bitmapImage[imageIdx + 2]; bitmapImage[imageIdx + 2] = tempRGB; } // 关闭bitmap图像文件 fclose(filePtr); return bitmapImage; } //读纹理图片 static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader, unsigned char* backgroundColor) { FILE *filePtr; // 文件指针 BITMAPFILEHEADER bitmapFileHeader; // bitmap文件头 unsigned char *bitmapImage; // bitmap图像数据 int imageIdx = 0; // 图像位置索引 // 以“二进制+读”模式打开文件filename filePtr = fopen(filename, "rb"); if (filePtr == NULL) { printf("file not open\n"); return NULL; } // 读入bitmap文件图 fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr); // 验证是否为bitmap文件 if (bitmapFileHeader.bfType != BITMAP_ID) { fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n"); return NULL; } // 读入bitmap信息头 fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr); // 将文件指针移至bitmap数据 fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); // 为装载图像数据创建足够的内存 bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage]; // 验证内存是否创建成功 if (!bitmapImage) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } // 读入bitmap图像数据 fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr); // 确认读入成功 if (bitmapImage == NULL) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } unsigned char* bitmapData; // 纹理数据 bitmapData = new unsigned char[bitmapInfoHeader->biSizeImage / 3 * 4]; int count = 0; //添加alpha通道 for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) { bitmapData[count] = bitmapImage[imageIdx + 2]; bitmapData[count + 1] = bitmapImage[imageIdx + 1]; bitmapData[count + 2] = bitmapImage[imageIdx]; if (bitmapData[count] >= backgroundColor[0] && bitmapData[count + 1] >= backgroundColor[1] && bitmapData[count + 2] >= backgroundColor[2]) { bitmapData[count + 3] = 0; } else bitmapData[count + 3] = 255; count += 4; } // 关闭bitmap图像文件 fclose(filePtr); return bitmapData; } //加载纹理的函数 void loadTex(int i, char *filename, GLuint* texture) { BITMAPINFOHEADER bitmapInfoHeader; // bitmap信息头 unsigned char* bitmapData; // 纹理数据 bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader); glBindTexture(GL_TEXTURE_2D, texture[i]); // 指定当前纹理的放大/缩小过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, //mipmap层次(通常为,表示最上层) GL_RGB, //我们希望该纹理有红、绿、蓝数据 bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2 bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2 0, //边框(0=无边框, 1=有边框) GL_RGB, //bitmap数据的格式 GL_UNSIGNED_BYTE, //每个颜色数据的类型 bitmapData); //bitmap数据指针 } //加载纹理的函数 void loadTex(int i, char *filename, GLuint* texture, unsigned char* backgroundColor) { BITMAPINFOHEADER bitmapInfoHeader; // bitmap信息头 unsigned char* bitmapData; // 纹理数据 bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader, backgroundColor); glBindTexture(GL_TEXTURE_2D, texture[i]); // 指定当前纹理的放大/缩小过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, //mipmap层次(通常为,表示最上层) GL_RGBA, //我们希望该纹理有红、绿、蓝、alpha数据 bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2 bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2 0, //边框(0=无边框, 1=有边框) GL_RGBA, //bitmap数据的格式 GL_UNSIGNED_BYTE, //每个颜色数据的类型 bitmapData); //bitmap数据指针 }
sprite.cpp
#include"test.h" sprite::sprite(int _col, int _num, float x, float y, GLuint _texture, int* _index,float _step) { count = count2 = count3 = 0; col = _col; num = _num; pos_x = x; pos_y = y; texture = _texture; int cnt = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 2; k++) { index[i][j][k] = _index[cnt++]; } } } step = _step; } void sprite::drawRect(GLuint texture, int i, int j) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture); //选择纹理texture[status] const GLfloat x1 = -0.5, x2 = 0.5; const GLfloat y1 = -0.5, y2 = 0.5; const GLfloat x = 1.0 / col, y = 1.0 / (num / col); const GLfloat point[4][2] = { { x1,y1 },{ x2,y1 },{ x2,y2 },{ x1,y2 } }; const GLfloat dir[4][2] = { { j*x,1 - (i + 1)*y },{ (j + 1)*x,1 - (i + 1)*y },{ (j + 1)*x ,1 - i*y },{ j*x,1 - i*y } }; glBegin(GL_QUADS); for (int k = 0; k < 4; k++) { glTexCoord2fv(dir[k]); glVertex2fv(point[k]); } glEnd(); glDisable(GL_TEXTURE_2D); } void sprite::drawSprite() { const int step = 50; count++; if (isStop) { if (dir == front) { drawRect(texture, index[0][1][0], index[0][1][1]); } else if (dir == back) { drawRect(texture, index[3][1][0], index[3][1][1]); } else if (dir == left) { drawRect(texture, index[1][1][0], index[1][1][1]); } else if (dir == right) { drawRect(texture, index[2][1][0], index[2][1][1]); } } else if (dir == front) { if (count <= step) { drawRect(texture, index[0][0][0], index[0][0][1]); } else if (count > step&&count <= step * 2) { drawRect(texture, index[0][1][0], index[0][1][1]); } else if (count > step * 2 && count <= step * 3) { drawRect(texture, index[0][2][0], index[0][2][1]); } } else if (dir == back) { if (count <= step) { drawRect(texture, index[3][0][0], index[3][0][1]); } else if (count > step && count <= step * 2) { drawRect(texture, index[3][1][0], index[3][1][1]); } else if (count > step * 2 && count <= step * 3) { drawRect(texture, index[3][2][0], index[3][2][1]); } } else if (dir == left) { if (count <= step) { drawRect(texture, index[1][0][0], index[1][0][1]); } else if (count > step && count <= step * 2) { drawRect(texture, index[1][1][0], index[1][1][1]); } else if (count > step * 2 && count <= step * 3) { drawRect(texture, index[1][2][0], index[1][2][1]); } } else if (dir == right) { if (count <= step) { drawRect(texture, index[2][0][0], index[2][0][1]); } else if (count > step && count <= step * 2) { drawRect(texture, index[2][1][0], index[2][1][1]); } else if (count > step * 2 && count <= step * 3) { drawRect(texture, index[2][2][0], index[2][2][1]); } } if (count%step == 0) { if (count2 == count3) { if (dir == front) { drawRect(texture, index[0][1][0], index[0][1][1]); } else if (dir == back) { drawRect(texture, index[3][1][0], index[3][1][1]); } else if (dir == left) { drawRect(texture, index[1][1][0], index[1][1][1]); } else if (dir == right) { drawRect(texture, index[2][1][0], index[2][1][1]); } isStop = true; } count3 = count2; } if (count == step * 3) { count = 0; } }
main.cpp
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include<time.h> #include <stdlib.h> #include"test.h" #include <math.h> /* for cos(), sin(), and sqrt() */ GLuint texture[2]; //视区 float whRatio; int wHeight; int wWidth; //分辨率 float h=900, w=800; //正投影 float size_x = 4.0f; float size_y = 4.0f; //分辨率中,宽的值是否更大 bool isWidthLarger; //水平、竖直方向的卷轴大小与窗口大小比例 int x = 3; int y = 2; //视点 float center[] = { 0, 0, 0 }; float eye[] = { 0, 0, 5 }; sprite *s; inline float min(float x, float y) { if (x < y) { isWidthLarger = false; return x; } else { isWidthLarger = true; return y; } } void drawRect(GLuint texture) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture); //选择纹理texture[status] const GLfloat x1 = -0.5, x2 = 0.5; const GLfloat y1 = -0.5, y2 = 0.5; const GLfloat point[4][2] = { { x1,y1 },{ x2,y1 },{ x2,y2 },{ x1,y2 } }; int dir[4][2] = { { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } }; glBegin(GL_QUADS); for (int i = 0; i < 4; i++) { glTexCoord2iv(dir[i]); glVertex2fv(point[i]); } glEnd(); glDisable(GL_TEXTURE_2D); } void drawScene() { glPushMatrix(); glScalef(size_x *2, size_y*2, 1); drawRect(texture[1]); glPopMatrix(); glPushMatrix(); glTranslatef(s->pos_x, s->pos_y, 2); glScalef(0.3f,0.3f, 1); s->drawSprite(); glPopMatrix(); } void updateView(int height, int width) { glViewport(0, 0, width * x, height * y); glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影 glLoadIdentity(); //初始化矩阵为单位矩阵 whRatio = (GLfloat)width / (GLfloat)height; //设置显示比例 glOrtho(-size_x, size_x,-size_y,size_y, -100, 100); //正投影 glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型 } void scroll_1() { if (s->dir == sprite::right || s->dir == sprite::left) { if (s->dir == sprite::right&&s->pos_x>-size_x + 1.5*size_x / x*w/h&& s->pos_x<size_x) { eye[0] += s->step; center[0] += s->step; if (eye[0]>2 * size_x - 2 * size_x / x*w / h) { eye[0] = 2 * size_x - 2 * size_x / x*w / h; center[0] = 2 * size_x - 2 * size_x / x*w / h; } } else if (s->dir == sprite::left&&s->pos_x>-size_x&& s->pos_x<size_x - 1.5*size_x / x*w / h) { eye[0] -= s->step; center[0] -= s->step; if (eye[0]<0) { eye[0] = 0; center[0] = 0; } } } if (s->dir == sprite::front || s->dir == sprite::back) { if (s->dir == sprite::back&&s->pos_y>-size_y + 1.5*size_y /y && s->pos_y<size_y) { eye[1] += s->step; center[1] += s->step; if (eye[1]>2 * size_y - 2 * size_y / y) { eye[1] = 2 * size_y - 2 * size_y / y; center[1] = 2 * size_y - 2 * size_y / y; } } else if (s->dir == sprite::front&&s->pos_y>-size_y&&s->pos_y<size_y - 1.5*size_y / y) { eye[1] -= s->step; center[1] -= s->step; if (eye[1]<0) { eye[1] = 0; center[1] = 0; } } } } void scroll_2() { if (s->dir == sprite::right || s->dir == sprite::left) { if (s->dir == sprite::right&&s->pos_x>-size_x + 1.5*size_x / x&& s->pos_x<size_x) { eye[0] += s->step; center[0] += s->step; if (eye[0]>2 * size_x - 2 * size_x / x) { eye[0] = 2 * size_x - 2 * size_x / x; center[0] = 2 * size_x - 2 * size_x / x; } } else if (s->dir == sprite::left&&s->pos_x>-size_x&& s->pos_x<size_x - 1.5*size_x / x) { eye[0] -= s->step; center[0] -= s->step; if (eye[0]<0) { eye[0] = 0; center[0] = 0; } } } if (s->dir == sprite::front || s->dir == sprite::back) { if (s->dir == sprite::back&&s->pos_y>-size_y + 1.5*size_y / y*w/h && s->pos_y<size_y) { eye[1] += s->step; center[1] += s->step; if (eye[1]>2 * size_y - 2 * size_y / y*w / h) { eye[1] = 2 * size_y - 2 * size_y / y*w / h; center[1] = 2 * size_y - 2 * size_y / y*w / h; } } else if (s->dir == sprite::front&&s->pos_y>-size_y&&s->pos_y<size_y - 1.5*size_y / y*w / h) { eye[1] -= s->step; center[1] -= s->step; if (eye[1]<0) { eye[1] = 0; center[1] = 0; } } } } void key(unsigned char k, int _x, int _y) { s->count2++; switch (k) { case 'a': { s->dir = sprite::left; s->isStop = false; s->pos_x -= s->step; if (s->pos_x < -size_x)s->pos_x = -size_x; break; } case 'd': { s->dir = sprite::right; s->isStop = false; s->pos_x += s->step; if (s->pos_x > size_x)s->pos_x = size_x; break; } case 'w': { s->dir = sprite::back; s->isStop = false; s->pos_y += s->step; if (s->pos_y > size_y)s->pos_y = size_y; break; } case 's': { s->dir = sprite::front; s->isStop = false; s->pos_y -= s->step; if (s->pos_y < -size_y)s->pos_y = -size_y; break; } } if(isWidthLarger)scroll_1(); else scroll_2(); updateView(wHeight, wWidth); //更新视角 } void reshape(int width, int height) { if (height == 0) //如果高度为0 { height = 1; //让高度为1(避免出现分母为0的现象) } h = height; w = width; wHeight = height; wWidth = width; wHeight = wWidth = min(width, height); updateView(wHeight, wWidth); //更新视角 } void idle() { glutPostRedisplay(); } void init() { srand(unsigned(time(NULL))); glEnable(GL_DEPTH_TEST);//开启深度测试 glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5); unsigned char color[3] = { 255,255,255 }; glGenTextures(2, texture); loadTex(0, "1.bmp", texture,color); loadTex(1, "2.bmp", texture); if (w > h)isWidthLarger = true; else isWidthLarger = false; int index[] = {0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2,3,0,3,1,3,2}; s = new sprite(12, 96, -1.5f, -1.5f, texture[0], index,0.06f); } void redraw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓存 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //初始化矩阵为单位矩阵 gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0); // 场景(0,0,0)的视点中心 (0,5,50),Y轴向上 glPolygonMode(GL_FRONT, GL_FILL); drawScene();//绘制场景 glutSwapBuffers();//交换缓冲区 } int main(int argc, char *argv[]) { glutInit(&argc, argv);//对glut的初始化 glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); //初始化显示模式:RGB颜色模型,深度测试,双缓冲 glutInitWindowSize(w,h);//设置窗口大小 int windowHandle = glutCreateWindow("Simple GLUT App");//设置窗口标题 glutDisplayFunc(redraw); //注册绘制回调函数 glutReshapeFunc(reshape); //注册重绘回调函数 glutKeyboardFunc(key); //注册按键回调函数 glutIdleFunc(idle);//注册全局回调函数:空闲时调用 init(); glutMainLoop(); // glut事件处理循环 return 0; }
图片资源来自网络:
相关文章推荐
- CMake编译opencv和argc、argv含义
- apache commons collections CollectionUtils工具类简单使用
- eval 特殊位置变量用法
- virtualbox虚拟机下的cdlinux找不到无线网卡的解决方法
- man帮助history的使用以及linux文件系统。
- 系统架构设计笔记
- magedu Linux 运维学习的第三天
- Linux 文件权限笔记
- Linux Samba 服务器配置
- Centos 建立一个新用户 详细讲解
- Tomcat内存溢出
- CentOS基础命令大全
- CentOS下php.ini配置文件详解
- Linux is not Matrix——MyCat实现读写分离
- hadoop入门级总结三:hive
- hadoop入门级总结三:hive
- CentOS服务器最新分区方案
- CentOS下查看MySQL的安装路径
- Linux特殊权限
- Centos 文件和目录访问权限设置