美文网首页
OpenGL初探 - 简易贪吃蛇

OpenGL初探 - 简易贪吃蛇

作者: 半邪亦正 | 来源:发表于2021-03-28 19:16 被阅读0次

    本文内容包括:
    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]);
        }
    }
    

    相关文章

      网友评论

          本文标题:OpenGL初探 - 简易贪吃蛇

          本文链接:https://www.haomeiwen.com/subject/iltmhltx.html