对于一个游戏来说,能够和玩家进行交互是很重要的。通过键盘、鼠标和手柄,游戏
程序接受玩家的各项指令,从而做出反应。在控制台程序中,我们可以使用kbhit()函数来判断玩家是否有输入,如果有,再调用getch函数就可以接受到玩家的输入信息。
如:
if (kbhit() != 0) //按下一个键 { c=getch(); ProcessInput(c); } else { GameRun(); }
glut也同样给我们提供了处理键盘鼠标事件的方法,是通过注册回调函数来实现的。
下面就介绍一下这些函数。
1. void glutKeyboardFunc(void(*func)(unsigned char key,int x,int y));
这个函数可以用来处理键盘上可以用ascii码表示的键按下的事件,key就是这个键ascii码,x和y则是键摁下时鼠标相对于窗口左上角的位置。
具体用法:
在main函数里,调用glutKeyboardFunc(&ProcessKeyboard),这样就注册了一个函数ProcessKeyboard。
再完成函数体:
void ProcessKeyboard(unsigned char key,int x,int y) { if (key == 'p') { SnapScreen(400,400,"data/screen.bmp"); } }
这样,编译运行程序后,按下p键,就能够实时截图了!
2. void glutSpecialFunc(void (*func)(int key,int x,int y));
这个函数是用来处理一些特殊键(如:F1,F2,etc)摁下的事件。参数key是这个键编号,在glut.h中预定义了这些常量:
#define GLUT_KEY_F1 1
#define GLUT_KEY_F2 2
#define GLUT_KEY_F3 3...
还有很多,大家可以自己打开glut.h找到自己需要的。
同样,x和y也是当前鼠标的位置。
具体实现:
main函数里,glutSpecialFunc(&ProcessSpecialKeyboead);
void ProcessSpecialKeyboead(int key, int x, int y) { if (key == GLUT_KEY_F1) { SnapScreen(400,400,"data/screen.bmp"); } if (key == GLUT_KEY_F2) { exit(0); } }
当摁下F1键,可以截图;摁下F2键,就会关闭程序。
3. void glutMouseFunc(void(*func)(int button,int state,int x,int y));
这个函数用来处理鼠标左右键和中键摁下的事件。
其中button保存的是摁下的鼠标键的键位信息。
在glut.h中,有定义:
#define GLUT_LEFT_BUTTON 0
#define GLUT_MIDDLE_BUTTON 1
#define GLUT_RIGHT_BUTTON 2
参数state表示这个事件发生是这个键是摁下还是松开。
#define GLUT_DOWN 0
#define GLUT_UP 1
参数x和y则表示当前鼠标的位置。
具体实现:
main函数里,glutMouseFunc(&ProcessMouse);
void ProcessMouse(int button,int state,int x,int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { SnapScreen(x,WinHeight-y,"data/screen.bmp"); //把glut坐标转化成OpenGL内的坐标 //因为得到的x,y是相对于屏幕左上角的坐标, //而SnapScreen函数内的glReadPixels函数处理的是按屏幕左下角为原点的//坐标系的坐标,所以这里需要转化一下。 } }
4. void glutMotionFunc(void(*func)(int x,int y));
这个函数是处理当鼠标键摁下时,鼠标拖动的事件。当鼠标拖动时,将每一帧都调用一次这个函数。
5. void glutPassiveMotionFunc(void (*func)(int x,int y));
这个函数是处理当没有鼠标键摁下时,鼠标拖动的事件。当鼠标拖动时,将每一帧都调用一次这个函数。同上面的差不多。
6. void glutEntryFunc(void(*func)(int state));
这个函数则是处理鼠标离开和进入窗口的事件。参数state的值在glut.h中有定义:
#define GLUT_LEFT 0
#define GLUT_ENTERED 1
好,那么在介绍了这些函数后,大家应该可以使用glut来制作一些游戏了,如连连看,俄罗斯方块等。
附本节全部代码:
#include <GL/glut.h> #include <stdio.h> #include <math.h> #include <time.h> #include <stdlib.h> #include <windows.h> #include <vector> #include <iostream> #define PI 3.1415926 #define BITMAP_ID 0x4D42 using std::vector; int WinWidth,WinHeight; int LoadBitmap(const char *file) { unsigned int ID; //纹理的id int width,height; byte *image; //接受图像数据 FILE *fp; //文件指针 BITMAPFILEHEADER FileHeader; //接受位图文件头 BITMAPINFOHEADER InfoHeader; //接受位图信息头 fp=fopen(file,"rb"); if (fp == NULL) { printf("Exception: Fail to open file!\n"); return -1; } fread(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp); if(FileHeader.bfType != BITMAP_ID) //确保文件是一个位图文件,效验文件类型 { printf("Exception: This file is not a bmp file!\n"); fclose(fp); return -1; } fread(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp); width=InfoHeader.biWidth; height=InfoHeader.biHeight; InfoHeader.biSizeImage = width*height*3; fseek(fp, FileHeader.bfOffBits, SEEK_SET); //将文件指针移动到实际图像数据处 image=(byte *)malloc(sizeof(byte)*InfoHeader.biSizeImage); //申请空间 if (image == NULL) { free(image); printf("Exception: No enough space!\n"); return -1; } fread(image, 1, InfoHeader.biSizeImage, fp); fclose(fp); glGenTextures(1, &ID); glBindTexture(GL_TEXTURE_2D, ID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, image); return ID; } vector<unsigned int> V_ID; //用来保存生成的纹理id int now,cnt; //now是当前所使用的纹理在vector中的下标 //cnt是用来计时的变量 bool SnapScreen(int width,int height,const char *file) { byte *image; //接受图像数据 FILE *fp; //文件指针 BITMAPFILEHEADER FileHeader; //接受位图文件头 BITMAPINFOHEADER InfoHeader; //接受位图信息头 FileHeader.bfType=BITMAP_ID; //ID设置为位图的id号 FileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); //实际图像数据的位置在文件头和信息头之后 FileHeader.bfReserved1=0; //必须设置为0 FileHeader.bfReserved2=0; //必须设置为0 FileHeader.bfSize=height*width*24+FileHeader.bfOffBits; //BMP图像文件大小 InfoHeader.biXPelsPerMeter = 0; //水平分辨率,这里暂时设为0就是 InfoHeader.biYPelsPerMeter = 0; //垂直分辨率,这里暂时设为0就是 InfoHeader.biClrUsed = 0; //图像使用的颜色,这里暂时设为0就是 InfoHeader.biClrImportant = 0; //重要的颜色数,这里暂时设为0就是 //垂直分辨率,这里暂时设为0就是 InfoHeader.biPlanes=1; //必须设置为1 InfoHeader.biCompression=BI_RGB; //设置为BI_RGB时,表示图像并没有彩色表 InfoHeader.biBitCount=24; //图像的位数 InfoHeader.biSize=sizeof(BITMAPINFOHEADER); //结构体的大小 InfoHeader.biHeight=height; InfoHeader.biWidth=width; InfoHeader.biSizeImage=height*width*4; image=(byte *)malloc(sizeof(byte)*InfoHeader.biSizeImage); if (image == NULL) { free(image); printf("Exception: No enough space!\n"); return false; } //像素格式设置4字节对齐 glPixelStorei(GL_UNPACK_ALIGNMENT,4); //接收出像素的数据 glReadPixels(0,0,width,height,GL_BGR_EXT,GL_UNSIGNED_BYTE,image); fp=fopen(file,"wb"); if (fp == NULL) { printf("Exception: Fail to open file!\n"); return false; } fwrite(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp); fwrite(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp); fwrite(image, InfoHeader.biSizeImage, 1, fp); free(image); fclose(fp); return true; } void Draw() { glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, V_ID[now]); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (cnt <= 50) glColor4f(1.0,1.0,1.0,(float)cnt/50.0); else glColor4f(1.0,1.0,1.0,1.0-((float)cnt-50.0)/50.0); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f);glVertex2f(-1,-1); glTexCoord2f(1.0f, 0.0f);glVertex2f(1,-1); glTexCoord2f(1.0f, 1.0f);glVertex2f(1,1); glTexCoord2f(0.0f, 1.0f);glVertex2f(-1,1); glEnd(); glDisable(GL_TEXTURE_2D); glutSwapBuffers(); } void Update() { cnt++; if (cnt >= 100) //当cnt加到100的时候,换下一张图片 { now++; now%=V_ID.size(); //保证now值的正确性 cnt=0; //将cnt值置0,开始下一个图片的显示 } glutPostRedisplay(); } void Reshape(int w,int h) { WinWidth=w; WinHeight=h; w=w>h?h:w; glViewport(0,0,(GLsizei)w,(GLsizei)w); } void init() { int n,i; char str[100],file[100]; V_ID.clear(); //清空容器 FILE *fp; fp=fopen("data/data.txt","r"); fscanf(fp,"%d",&n); while (n--) { fscanf(fp,"%s",str); sprintf(file,"data/%s",str); i=LoadBitmap(file); if (i == -1) //读取图片失败 continue; else V_ID.push_back(i); //把图片的id放到V_ID的末尾保存起来 } fclose(fp); now=0; } void ProcessKeyboard(unsigned char key,int x,int y) { if (key == 'p') { SnapScreen(WinWidth,WinHeight,"data/screen.bmp"); } } void ProcessSpecialKeyboead(int key, int x, int y) { if (key == GLUT_KEY_F1) { SnapScreen(WinWidth,WinHeight,"data/screen.bmp"); } if (key == GLUT_KEY_F2) { exit(0); } } void ProcessMouse(int button,int state,int x,int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { SnapScreen(x,WinHeight-y,"data/screen.bmp"); //把glut坐标转化成OpenGL内的坐标 } } int main(int argc, char *argv[]) { WinWidth=400; WinHeight=400; glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInitWindowPosition(100, 100); glutInitWindowSize(WinWidth, WinHeight); glutCreateWindow("HelloOpenGL"); glutReshapeFunc(&Reshape); glutIdleFunc(&Update); glutDisplayFunc(&Draw); glutKeyboardFunc(&ProcessKeyboard); glutSpecialFunc(&ProcessSpecialKeyboead); glutMouseFunc(&ProcessMouse); init(); glutMainLoop(); return 0; }