OpenGL 圖形庫(kù)的使用(五十)—— 實(shí)戰(zhàn)之2D游戲 - 關(guān)卡

版本記錄

版本號(hào) 時(shí)間
V1.0 2018.01.20

前言

OpenGL 圖形庫(kù)項(xiàng)目中一直也沒(méi)用過(guò)当叭,最近也想學(xué)著使用這個(gè)圖形庫(kù),感覺(jué)還是很有意思,也就自然想著好好的總結(jié)一下挑宠,希望對(duì)大家能有所幫助。下面內(nèi)容來(lái)自歡迎來(lái)到OpenGL的世界颓影。
1. OpenGL 圖形庫(kù)使用(一) —— 概念基礎(chǔ)
2. OpenGL 圖形庫(kù)使用(二) —— 渲染模式各淀、對(duì)象、擴(kuò)展和狀態(tài)機(jī)
3. OpenGL 圖形庫(kù)使用(三) —— 著色器诡挂、數(shù)據(jù)類(lèi)型與輸入輸出
4. OpenGL 圖形庫(kù)使用(四) —— Uniform及更多屬性
5. OpenGL 圖形庫(kù)使用(五) —— 紋理
6. OpenGL 圖形庫(kù)使用(六) —— 變換
7. OpenGL 圖形庫(kù)的使用(七)—— 坐標(biāo)系統(tǒng)之五種不同的坐標(biāo)系統(tǒng)(一)
8. OpenGL 圖形庫(kù)的使用(八)—— 坐標(biāo)系統(tǒng)之3D效果(二)
9. OpenGL 圖形庫(kù)的使用(九)—— 攝像機(jī)(一)
10. OpenGL 圖形庫(kù)的使用(十)—— 攝像機(jī)(二)
11. OpenGL 圖形庫(kù)的使用(十一)—— 光照之顏色
12. OpenGL 圖形庫(kù)的使用(十二)—— 光照之基礎(chǔ)光照
13. OpenGL 圖形庫(kù)的使用(十三)—— 光照之材質(zhì)
14. OpenGL 圖形庫(kù)的使用(十四)—— 光照之光照貼圖
15. OpenGL 圖形庫(kù)的使用(十五)—— 光照之投光物
16. OpenGL 圖形庫(kù)的使用(十六)—— 光照之多光源
17. OpenGL 圖形庫(kù)的使用(十七)—— 光照之復(fù)習(xí)總結(jié)
18. OpenGL 圖形庫(kù)的使用(十八)—— 模型加載之Assimp
19. OpenGL 圖形庫(kù)的使用(十九)—— 模型加載之網(wǎng)格
20. OpenGL 圖形庫(kù)的使用(二十)—— 模型加載之模型
21. OpenGL 圖形庫(kù)的使用(二十一)—— 高級(jí)OpenGL之深度測(cè)試
22. OpenGL 圖形庫(kù)的使用(二十二)—— 高級(jí)OpenGL之模板測(cè)試Stencil testing
23. OpenGL 圖形庫(kù)的使用(二十三)—— 高級(jí)OpenGL之混合Blending
24. OpenGL 圖形庫(kù)的使用(二十四)—— 高級(jí)OpenGL之面剔除Face culling
25. OpenGL 圖形庫(kù)的使用(二十五)—— 高級(jí)OpenGL之幀緩沖Framebuffers
26. OpenGL 圖形庫(kù)的使用(二十六)—— 高級(jí)OpenGL之立方體貼圖Cubemaps
27. OpenGL 圖形庫(kù)的使用(二十七)—— 高級(jí)OpenGL之高級(jí)數(shù)據(jù)Advanced Data
28. OpenGL 圖形庫(kù)的使用(二十八)—— 高級(jí)OpenGL之高級(jí)GLSL Advanced GLSL
29. OpenGL 圖形庫(kù)的使用(二十九)—— 高級(jí)OpenGL之幾何著色器Geometry Shader
30. OpenGL 圖形庫(kù)的使用(三十)—— 高級(jí)OpenGL之實(shí)例化Instancing
31. OpenGL 圖形庫(kù)的使用(三十一)—— 高級(jí)OpenGL之抗鋸齒Anti Aliasing
32. OpenGL 圖形庫(kù)的使用(三十二)—— 高級(jí)光照之高級(jí)光照Advanced Lighting
33. OpenGL 圖形庫(kù)的使用(三十三)—— 高級(jí)光照之Gamma校正Gamma Correction
34. OpenGL 圖形庫(kù)的使用(三十四)—— 高級(jí)光照之陰影 - 陰影映射Shadow Mapping
35. OpenGL 圖形庫(kù)的使用(三十五)—— 高級(jí)光照之陰影 - 點(diǎn)陰影Point Shadows
36. OpenGL 圖形庫(kù)的使用(三十六)—— 高級(jí)光照之法線(xiàn)貼圖Normal Mapping
37. OpenGL 圖形庫(kù)的使用(三十七)—— 高級(jí)光照之視差貼圖Parallax Mapping
38. OpenGL 圖形庫(kù)的使用(三十八)—— 高級(jí)光照之HDR
39. OpenGL 圖形庫(kù)的使用(三十九)—— 高級(jí)光照之泛光
40. OpenGL 圖形庫(kù)的使用(四十)—— 高級(jí)光照之延遲著色法Deferred Shading
41. OpenGL 圖形庫(kù)的使用(四十一)—— 高級(jí)光照之SSAO
42. OpenGL 圖形庫(kù)的使用(四十二)—— PBR之理論Theory
43. OpenGL 圖形庫(kù)的使用(四十三)—— PBR之光照Lighting
44. OpenGL 圖形庫(kù)的使用(四十四)—— PBR之幾篇沒(méi)有翻譯的英文原稿
45. OpenGL 圖形庫(kù)的使用(四十五)—— 實(shí)戰(zhàn)之調(diào)試Debugging
46. OpenGL 圖形庫(kù)的使用(四十六)—— 實(shí)戰(zhàn)之文本渲染Text Rendering
47. OpenGL 圖形庫(kù)的使用(四十七)—— 實(shí)戰(zhàn)之2D游戲 - Breakout
48. OpenGL 圖形庫(kù)的使用(四十八)—— 實(shí)戰(zhàn)之2D游戲 - 準(zhǔn)備工作
49. OpenGL 圖形庫(kù)的使用(四十九)—— 實(shí)戰(zhàn)之2D游戲 - 渲染精靈

關(guān)卡

Breakout不會(huì)只是一個(gè)單一的綠色笑臉碎浇,而是一些由許多彩色磚塊組成的完整關(guān)卡疗我。我們希望這些關(guān)卡有以下特性:他們足夠靈活以便于支持任意數(shù)量的行或列、可以擁有不可摧毀的堅(jiān)固磚塊南捂、支持多種類(lèi)型的磚塊且這些信息被存儲(chǔ)在外部文件中吴裤。

在本教程中,我們將簡(jiǎn)要介紹用于管理大量磚塊的游戲關(guān)卡對(duì)象的代碼溺健,首先我們需要先定義什么是一個(gè)磚塊麦牺。

我們創(chuàng)建一個(gè)被稱(chēng)為游戲?qū)ο蟮慕M件作為一個(gè)游戲內(nèi)物體的基本表示。這樣的游戲?qū)ο蟪钟幸恍顟B(tài)數(shù)據(jù)鞭缭,如其位置剖膳、大小與速率。它還持有顏色岭辣、旋轉(zhuǎn)吱晒、是否堅(jiān)硬(不可被摧毀)、是否被摧毀的屬性沦童,除此之外仑濒,它還存儲(chǔ)了一個(gè)Texture2D變量作為其精靈(Sprite)。

游戲中的每個(gè)物體都可以被表示為GameObject或這個(gè)類(lèi)的派生類(lèi)偷遗,你可以在下面找到GameObject的代碼:

/*******************************************************************
** 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 "game_object.h"


GameObject::GameObject() 
    : Position(0, 0), Size(1, 1), Velocity(0.0f), Color(1.0f), Rotation(0.0f), Sprite(), IsSolid(false), Destroyed(false) { }

GameObject::GameObject(glm::vec2 pos, glm::vec2 size, Texture2D sprite, glm::vec3 color, glm::vec2 velocity) 
    : Position(pos), Size(size), Velocity(velocity), Color(color), Rotation(0.0f), Sprite(sprite), IsSolid(false), Destroyed(false) { }

void GameObject::Draw(SpriteRenderer &renderer)
{
    renderer.DrawSprite(this->Sprite, this->Position, this->Size, this->Rotation, this->Color);
}

Breakout中的關(guān)卡基本由磚塊組成,因此我們可以用一個(gè)磚塊的集合表示一個(gè)關(guān)卡氏豌。因?yàn)榇u塊需要和游戲?qū)ο髱缀跸嗤臓顟B(tài)喉酌,所以我們將關(guān)卡中的每個(gè)磚塊表示為GameObject。GameLevel類(lèi)的布局如下所示:

class GameLevel
{
public:
    std::vector<GameObject> Bricks;

    GameLevel() { }
    // 從文件中加載關(guān)卡
    void Load(const GLchar *file, GLuint levelWidth, GLuint levelHeight);
    // 渲染關(guān)卡
    void Draw(SpriteRenderer &renderer);
    // 檢查一個(gè)關(guān)卡是否已完成 (所有非堅(jiān)硬的瓷磚均被摧毀)
    GLboolean IsCompleted();
private:
    // 由磚塊數(shù)據(jù)初始化關(guān)卡
    void init(std::vector<std::vector<GLuint>> tileData, GLuint levelWidth, GLuint levelHeight);
};

由于關(guān)卡數(shù)據(jù)從外部文本中加載泵喘,所以我們需要提出某種關(guān)卡的數(shù)據(jù)結(jié)構(gòu)泪电,以下是關(guān)卡數(shù)據(jù)在文本文件中可能的表示形式的一個(gè)例子:

1 1 1 1 1 1 
2 2 0 0 2 2
3 3 4 4 3 3

在這里一個(gè)關(guān)卡被存儲(chǔ)在一個(gè)矩陣結(jié)構(gòu)中,每個(gè)數(shù)字代表一種類(lèi)型的磚塊纪铺,并以空格分隔相速。在關(guān)卡代碼中我們可以假定每個(gè)數(shù)字代表什么:

  • 數(shù)字0:無(wú)磚塊,表示關(guān)卡中空的區(qū)域
  • 數(shù)字1:一個(gè)堅(jiān)硬的磚塊霹陡,不可被摧毀
  • 大于1的數(shù)字:一個(gè)可被摧毀的磚塊和蚪,不同的數(shù)字區(qū)分磚塊的顏色

上面的示例關(guān)卡在被GameLevel處理后,看起來(lái)會(huì)像這樣:

GameLevel類(lèi)使用兩個(gè)函數(shù)從文件中生成一個(gè)關(guān)卡烹棉。它首先將所有數(shù)字在Load函數(shù)中加載到二維容器(vector)里桩卵,然后在init函數(shù)中處理這些數(shù)字惨险,以創(chuàng)建所有的游戲?qū)ο蟆?/p>

void GameLevel::Load(const GLchar *file, GLuint levelWidth, GLuint levelHeight)
{
    // 清空過(guò)期數(shù)據(jù)
    this->Bricks.clear();
    // 從文件中加載
    GLuint tileCode;
    GameLevel level;
    std::string line;
    std::ifstream fstream(file);
    std::vector<std::vector<GLuint>> tileData;
    if (fstream)
    {
        while (std::getline(fstream, line)) // 讀取關(guān)卡文件的每一行
        {
            std::istringstream sstream(line);
            std::vector<GLuint> row;
            while (sstream >> tileCode) // 讀取被空格分隔的每個(gè)數(shù)字
                row.push_back(tileCode);
            tileData.push_back(row);
        }
        if (tileData.size() > 0)
            this->init(tileData, levelWidth, levelHeight);
    }
} 

被加載后的tileData數(shù)據(jù)被傳遞到GameLevel的init函數(shù):

void GameLevel::init(std::vector<std::vector<GLuint>> tileData, GLuint lvlWidth, GLuint lvlHeight)
{
    // 計(jì)算每個(gè)維度的大小
    GLuint height = tileData.size();
    GLuint width = tileData[0].size();
    GLfloat unit_width = lvlWidth / static_cast<GLfloat>(width);
    GLfloat unit_height = lvlHeight / height;
    // 基于tileDataC初始化關(guān)卡     
    for (GLuint y = 0; y < height; ++y)
    {
        for (GLuint x = 0; x < width; ++x)
        {
            // 檢查磚塊類(lèi)型
            if (tileData[y][x] == 1)
            {
                glm::vec2 pos(unit_width * x, unit_height * y);
                glm::vec2 size(unit_width, unit_height);
                GameObject obj(pos, size, 
                    ResourceManager::GetTexture("block_solid"), 
                    glm::vec3(0.8f, 0.8f, 0.7f)
                );
                obj.IsSolid = GL_TRUE;
                this->Bricks.push_back(obj);
            }
            else if (tileData[y][x] > 1)    
            {
                glm::vec3 color = glm::vec3(1.0f); // 默認(rèn)為白色
                if (tileData[y][x] == 2)
                    color = glm::vec3(0.2f, 0.6f, 1.0f);
                else if (tileData[y][x] == 3)
                    color = glm::vec3(0.0f, 0.7f, 0.0f);
                else if (tileData[y][x] == 4)
                    color = glm::vec3(0.8f, 0.8f, 0.4f);
                else if (tileData[y][x] == 5)
                    color = glm::vec3(1.0f, 0.5f, 0.0f);

                glm::vec2 pos(unit_width * x, unit_height * y);
                glm::vec2 size(unit_width, unit_height);
                this->Bricks.push_back(
                    GameObject(pos, size, ResourceManager::GetTexture("block"), color)
                );
            }
        }
    }  
}

init函數(shù)遍歷每個(gè)被加載的數(shù)字友雳,處理后將一個(gè)相應(yīng)的GameObject添加到關(guān)卡的容器中嘶摊。每個(gè)磚塊的尺寸(unit_widthunit_height)根據(jù)磚塊的總數(shù)被自動(dòng)計(jì)算以便于每塊磚可以完美地適合屏幕邊界。

在這里我們用兩個(gè)新的紋理加載游戲?qū)ο蠓纾謩e為block紋理與solid block紋理抠刺。

這里有一個(gè)很好的小竅門(mén)塔淤,即這些紋理是完全灰度的。其效果是速妖,我們可以在游戲代碼中高蜂,通過(guò)將灰度值與定義好的顏色矢量相乘來(lái)巧妙地操縱它們的顏色,就如同我們?cè)?code>SpriteRenderer中所做的那樣罕容。這樣一來(lái)备恤,自定義的顏色/外觀(guān)就不會(huì)顯得怪異或不平衡。

GameLevel類(lèi)還包含一些其他的功能锦秒,比如渲染所有未被破壞的磚塊露泊,或驗(yàn)證是否所有的可破壞磚塊均被摧毀。你可以在下面找到GameLevel類(lèi)的源碼:

/*******************************************************************
** 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 "game_level.h"

#include <fstream>
#include <sstream>


void GameLevel::Load(const GLchar *file, GLuint levelWidth, GLuint levelHeight)
{
    // Clear old data
    this->Bricks.clear();
    // Load from file
    GLuint tileCode;
    GameLevel level;
    std::string line;
    std::ifstream fstream(file);
    std::vector<std::vector<GLuint>> tileData;
    if (fstream)
    {
        while (std::getline(fstream, line)) // Read each line from level file
        {
            std::istringstream sstream(line);
            std::vector<GLuint> row;
            while (sstream >> tileCode) // Read each word seperated by spaces
                row.push_back(tileCode);
            tileData.push_back(row);
        }
        if (tileData.size() > 0)
            this->init(tileData, levelWidth, levelHeight);
    }
}

void GameLevel::Draw(SpriteRenderer &renderer)
{
    for (GameObject &tile : this->Bricks)
        if (!tile.Destroyed)
            tile.Draw(renderer);
}

GLboolean GameLevel::IsCompleted()
{
    for (GameObject &tile : this->Bricks)
        if (!tile.IsSolid && !tile.Destroyed)
            return GL_FALSE;
    return GL_TRUE;
}

void GameLevel::init(std::vector<std::vector<GLuint>> tileData, GLuint levelWidth, GLuint levelHeight)
{
    // Calculate dimensions
    GLuint height = tileData.size();
    GLuint width = tileData[0].size(); // Note we can index vector at [0] since this function is only called if height > 0
    GLfloat unit_width = levelWidth / static_cast<GLfloat>(width), unit_height = levelHeight / height; 
    // Initialize level tiles based on tileData     
    for (GLuint y = 0; y < height; ++y)
    {
        for (GLuint x = 0; x < width; ++x)
        {
            // Check block type from level data (2D level array)
            if (tileData[y][x] == 1) // Solid
            {
                glm::vec2 pos(unit_width * x, unit_height * y);
                glm::vec2 size(unit_width, unit_height);
                GameObject obj(pos, size, ResourceManager::GetTexture("block_solid"), glm::vec3(0.8f, 0.8f, 0.7f));
                obj.IsSolid = GL_TRUE;
                this->Bricks.push_back(obj);
            }
            else if (tileData[y][x] > 1)    // Non-solid; now determine its color based on level data
            {
                glm::vec3 color = glm::vec3(1.0f); // original: white
                if (tileData[y][x] == 2)
                    color = glm::vec3(0.2f, 0.6f, 1.0f);
                else if (tileData[y][x] == 3)
                    color = glm::vec3(0.0f, 0.7f, 0.0f);
                else if (tileData[y][x] == 4)
                    color = glm::vec3(0.8f, 0.8f, 0.4f);
                else if (tileData[y][x] == 5)
                    color = glm::vec3(1.0f, 0.5f, 0.0f);

                glm::vec2 pos(unit_width * x, unit_height * y);
                glm::vec2 size(unit_width, unit_height);
                this->Bricks.push_back(GameObject(pos, size, ResourceManager::GetTexture("block"), color));
            }
        }
    }
}

因?yàn)橹С秩我鈹?shù)量的行和列惭笑,這個(gè)游戲關(guān)卡類(lèi)給我們帶來(lái)了很大的靈活性,用戶(hù)可以通過(guò)修改關(guān)卡文件輕松創(chuàng)建自己的關(guān)卡生真。


在游戲中

我們希望在Breakout游戲中支持多個(gè)關(guān)卡沉噩,因此我們將在Game類(lèi)中添加一個(gè)持有GameLevel變量的容器。同時(shí)我們還將存儲(chǔ)當(dāng)前的游戲關(guān)卡汇歹。

class Game
{
    [...]
    std::vector<GameLevel> Levels;
    GLuint                 Level;
    [...]  
};

這個(gè)教程的Breakout版本共有4個(gè)游戲關(guān)卡:

然后Game類(lèi)的init函數(shù)初始化每個(gè)紋理和關(guān)卡:

void Game::Init()
{
    [...]
    // 加載紋理
    ResourceManager::LoadTexture("textures/background.jpg", GL_FALSE, "background");
    ResourceManager::LoadTexture("textures/awesomeface.png", GL_TRUE, "face");
    ResourceManager::LoadTexture("textures/block.png", GL_FALSE, "block");
    ResourceManager::LoadTexture("textures/block_solid.png", GL_FALSE, "block_solid");
    // 加載關(guān)卡
    GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height * 0.5);
    GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height * 0.5);
    GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height * 0.5);
    GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height * 0.5);
    this->Levels.push_back(one);
    this->Levels.push_back(two);
    this->Levels.push_back(three);
    this->Levels.push_back(four);
    this->Level = 1;
}  

現(xiàn)在剩下要做的就是通過(guò)調(diào)用當(dāng)前關(guān)卡的Draw函數(shù)來(lái)渲染我們完成的關(guān)卡屁擅,然后使用給定的sprite渲染器調(diào)用每個(gè)GameObjectDraw函數(shù)。除了關(guān)卡之外产弹,我們還會(huì)用一個(gè)很好的背景圖片來(lái)渲染這個(gè)場(chǎng)景:

void Game::Render()
{
    if(this->State == GAME_ACTIVE)
    {
        // 繪制背景
        Renderer->DrawSprite(ResourceManager::GetTexture("background"), 
            glm::vec2(0, 0), glm::vec2(this->Width, this->Height), 0.0f
        );
        // 繪制關(guān)卡
        this->Levels[this->Level].Draw(*Renderer);
    }
}

結(jié)果便是如下這個(gè)被呈現(xiàn)的關(guān)卡,它使我們的游戲變得開(kāi)始生動(dòng)起來(lái):



玩家擋板

此時(shí)我們?cè)趫?chǎng)景底部引入一個(gè)由玩家控制的擋板弯囊,擋板只允許水平移動(dòng)痰哨,并且在它接觸任意場(chǎng)景邊緣時(shí)停止。對(duì)于玩家擋板匾嘱,我們將使用以下紋理:

一個(gè)擋板對(duì)象擁有位置斤斧、大小、渲染紋理等屬性霎烙,所以我們理所當(dāng)然地將其定義為一個(gè)GameObject撬讽。

// 初始化擋板的大小
const glm::vec2 PLAYER_SIZE(100, 20);
// 初始化當(dāng)班的速率
const GLfloat PLAYER_VELOCITY(500.0f);

GameObject      *Player;

void Game::Init()
{
    [...]    
    ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
    [...]
    glm::vec2 playerPos = glm::vec2(
        this->Width / 2 - PLAYER_SIZE.x / 2, 
        this->Height - PLAYER_SIZE.y
    );
    Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
}

這里我們定義了幾個(gè)常量來(lái)初始化擋板的大小與速率。在Game的Init函數(shù)中我們計(jì)算擋板的初始位置悬垃,使其中心與場(chǎng)景的水平中心對(duì)齊游昼。

除此之外我們還需要在Game的Render函數(shù)中添加:

Player->Draw(*Renderer);

如果你現(xiàn)在啟動(dòng)游戲,你不僅會(huì)看到關(guān)卡畫(huà)面尝蠕,還會(huì)有一個(gè)在場(chǎng)景底部邊緣的奇特的擋板烘豌。到目前為止,它除了靜態(tài)地放置在那以外不會(huì)發(fā)生任何事情看彼,因此我們需要進(jìn)入游戲的ProcessInput函數(shù)廊佩,使得當(dāng)玩家按下A和D時(shí)囚聚,擋板可以水平移動(dòng)。

void Game::ProcessInput(GLfloat dt)
{
    if (this->State == GAME_ACTIVE)
    {
        GLfloat velocity = PLAYER_VELOCITY * dt;
        // 移動(dòng)擋板
        if (this->Keys[GLFW_KEY_A])
        {
            if (Player->Position.x >= 0)
                Player->Position.x -= velocity;
        }
        if (this->Keys[GLFW_KEY_D])
        {
            if (Player->Position.x <= this->Width - Player->Size.x)
                Player->Position.x += velocity;
        }
    }
} 

在這里标锄,我們根據(jù)用戶(hù)按下的鍵顽铸,向左或向右移動(dòng)擋板(注意我們將速率與deltaTime相乘)。當(dāng)擋板的x值小于0料皇,它將移動(dòng)出游戲場(chǎng)景的最左側(cè)跋破,所以我們只允許擋板的x值大于0時(shí)向左移動(dòng)。對(duì)于右側(cè)邊緣我們做相同的處理瓶蝴,但我們必須比較場(chǎng)景的右側(cè)邊緣與擋板的右側(cè)邊緣毒返,即場(chǎng)景寬度減去擋板寬度。

現(xiàn)在啟動(dòng)游戲舷手,將呈現(xiàn)一個(gè)玩家可控制在整個(gè)場(chǎng)景底部自由移動(dòng)的擋板拧簸。

你可以在下面找到更新后的Game類(lèi)代碼:

/*******************************************************************
** 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 "game.h"
#include "resource_manager.h"
#include "sprite_renderer.h"
#include "game_object.h"


// Game-related State data
SpriteRenderer  *Renderer;
GameObject      *Player;


Game::Game(GLuint width, GLuint height) 
    : State(GAME_ACTIVE), Keys(), Width(width), Height(height) 
{ 

}

Game::~Game()
{
    delete Renderer;
    delete Player;
}

void Game::Init()
{
    // Load shaders
    ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
    // Configure shaders
    glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(this->Width), static_cast<GLfloat>(this->Height), 0.0f, -1.0f, 1.0f);
    ResourceManager::GetShader("sprite").Use().SetInteger("sprite", 0);
    ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
    // Load textures
    ResourceManager::LoadTexture("textures/background.jpg", GL_FALSE, "background");
    ResourceManager::LoadTexture("textures/awesomeface.png", GL_TRUE, "face");
    ResourceManager::LoadTexture("textures/block.png", GL_FALSE, "block");
    ResourceManager::LoadTexture("textures/block_solid.png", GL_FALSE, "block_solid");
    ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
    // Set render-specific controls
    Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
    // Load levels
    GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height * 0.5);
    GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height * 0.5);
    GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height * 0.5);
    GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height * 0.5);
    this->Levels.push_back(one);
    this->Levels.push_back(two);
    this->Levels.push_back(three);
    this->Levels.push_back(four);
    this->Level = 0;
    // Configure game objects
    glm::vec2 playerPos = glm::vec2(this->Width / 2 - PLAYER_SIZE.x / 2, this->Height - PLAYER_SIZE.y);
    Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
}

void Game::Update(GLfloat dt)
{
    
}


void Game::ProcessInput(GLfloat dt)
{
    if (this->State == GAME_ACTIVE)
    {
        GLfloat velocity = PLAYER_VELOCITY * dt;
        // Move playerboard
        if (this->Keys[GLFW_KEY_A])
        {
            if (Player->Position.x >= 0)
                Player->Position.x -= velocity;
        }
        if (this->Keys[GLFW_KEY_D])
        {
            if (Player->Position.x <= this->Width - Player->Size.x)
                Player->Position.x += velocity;
        }
    }
}

void Game::Render()
{
    if (this->State == GAME_ACTIVE)
    {
        // Draw background
        Renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0, 0), glm::vec2(this->Width, this->Height), 0.0f);
        // Draw level
        this->Levels[this->Level].Draw(*Renderer);
        // Draw player
        Player->Draw(*Renderer);
    }
}

后記

本篇已結(jié)束男窟,后面更精彩~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盆赤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歉眷,更是在濱河造成了極大的恐慌牺六,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汗捡,死亡現(xiàn)場(chǎng)離奇詭異淑际,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)扇住,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)春缕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人艘蹋,你說(shuō)我怎么就攤上這事锄贼。” “怎么了女阀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵宅荤,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我浸策,道長(zhǎng)冯键,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任的榛,我火速辦了婚禮琼了,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己雕薪,他們只是感情好昧诱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著所袁,像睡著了一般盏档。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上燥爷,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天蜈亩,我揣著相機(jī)與錄音,去河邊找鬼前翎。 笑死稚配,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的港华。 我是一名探鬼主播道川,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼立宜!你這毒婦竟也來(lái)了冒萄?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤橙数,失蹤者是張志新(化名)和其女友劉穎尊流,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體灯帮,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崖技,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了施流。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片响疚。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瞪醋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情装诡,我是刑警寧澤银受,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站鸦采,受9級(jí)特大地震影響宾巍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜渔伯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一顶霞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦选浑、人聲如沸蓝厌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拓提。三九已至,卻和暖如春隧膘,著一層夾襖步出監(jiān)牢的瞬間代态,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工疹吃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹦疑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓萨驶,卻偏偏與公主長(zhǎng)得像歉摧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子篡撵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容