美文网首页iOSopenGLGPUImage学习【资料】
OpenGL 图形库的使用(五十一)—— 实战之2D游戏 - 碰

OpenGL 图形库的使用(五十一)—— 实战之2D游戏 - 碰

作者: 刀客传奇 | 来源:发表于2018-01-20 17:51 被阅读263次

    版本记录

    版本号 时间
    V1.0 2018.01.20

    前言

    OpenGL 图形库项目中一直也没用过,最近也想学着使用这个图形库,感觉还是很有意思,也就自然想着好好的总结一下,希望对大家能有所帮助。下面内容来自欢迎来到OpenGL的世界
    1. OpenGL 图形库使用(一) —— 概念基础
    2. OpenGL 图形库使用(二) —— 渲染模式、对象、扩展和状态机
    3. OpenGL 图形库使用(三) —— 着色器、数据类型与输入输出
    4. OpenGL 图形库使用(四) —— Uniform及更多属性
    5. OpenGL 图形库使用(五) —— 纹理
    6. OpenGL 图形库使用(六) —— 变换
    7. OpenGL 图形库的使用(七)—— 坐标系统之五种不同的坐标系统(一)
    8. OpenGL 图形库的使用(八)—— 坐标系统之3D效果(二)
    9. OpenGL 图形库的使用(九)—— 摄像机(一)
    10. OpenGL 图形库的使用(十)—— 摄像机(二)
    11. OpenGL 图形库的使用(十一)—— 光照之颜色
    12. OpenGL 图形库的使用(十二)—— 光照之基础光照
    13. OpenGL 图形库的使用(十三)—— 光照之材质
    14. OpenGL 图形库的使用(十四)—— 光照之光照贴图
    15. OpenGL 图形库的使用(十五)—— 光照之投光物
    16. OpenGL 图形库的使用(十六)—— 光照之多光源
    17. OpenGL 图形库的使用(十七)—— 光照之复习总结
    18. OpenGL 图形库的使用(十八)—— 模型加载之Assimp
    19. OpenGL 图形库的使用(十九)—— 模型加载之网格
    20. OpenGL 图形库的使用(二十)—— 模型加载之模型
    21. OpenGL 图形库的使用(二十一)—— 高级OpenGL之深度测试
    22. OpenGL 图形库的使用(二十二)—— 高级OpenGL之模板测试Stencil testing
    23. OpenGL 图形库的使用(二十三)—— 高级OpenGL之混合Blending
    24. OpenGL 图形库的使用(二十四)—— 高级OpenGL之面剔除Face culling
    25. OpenGL 图形库的使用(二十五)—— 高级OpenGL之帧缓冲Framebuffers
    26. OpenGL 图形库的使用(二十六)—— 高级OpenGL之立方体贴图Cubemaps
    27. OpenGL 图形库的使用(二十七)—— 高级OpenGL之高级数据Advanced Data
    28. OpenGL 图形库的使用(二十八)—— 高级OpenGL之高级GLSL Advanced GLSL
    29. OpenGL 图形库的使用(二十九)—— 高级OpenGL之几何着色器Geometry Shader
    30. OpenGL 图形库的使用(三十)—— 高级OpenGL之实例化Instancing
    31. OpenGL 图形库的使用(三十一)—— 高级OpenGL之抗锯齿Anti Aliasing
    32. OpenGL 图形库的使用(三十二)—— 高级光照之高级光照Advanced Lighting
    33. OpenGL 图形库的使用(三十三)—— 高级光照之Gamma校正Gamma Correction
    34. OpenGL 图形库的使用(三十四)—— 高级光照之阴影 - 阴影映射Shadow Mapping
    35. OpenGL 图形库的使用(三十五)—— 高级光照之阴影 - 点阴影Point Shadows
    36. OpenGL 图形库的使用(三十六)—— 高级光照之法线贴图Normal Mapping
    37. OpenGL 图形库的使用(三十七)—— 高级光照之视差贴图Parallax Mapping
    38. OpenGL 图形库的使用(三十八)—— 高级光照之HDR
    39. OpenGL 图形库的使用(三十九)—— 高级光照之泛光
    40. OpenGL 图形库的使用(四十)—— 高级光照之延迟着色法Deferred Shading
    41. OpenGL 图形库的使用(四十一)—— 高级光照之SSAO
    42. OpenGL 图形库的使用(四十二)—— PBR之理论Theory
    43. OpenGL 图形库的使用(四十三)—— PBR之光照Lighting
    44. OpenGL 图形库的使用(四十四)—— PBR之几篇没有翻译的英文原稿
    45. OpenGL 图形库的使用(四十五)—— 实战之调试Debugging
    46. OpenGL 图形库的使用(四十六)—— 实战之文本渲染Text Rendering
    47. OpenGL 图形库的使用(四十七)—— 实战之2D游戏 - Breakout
    48. OpenGL 图形库的使用(四十八)—— 实战之2D游戏 - 准备工作
    49. OpenGL 图形库的使用(四十九)—— 实战之2D游戏 - 渲染精灵
    50. OpenGL 图形库的使用(五十)—— 实战之2D游戏 - 关卡

    此时我们已经有了一个包含有很多砖块和玩家的一个挡板的关卡。与经典的Breakout内容相比还差一个球。游戏的目的是让球撞击所有的砖块,直到所有的可销毁砖块都被销毁,但同时也要满足条件:球不能碰触屏幕的下边缘。

    除了通用的游戏对象组件,球还需要有半径和一个布尔值,该布尔值用于指示球被固定(stuck)在玩家挡板上还是被允许自由运动的状态。当游戏开始时,球被初始固定在玩家挡板上,直到玩家按下任意键开始游戏。

    由于球只是一个附带了一些额外属性的GameObject,所以按照常规需要创建BallObject类作为GameObject的子类。

    class BallObject : public GameObject
    {
        public:
            // 球的状态 
            GLfloat   Radius;
            GLboolean Stuck;
    
    
            BallObject();
            BallObject(glm::vec2 pos, GLfloat radius, glm::vec2 velocity, Texture2D sprite);
    
            glm::vec2 Move(GLfloat dt, GLuint window_width);
            void      Reset(glm::vec2 position, glm::vec2 velocity);
    }; 
    

    BallObject的构造函数不但初始化了其自身的值,而且实际上也潜在地初始化了GameObject。BallObject类拥有一个Move函数,该函数用于根据球的速度来移动球,并检查它是否碰到了场景的任何边界,如果碰到的话就会反转球的速度:

    glm::vec2 BallObject::Move(GLfloat dt, GLuint window_width)
    {
        // 如果没有被固定在挡板上
        if (!this->Stuck)
        { 
            // 移动球
            this->Position += this->Velocity * dt;
            // 检查是否在窗口边界以外,如果是的话反转速度并恢复到正确的位置
            if (this->Position.x <= 0.0f)
            {
                this->Velocity.x = -this->Velocity.x;
                this->Position.x = 0.0f;
            }
            else if (this->Position.x + this->Size.x >= window_width)
            {
                this->Velocity.x = -this->Velocity.x;
                this->Position.x = window_width - this->Size.x;
            }
            if (this->Position.y <= 0.0f)
            {
                this->Velocity.y = -this->Velocity.y;
                this->Position.y = 0.0f;
            }
    
        }
        return this->Position;
    }  
    

    除了反转球的速度之外,我们还需要把球沿着边界重新放置回来。只有在没有被固定时球才能够移动。

    因为如果球碰触到底部边界时玩家会结束游戏(或失去一条命),所以在底部边界没有代码来控制球反弹。我们稍后需要在代码中某些地方实现这一逻辑。

    你可以在下边看到BallObject的代码:

    /******************************************************************
    ** This code is part of Breakout.
    **
    ** Breakout is free software: you can redistribute it and/or modify
    ** it under the terms of the CC BY 4.0 license as published by
    ** Creative Commons, either version 4 of the License, or (at your
    ** option) any later version.
    ******************************************************************/
    #include "ball_object.h"
    
    BallObject::BallObject() 
        : GameObject(), Radius(12.5f), Stuck(true)  { }
    
    BallObject::BallObject(glm::vec2 pos, GLfloat radius, glm::vec2 velocity, Texture2D sprite)
        :  GameObject(pos, glm::vec2(radius * 2, radius * 2), sprite, glm::vec3(1.0f), velocity), Radius(radius), Stuck(true) { }
    
    glm::vec2 BallObject::Move(GLfloat dt, GLuint window_width)
    {
        // If not stuck to player board
        if (!this->Stuck)
        {
            // Move the ball
            this->Position += this->Velocity * dt;
            // Then check if outside window bounds and if so, reverse velocity and restore at correct position
            if (this->Position.x <= 0.0f)
            {
                this->Velocity.x = -this->Velocity.x;
                this->Position.x = 0.0f;
            }
            else if (this->Position.x + this->Size.x >= window_width)
            {
                this->Velocity.x = -this->Velocity.x;
                this->Position.x = window_width - this->Size.x;
            }
            if (this->Position.y <= 0.0f)
            {
                this->Velocity.y = -this->Velocity.y;
                this->Position.y = 0.0f;
            }
        }
        return this->Position;
    }
    
    // Resets the ball to initial Stuck Position (if ball is outside window bounds)
    void BallObject::Reset(glm::vec2 position, glm::vec2 velocity)
    {
        this->Position = position;
        this->Velocity = velocity;
        this->Stuck = true;
    }
    

    首先我们在游戏中添加球。与玩家挡板相似,我们创建一个球对象并且定义两个用来初始化球的常量。对于球的纹理,我们会使用在LearnOpenGL Breakout游戏中完美适用的一张图片:球纹理

    // 初始化球的速度
    const glm::vec2 INITIAL_BALL_VELOCITY(100.0f, -350.0f);
    // 球的半径
    const GLfloat BALL_RADIUS = 12.5f;
    
    BallObject     *Ball; 
    
    void Game::Init()
    {
        [...]
        glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2 - BALL_RADIUS, -BALL_RADIUS * 2);
        Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY,
            ResourceManager::GetTexture("face"));
    }
    

    然后我们在每帧中调用游戏代码中Update函数里的Move函数来更新球的位置:

    void Game::Update(GLfloat dt)
    {
        Ball->Move(dt, this->Width);
    }  
    

    除此之外,由于球初始是固定在挡板上的,我们必须让玩家能够从固定的位置重新移动它。我们选择使用空格键来从挡板释放球。这意味着我们必须稍微修改ProcessInput函数:

    void Game::ProcessInput(GLfloat dt)
    {
        if (this->State == GAME_ACTIVE)
        {
            GLfloat velocity = PLAYER_VELOCITY * dt;
            // 移动玩家挡板
            if (this->Keys[GLFW_KEY_A])
            {
                if (Player->Position.x >= 0)
                {
                    Player->Position.x -= velocity;
                    if (Ball->Stuck)
                        Ball->Position.x -= velocity;
                }
            }
            if (this->Keys[GLFW_KEY_D])
            {
                if (Player->Position.x <= this->Width - Player->Size.x)
                {
                    Player->Position.x += velocity;
                    if (Ball->Stuck)
                        Ball->Position.x += velocity;
                }
            }
            if (this->Keys[GLFW_KEY_SPACE])
                Ball->Stuck = false;
        }
    }
    

    现在如果玩家按下了空格键,球的Stuck值会设置为false。我们还需要更新ProcessInput函数,当球被固定的时候,会跟随挡板的位置来移动球。

    最后我们需要渲染球,此时这应该很显而易见了:

    void Game::Render()
    {
        if (this->State == GAME_ACTIVE)
        {
            [...]
            Ball->Draw(*Renderer);
        }
    }  
    

    结果就是球会跟随着挡板,并且当我们按下空格键时球开始自由运动。球会在左侧、右侧和顶部边界合理地反弹,但看起来不会撞击任何的砖块,就像我们可以在下边的视频中看到的那样:

    我们要做的就是创建一个或多个函数用于检查球对象是否撞击关卡中的任何砖块,如果撞击的话就销毁砖块。这些所谓的碰撞检测(collision detection)功能将是我们下一个教程的重点。

    后记

    本篇已结束,后面更精彩~~~

    相关文章

      网友评论

        本文标题:OpenGL 图形库的使用(五十一)—— 实战之2D游戏 - 碰

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