这一节主要给界面添加一些文字信息,也是这个教程的最后一个章节(后面还有个结语)。
TextRenderer.h:
#pragma once
//放前面:里面包含有glad.h
#include "Shader.h"
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <map>
#include "Texture2D.h"
struct Character
{
GLuint textureID; // ID handle of the glyph texture
glm::ivec2 size; // Size of glyph
glm::ivec2 bearing; // Offset from baseline to left/top of glyph
GLuint advance; // Horizontal offset to advance to next glyph
};
class TextRenderer
{
public:
//holds a list of pre-compiled characters
std::map<GLchar, Character> characters;
//shader used for text rendering
Shader textShader;
//constructor
TextRenderer(GLuint width, GLuint height);
//pre-compiles a list of characters from the given font
void Load(std::string font, GLuint fontSize);
//render a string of text using the precompiled list of characters
void RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color = glm::vec3(1.0f));
private:
GLuint VAO, VBO;
};
TextRenderer.cpp:
#include "TextRenderer.h"
#include "ResourceManager.h"
#include <ft2build.h>
#include FT_FREETYPE_H
TextRenderer::TextRenderer(GLuint width, GLuint height)
{
//load and configure shader
this->textShader = ResourceManager::LoadShader("Breakout/Shaders/text.vs", "Breakout/Shaders/text.fs", nullptr, "text");
this->textShader.SetMatrix4("projection", glm::ortho(0.0f, static_cast<GLfloat>(width), static_cast<GLfloat>(height), 0.0f), GL_TRUE);
this->textShader.SetInteger("text", 0);
//configure VAO/VBO for texture quads
glGenVertexArrays(1, &this->VAO);
glGenBuffers(1, &this->VBO);
glBindVertexArray(this->VAO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void TextRenderer::Load(std::string font, GLuint fontSize)
{
//first clear the previously loaded characters
this->characters.clear();
//init and load the freetype library
FT_Library ft;
if (FT_Init_FreeType(&ft))
std::cout << "Error::freetype:could not init freetype library" << std::endl;
//load font as face
FT_Face face;
if (FT_New_Face(ft, font.c_str(), 0, &face))
std::cout << "Error::freetype:could not init freetype library" << std::endl;
//set size to load glyphs as
FT_Set_Pixel_Sizes(face, 0, fontSize);
//disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//then for the first 128 ascii characters pre-load/compile their characters and store them
for (GLubyte c = 0; c < 128; c++)
{
// Load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
continue;
}
// Generate texture
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
// Set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Now store character for later use
Character character = {
texture,
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
face->glyph->advance.x
};
characters.insert(std::pair<GLchar, Character>(c, character));
}
glBindTexture(GL_TEXTURE_2D, 0);
// Destroy FreeType once we're finished
FT_Done_Face(face);
FT_Done_FreeType(ft);
}
void TextRenderer::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color)
{
// Activate corresponding render state
this->textShader.Use();
this->textShader.SetVector3f("textColor", color);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(this->VAO);
// Iterate through all characters
std::string::const_iterator c;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = characters[*c];
GLfloat xpos = x + ch.bearing.x * scale;
GLfloat ypos = y + (this->characters['H'].bearing.y - ch.bearing.y) * scale;
GLfloat w = ch.size.x * scale;
GLfloat h = ch.size.y * scale;
// Update VBO for each character
GLfloat vertices[6][4] = {
{ xpos, ypos + h, 0.0, 1.0 },
{ xpos + w, ypos, 1.0, 0.0 },
{ xpos, ypos, 0.0, 0.0 },
{ xpos, ypos + h, 0.0, 1.0 },
{ xpos + w, ypos + h, 1.0, 1.0 },
{ xpos + w, ypos, 1.0, 0.0 }
};
// Render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.textureID);
// Update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
// Now advance cursors for next glyph
x += (ch.advance >> 6) * scale; // Bitshift by 6 to get value in pixels (1/64th times 2^6 = 64)
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
Game.h:
#pragma once
#include <GLFW/glfw3.h>
#include <vector>
#include "GameObject.h"
#include "GameLevel.h"
#include "PowerUp.h"
// 代表了游戏的当前状态
enum GameState {
GAME_ACTIVE,
GAME_MENU,
GAME_WIN
};
enum Direction
{
UP,
RIGHT,
DOWN,
LEFT
};
typedef std::tuple<GLboolean, Direction, glm::vec2> Collision;
//player
const glm::vec2 PLAYER_SIZE(100, 20);
const GLfloat PLAYER_VELOCITY(500.0f);
//ball init velocity
const glm::vec2 INITIAL_BALL_VELOCITY(100.0f, -350.0f);
//ball radius
const GLfloat BALL_RADIUS = 12.5f;
class Game
{
public:
GLuint lives;
GameState state;
GLboolean keys[1024];
GLboolean keysProcessed[1024];
GLuint width, height;
std::vector<GameLevel> levels;
GLuint currentLevel;
std::vector<PowerUp> powerups;
Game(GLuint w, GLuint h);
~Game();
void Init();
void ProcessInput(GLfloat dt);
void Update(GLfloat dt);
void Render();
void DoCollisions();
//Reset
void ResetLevel();
void ResetPlayer();
//powerup
void SpawnPowerUps(GameObject& block);
void UpdatePowerUps(GLfloat dt);
};
Game.cpp:
#include "ResourceManager.h"
#include "Game.h"
#include "SpriteRenderer.h"
#include "BallObject.h"
#include "ParticleGenerator.h"
#include "PostProcessor.h"
#include <irrklang/irrKlang.h>
#include "TextRenderer.h"
using namespace irrklang;
SpriteRenderer* renderer;
GameObject* player;
BallObject* ball;
ParticleGenerator* particles;
PostProcessor* effects;
ISoundEngine* soundEngine = createIrrKlangDevice();
TextRenderer* text;
GLfloat shakeTime = 0.0f;
Game::Game(GLuint w, GLuint h) :state(GAME_ACTIVE), keys(), width(w), height(h), lives(3) {}
Game::~Game()
{
delete renderer;
delete player;
delete ball;
delete particles;
delete effects;
delete text;
soundEngine->drop();
}
void Game::Init()
{
//load shader
ResourceManager::LoadShader("Breakout/Shaders/Sprite.vs", "Breakout/Shaders/Sprite.fs", nullptr, "sprite");
ResourceManager::LoadShader("Breakout/Shaders/Particle.vs", "Breakout/Shaders/Particle.fs", nullptr, "particle");
ResourceManager::LoadShader("Breakout/Shaders/postprocessor.vs", "Breakout/Shaders/postprocessor.fs", nullptr, "postprocessor");
//config shader
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("image", 0);
ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
ResourceManager::GetShader("particle").Use().SetInteger("sprite", 0);
ResourceManager::GetShader("particle").SetMatrix4("projection", projection);
//load texture
ResourceManager::LoadTexture("Breakout/Images/background.jpg", GL_FALSE, "background");
ResourceManager::LoadTexture("Breakout/Images/block.png", GL_FALSE, "block");
ResourceManager::LoadTexture("Breakout/Images/block_solid.png", GL_FALSE, "block_solid");
ResourceManager::LoadTexture("Breakout/paddle.png", GL_TRUE, "paddle");//player
ResourceManager::LoadTexture("Breakout/face.png", GL_TRUE, "ball");//ball
ResourceManager::LoadTexture("Breakout/particle.png", GL_TRUE, "particle");//particle
ResourceManager::LoadTexture("Breakout/tex_speed.png", GL_TRUE, "tex_speed");
ResourceManager::LoadTexture("Breakout/tex_sticky.png", GL_TRUE, "tex_sticky");
ResourceManager::LoadTexture("Breakout/tex_pass.png", GL_TRUE, "tex_pass");
ResourceManager::LoadTexture("Breakout/tex_size.png", GL_TRUE, "tex_size");
ResourceManager::LoadTexture("Breakout/tex_confuse.png", GL_TRUE, "tex_confuse");
ResourceManager::LoadTexture("Breakout/tex_chaos.png", GL_TRUE, "tex_chaos");
//setup render control
renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
particles = new ParticleGenerator(ResourceManager::GetShader("particle"), ResourceManager::GetTexture("particle"), 500);
effects = new PostProcessor(ResourceManager::GetShader("postprocessor"), this->width, this->height);
//load levels
GameLevel one; one.Load("Breakout/Levels/1.lvl", this->width, this->height * 0.5);
GameLevel two; two.Load("Breakout/Levels/2.lvl", this->width, this->height * 0.5);
GameLevel three; three.Load("Breakout/Levels/3.lvl", this->width, this->height * 0.5);
GameLevel four; four.Load("Breakout/Levels/4.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->currentLevel = 0;
//player init
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"));
//ball 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("ball"));
//Audio
soundEngine->play2D("Breakout/Audios/breakout.mp3", true);
//Text
text = new TextRenderer(this->width, this->height);
text->Load("Fonts/OCRAEXT.TTF", 24);
}
void Game::Update(GLfloat dt)
{
//update ball
ball->Move(dt, this->width);
//collision detection
this->DoCollisions();
//particle
particles->Update(dt, *ball, 2, glm::vec2(ball->radius / 2));
//update powerups
this->UpdatePowerUps(dt);
// Reduce shake time
if (shakeTime > 0.0f)
{
shakeTime -= dt;
if (shakeTime <= 0.0f)
effects->shake = GL_FALSE;
}
//球是否接触底部边界?
if (ball->position.y >= this->height)
{
--this->lives;
if (this->lives == 0)
{
this->ResetLevel();
this->state = GAME_MENU;
}
this->ResetPlayer();
}
if (this->state == GAME_ACTIVE && this->levels[this->currentLevel].IsCompleted())
{
this->ResetLevel();
this->ResetPlayer();
effects->chaos = GL_TRUE;
this->state = GAME_WIN;
}
}
void Game::ProcessInput(GLfloat dt)
{
if (this->state == GAME_WIN)
{
if (this->keys[GLFW_KEY_ENTER])
{
this->keysProcessed[GLFW_KEY_ENTER] = GL_TRUE;
effects->chaos = GL_FALSE;
this->state = GAME_MENU;
}
}
if (this->state == GAME_MENU)
{
if (this->keys[GLFW_KEY_ENTER] && !this->keysProcessed[GLFW_KEY_ENTER])
{
this->state = GAME_ACTIVE;
this->keysProcessed[GLFW_KEY_ENTER] = GL_TRUE;
}
if (this->keys[GLFW_KEY_W] && !this->keysProcessed[GLFW_KEY_W])
{
this->currentLevel = (this->currentLevel + 1) % 4;
this->keysProcessed[GLFW_KEY_W] = GL_TRUE;
}
if (this->keys[GLFW_KEY_S] && !this->keysProcessed[GLFW_KEY_S])
{
if (this->currentLevel > 0)
--this->currentLevel;
else
this->currentLevel = 3;
this->keysProcessed[GLFW_KEY_S] = GL_TRUE;
}
}
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 = GL_FALSE;
}
}
void Game::Render()
{
if (this->state == GAME_ACTIVE || this->state == GAME_MENU || this->state == GAME_WIN)
{
effects->BeginRender();
//绘制背景
renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0, 0), glm::vec2(this->width, this->height), 0.0f);
//绘制关卡
this->levels[this->currentLevel].Draw(*renderer);
//绘制玩家挡板
player->Draw(*renderer);
//绘制道具
for (PowerUp& powerup : this->powerups)
{
if (!powerup.destroyed)
powerup.Draw(*renderer);
}
//绘制粒子
particles->Draw();
//绘制球
ball->Draw(*renderer);
effects->EndRender();
effects->Render(glfwGetTime());
std::stringstream ss;
ss << this->lives;
text->RenderText("Lives:" + ss.str(), 5.0f, 5.0f, 1.0f);
}
if (this->state == GAME_MENU)
{
text->RenderText("Press ENTER to start", 250.0f, height / 2, 1.0f);
text->RenderText("Press W or S to select level", 245.0f, height / 2 + 20.0f, 0.75f);
}
if (this->state == GAME_WIN)
{
text->RenderText("You WON!!!", 320.0, height / 2 - 20.0, 1.0, glm::vec3(0.0, 1.0, 0.0));
text->RenderText("Press ENTER to retry or ESC to quit", 130.0, height / 2, 1.0, glm::vec3(1.0, 1.0, 0.0));
}
}
Direction VectorDirection(glm::vec2 target)
{
glm::vec2 compass[] =
{
glm::vec2(0.0f,1.0f),
glm::vec2(1.0f,0.0f),
glm::vec2(0.0f,-1.0f),
glm::vec2(-1.0f,0.0f),
};
GLfloat max = 0.0f;
GLuint bestMatch = -1;
for (GLuint i = 0; i < 4; i++)
{
GLfloat dot = glm::dot(glm::normalize(target), compass[i]);
if (dot > max)
{
max = dot;
bestMatch = i;
}
}
return (Direction)bestMatch;
}
//碰撞检测::AABB
GLboolean CheckCollision(GameObject& one, GameObject& two) // AABB - AABB collision
{
// x轴方向碰撞?
bool collisionX = one.position.x + one.size.x >= two.position.x &&
two.position.x + two.size.x >= one.position.x;
// y轴方向碰撞?
bool collisionY = one.position.y + one.size.y >= two.position.y &&
two.position.y + two.size.y >= one.position.y;
// 只有两个轴向都有碰撞时才碰撞
return collisionX && collisionY;
}
//碰撞检测:方形和圆形
Collision CheckCollision(BallObject& one, GameObject& two) // AABB - Circle collision
{
//获取圆的中心
glm::vec2 center(one.position + one.radius);
//计算AABB的信息(中心,半边长)
glm::vec2 aabb_half_extents(two.size.x / 2, two.size.y / 2);
glm::vec2 aabb_center(two.position.x + aabb_half_extents.x, two.position.y + aabb_half_extents.y);
//获取两个中心的差矢量
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
//AABB_center 加上clampled这样就得到了碰撞箱上距离最近点closest
glm::vec2 closest = aabb_center + clamped;
//获得圆心center和最近点closest的矢量,并判断是否length<=radius
difference = closest - center;
if (glm::length(difference) < one.radius)
return std::make_tuple(GL_TRUE, VectorDirection(difference), difference);
else
return std::make_tuple(GL_FALSE, UP, glm::vec2(0, 0));
}
void ActivatePowerUp(PowerUp& powerup)
{
// 根据道具类型发动道具
if (powerup.type == "speed")
{
ball->velocity *= 1.2;
}
else if (powerup.type == "sticky")
{
ball->sticky = GL_TRUE;
player->color = glm::vec3(1.0f, 0.5f, 1.0f);
}
else if (powerup.type == "pass-through")
{
ball->passThrough = GL_TRUE;
ball->color = glm::vec3(1.0f, 0.5f, 0.5f);
}
else if (powerup.type == "pad-size-increase")
{
player->size.x += 50;
}
else if (powerup.type == "confuse")
{
if (!effects->chaos)
effects->confuse = GL_TRUE; // 只在chaos未激活时生效,chaos同理
}
else if (powerup.type == "chaos")
{
if (!effects->confuse)
effects->chaos = GL_TRUE;
}
}
void Game::DoCollisions()
{
for (GameObject& box : this->levels[this->currentLevel].bricks)
{
if (!box.destroyed)
{
Collision collision = CheckCollision(*ball, box);
if (std::get<0>(collision))
{
if (!box.isSoild)
{
box.destroyed = GL_TRUE;
this->SpawnPowerUps(box);
soundEngine->play2D("Breakout/Audios/bleep.mp3", GL_FALSE);
}
else
{
shakeTime = 0.05f;
effects->shake = true;
soundEngine->play2D("Breakout/Audios/solid.wav", GL_FALSE);
}
//碰撞处理
Direction dir = std::get<1>(collision);
glm::vec2 diff_vector = std::get<2>(collision);
if (!(ball->passThrough && !box.isSoild))
{
if (dir == LEFT || dir == RIGHT)
{
ball->velocity.x = -ball->velocity.x;//反转水平速度
//重定位
GLfloat penetration = ball->radius - std::abs(diff_vector.x);
if (dir == LEFT)
ball->position.x += penetration;//球右移
else
ball->position.x -= penetration;//球左移
}
else//垂直方形碰撞
{
ball->velocity.y = -ball->velocity.y;//反转垂直速度
//重定位
GLfloat penetration = ball->radius - std::abs(diff_vector.y);
if (dir == UP)
ball->position.y -= penetration;//球上移
else
ball->position.y += penetration;//球下移
}
}
}
for (PowerUp& powerup : this->powerups)
{
if (!powerup.destroyed)
{
if (powerup.position.y >= this->height)
{
powerup.destroyed = GL_TRUE;
}
if (CheckCollision(*player, powerup))
{
ActivatePowerUp(powerup);
powerup.destroyed = GL_TRUE;
powerup.activated = GL_TRUE;
soundEngine->play2D("Breakout/Audios/powerup.wav", GL_FALSE);
}
}
}
//玩家--球碰撞
Collision result = CheckCollision(*ball, *player);
if (!ball->stuck && std::get<0>(result))
{
//检查碰到了挡板的哪个位置,并根据碰撞到哪个位置改变速度
GLfloat centerBoard = player->position.x + player->size.x / 2;
GLfloat distance = (ball->position.x + ball->radius) - centerBoard;
GLfloat percentage = distance / (player->size.x / 2);
//依据结果移动
GLfloat strength = 2.0f;
glm::vec2 oldVelocity = ball->velocity;
ball->velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
//ball->velocity.y = -ball->velocity.y;
//解决粘板问题:保证最后的Y方向始终向上
ball->velocity.y = -1 * abs(ball->velocity.y);
//新的速度:新的归一化方向*原来速度的大小
ball->velocity = glm::normalize(ball->velocity) * glm::length(oldVelocity);
ball->stuck = ball->sticky;
soundEngine->play2D("Breakout/Audios/bleep.wav", GL_FALSE);
}
}
}
}
void Game::ResetLevel()
{
if (this->currentLevel == 0)
this->levels[0].Load("Breakout/Levels/1.lvl", this->width, this->height * 0.5f);
else if (this->currentLevel == 1)
this->levels[1].Load("Breakout/Levels/2.lvl", this->width, this->height * 0.5f);
else if (this->currentLevel == 2)
this->levels[1].Load("Breakout/Levels/3.lvl", this->width, this->height * 0.5f);
else if (this->currentLevel == 3)
this->levels[1].Load("Breakout/Levels/4.lvl", this->width, this->height * 0.5f);
this->lives == 3;
}
void Game::ResetPlayer()
{
player->size = PLAYER_SIZE;
player->position = glm::vec2(this->width / 2 - PLAYER_SIZE.x / 2, this->height - PLAYER_SIZE.y);
ball->Reset(player->position + glm::vec2(PLAYER_SIZE.x / 2 - ball->radius, -(BALL_RADIUS * 2)), INITIAL_BALL_VELOCITY);
}
GLboolean ShouldSpawn(GLuint chance)
{
GLuint random = rand() % chance;
return random == 0;
}
void Game::SpawnPowerUps(GameObject& block)
{
if (ShouldSpawn(10))
this->powerups.push_back(PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.position, ResourceManager::GetTexture("tex_speed")));
if (ShouldSpawn(10))
this->powerups.push_back(PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.position, ResourceManager::GetTexture("tex_sticky")));
if (ShouldSpawn(10))
this->powerups.push_back(PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.position, ResourceManager::GetTexture("tex_pass")));
if (ShouldSpawn(10))
this->powerups.push_back(PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.position, ResourceManager::GetTexture("tex_size")));
//if (ShouldSpawn(15))
//this->powerups.push_back(PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.position, ResourceManager::GetTexture("tex_confuse")));
//if (ShouldSpawn(10))
//this->powerups.push_back(PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.position, ResourceManager::GetTexture("tex_chaos")));
}
GLboolean IsOtherPowerUpActive(std::vector<PowerUp>& powerUps, std::string type)
{
for (const PowerUp& powerUp : powerUps)
{
if (powerUp.activated)
if (powerUp.type == type)
return GL_TRUE;
}
return GL_FALSE;
}
void Game::UpdatePowerUps(GLfloat dt)
{
for (PowerUp& powerUp : this->powerups)
{
powerUp.position += powerUp.velocity * dt;
if (powerUp.activated)
{
powerUp.duration -= dt;
if (powerUp.duration <= 0.0f)
{
powerUp.activated = GL_FALSE;
if (powerUp.type == "sticky")
{
if (!IsOtherPowerUpActive(this->powerups, "sticky"))
{
ball->sticky = GL_FALSE;
player->color = glm::vec3(1.0f);
}
}
else if (powerUp.type == "pass-through")
{
if (!IsOtherPowerUpActive(this->powerups, "pass-through"))
{
ball->passThrough = GL_FALSE;
player->color = glm::vec3(1.0f);
}
}
else if (powerUp.type == "pass-through")
{
if (!IsOtherPowerUpActive(this->powerups, "pass-through"))
{
ball->passThrough = GL_FALSE;
player->color = glm::vec3(1.0f);
}
}
}
}
}
this->powerups.erase(std::remove_if(this->powerups.begin(), this->powerups.end(),
[](const PowerUp& powerUp) { return powerUp.destroyed && !powerUp.activated; }
), this->powerups.end());
}
Text.vs:
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
Text.fs:
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}