Change CMakeLists.txt to add 2d_game project, and added irrKlang to LIBS.
Added irrKlang.dll and ikpMP2.dll to dlls folder. Added irrKlang as a sub-folder under includes. Added irrKlang.lib to lib folder. At this point running cmake gave an error it couldn't find source files under 2d_game. Moved 2d_game source up one folder level, then cmake found the files.
@@ -1,54 +0,0 @@
|
||||
/******************************************************************
|
||||
** 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), Sticky(false), PassThrough(false) { }
|
||||
|
||||
BallObject::BallObject(glm::vec2 pos, float radius, glm::vec2 velocity, Texture2D sprite)
|
||||
: GameObject(pos, glm::vec2(radius * 2.0f, radius * 2.0f), sprite, glm::vec3(1.0f), velocity), Radius(radius), Stuck(true), Sticky(false), PassThrough(false) { }
|
||||
|
||||
glm::vec2 BallObject::Move(float dt, unsigned int 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;
|
||||
this->Sticky = false;
|
||||
this->PassThrough = false;
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef BALLOBJECT_H
|
||||
#define BALLOBJECT_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "game_object.h"
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
// BallObject holds the state of the Ball object inheriting
|
||||
// relevant state data from GameObject. Contains some extra
|
||||
// functionality specific to Breakout's ball object that
|
||||
// were too specific for within GameObject alone.
|
||||
class BallObject : public GameObject
|
||||
{
|
||||
public:
|
||||
// ball state
|
||||
float Radius;
|
||||
bool Stuck;
|
||||
bool Sticky, PassThrough;
|
||||
// constructor(s)
|
||||
BallObject();
|
||||
BallObject(glm::vec2 pos, float radius, glm::vec2 velocity, Texture2D sprite);
|
||||
// moves the ball, keeping it constrained within the window bounds (except bottom edge); returns new position
|
||||
glm::vec2 Move(float dt, unsigned int window_width);
|
||||
// resets the ball to original state with given position and velocity
|
||||
void Reset(glm::vec2 position, glm::vec2 velocity);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,551 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <irrklang/irrKlang.h>
|
||||
using namespace irrklang;
|
||||
|
||||
#include "game.h"
|
||||
#include "resource_manager.h"
|
||||
#include "sprite_renderer.h"
|
||||
#include "game_object.h"
|
||||
#include "ball_object.h"
|
||||
#include "particle_generator.h"
|
||||
#include "post_processor.h"
|
||||
#include "text_renderer.h"
|
||||
|
||||
|
||||
// Game-related State data
|
||||
SpriteRenderer *Renderer;
|
||||
GameObject *Player;
|
||||
BallObject *Ball;
|
||||
ParticleGenerator *Particles;
|
||||
PostProcessor *Effects;
|
||||
ISoundEngine *SoundEngine = createIrrKlangDevice();
|
||||
TextRenderer *Text;
|
||||
|
||||
float ShakeTime = 0.0f;
|
||||
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_MENU), Keys(), KeysProcessed(), Width(width), Height(height), Level(0), Lives(3)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete Renderer;
|
||||
delete Player;
|
||||
delete Ball;
|
||||
delete Particles;
|
||||
delete Effects;
|
||||
delete Text;
|
||||
SoundEngine->drop();
|
||||
}
|
||||
|
||||
void Game::Init()
|
||||
{
|
||||
// load shaders
|
||||
ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
|
||||
ResourceManager::LoadShader("shaders/particle.vs", "shaders/particle.frag", nullptr, "particle");
|
||||
ResourceManager::LoadShader("shaders/post_processing.vs", "shaders/post_processing.frag", nullptr, "postprocessing");
|
||||
// configure shaders
|
||||
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(this->Width), static_cast<float>(this->Height), 0.0f, -1.0f, 1.0f);
|
||||
ResourceManager::GetShader("sprite").Use().SetInteger("sprite", 0);
|
||||
ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
|
||||
ResourceManager::GetShader("particle").Use().SetInteger("sprite", 0);
|
||||
ResourceManager::GetShader("particle").SetMatrix4("projection", projection);
|
||||
// load textures
|
||||
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
ResourceManager::LoadTexture("textures/block.png", false, "block");
|
||||
ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid");
|
||||
ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
|
||||
ResourceManager::LoadTexture("textures/particle.png", true, "particle");
|
||||
ResourceManager::LoadTexture("textures/powerup_speed.png", true, "powerup_speed");
|
||||
ResourceManager::LoadTexture("textures/powerup_sticky.png", true, "powerup_sticky");
|
||||
ResourceManager::LoadTexture("textures/powerup_increase.png", true, "powerup_increase");
|
||||
ResourceManager::LoadTexture("textures/powerup_confuse.png", true, "powerup_confuse");
|
||||
ResourceManager::LoadTexture("textures/powerup_chaos.png", true, "powerup_chaos");
|
||||
ResourceManager::LoadTexture("textures/powerup_passthrough.png", true, "powerup_passthrough");
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
Particles = new ParticleGenerator(ResourceManager::GetShader("particle"), ResourceManager::GetTexture("particle"), 500);
|
||||
Effects = new PostProcessor(ResourceManager::GetShader("postprocessing"), this->Width, this->Height);
|
||||
Text = new TextRenderer(this->Width, this->Height);
|
||||
Text->Load("fonts/OCRAEXT.TTF", 24);
|
||||
// load levels
|
||||
GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height /2 );
|
||||
GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
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.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
|
||||
glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -BALL_RADIUS * 2.0f);
|
||||
Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("face"));
|
||||
// audio
|
||||
SoundEngine->play2D("audio/breakout.mp3", true);
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
// update objects
|
||||
Ball->Move(dt, this->Width);
|
||||
// check for collisions
|
||||
this->DoCollisions();
|
||||
// update particles
|
||||
Particles->Update(dt, *Ball, 2, glm::vec2(Ball->Radius / 2.0f));
|
||||
// update PowerUps
|
||||
this->UpdatePowerUps(dt);
|
||||
// reduce shake time
|
||||
if (ShakeTime > 0.0f)
|
||||
{
|
||||
ShakeTime -= dt;
|
||||
if (ShakeTime <= 0.0f)
|
||||
Effects->Shake = false;
|
||||
}
|
||||
// check loss condition
|
||||
if (Ball->Position.y >= this->Height) // did ball reach bottom edge?
|
||||
{
|
||||
--this->Lives;
|
||||
// did the player lose all his lives? : game over
|
||||
if (this->Lives == 0)
|
||||
{
|
||||
this->ResetLevel();
|
||||
this->State = GAME_MENU;
|
||||
}
|
||||
this->ResetPlayer();
|
||||
}
|
||||
// check win condition
|
||||
if (this->State == GAME_ACTIVE && this->Levels[this->Level].IsCompleted())
|
||||
{
|
||||
this->ResetLevel();
|
||||
this->ResetPlayer();
|
||||
Effects->Chaos = true;
|
||||
this->State = GAME_WIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
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] = true;
|
||||
}
|
||||
if (this->Keys[GLFW_KEY_W] && !this->KeysProcessed[GLFW_KEY_W])
|
||||
{
|
||||
this->Level = (this->Level + 1) % 4;
|
||||
this->KeysProcessed[GLFW_KEY_W] = true;
|
||||
}
|
||||
if (this->Keys[GLFW_KEY_S] && !this->KeysProcessed[GLFW_KEY_S])
|
||||
{
|
||||
if (this->Level > 0)
|
||||
--this->Level;
|
||||
else
|
||||
this->Level = 3;
|
||||
//this->Level = (this->Level - 1) % 4;
|
||||
this->KeysProcessed[GLFW_KEY_S] = true;
|
||||
}
|
||||
}
|
||||
if (this->State == GAME_WIN)
|
||||
{
|
||||
if (this->Keys[GLFW_KEY_ENTER])
|
||||
{
|
||||
this->KeysProcessed[GLFW_KEY_ENTER] = true;
|
||||
Effects->Chaos = false;
|
||||
this->State = GAME_MENU;
|
||||
}
|
||||
}
|
||||
if (this->State == GAME_ACTIVE)
|
||||
{
|
||||
float velocity = PLAYER_VELOCITY * dt;
|
||||
// move playerboard
|
||||
if (this->Keys[GLFW_KEY_A])
|
||||
{
|
||||
if (Player->Position.x >= 0.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
if (this->State == GAME_ACTIVE || this->State == GAME_MENU || this->State == GAME_WIN)
|
||||
{
|
||||
// begin rendering to postprocessing framebuffer
|
||||
Effects->BeginRender();
|
||||
// draw background
|
||||
Renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f);
|
||||
// draw level
|
||||
this->Levels[this->Level].Draw(*Renderer);
|
||||
// draw player
|
||||
Player->Draw(*Renderer);
|
||||
// draw PowerUps
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
if (!powerUp.Destroyed)
|
||||
powerUp.Draw(*Renderer);
|
||||
// draw particles
|
||||
Particles->Draw();
|
||||
// draw ball
|
||||
Ball->Draw(*Renderer);
|
||||
// end rendering to postprocessing framebuffer
|
||||
Effects->EndRender();
|
||||
// render postprocessing quad
|
||||
Effects->Render(glfwGetTime());
|
||||
// render text (don't include in postprocessing)
|
||||
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, this->Height / 2.0f, 1.0f);
|
||||
Text->RenderText("Press W or S to select level", 245.0f, this->Height / 2.0f + 20.0f, 0.75f);
|
||||
}
|
||||
if (this->State == GAME_WIN)
|
||||
{
|
||||
Text->RenderText("You WON!!!", 320.0f, this->Height / 2.0f - 20.0f, 1.0f, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
Text->RenderText("Press ENTER to retry or ESC to quit", 130.0f, this->Height / 2.0f, 1.0f, glm::vec3(1.0f, 1.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::ResetLevel()
|
||||
{
|
||||
if (this->Level == 0)
|
||||
this->Levels[0].Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 1)
|
||||
this->Levels[1].Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 2)
|
||||
this->Levels[2].Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 3)
|
||||
this->Levels[3].Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
|
||||
this->Lives = 3;
|
||||
}
|
||||
|
||||
void Game::ResetPlayer()
|
||||
{
|
||||
// reset player/ball stats
|
||||
Player->Size = PLAYER_SIZE;
|
||||
Player->Position = glm::vec2(this->Width / 2.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Ball->Reset(Player->Position + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -(BALL_RADIUS * 2.0f)), INITIAL_BALL_VELOCITY);
|
||||
// also disable all active powerups
|
||||
Effects->Chaos = Effects->Confuse = false;
|
||||
Ball->PassThrough = Ball->Sticky = false;
|
||||
Player->Color = glm::vec3(1.0f);
|
||||
Ball->Color = glm::vec3(1.0f);
|
||||
}
|
||||
|
||||
|
||||
// powerups
|
||||
bool IsOtherPowerUpActive(std::vector<PowerUp> &powerUps, std::string type);
|
||||
|
||||
void Game::UpdatePowerUps(float dt)
|
||||
{
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
{
|
||||
powerUp.Position += powerUp.Velocity * dt;
|
||||
if (powerUp.Activated)
|
||||
{
|
||||
powerUp.Duration -= dt;
|
||||
|
||||
if (powerUp.Duration <= 0.0f)
|
||||
{
|
||||
// remove powerup from list (will later be removed)
|
||||
powerUp.Activated = false;
|
||||
// deactivate effects
|
||||
if (powerUp.Type == "sticky")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "sticky"))
|
||||
{ // only reset if no other PowerUp of type sticky is active
|
||||
Ball->Sticky = false;
|
||||
Player->Color = glm::vec3(1.0f);
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "pass-through")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "pass-through"))
|
||||
{ // only reset if no other PowerUp of type pass-through is active
|
||||
Ball->PassThrough = false;
|
||||
Ball->Color = glm::vec3(1.0f);
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "confuse")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "confuse"))
|
||||
{ // only reset if no other PowerUp of type confuse is active
|
||||
Effects->Confuse = false;
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "chaos")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "chaos"))
|
||||
{ // only reset if no other PowerUp of type chaos is active
|
||||
Effects->Chaos = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove all PowerUps from vector that are destroyed AND !activated (thus either off the map or finished)
|
||||
// Note we use a lambda expression to remove each PowerUp which is destroyed and not activated
|
||||
this->PowerUps.erase(std::remove_if(this->PowerUps.begin(), this->PowerUps.end(),
|
||||
[](const PowerUp &powerUp) { return powerUp.Destroyed && !powerUp.Activated; }
|
||||
), this->PowerUps.end());
|
||||
}
|
||||
|
||||
bool ShouldSpawn(unsigned int chance)
|
||||
{
|
||||
unsigned int random = rand() % chance;
|
||||
return random == 0;
|
||||
}
|
||||
void Game::SpawnPowerUps(GameObject &block)
|
||||
{
|
||||
if (ShouldSpawn(75)) // 1 in 75 chance
|
||||
this->PowerUps.push_back(PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.Position, ResourceManager::GetTexture("powerup_speed")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.Position, ResourceManager::GetTexture("powerup_sticky")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.Position, ResourceManager::GetTexture("powerup_passthrough")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.Position, ResourceManager::GetTexture("powerup_increase")));
|
||||
if (ShouldSpawn(15)) // Negative powerups should spawn more often
|
||||
this->PowerUps.push_back(PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_confuse")));
|
||||
if (ShouldSpawn(15))
|
||||
this->PowerUps.push_back(PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_chaos")));
|
||||
}
|
||||
|
||||
void ActivatePowerUp(PowerUp &powerUp)
|
||||
{
|
||||
if (powerUp.Type == "speed")
|
||||
{
|
||||
Ball->Velocity *= 1.2;
|
||||
}
|
||||
else if (powerUp.Type == "sticky")
|
||||
{
|
||||
Ball->Sticky = true;
|
||||
Player->Color = glm::vec3(1.0f, 0.5f, 1.0f);
|
||||
}
|
||||
else if (powerUp.Type == "pass-through")
|
||||
{
|
||||
Ball->PassThrough = 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 = true; // only activate if chaos wasn't already active
|
||||
}
|
||||
else if (powerUp.Type == "chaos")
|
||||
{
|
||||
if (!Effects->Confuse)
|
||||
Effects->Chaos = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOtherPowerUpActive(std::vector<PowerUp> &powerUps, std::string type)
|
||||
{
|
||||
// Check if another PowerUp of the same type is still active
|
||||
// in which case we don't disable its effect (yet)
|
||||
for (const PowerUp &powerUp : powerUps)
|
||||
{
|
||||
if (powerUp.Activated)
|
||||
if (powerUp.Type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// collision detection
|
||||
bool CheckCollision(GameObject &one, GameObject &two);
|
||||
Collision CheckCollision(BallObject &one, GameObject &two);
|
||||
Direction VectorDirection(glm::vec2 closest);
|
||||
|
||||
void Game::DoCollisions()
|
||||
{
|
||||
for (GameObject &box : this->Levels[this->Level].Bricks)
|
||||
{
|
||||
if (!box.Destroyed)
|
||||
{
|
||||
Collision collision = CheckCollision(*Ball, box);
|
||||
if (std::get<0>(collision)) // if collision is true
|
||||
{
|
||||
// destroy block if not solid
|
||||
if (!box.IsSolid)
|
||||
{
|
||||
box.Destroyed = true;
|
||||
this->SpawnPowerUps(box);
|
||||
SoundEngine->play2D("audio/bleep.mp3", false);
|
||||
}
|
||||
else
|
||||
{ // if block is solid, enable shake effect
|
||||
ShakeTime = 0.05f;
|
||||
Effects->Shake = true;
|
||||
SoundEngine->play2D("audio/solid.wav", false);
|
||||
}
|
||||
// collision resolution
|
||||
Direction dir = std::get<1>(collision);
|
||||
glm::vec2 diff_vector = std::get<2>(collision);
|
||||
if (!(Ball->PassThrough && !box.IsSolid)) // don't do collision resolution on non-solid bricks if pass-through is activated
|
||||
{
|
||||
if (dir == LEFT || dir == RIGHT) // horizontal collision
|
||||
{
|
||||
Ball->Velocity.x = -Ball->Velocity.x; // reverse horizontal velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.x);
|
||||
if (dir == LEFT)
|
||||
Ball->Position.x += penetration; // move ball to right
|
||||
else
|
||||
Ball->Position.x -= penetration; // move ball to left;
|
||||
}
|
||||
else // vertical collision
|
||||
{
|
||||
Ball->Velocity.y = -Ball->Velocity.y; // reverse vertical velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.y);
|
||||
if (dir == UP)
|
||||
Ball->Position.y -= penetration; // move ball bback up
|
||||
else
|
||||
Ball->Position.y += penetration; // move ball back down
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also check collisions on PowerUps and if so, activate them
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
{
|
||||
if (!powerUp.Destroyed)
|
||||
{
|
||||
// first check if powerup passed bottom edge, if so: keep as inactive and destroy
|
||||
if (powerUp.Position.y >= this->Height)
|
||||
powerUp.Destroyed = true;
|
||||
|
||||
if (CheckCollision(*Player, powerUp))
|
||||
{ // collided with player, now activate powerup
|
||||
ActivatePowerUp(powerUp);
|
||||
powerUp.Destroyed = true;
|
||||
powerUp.Activated = true;
|
||||
SoundEngine->play2D("audio/powerup.wav", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// and finally check collisions for player pad (unless stuck)
|
||||
Collision result = CheckCollision(*Ball, *Player);
|
||||
if (!Ball->Stuck && std::get<0>(result))
|
||||
{
|
||||
// check where it hit the board, and change velocity based on where it hit the board
|
||||
float centerBoard = Player->Position.x + Player->Size.x / 2.0f;
|
||||
float distance = (Ball->Position.x + Ball->Radius) - centerBoard;
|
||||
float percentage = distance / (Player->Size.x / 2.0f);
|
||||
// then move accordingly
|
||||
float strength = 2.0f;
|
||||
glm::vec2 oldVelocity = Ball->Velocity;
|
||||
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
|
||||
//Ball->Velocity.y = -Ball->Velocity.y;
|
||||
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
|
||||
// fix sticky paddle
|
||||
Ball->Velocity.y = -1.0f * abs(Ball->Velocity.y);
|
||||
|
||||
// if Sticky powerup is activated, also stick ball to paddle once new velocity vectors were calculated
|
||||
Ball->Stuck = Ball->Sticky;
|
||||
|
||||
SoundEngine->play2D("audio/bleep.wav", false);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
|
||||
{
|
||||
// collision x-axis?
|
||||
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
|
||||
two.Position.x + two.Size.x >= one.Position.x;
|
||||
// collision y-axis?
|
||||
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
|
||||
two.Position.y + two.Size.y >= one.Position.y;
|
||||
// collision only if on both axes
|
||||
return collisionX && collisionY;
|
||||
}
|
||||
|
||||
Collision CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
|
||||
{
|
||||
// get center point circle first
|
||||
glm::vec2 center(one.Position + one.Radius);
|
||||
// calculate AABB info (center, half-extents)
|
||||
glm::vec2 aabb_half_extents(two.Size.x / 2.0f, two.Size.y / 2.0f);
|
||||
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
|
||||
// get difference vector between both centers
|
||||
glm::vec2 difference = center - aabb_center;
|
||||
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
|
||||
// now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
|
||||
glm::vec2 closest = aabb_center + clamped;
|
||||
// now retrieve vector between center circle and closest point AABB and check if length < radius
|
||||
difference = closest - center;
|
||||
|
||||
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
|
||||
return std::make_tuple(true, VectorDirection(difference), difference);
|
||||
else
|
||||
return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// calculates which direction a vector is facing (N,E,S or W)
|
||||
Direction VectorDirection(glm::vec2 target)
|
||||
{
|
||||
glm::vec2 compass[] = {
|
||||
glm::vec2(0.0f, 1.0f), // up
|
||||
glm::vec2(1.0f, 0.0f), // right
|
||||
glm::vec2(0.0f, -1.0f), // down
|
||||
glm::vec2(-1.0f, 0.0f) // left
|
||||
};
|
||||
float max = 0.0f;
|
||||
unsigned int best_match = -1;
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
float dot_product = glm::dot(glm::normalize(target), compass[i]);
|
||||
if (dot_product > max)
|
||||
{
|
||||
max = dot_product;
|
||||
best_match = i;
|
||||
}
|
||||
}
|
||||
return (Direction)best_match;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "game_level.h"
|
||||
#include "power_up.h"
|
||||
|
||||
// Represents the current state of the game
|
||||
enum GameState {
|
||||
GAME_ACTIVE,
|
||||
GAME_MENU,
|
||||
GAME_WIN
|
||||
};
|
||||
|
||||
// Represents the four possible (collision) directions
|
||||
enum Direction {
|
||||
UP,
|
||||
RIGHT,
|
||||
DOWN,
|
||||
LEFT
|
||||
};
|
||||
// Defines a Collision typedef that represents collision data
|
||||
typedef std::tuple<bool, Direction, glm::vec2> Collision; // <collision?, what direction?, difference vector center - closest point>
|
||||
|
||||
// Initial size of the player paddle
|
||||
const glm::vec2 PLAYER_SIZE(100.0f, 20.0f);
|
||||
// Initial velocity of the player paddle
|
||||
const float PLAYER_VELOCITY(500.0f);
|
||||
// Initial velocity of the Ball
|
||||
const glm::vec2 INITIAL_BALL_VELOCITY(100.0f, -350.0f);
|
||||
// Radius of the ball object
|
||||
const float BALL_RADIUS = 12.5f;
|
||||
|
||||
// Game holds all game-related state and functionality.
|
||||
// Combines all game-related data into a single class for
|
||||
// easy access to each of the components and manageability.
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
// game state
|
||||
GameState State;
|
||||
bool Keys[1024];
|
||||
bool KeysProcessed[1024];
|
||||
unsigned int Width, Height;
|
||||
std::vector<GameLevel> Levels;
|
||||
std::vector<PowerUp> PowerUps;
|
||||
unsigned int Level;
|
||||
unsigned int Lives;
|
||||
// constructor/destructor
|
||||
Game(unsigned int width, unsigned int height);
|
||||
~Game();
|
||||
// initialize game state (load all shaders/textures/levels)
|
||||
void Init();
|
||||
// game loop
|
||||
void ProcessInput(float dt);
|
||||
void Update(float dt);
|
||||
void Render();
|
||||
void DoCollisions();
|
||||
// reset
|
||||
void ResetLevel();
|
||||
void ResetPlayer();
|
||||
// powerups
|
||||
void SpawnPowerUps(GameObject &block);
|
||||
void UpdatePowerUps(float dt);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,93 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 char *file, unsigned int levelWidth, unsigned int levelHeight)
|
||||
{
|
||||
// clear old data
|
||||
this->Bricks.clear();
|
||||
// load from file
|
||||
unsigned int tileCode;
|
||||
GameLevel level;
|
||||
std::string line;
|
||||
std::ifstream fstream(file);
|
||||
std::vector<std::vector<unsigned int>> tileData;
|
||||
if (fstream)
|
||||
{
|
||||
while (std::getline(fstream, line)) // read each line from level file
|
||||
{
|
||||
std::istringstream sstream(line);
|
||||
std::vector<unsigned int> 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);
|
||||
}
|
||||
|
||||
bool GameLevel::IsCompleted()
|
||||
{
|
||||
for (GameObject &tile : this->Bricks)
|
||||
if (!tile.IsSolid && !tile.Destroyed)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameLevel::init(std::vector<std::vector<unsigned int>> tileData, unsigned int levelWidth, unsigned int levelHeight)
|
||||
{
|
||||
// calculate dimensions
|
||||
unsigned int height = tileData.size();
|
||||
unsigned int width = tileData[0].size(); // note we can index vector at [0] since this function is only called if height > 0
|
||||
float unit_width = levelWidth / static_cast<float>(width), unit_height = levelHeight / height;
|
||||
// initialize level tiles based on tileData
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
for (unsigned int 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 = 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef GAMELEVEL_H
|
||||
#define GAMELEVEL_H
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "game_object.h"
|
||||
#include "sprite_renderer.h"
|
||||
#include "resource_manager.h"
|
||||
|
||||
|
||||
/// GameLevel holds all Tiles as part of a Breakout level and
|
||||
/// hosts functionality to Load/render levels from the harddisk.
|
||||
class GameLevel
|
||||
{
|
||||
public:
|
||||
// level state
|
||||
std::vector<GameObject> Bricks;
|
||||
// constructor
|
||||
GameLevel() { }
|
||||
// loads level from file
|
||||
void Load(const char *file, unsigned int levelWidth, unsigned int levelHeight);
|
||||
// render level
|
||||
void Draw(SpriteRenderer &renderer);
|
||||
// check if the level is completed (all non-solid tiles are destroyed)
|
||||
bool IsCompleted();
|
||||
private:
|
||||
// initialize level from tile data
|
||||
void init(std::vector<std::vector<unsigned int>> tileData, unsigned int levelWidth, unsigned int levelHeight);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.0f, 0.0f), Size(1.0f, 1.0f), 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);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef GAMEOBJECT_H
|
||||
#define GAMEOBJECT_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "texture.h"
|
||||
#include "sprite_renderer.h"
|
||||
|
||||
|
||||
// Container object for holding all state relevant for a single
|
||||
// game object entity. Each object in the game likely needs the
|
||||
// minimal of state as described within GameObject.
|
||||
class GameObject
|
||||
{
|
||||
public:
|
||||
// object state
|
||||
glm::vec2 Position, Size, Velocity;
|
||||
glm::vec3 Color;
|
||||
float Rotation;
|
||||
bool IsSolid;
|
||||
bool Destroyed;
|
||||
// render state
|
||||
Texture2D Sprite;
|
||||
// constructor(s)
|
||||
GameObject();
|
||||
GameObject(glm::vec2 pos, glm::vec2 size, Texture2D sprite, glm::vec3 color = glm::vec3(1.0f), glm::vec2 velocity = glm::vec2(0.0f, 0.0f));
|
||||
// draw sprite
|
||||
virtual void Draw(SpriteRenderer &renderer);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +0,0 @@
|
||||
1 2 1 2 1 2 1 2 1 2 1 2 1
|
||||
2 2 2 2 2 2 2 2 2 2 2 2 2
|
||||
2 1 3 1 4 1 5 1 4 1 3 1 2
|
||||
2 3 3 4 4 5 5 5 4 4 3 3 2
|
||||
2 1 3 1 4 1 5 1 4 1 3 1 2
|
||||
2 2 3 3 4 4 5 4 4 3 3 2 2
|
||||
@@ -1,8 +0,0 @@
|
||||
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
||||
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
|
||||
4 4 4 4 4 0 0 0 0 0 4 4 4 4 4
|
||||
4 1 4 1 4 0 0 1 0 0 4 1 4 1 4
|
||||
3 3 3 3 3 0 0 0 0 0 3 3 3 3 3
|
||||
3 3 1 3 3 3 3 3 3 3 3 3 1 3 3
|
||||
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
|
||||
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
|
||||
@@ -1,9 +0,0 @@
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
0 0 2 0 0 0 0 0 0 0 2 0 0
|
||||
0 0 0 2 0 0 0 0 0 2 0 0 0
|
||||
0 0 0 5 5 5 5 5 5 5 0 0 0
|
||||
0 0 5 5 0 5 5 5 0 5 5 0 0
|
||||
0 5 5 5 5 5 5 5 5 5 5 5 0
|
||||
0 3 0 1 1 1 1 1 1 1 0 3 0
|
||||
0 3 0 3 0 0 0 0 0 3 0 3 0
|
||||
0 0 0 0 4 4 0 4 4 0 0 0 0
|
||||
@@ -1,8 +0,0 @@
|
||||
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
|
||||
1 0 5 5 0 5 5 0 5 5 0 5 5 0 1
|
||||
1 5 5 5 5 5 5 5 5 5 5 5 5 5 1
|
||||
1 0 3 3 0 3 3 0 3 3 0 3 3 0 1
|
||||
1 3 3 3 3 3 3 3 3 3 3 3 3 3 1
|
||||
1 0 2 2 0 2 2 0 2 2 0 2 2 0 1
|
||||
1 2 2 2 2 2 2 2 2 2 2 2 2 2 1
|
||||
1 0 1 1 0 1 1 0 1 1 0 1 1 0 1
|
||||
@@ -1,120 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 "particle_generator.h"
|
||||
|
||||
ParticleGenerator::ParticleGenerator(Shader shader, Texture2D texture, unsigned int amount)
|
||||
: shader(shader), texture(texture), amount(amount)
|
||||
{
|
||||
this->init();
|
||||
}
|
||||
|
||||
void ParticleGenerator::Update(float dt, GameObject &object, unsigned int newParticles, glm::vec2 offset)
|
||||
{
|
||||
// add new particles
|
||||
for (unsigned int i = 0; i < newParticles; ++i)
|
||||
{
|
||||
int unusedParticle = this->firstUnusedParticle();
|
||||
this->respawnParticle(this->particles[unusedParticle], object, offset);
|
||||
}
|
||||
// update all particles
|
||||
for (unsigned int i = 0; i < this->amount; ++i)
|
||||
{
|
||||
Particle &p = this->particles[i];
|
||||
p.Life -= dt; // reduce life
|
||||
if (p.Life > 0.0f)
|
||||
{ // particle is alive, thus update
|
||||
p.Position -= p.Velocity * dt;
|
||||
p.Color.a -= dt * 2.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render all particles
|
||||
void ParticleGenerator::Draw()
|
||||
{
|
||||
// use additive blending to give it a 'glow' effect
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
this->shader.Use();
|
||||
for (Particle particle : this->particles)
|
||||
{
|
||||
if (particle.Life > 0.0f)
|
||||
{
|
||||
this->shader.SetVector2f("offset", particle.Position);
|
||||
this->shader.SetVector4f("color", particle.Color);
|
||||
this->texture.Bind();
|
||||
glBindVertexArray(this->VAO);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
// don't forget to reset to default blending mode
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
void ParticleGenerator::init()
|
||||
{
|
||||
// set up mesh and attribute properties
|
||||
unsigned int VBO;
|
||||
float particle_quad[] = {
|
||||
0.0f, 1.0f, 0.0f, 1.0f,
|
||||
1.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
|
||||
0.0f, 1.0f, 0.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 0.0f, 1.0f, 0.0f
|
||||
};
|
||||
glGenVertexArrays(1, &this->VAO);
|
||||
glGenBuffers(1, &VBO);
|
||||
glBindVertexArray(this->VAO);
|
||||
// fill mesh buffer
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(particle_quad), particle_quad, GL_STATIC_DRAW);
|
||||
// set mesh attributes
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
// create this->amount default particle instances
|
||||
for (unsigned int i = 0; i < this->amount; ++i)
|
||||
this->particles.push_back(Particle());
|
||||
}
|
||||
|
||||
// stores the index of the last particle used (for quick access to next dead particle)
|
||||
unsigned int lastUsedParticle = 0;
|
||||
unsigned int ParticleGenerator::firstUnusedParticle()
|
||||
{
|
||||
// first search from last used particle, this will usually return almost instantly
|
||||
for (unsigned int i = lastUsedParticle; i < this->amount; ++i){
|
||||
if (this->particles[i].Life <= 0.0f){
|
||||
lastUsedParticle = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// otherwise, do a linear search
|
||||
for (unsigned int i = 0; i < lastUsedParticle; ++i){
|
||||
if (this->particles[i].Life <= 0.0f){
|
||||
lastUsedParticle = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// all particles are taken, override the first one (note that if it repeatedly hits this case, more particles should be reserved)
|
||||
lastUsedParticle = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ParticleGenerator::respawnParticle(Particle &particle, GameObject &object, glm::vec2 offset)
|
||||
{
|
||||
float random = ((rand() % 100) - 50) / 10.0f;
|
||||
float rColor = 0.5f + ((rand() % 100) / 100.0f);
|
||||
particle.Position = object.Position + random + offset;
|
||||
particle.Color = glm::vec4(rColor, rColor, rColor, 1.0f);
|
||||
particle.Life = 1.0f;
|
||||
particle.Velocity = object.Velocity * 0.1f;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef PARTICLE_GENERATOR_H
|
||||
#define PARTICLE_GENERATOR_H
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "shader.h"
|
||||
#include "texture.h"
|
||||
#include "game_object.h"
|
||||
|
||||
|
||||
// Represents a single particle and its state
|
||||
struct Particle {
|
||||
glm::vec2 Position, Velocity;
|
||||
glm::vec4 Color;
|
||||
float Life;
|
||||
|
||||
Particle() : Position(0.0f), Velocity(0.0f), Color(1.0f), Life(0.0f) { }
|
||||
};
|
||||
|
||||
|
||||
// ParticleGenerator acts as a container for rendering a large number of
|
||||
// particles by repeatedly spawning and updating particles and killing
|
||||
// them after a given amount of time.
|
||||
class ParticleGenerator
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
ParticleGenerator(Shader shader, Texture2D texture, unsigned int amount);
|
||||
// update all particles
|
||||
void Update(float dt, GameObject &object, unsigned int newParticles, glm::vec2 offset = glm::vec2(0.0f, 0.0f));
|
||||
// render all particles
|
||||
void Draw();
|
||||
private:
|
||||
// state
|
||||
std::vector<Particle> particles;
|
||||
unsigned int amount;
|
||||
// render state
|
||||
Shader shader;
|
||||
Texture2D texture;
|
||||
unsigned int VAO;
|
||||
// initializes buffer and vertex attributes
|
||||
void init();
|
||||
// returns the first Particle index that's currently unused e.g. Life <= 0.0f or 0 if no particle is currently inactive
|
||||
unsigned int firstUnusedParticle();
|
||||
// respawns particle
|
||||
void respawnParticle(Particle &particle, GameObject &object, glm::vec2 offset = glm::vec2(0.0f, 0.0f));
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,120 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 "post_processor.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
PostProcessor::PostProcessor(Shader shader, unsigned int width, unsigned int height)
|
||||
: PostProcessingShader(shader), Texture(), Width(width), Height(height), Confuse(false), Chaos(false), Shake(false)
|
||||
{
|
||||
// initialize renderbuffer/framebuffer object
|
||||
glGenFramebuffers(1, &this->MSFBO);
|
||||
glGenFramebuffers(1, &this->FBO);
|
||||
glGenRenderbuffers(1, &this->RBO);
|
||||
// initialize renderbuffer storage with a multisampled color buffer (don't need a depth/stencil buffer)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->MSFBO);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, this->RBO);
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGB, width, height); // allocate storage for render buffer object
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, this->RBO); // attach MS render buffer object to framebuffer
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
std::cout << "ERROR::POSTPROCESSOR: Failed to initialize MSFBO" << std::endl;
|
||||
// also initialize the FBO/texture to blit multisampled color-buffer to; used for shader operations (for postprocessing effects)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->FBO);
|
||||
this->Texture.Generate(width, height, NULL);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->Texture.ID, 0); // attach texture to framebuffer as its color attachment
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
std::cout << "ERROR::POSTPROCESSOR: Failed to initialize FBO" << std::endl;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
// initialize render data and uniforms
|
||||
this->initRenderData();
|
||||
this->PostProcessingShader.SetInteger("scene", 0, true);
|
||||
float offset = 1.0f / 300.0f;
|
||||
float offsets[9][2] = {
|
||||
{ -offset, offset }, // top-left
|
||||
{ 0.0f, offset }, // top-center
|
||||
{ offset, offset }, // top-right
|
||||
{ -offset, 0.0f }, // center-left
|
||||
{ 0.0f, 0.0f }, // center-center
|
||||
{ offset, 0.0f }, // center - right
|
||||
{ -offset, -offset }, // bottom-left
|
||||
{ 0.0f, -offset }, // bottom-center
|
||||
{ offset, -offset } // bottom-right
|
||||
};
|
||||
glUniform2fv(glGetUniformLocation(this->PostProcessingShader.ID, "offsets"), 9, (float*)offsets);
|
||||
int edge_kernel[9] = {
|
||||
-1, -1, -1,
|
||||
-1, 8, -1,
|
||||
-1, -1, -1
|
||||
};
|
||||
glUniform1iv(glGetUniformLocation(this->PostProcessingShader.ID, "edge_kernel"), 9, edge_kernel);
|
||||
float blur_kernel[9] = {
|
||||
1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f,
|
||||
2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f,
|
||||
1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f
|
||||
};
|
||||
glUniform1fv(glGetUniformLocation(this->PostProcessingShader.ID, "blur_kernel"), 9, blur_kernel);
|
||||
}
|
||||
|
||||
void PostProcessor::BeginRender()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->MSFBO);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
void PostProcessor::EndRender()
|
||||
{
|
||||
// now resolve multisampled color-buffer into intermediate FBO to store to texture
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, this->MSFBO);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->FBO);
|
||||
glBlitFramebuffer(0, 0, this->Width, this->Height, 0, 0, this->Width, this->Height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0); // binds both READ and WRITE framebuffer to default framebuffer
|
||||
}
|
||||
|
||||
void PostProcessor::Render(float time)
|
||||
{
|
||||
// set uniforms/options
|
||||
this->PostProcessingShader.Use();
|
||||
this->PostProcessingShader.SetFloat("time", time);
|
||||
this->PostProcessingShader.SetInteger("confuse", this->Confuse);
|
||||
this->PostProcessingShader.SetInteger("chaos", this->Chaos);
|
||||
this->PostProcessingShader.SetInteger("shake", this->Shake);
|
||||
// render textured quad
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
this->Texture.Bind();
|
||||
glBindVertexArray(this->VAO);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void PostProcessor::initRenderData()
|
||||
{
|
||||
// configure VAO/VBO
|
||||
unsigned int VBO;
|
||||
float vertices[] = {
|
||||
// pos // tex
|
||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||
|
||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
glGenVertexArrays(1, &this->VAO);
|
||||
glGenBuffers(1, &VBO);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(this->VAO);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef POST_PROCESSOR_H
|
||||
#define POST_PROCESSOR_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "texture.h"
|
||||
#include "sprite_renderer.h"
|
||||
#include "shader.h"
|
||||
|
||||
|
||||
// PostProcessor hosts all PostProcessing effects for the Breakout
|
||||
// Game. It renders the game on a textured quad after which one can
|
||||
// enable specific effects by enabling either the Confuse, Chaos or
|
||||
// Shake boolean.
|
||||
// It is required to call BeginRender() before rendering the game
|
||||
// and EndRender() after rendering the game for the class to work.
|
||||
class PostProcessor
|
||||
{
|
||||
public:
|
||||
// state
|
||||
Shader PostProcessingShader;
|
||||
Texture2D Texture;
|
||||
unsigned int Width, Height;
|
||||
// options
|
||||
bool Confuse, Chaos, Shake;
|
||||
// constructor
|
||||
PostProcessor(Shader shader, unsigned int width, unsigned int height);
|
||||
// prepares the postprocessor's framebuffer operations before rendering the game
|
||||
void BeginRender();
|
||||
// should be called after rendering the game, so it stores all the rendered data into a texture object
|
||||
void EndRender();
|
||||
// renders the PostProcessor texture quad (as a screen-encompassing large sprite)
|
||||
void Render(float time);
|
||||
private:
|
||||
// render state
|
||||
unsigned int MSFBO, FBO; // MSFBO = Multisampled FBO. FBO is regular, used for blitting MS color-buffer to texture
|
||||
unsigned int RBO; // RBO is used for multisampled color buffer
|
||||
unsigned int VAO;
|
||||
// initialize quad for rendering postprocessing texture
|
||||
void initRenderData();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,41 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef POWER_UP_H
|
||||
#define POWER_UP_H
|
||||
#include <string>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "game_object.h"
|
||||
|
||||
|
||||
// The size of a PowerUp block
|
||||
const glm::vec2 POWERUP_SIZE(60.0f, 20.0f);
|
||||
// Velocity a PowerUp block has when spawned
|
||||
const glm::vec2 VELOCITY(0.0f, 150.0f);
|
||||
|
||||
|
||||
// PowerUp inherits its state and rendering functions from
|
||||
// GameObject but also holds extra information to state its
|
||||
// active duration and whether it is activated or not.
|
||||
// The type of PowerUp is stored as a string.
|
||||
class PowerUp : public GameObject
|
||||
{
|
||||
public:
|
||||
// powerup state
|
||||
std::string Type;
|
||||
float Duration;
|
||||
bool Activated;
|
||||
// constructor
|
||||
PowerUp(std::string type, glm::vec3 color, float duration, glm::vec2 position, Texture2D texture)
|
||||
: GameObject(position, POWERUP_SIZE, texture, color, VELOCITY), Type(type), Duration(duration), Activated() { }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,128 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "game.h"
|
||||
#include "resource_manager.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// GLFW function declerations
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
|
||||
|
||||
// The Width of the screen
|
||||
const unsigned int SCREEN_WIDTH = 800;
|
||||
// The height of the screen
|
||||
const unsigned int SCREEN_HEIGHT = 600;
|
||||
|
||||
Game Breakout(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
glfwWindowHint(GLFW_RESIZABLE, false);
|
||||
|
||||
GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Breakout", nullptr, nullptr);
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
// glad: load all OpenGL function pointers
|
||||
// ---------------------------------------
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
|
||||
{
|
||||
std::cout << "Failed to initialize GLAD" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
glfwSetKeyCallback(window, key_callback);
|
||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
|
||||
// OpenGL configuration
|
||||
// --------------------
|
||||
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// initialize game
|
||||
// ---------------
|
||||
Breakout.Init();
|
||||
|
||||
// deltaTime variables
|
||||
// -------------------
|
||||
float deltaTime = 0.0f;
|
||||
float lastFrame = 0.0f;
|
||||
|
||||
// start game within menu state
|
||||
// ----------------------------
|
||||
Breakout.State = GAME_MENU;
|
||||
|
||||
while (!glfwWindowShouldClose(window))
|
||||
{
|
||||
// calculate delta time
|
||||
// --------------------
|
||||
float currentFrame = glfwGetTime();
|
||||
deltaTime = currentFrame - lastFrame;
|
||||
lastFrame = currentFrame;
|
||||
glfwPollEvents();
|
||||
|
||||
// manage user input
|
||||
// -----------------
|
||||
Breakout.ProcessInput(deltaTime);
|
||||
|
||||
// update game state
|
||||
// -----------------
|
||||
Breakout.Update(deltaTime);
|
||||
|
||||
// render
|
||||
// ------
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
Breakout.Render();
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
// delete all resources as loaded using the resource manager
|
||||
// ---------------------------------------------------------
|
||||
ResourceManager::Clear();
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
|
||||
{
|
||||
// when a user presses the escape key, we set the WindowShouldClose property to true, closing the application
|
||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
if (key >= 0 && key < 1024)
|
||||
{
|
||||
if (action == GLFW_PRESS)
|
||||
Breakout.Keys[key] = true;
|
||||
else if (action == GLFW_RELEASE)
|
||||
{
|
||||
Breakout.Keys[key] = false;
|
||||
Breakout.KeysProcessed[key] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
// make sure the viewport matches the new window dimensions; note that width and
|
||||
// height will be significantly larger than specified on retina displays.
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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"
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Game::Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
// Represents the current state of the game
|
||||
enum GameState {
|
||||
GAME_ACTIVE,
|
||||
GAME_MENU,
|
||||
GAME_WIN
|
||||
};
|
||||
|
||||
// Game holds all game-related state and functionality.
|
||||
// Combines all game-related data into a single class for
|
||||
// easy access to each of the components and manageability.
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
// game state
|
||||
GameState State;
|
||||
bool Keys[1024];
|
||||
unsigned int Width, Height;
|
||||
// constructor/destructor
|
||||
Game(unsigned int width, unsigned int height);
|
||||
~Game();
|
||||
// initialize game state (load all shaders/textures/levels)
|
||||
void Init();
|
||||
// game loop
|
||||
void ProcessInput(float dt);
|
||||
void Update(float dt);
|
||||
void Render();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,125 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "game.h"
|
||||
#include "resource_manager.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// GLFW function declerations
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
|
||||
|
||||
// The Width of the screen
|
||||
const unsigned int SCREEN_WIDTH = 800;
|
||||
// The height of the screen
|
||||
const unsigned int SCREEN_HEIGHT = 600;
|
||||
|
||||
Game Breakout(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
glfwWindowHint(GLFW_RESIZABLE, false);
|
||||
|
||||
GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Breakout", nullptr, nullptr);
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
// glad: load all OpenGL function pointers
|
||||
// ---------------------------------------
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
|
||||
{
|
||||
std::cout << "Failed to initialize GLAD" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
glfwSetKeyCallback(window, key_callback);
|
||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
|
||||
// OpenGL configuration
|
||||
// --------------------
|
||||
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// initialize game
|
||||
// ---------------
|
||||
Breakout.Init();
|
||||
|
||||
// deltaTime variables
|
||||
// -------------------
|
||||
float deltaTime = 0.0f;
|
||||
float lastFrame = 0.0f;
|
||||
|
||||
// start game within menu state
|
||||
// ----------------------------
|
||||
Breakout.State = GAME_MENU;
|
||||
|
||||
while (!glfwWindowShouldClose(window))
|
||||
{
|
||||
// calculate delta time
|
||||
// --------------------
|
||||
float currentFrame = glfwGetTime();
|
||||
deltaTime = currentFrame - lastFrame;
|
||||
lastFrame = currentFrame;
|
||||
glfwPollEvents();
|
||||
|
||||
// manage user input
|
||||
// -----------------
|
||||
Breakout.ProcessInput(deltaTime);
|
||||
|
||||
// update game state
|
||||
// -----------------
|
||||
Breakout.Update(deltaTime);
|
||||
|
||||
// render
|
||||
// ------
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
Breakout.Render();
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
// delete all resources as loaded using the resource manager
|
||||
// ---------------------------------------------------------
|
||||
ResourceManager::Clear();
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
|
||||
{
|
||||
// when a user presses the escape key, we set the WindowShouldClose property to true, closing the application
|
||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
if (key >= 0 && key < 1024)
|
||||
{
|
||||
if (action == GLFW_PRESS)
|
||||
Breakout.Keys[key] = true;
|
||||
else if (action == GLFW_RELEASE)
|
||||
Breakout.Keys[key] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
// make sure the viewport matches the new window dimensions; note that width and
|
||||
// height will be significantly larger than specified on retina displays.
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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"
|
||||
|
||||
|
||||
// Game-related State data
|
||||
SpriteRenderer *Renderer;
|
||||
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete Renderer;
|
||||
}
|
||||
|
||||
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<float>(this->Width),
|
||||
static_cast<float>(this->Height), 0.0f, -1.0f, 1.0f);
|
||||
ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
|
||||
ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
// load textures
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
Renderer->DrawSprite(ResourceManager::GetTexture("face"), glm::vec2(200.0f, 200.0f), glm::vec2(300.0f, 400.0f), 45.0f, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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(unsigned int width, unsigned int 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<float>(this->Width),
|
||||
static_cast<float>(this->Height), 0.0f, -1.0f, 1.0f);
|
||||
ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
|
||||
ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
// load textures
|
||||
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
ResourceManager::LoadTexture("textures/block.png", false, "block");
|
||||
ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid");
|
||||
ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
|
||||
// load levels
|
||||
GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
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.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
if (this->State == GAME_ACTIVE)
|
||||
{
|
||||
float velocity = PLAYER_VELOCITY * dt;
|
||||
// move playerboard
|
||||
if (this->Keys[GLFW_KEY_A])
|
||||
{
|
||||
if (Player->Position.x >= 0.0f)
|
||||
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.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f);
|
||||
// draw level
|
||||
this->Levels[this->Level].Draw(*Renderer);
|
||||
// draw player
|
||||
Player->Draw(*Renderer);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "game_level.h"
|
||||
|
||||
// Represents the current state of the game
|
||||
enum GameState {
|
||||
GAME_ACTIVE,
|
||||
GAME_MENU,
|
||||
GAME_WIN
|
||||
};
|
||||
|
||||
// Initial size of the player paddle
|
||||
const glm::vec2 PLAYER_SIZE(100.0f, 20.0f);
|
||||
// Initial velocity of the player paddle
|
||||
const float PLAYER_VELOCITY(500.0f);
|
||||
|
||||
// Game holds all game-related state and functionality.
|
||||
// Combines all game-related data into a single class for
|
||||
// easy access to each of the components and manageability.
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
// game state
|
||||
GameState State;
|
||||
bool Keys[1024];
|
||||
unsigned int Width, Height;
|
||||
std::vector<GameLevel> Levels;
|
||||
unsigned int Level;
|
||||
// constructor/destructor
|
||||
Game(unsigned int width, unsigned int height);
|
||||
~Game();
|
||||
// initialize game state (load all shaders/textures/levels)
|
||||
void Init();
|
||||
// game loop
|
||||
void ProcessInput(float dt);
|
||||
void Update(float dt);
|
||||
void Render();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,51 +0,0 @@
|
||||
/******************************************************************
|
||||
** 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, float radius, glm::vec2 velocity, Texture2D sprite)
|
||||
: GameObject(pos, glm::vec2(radius * 2.0f, radius * 2.0f), sprite, glm::vec3(1.0f), velocity), Radius(radius), Stuck(true) { }
|
||||
|
||||
glm::vec2 BallObject::Move(float dt, unsigned int 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;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef BALLOBJECT_H
|
||||
#define BALLOBJECT_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "game_object.h"
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
// BallObject holds the state of the Ball object inheriting
|
||||
// relevant state data from GameObject. Contains some extra
|
||||
// functionality specific to Breakout's ball object that
|
||||
// were too specific for within GameObject alone.
|
||||
class BallObject : public GameObject
|
||||
{
|
||||
public:
|
||||
// ball state
|
||||
float Radius;
|
||||
bool Stuck;
|
||||
// constructor(s)
|
||||
BallObject();
|
||||
BallObject(glm::vec2 pos, float radius, glm::vec2 velocity, Texture2D sprite);
|
||||
// moves the ball, keeping it constrained within the window bounds (except bottom edge); returns new position
|
||||
glm::vec2 Move(float dt, unsigned int window_width);
|
||||
// resets the ball to original state with given position and velocity
|
||||
void Reset(glm::vec2 position, glm::vec2 velocity);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,262 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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"
|
||||
#include "ball_object.h"
|
||||
|
||||
// Game-related State data
|
||||
SpriteRenderer *Renderer;
|
||||
GameObject *Player;
|
||||
BallObject *Ball;
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete Renderer;
|
||||
delete Player;
|
||||
delete Ball;
|
||||
}
|
||||
|
||||
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<float>(this->Width),
|
||||
static_cast<float>(this->Height), 0.0f, -1.0f, 1.0f);
|
||||
ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
|
||||
ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
// load textures
|
||||
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
ResourceManager::LoadTexture("textures/block.png", false, "block");
|
||||
ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid");
|
||||
ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
|
||||
// load levels
|
||||
GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
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.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
|
||||
glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -BALL_RADIUS * 2.0f);
|
||||
Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("face"));
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
// update objects
|
||||
Ball->Move(dt, this->Width);
|
||||
// check for collisions
|
||||
this->DoCollisions();
|
||||
// check loss condition
|
||||
if (Ball->Position.y >= this->Height) // did ball reach bottom edge?
|
||||
{
|
||||
this->ResetLevel();
|
||||
this->ResetPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
if (this->State == GAME_ACTIVE)
|
||||
{
|
||||
float velocity = PLAYER_VELOCITY * dt;
|
||||
// move playerboard
|
||||
if (this->Keys[GLFW_KEY_A])
|
||||
{
|
||||
if (Player->Position.x >= 0.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
if(this->State == GAME_ACTIVE)
|
||||
{
|
||||
// draw background
|
||||
Renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f);
|
||||
// draw level
|
||||
this->Levels[this->Level].Draw(*Renderer);
|
||||
// draw player
|
||||
Player->Draw(*Renderer);
|
||||
// draw ball
|
||||
Ball->Draw(*Renderer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::ResetLevel()
|
||||
{
|
||||
if (this->Level == 0)
|
||||
this->Levels[0].Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 1)
|
||||
this->Levels[1].Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 2)
|
||||
this->Levels[2].Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 3)
|
||||
this->Levels[3].Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
}
|
||||
|
||||
void Game::ResetPlayer()
|
||||
{
|
||||
// reset player/ball stats
|
||||
Player->Size = PLAYER_SIZE;
|
||||
Player->Position = glm::vec2(this->Width / 2.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Ball->Reset(Player->Position + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -(BALL_RADIUS * 2.0f)), INITIAL_BALL_VELOCITY);
|
||||
}
|
||||
|
||||
// collision detection
|
||||
bool CheckCollision(GameObject &one, GameObject &two);
|
||||
Collision CheckCollision(BallObject &one, GameObject &two);
|
||||
Direction VectorDirection(glm::vec2 closest);
|
||||
|
||||
void Game::DoCollisions()
|
||||
{
|
||||
for (GameObject &box : this->Levels[this->Level].Bricks)
|
||||
{
|
||||
if (!box.Destroyed)
|
||||
{
|
||||
Collision collision = CheckCollision(*Ball, box);
|
||||
if (std::get<0>(collision)) // if collision is true
|
||||
{
|
||||
// destroy block if not solid
|
||||
if (!box.IsSolid)
|
||||
box.Destroyed = true;
|
||||
// collision resolution
|
||||
Direction dir = std::get<1>(collision);
|
||||
glm::vec2 diff_vector = std::get<2>(collision);
|
||||
if (dir == LEFT || dir == RIGHT) // horizontal collision
|
||||
{
|
||||
Ball->Velocity.x = -Ball->Velocity.x; // reverse horizontal velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.x);
|
||||
if (dir == LEFT)
|
||||
Ball->Position.x += penetration; // move ball to right
|
||||
else
|
||||
Ball->Position.x -= penetration; // move ball to left;
|
||||
}
|
||||
else // vertical collision
|
||||
{
|
||||
Ball->Velocity.y = -Ball->Velocity.y; // reverse vertical velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.y);
|
||||
if (dir == UP)
|
||||
Ball->Position.y -= penetration; // move ball bback up
|
||||
else
|
||||
Ball->Position.y += penetration; // move ball back down
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check collisions for player pad (unless stuck)
|
||||
Collision result = CheckCollision(*Ball, *Player);
|
||||
if (!Ball->Stuck && std::get<0>(result))
|
||||
{
|
||||
// check where it hit the board, and change velocity based on where it hit the board
|
||||
float centerBoard = Player->Position.x + Player->Size.x / 2.0f;
|
||||
float distance = (Ball->Position.x + Ball->Radius) - centerBoard;
|
||||
float percentage = distance / (Player->Size.x / 2.0f);
|
||||
// then move accordingly
|
||||
float strength = 2.0f;
|
||||
glm::vec2 oldVelocity = Ball->Velocity;
|
||||
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
|
||||
//Ball->Velocity.y = -Ball->Velocity.y;
|
||||
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
|
||||
// fix sticky paddle
|
||||
Ball->Velocity.y = -1.0f * abs(Ball->Velocity.y);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
|
||||
{
|
||||
// collision x-axis?
|
||||
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
|
||||
two.Position.x + two.Size.x >= one.Position.x;
|
||||
// collision y-axis?
|
||||
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
|
||||
two.Position.y + two.Size.y >= one.Position.y;
|
||||
// collision only if on both axes
|
||||
return collisionX && collisionY;
|
||||
}
|
||||
|
||||
Collision CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
|
||||
{
|
||||
// get center point circle first
|
||||
glm::vec2 center(one.Position + one.Radius);
|
||||
// calculate AABB info (center, half-extents)
|
||||
glm::vec2 aabb_half_extents(two.Size.x / 2.0f, two.Size.y / 2.0f);
|
||||
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
|
||||
// get difference vector between both centers
|
||||
glm::vec2 difference = center - aabb_center;
|
||||
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
|
||||
// now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
|
||||
glm::vec2 closest = aabb_center + clamped;
|
||||
// now retrieve vector between center circle and closest point AABB and check if length < radius
|
||||
difference = closest - center;
|
||||
|
||||
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
|
||||
return std::make_tuple(true, VectorDirection(difference), difference);
|
||||
else
|
||||
return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// calculates which direction a vector is facing (N,E,S or W)
|
||||
Direction VectorDirection(glm::vec2 target)
|
||||
{
|
||||
glm::vec2 compass[] = {
|
||||
glm::vec2(0.0f, 1.0f), // up
|
||||
glm::vec2(1.0f, 0.0f), // right
|
||||
glm::vec2(0.0f, -1.0f), // down
|
||||
glm::vec2(-1.0f, 0.0f) // left
|
||||
};
|
||||
float max = 0.0f;
|
||||
unsigned int best_match = -1;
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
float dot_product = glm::dot(glm::normalize(target), compass[i]);
|
||||
if (dot_product > max)
|
||||
{
|
||||
max = dot_product;
|
||||
best_match = i;
|
||||
}
|
||||
}
|
||||
return (Direction)best_match;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "game_level.h"
|
||||
|
||||
// Represents the current state of the game
|
||||
enum GameState {
|
||||
GAME_ACTIVE,
|
||||
GAME_MENU,
|
||||
GAME_WIN
|
||||
};
|
||||
|
||||
// Represents the four possible (collision) directions
|
||||
enum Direction {
|
||||
UP,
|
||||
RIGHT,
|
||||
DOWN,
|
||||
LEFT
|
||||
};
|
||||
// Defines a Collision typedef that represents collision data
|
||||
typedef std::tuple<bool, Direction, glm::vec2> Collision; // <collision?, what direction?, difference vector center - closest point>
|
||||
|
||||
// Initial size of the player paddle
|
||||
const glm::vec2 PLAYER_SIZE(100.0f, 20.0f);
|
||||
// Initial velocity of the player paddle
|
||||
const float PLAYER_VELOCITY(500.0f);
|
||||
// Initial velocity of the Ball
|
||||
const glm::vec2 INITIAL_BALL_VELOCITY(100.0f, -350.0f);
|
||||
// Radius of the ball object
|
||||
const float BALL_RADIUS = 12.5f;
|
||||
|
||||
// Game holds all game-related state and functionality.
|
||||
// Combines all game-related data into a single class for
|
||||
// easy access to each of the components and manageability.
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
// game state
|
||||
GameState State;
|
||||
bool Keys[1024];
|
||||
unsigned int Width, Height;
|
||||
std::vector<GameLevel> Levels;
|
||||
unsigned int Level;
|
||||
// constructor/destructor
|
||||
Game(unsigned int width, unsigned int height);
|
||||
~Game();
|
||||
// initialize game state (load all shaders/textures/levels)
|
||||
void Init();
|
||||
// game loop
|
||||
void ProcessInput(float dt);
|
||||
void Update(float dt);
|
||||
void Render();
|
||||
void DoCollisions();
|
||||
// reset
|
||||
void ResetLevel();
|
||||
void ResetPlayer();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,274 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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"
|
||||
#include "ball_object.h"
|
||||
#include "particle_generator.h"
|
||||
|
||||
// Game-related State data
|
||||
SpriteRenderer *Renderer;
|
||||
GameObject *Player;
|
||||
BallObject *Ball;
|
||||
ParticleGenerator *Particles;
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete Renderer;
|
||||
delete Player;
|
||||
delete Ball;
|
||||
delete Particles;
|
||||
}
|
||||
|
||||
void Game::Init()
|
||||
{
|
||||
// load shaders
|
||||
ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
|
||||
ResourceManager::LoadShader("shaders/particle.vs", "shaders/particle.frag", nullptr, "particle");
|
||||
// configure shaders
|
||||
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(this->Width),
|
||||
static_cast<float>(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 textures
|
||||
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
ResourceManager::LoadTexture("textures/block.png", false, "block");
|
||||
ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid");
|
||||
ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
|
||||
ResourceManager::LoadTexture("textures/particle.png", true, "particle");
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
Particles = new ParticleGenerator(ResourceManager::GetShader("particle"), ResourceManager::GetTexture("particle"), 500);
|
||||
// load levels
|
||||
GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
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.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
|
||||
glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -BALL_RADIUS * 2.0f);
|
||||
Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("face"));
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
// update objects
|
||||
Ball->Move(dt, this->Width);
|
||||
// check for collisions
|
||||
this->DoCollisions();
|
||||
// update particles
|
||||
Particles->Update(dt, *Ball, 2, glm::vec2(Ball->Radius / 2.0f));
|
||||
// check loss condition
|
||||
if (Ball->Position.y >= this->Height) // did ball reach bottom edge?
|
||||
{
|
||||
this->ResetLevel();
|
||||
this->ResetPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
if (this->State == GAME_ACTIVE)
|
||||
{
|
||||
float velocity = PLAYER_VELOCITY * dt;
|
||||
// move playerboard
|
||||
if (this->Keys[GLFW_KEY_A])
|
||||
{
|
||||
if (Player->Position.x >= 0.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
if(this->State == GAME_ACTIVE)
|
||||
{
|
||||
// draw background
|
||||
Renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f);
|
||||
// draw level
|
||||
this->Levels[this->Level].Draw(*Renderer);
|
||||
// draw player
|
||||
Player->Draw(*Renderer);
|
||||
// draw particles
|
||||
Particles->Draw();
|
||||
// draw ball
|
||||
Ball->Draw(*Renderer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::ResetLevel()
|
||||
{
|
||||
if (this->Level == 0)
|
||||
this->Levels[0].Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 1)
|
||||
this->Levels[1].Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 2)
|
||||
this->Levels[2].Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 3)
|
||||
this->Levels[3].Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
}
|
||||
|
||||
void Game::ResetPlayer()
|
||||
{
|
||||
// reset player/ball stats
|
||||
Player->Size = PLAYER_SIZE;
|
||||
Player->Position = glm::vec2(this->Width / 2.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Ball->Reset(Player->Position + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -(BALL_RADIUS * 2.0f)), INITIAL_BALL_VELOCITY);
|
||||
}
|
||||
|
||||
// collision detection
|
||||
bool CheckCollision(GameObject &one, GameObject &two);
|
||||
Collision CheckCollision(BallObject &one, GameObject &two);
|
||||
Direction VectorDirection(glm::vec2 closest);
|
||||
|
||||
void Game::DoCollisions()
|
||||
{
|
||||
for (GameObject &box : this->Levels[this->Level].Bricks)
|
||||
{
|
||||
if (!box.Destroyed)
|
||||
{
|
||||
Collision collision = CheckCollision(*Ball, box);
|
||||
if (std::get<0>(collision)) // if collision is true
|
||||
{
|
||||
// destroy block if not solid
|
||||
if (!box.IsSolid)
|
||||
box.Destroyed = true;
|
||||
// collision resolution
|
||||
Direction dir = std::get<1>(collision);
|
||||
glm::vec2 diff_vector = std::get<2>(collision);
|
||||
if (dir == LEFT || dir == RIGHT) // horizontal collision
|
||||
{
|
||||
Ball->Velocity.x = -Ball->Velocity.x; // reverse horizontal velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.x);
|
||||
if (dir == LEFT)
|
||||
Ball->Position.x += penetration; // move ball to right
|
||||
else
|
||||
Ball->Position.x -= penetration; // move ball to left;
|
||||
}
|
||||
else // vertical collision
|
||||
{
|
||||
Ball->Velocity.y = -Ball->Velocity.y; // reverse vertical velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.y);
|
||||
if (dir == UP)
|
||||
Ball->Position.y -= penetration; // move ball bback up
|
||||
else
|
||||
Ball->Position.y += penetration; // move ball back down
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check collisions for player pad (unless stuck)
|
||||
Collision result = CheckCollision(*Ball, *Player);
|
||||
if (!Ball->Stuck && std::get<0>(result))
|
||||
{
|
||||
// check where it hit the board, and change velocity based on where it hit the board
|
||||
float centerBoard = Player->Position.x + Player->Size.x / 2.0f;
|
||||
float distance = (Ball->Position.x + Ball->Radius) - centerBoard;
|
||||
float percentage = distance / (Player->Size.x / 2.0f);
|
||||
// then move accordingly
|
||||
float strength = 2.0f;
|
||||
glm::vec2 oldVelocity = Ball->Velocity;
|
||||
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
|
||||
//Ball->Velocity.y = -Ball->Velocity.y;
|
||||
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
|
||||
// fix sticky paddle
|
||||
Ball->Velocity.y = -1.0f * abs(Ball->Velocity.y);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
|
||||
{
|
||||
// collision x-axis?
|
||||
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
|
||||
two.Position.x + two.Size.x >= one.Position.x;
|
||||
// collision y-axis?
|
||||
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
|
||||
two.Position.y + two.Size.y >= one.Position.y;
|
||||
// collision only if on both axes
|
||||
return collisionX && collisionY;
|
||||
}
|
||||
|
||||
Collision CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
|
||||
{
|
||||
// get center point circle first
|
||||
glm::vec2 center(one.Position + one.Radius);
|
||||
// calculate AABB info (center, half-extents)
|
||||
glm::vec2 aabb_half_extents(two.Size.x / 2.0f, two.Size.y / 2.0f);
|
||||
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
|
||||
// get difference vector between both centers
|
||||
glm::vec2 difference = center - aabb_center;
|
||||
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
|
||||
// now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
|
||||
glm::vec2 closest = aabb_center + clamped;
|
||||
// now retrieve vector between center circle and closest point AABB and check if length < radius
|
||||
difference = closest - center;
|
||||
|
||||
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
|
||||
return std::make_tuple(true, VectorDirection(difference), difference);
|
||||
else
|
||||
return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// calculates which direction a vector is facing (N,E,S or W)
|
||||
Direction VectorDirection(glm::vec2 target)
|
||||
{
|
||||
glm::vec2 compass[] = {
|
||||
glm::vec2(0.0f, 1.0f), // up
|
||||
glm::vec2(1.0f, 0.0f), // right
|
||||
glm::vec2(0.0f, -1.0f), // down
|
||||
glm::vec2(-1.0f, 0.0f) // left
|
||||
};
|
||||
float max = 0.0f;
|
||||
unsigned int best_match = -1;
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
float dot_product = glm::dot(glm::normalize(target), compass[i]);
|
||||
if (dot_product > max)
|
||||
{
|
||||
max = dot_product;
|
||||
best_match = i;
|
||||
}
|
||||
}
|
||||
return (Direction)best_match;
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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"
|
||||
#include "ball_object.h"
|
||||
#include "particle_generator.h"
|
||||
#include "post_processor.h"
|
||||
|
||||
// Game-related State data
|
||||
SpriteRenderer *Renderer;
|
||||
GameObject *Player;
|
||||
BallObject *Ball;
|
||||
ParticleGenerator *Particles;
|
||||
PostProcessor *Effects;
|
||||
|
||||
float ShakeTime = 0.0f;
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete Renderer;
|
||||
delete Player;
|
||||
delete Ball;
|
||||
delete Particles;
|
||||
delete Effects;
|
||||
}
|
||||
|
||||
void Game::Init()
|
||||
{
|
||||
// load shaders
|
||||
ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
|
||||
ResourceManager::LoadShader("shaders/particle.vs", "shaders/particle.frag", nullptr, "particle");
|
||||
ResourceManager::LoadShader("shaders/post_processing.vs", "shaders/post_processing.frag", nullptr, "postprocessing");
|
||||
// configure shaders
|
||||
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(this->Width),
|
||||
static_cast<float>(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 textures
|
||||
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
ResourceManager::LoadTexture("textures/block.png", false, "block");
|
||||
ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid");
|
||||
ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
|
||||
ResourceManager::LoadTexture("textures/particle.png", true, "particle");
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
Particles = new ParticleGenerator(ResourceManager::GetShader("particle"), ResourceManager::GetTexture("particle"), 500);
|
||||
Effects = new PostProcessor(ResourceManager::GetShader("postprocessing"), this->Width, this->Height);
|
||||
// load levels
|
||||
GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
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.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
|
||||
glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -BALL_RADIUS * 2.0f);
|
||||
Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("face"));
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
// update objects
|
||||
Ball->Move(dt, this->Width);
|
||||
// check for collisions
|
||||
this->DoCollisions();
|
||||
// update particles
|
||||
Particles->Update(dt, *Ball, 2, glm::vec2(Ball->Radius / 2.0f));
|
||||
// reduce shake time
|
||||
if (ShakeTime > 0.0f)
|
||||
{
|
||||
ShakeTime -= dt;
|
||||
if (ShakeTime <= 0.0f)
|
||||
Effects->Shake = false;
|
||||
}
|
||||
// check loss condition
|
||||
if (Ball->Position.y >= this->Height) // did ball reach bottom edge?
|
||||
{
|
||||
this->ResetLevel();
|
||||
this->ResetPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
if (this->State == GAME_ACTIVE)
|
||||
{
|
||||
float velocity = PLAYER_VELOCITY * dt;
|
||||
// move playerboard
|
||||
if (this->Keys[GLFW_KEY_A])
|
||||
{
|
||||
if (Player->Position.x >= 0.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
if(this->State == GAME_ACTIVE)
|
||||
{
|
||||
// begin rendering to postprocessing framebuffer
|
||||
Effects->BeginRender();
|
||||
// draw background
|
||||
Renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f);
|
||||
// draw level
|
||||
this->Levels[this->Level].Draw(*Renderer);
|
||||
// draw player
|
||||
Player->Draw(*Renderer);
|
||||
// draw particles
|
||||
Particles->Draw();
|
||||
// draw ball
|
||||
Ball->Draw(*Renderer);
|
||||
// end rendering to postprocessing framebuffer
|
||||
Effects->EndRender();
|
||||
// render postprocessing quad
|
||||
Effects->Render(glfwGetTime());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::ResetLevel()
|
||||
{
|
||||
if (this->Level == 0)
|
||||
this->Levels[0].Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 1)
|
||||
this->Levels[1].Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 2)
|
||||
this->Levels[2].Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 3)
|
||||
this->Levels[3].Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
}
|
||||
|
||||
void Game::ResetPlayer()
|
||||
{
|
||||
// reset player/ball stats
|
||||
Player->Size = PLAYER_SIZE;
|
||||
Player->Position = glm::vec2(this->Width / 2.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Ball->Reset(Player->Position + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -(BALL_RADIUS * 2.0f)), INITIAL_BALL_VELOCITY);
|
||||
}
|
||||
|
||||
// collision detection
|
||||
bool CheckCollision(GameObject &one, GameObject &two);
|
||||
Collision CheckCollision(BallObject &one, GameObject &two);
|
||||
Direction VectorDirection(glm::vec2 closest);
|
||||
|
||||
void Game::DoCollisions()
|
||||
{
|
||||
for (GameObject &box : this->Levels[this->Level].Bricks)
|
||||
{
|
||||
if (!box.Destroyed)
|
||||
{
|
||||
Collision collision = CheckCollision(*Ball, box);
|
||||
if (std::get<0>(collision)) // if collision is true
|
||||
{
|
||||
// destroy block if not solid
|
||||
if (!box.IsSolid)
|
||||
box.Destroyed = true;
|
||||
else
|
||||
{ // if block is solid, enable shake effect
|
||||
ShakeTime = 0.05f;
|
||||
Effects->Shake = true;
|
||||
}
|
||||
// collision resolution
|
||||
Direction dir = std::get<1>(collision);
|
||||
glm::vec2 diff_vector = std::get<2>(collision);
|
||||
if (dir == LEFT || dir == RIGHT) // horizontal collision
|
||||
{
|
||||
Ball->Velocity.x = -Ball->Velocity.x; // reverse horizontal velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.x);
|
||||
if (dir == LEFT)
|
||||
Ball->Position.x += penetration; // move ball to right
|
||||
else
|
||||
Ball->Position.x -= penetration; // move ball to left;
|
||||
}
|
||||
else // vertical collision
|
||||
{
|
||||
Ball->Velocity.y = -Ball->Velocity.y; // reverse vertical velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.y);
|
||||
if (dir == UP)
|
||||
Ball->Position.y -= penetration; // move ball bback up
|
||||
else
|
||||
Ball->Position.y += penetration; // move ball back down
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check collisions for player pad (unless stuck)
|
||||
Collision result = CheckCollision(*Ball, *Player);
|
||||
if (!Ball->Stuck && std::get<0>(result))
|
||||
{
|
||||
// check where it hit the board, and change velocity based on where it hit the board
|
||||
float centerBoard = Player->Position.x + Player->Size.x / 2.0f;
|
||||
float distance = (Ball->Position.x + Ball->Radius) - centerBoard;
|
||||
float percentage = distance / (Player->Size.x / 2.0f);
|
||||
// then move accordingly
|
||||
float strength = 2.0f;
|
||||
glm::vec2 oldVelocity = Ball->Velocity;
|
||||
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
|
||||
//Ball->Velocity.y = -Ball->Velocity.y;
|
||||
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
|
||||
// fix sticky paddle
|
||||
Ball->Velocity.y = -1.0f * abs(Ball->Velocity.y);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
|
||||
{
|
||||
// collision x-axis?
|
||||
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
|
||||
two.Position.x + two.Size.x >= one.Position.x;
|
||||
// collision y-axis?
|
||||
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
|
||||
two.Position.y + two.Size.y >= one.Position.y;
|
||||
// collision only if on both axes
|
||||
return collisionX && collisionY;
|
||||
}
|
||||
|
||||
Collision CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
|
||||
{
|
||||
// get center point circle first
|
||||
glm::vec2 center(one.Position + one.Radius);
|
||||
// calculate AABB info (center, half-extents)
|
||||
glm::vec2 aabb_half_extents(two.Size.x / 2.0f, two.Size.y / 2.0f);
|
||||
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
|
||||
// get difference vector between both centers
|
||||
glm::vec2 difference = center - aabb_center;
|
||||
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
|
||||
// now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
|
||||
glm::vec2 closest = aabb_center + clamped;
|
||||
// now retrieve vector between center circle and closest point AABB and check if length < radius
|
||||
difference = closest - center;
|
||||
|
||||
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
|
||||
return std::make_tuple(true, VectorDirection(difference), difference);
|
||||
else
|
||||
return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// calculates which direction a vector is facing (N,E,S or W)
|
||||
Direction VectorDirection(glm::vec2 target)
|
||||
{
|
||||
glm::vec2 compass[] = {
|
||||
glm::vec2(0.0f, 1.0f), // up
|
||||
glm::vec2(1.0f, 0.0f), // right
|
||||
glm::vec2(0.0f, -1.0f), // down
|
||||
glm::vec2(-1.0f, 0.0f) // left
|
||||
};
|
||||
float max = 0.0f;
|
||||
unsigned int best_match = -1;
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
float dot_product = glm::dot(glm::normalize(target), compass[i]);
|
||||
if (dot_product > max)
|
||||
{
|
||||
max = dot_product;
|
||||
best_match = i;
|
||||
}
|
||||
}
|
||||
return (Direction)best_match;
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 <algorithm>
|
||||
|
||||
#include "game.h"
|
||||
#include "resource_manager.h"
|
||||
#include "sprite_renderer.h"
|
||||
#include "game_object.h"
|
||||
#include "ball_object.h"
|
||||
#include "particle_generator.h"
|
||||
#include "post_processor.h"
|
||||
|
||||
// Game-related State data
|
||||
SpriteRenderer *Renderer;
|
||||
GameObject *Player;
|
||||
BallObject *Ball;
|
||||
ParticleGenerator *Particles;
|
||||
PostProcessor *Effects;
|
||||
|
||||
float ShakeTime = 0.0f;
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete Renderer;
|
||||
delete Player;
|
||||
delete Ball;
|
||||
delete Particles;
|
||||
delete Effects;
|
||||
}
|
||||
|
||||
void Game::Init()
|
||||
{
|
||||
// load shaders
|
||||
ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
|
||||
ResourceManager::LoadShader("shaders/particle.vs", "shaders/particle.frag", nullptr, "particle");
|
||||
ResourceManager::LoadShader("shaders/post_processing.vs", "shaders/post_processing.frag", nullptr, "postprocessing");
|
||||
// configure shaders
|
||||
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(this->Width),
|
||||
static_cast<float>(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 textures
|
||||
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
ResourceManager::LoadTexture("textures/block.png", false, "block");
|
||||
ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid");
|
||||
ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
|
||||
ResourceManager::LoadTexture("textures/particle.png", true, "particle");
|
||||
ResourceManager::LoadTexture("textures/powerup_speed.png", true, "powerup_speed");
|
||||
ResourceManager::LoadTexture("textures/powerup_sticky.png", true, "powerup_sticky");
|
||||
ResourceManager::LoadTexture("textures/powerup_increase.png", true, "powerup_increase");
|
||||
ResourceManager::LoadTexture("textures/powerup_confuse.png", true, "powerup_confuse");
|
||||
ResourceManager::LoadTexture("textures/powerup_chaos.png", true, "powerup_chaos");
|
||||
ResourceManager::LoadTexture("textures/powerup_passthrough.png", true, "powerup_passthrough");
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
Particles = new ParticleGenerator(ResourceManager::GetShader("particle"), ResourceManager::GetTexture("particle"), 500);
|
||||
Effects = new PostProcessor(ResourceManager::GetShader("postprocessing"), this->Width, this->Height);
|
||||
// load levels
|
||||
GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
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.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
|
||||
glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -BALL_RADIUS * 2.0f);
|
||||
Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("face"));
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
// update objects
|
||||
Ball->Move(dt, this->Width);
|
||||
// check for collisions
|
||||
this->DoCollisions();
|
||||
// update particles
|
||||
Particles->Update(dt, *Ball, 2, glm::vec2(Ball->Radius / 2.0f));
|
||||
// update PowerUps
|
||||
this->UpdatePowerUps(dt);
|
||||
// reduce shake time
|
||||
if (ShakeTime > 0.0f)
|
||||
{
|
||||
ShakeTime -= dt;
|
||||
if (ShakeTime <= 0.0f)
|
||||
Effects->Shake = false;
|
||||
}
|
||||
// check loss condition
|
||||
if (Ball->Position.y >= this->Height) // did ball reach bottom edge?
|
||||
{
|
||||
this->ResetLevel();
|
||||
this->ResetPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
if (this->State == GAME_ACTIVE)
|
||||
{
|
||||
float velocity = PLAYER_VELOCITY * dt;
|
||||
// move playerboard
|
||||
if (this->Keys[GLFW_KEY_A])
|
||||
{
|
||||
if (Player->Position.x >= 0.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
if(this->State == GAME_ACTIVE)
|
||||
{
|
||||
// begin rendering to postprocessing framebuffer
|
||||
Effects->BeginRender();
|
||||
// draw background
|
||||
Renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f);
|
||||
// draw level
|
||||
this->Levels[this->Level].Draw(*Renderer);
|
||||
// draw player
|
||||
Player->Draw(*Renderer);
|
||||
// draw PowerUps
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
if (!powerUp.Destroyed)
|
||||
powerUp.Draw(*Renderer);
|
||||
// draw particles
|
||||
Particles->Draw();
|
||||
// draw ball
|
||||
Ball->Draw(*Renderer);
|
||||
// end rendering to postprocessing framebuffer
|
||||
Effects->EndRender();
|
||||
// render postprocessing quad
|
||||
Effects->Render(glfwGetTime());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::ResetLevel()
|
||||
{
|
||||
if (this->Level == 0)
|
||||
this->Levels[0].Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 1)
|
||||
this->Levels[1].Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 2)
|
||||
this->Levels[2].Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 3)
|
||||
this->Levels[3].Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
}
|
||||
|
||||
void Game::ResetPlayer()
|
||||
{
|
||||
// reset player/ball stats
|
||||
Player->Size = PLAYER_SIZE;
|
||||
Player->Position = glm::vec2(this->Width / 2.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Ball->Reset(Player->Position + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -(BALL_RADIUS * 2.0f)), INITIAL_BALL_VELOCITY);
|
||||
// also disable all active powerups
|
||||
Effects->Chaos = Effects->Confuse = false;
|
||||
Ball->PassThrough = Ball->Sticky = false;
|
||||
Player->Color = glm::vec3(1.0f);
|
||||
Ball->Color = glm::vec3(1.0f);
|
||||
}
|
||||
|
||||
// powerups
|
||||
bool IsOtherPowerUpActive(std::vector<PowerUp> &powerUps, std::string type);
|
||||
|
||||
void Game::UpdatePowerUps(float dt)
|
||||
{
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
{
|
||||
powerUp.Position += powerUp.Velocity * dt;
|
||||
if (powerUp.Activated)
|
||||
{
|
||||
powerUp.Duration -= dt;
|
||||
|
||||
if (powerUp.Duration <= 0.0f)
|
||||
{
|
||||
// remove powerup from list (will later be removed)
|
||||
powerUp.Activated = false;
|
||||
// deactivate effects
|
||||
if (powerUp.Type == "sticky")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "sticky"))
|
||||
{ // only reset if no other PowerUp of type sticky is active
|
||||
Ball->Sticky = false;
|
||||
Player->Color = glm::vec3(1.0f);
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "pass-through")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "pass-through"))
|
||||
{ // only reset if no other PowerUp of type pass-through is active
|
||||
Ball->PassThrough = false;
|
||||
Ball->Color = glm::vec3(1.0f);
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "confuse")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "confuse"))
|
||||
{ // only reset if no other PowerUp of type confuse is active
|
||||
Effects->Confuse = false;
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "chaos")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "chaos"))
|
||||
{ // only reset if no other PowerUp of type chaos is active
|
||||
Effects->Chaos = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove all PowerUps from vector that are destroyed AND !activated (thus either off the map or finished)
|
||||
// Note we use a lambda expression to remove each PowerUp which is destroyed and not activated
|
||||
this->PowerUps.erase(std::remove_if(this->PowerUps.begin(), this->PowerUps.end(),
|
||||
[](const PowerUp &powerUp) { return powerUp.Destroyed && !powerUp.Activated; }
|
||||
), this->PowerUps.end());
|
||||
}
|
||||
|
||||
bool ShouldSpawn(unsigned int chance)
|
||||
{
|
||||
unsigned int random = rand() % chance;
|
||||
return random == 0;
|
||||
}
|
||||
void Game::SpawnPowerUps(GameObject &block)
|
||||
{
|
||||
if (ShouldSpawn(75)) // 1 in 75 chance
|
||||
this->PowerUps.push_back(PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.Position, ResourceManager::GetTexture("powerup_speed")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.Position, ResourceManager::GetTexture("powerup_sticky")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.Position, ResourceManager::GetTexture("powerup_passthrough")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.Position, ResourceManager::GetTexture("powerup_increase")));
|
||||
if (ShouldSpawn(15)) // Negative powerups should spawn more often
|
||||
this->PowerUps.push_back(PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_confuse")));
|
||||
if (ShouldSpawn(15))
|
||||
this->PowerUps.push_back(PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_chaos")));
|
||||
}
|
||||
|
||||
void ActivatePowerUp(PowerUp &powerUp)
|
||||
{
|
||||
if (powerUp.Type == "speed")
|
||||
{
|
||||
Ball->Velocity *= 1.2;
|
||||
}
|
||||
else if (powerUp.Type == "sticky")
|
||||
{
|
||||
Ball->Sticky = true;
|
||||
Player->Color = glm::vec3(1.0f, 0.5f, 1.0f);
|
||||
}
|
||||
else if (powerUp.Type == "pass-through")
|
||||
{
|
||||
Ball->PassThrough = 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 = true; // only activate if chaos wasn't already active
|
||||
}
|
||||
else if (powerUp.Type == "chaos")
|
||||
{
|
||||
if (!Effects->Confuse)
|
||||
Effects->Chaos = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOtherPowerUpActive(std::vector<PowerUp> &powerUps, std::string type)
|
||||
{
|
||||
// Check if another PowerUp of the same type is still active
|
||||
// in which case we don't disable its effect (yet)
|
||||
for (const PowerUp &powerUp : powerUps)
|
||||
{
|
||||
if (powerUp.Activated)
|
||||
if (powerUp.Type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// collision detection
|
||||
bool CheckCollision(GameObject &one, GameObject &two);
|
||||
Collision CheckCollision(BallObject &one, GameObject &two);
|
||||
Direction VectorDirection(glm::vec2 closest);
|
||||
|
||||
void Game::DoCollisions()
|
||||
{
|
||||
for (GameObject &box : this->Levels[this->Level].Bricks)
|
||||
{
|
||||
if (!box.Destroyed)
|
||||
{
|
||||
Collision collision = CheckCollision(*Ball, box);
|
||||
if (std::get<0>(collision)) // if collision is true
|
||||
{
|
||||
// destroy block if not solid
|
||||
if (!box.IsSolid)
|
||||
{
|
||||
box.Destroyed = true;
|
||||
this->SpawnPowerUps(box);
|
||||
}
|
||||
else
|
||||
{ // if block is solid, enable shake effect
|
||||
ShakeTime = 0.05f;
|
||||
Effects->Shake = true;
|
||||
}
|
||||
// collision resolution
|
||||
Direction dir = std::get<1>(collision);
|
||||
glm::vec2 diff_vector = std::get<2>(collision);
|
||||
if (!(Ball->PassThrough && !box.IsSolid)) // don't do collision resolution on non-solid bricks if pass-through is activated
|
||||
{
|
||||
if (dir == LEFT || dir == RIGHT) // horizontal collision
|
||||
{
|
||||
Ball->Velocity.x = -Ball->Velocity.x; // reverse horizontal velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.x);
|
||||
if (dir == LEFT)
|
||||
Ball->Position.x += penetration; // move ball to right
|
||||
else
|
||||
Ball->Position.x -= penetration; // move ball to left;
|
||||
}
|
||||
else // vertical collision
|
||||
{
|
||||
Ball->Velocity.y = -Ball->Velocity.y; // reverse vertical velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.y);
|
||||
if (dir == UP)
|
||||
Ball->Position.y -= penetration; // move ball bback up
|
||||
else
|
||||
Ball->Position.y += penetration; // move ball back down
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also check collisions on PowerUps and if so, activate them
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
{
|
||||
if (!powerUp.Destroyed)
|
||||
{
|
||||
// first check if powerup passed bottom edge, if so: keep as inactive and destroy
|
||||
if (powerUp.Position.y >= this->Height)
|
||||
powerUp.Destroyed = true;
|
||||
|
||||
if (CheckCollision(*Player, powerUp))
|
||||
{ // collided with player, now activate powerup
|
||||
ActivatePowerUp(powerUp);
|
||||
powerUp.Destroyed = true;
|
||||
powerUp.Activated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// and finally check collisions for player pad (unless stuck)
|
||||
Collision result = CheckCollision(*Ball, *Player);
|
||||
if (!Ball->Stuck && std::get<0>(result))
|
||||
{
|
||||
// check where it hit the board, and change velocity based on where it hit the board
|
||||
float centerBoard = Player->Position.x + Player->Size.x / 2.0f;
|
||||
float distance = (Ball->Position.x + Ball->Radius) - centerBoard;
|
||||
float percentage = distance / (Player->Size.x / 2.0f);
|
||||
// then move accordingly
|
||||
float strength = 2.0f;
|
||||
glm::vec2 oldVelocity = Ball->Velocity;
|
||||
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
|
||||
//Ball->Velocity.y = -Ball->Velocity.y;
|
||||
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
|
||||
// fix sticky paddle
|
||||
Ball->Velocity.y = -1.0f * abs(Ball->Velocity.y);
|
||||
|
||||
// if Sticky powerup is activated, also stick ball to paddle once new velocity vectors were calculated
|
||||
Ball->Stuck = Ball->Sticky;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
|
||||
{
|
||||
// collision x-axis?
|
||||
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
|
||||
two.Position.x + two.Size.x >= one.Position.x;
|
||||
// collision y-axis?
|
||||
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
|
||||
two.Position.y + two.Size.y >= one.Position.y;
|
||||
// collision only if on both axes
|
||||
return collisionX && collisionY;
|
||||
}
|
||||
|
||||
Collision CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
|
||||
{
|
||||
// get center point circle first
|
||||
glm::vec2 center(one.Position + one.Radius);
|
||||
// calculate AABB info (center, half-extents)
|
||||
glm::vec2 aabb_half_extents(two.Size.x / 2.0f, two.Size.y / 2.0f);
|
||||
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
|
||||
// get difference vector between both centers
|
||||
glm::vec2 difference = center - aabb_center;
|
||||
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
|
||||
// now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
|
||||
glm::vec2 closest = aabb_center + clamped;
|
||||
// now retrieve vector between center circle and closest point AABB and check if length < radius
|
||||
difference = closest - center;
|
||||
|
||||
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
|
||||
return std::make_tuple(true, VectorDirection(difference), difference);
|
||||
else
|
||||
return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// calculates which direction a vector is facing (N,E,S or W)
|
||||
Direction VectorDirection(glm::vec2 target)
|
||||
{
|
||||
glm::vec2 compass[] = {
|
||||
glm::vec2(0.0f, 1.0f), // up
|
||||
glm::vec2(1.0f, 0.0f), // right
|
||||
glm::vec2(0.0f, -1.0f), // down
|
||||
glm::vec2(-1.0f, 0.0f) // left
|
||||
};
|
||||
float max = 0.0f;
|
||||
unsigned int best_match = -1;
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
float dot_product = glm::dot(glm::normalize(target), compass[i]);
|
||||
if (dot_product > max)
|
||||
{
|
||||
max = dot_product;
|
||||
best_match = i;
|
||||
}
|
||||
}
|
||||
return (Direction)best_match;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "game_level.h"
|
||||
#include "power_up.h"
|
||||
|
||||
// Represents the current state of the game
|
||||
enum GameState {
|
||||
GAME_ACTIVE,
|
||||
GAME_MENU,
|
||||
GAME_WIN
|
||||
};
|
||||
|
||||
// Represents the four possible (collision) directions
|
||||
enum Direction {
|
||||
UP,
|
||||
RIGHT,
|
||||
DOWN,
|
||||
LEFT
|
||||
};
|
||||
// Defines a Collision typedef that represents collision data
|
||||
typedef std::tuple<bool, Direction, glm::vec2> Collision; // <collision?, what direction?, difference vector center - closest point>
|
||||
|
||||
// Initial size of the player paddle
|
||||
const glm::vec2 PLAYER_SIZE(100.0f, 20.0f);
|
||||
// Initial velocity of the player paddle
|
||||
const float PLAYER_VELOCITY(500.0f);
|
||||
// Initial velocity of the Ball
|
||||
const glm::vec2 INITIAL_BALL_VELOCITY(100.0f, -350.0f);
|
||||
// Radius of the ball object
|
||||
const float BALL_RADIUS = 12.5f;
|
||||
|
||||
// Game holds all game-related state and functionality.
|
||||
// Combines all game-related data into a single class for
|
||||
// easy access to each of the components and manageability.
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
// game state
|
||||
GameState State;
|
||||
bool Keys[1024];
|
||||
unsigned int Width, Height;
|
||||
std::vector<GameLevel> Levels;
|
||||
std::vector<PowerUp> PowerUps;
|
||||
unsigned int Level;
|
||||
// constructor/destructor
|
||||
Game(unsigned int width, unsigned int height);
|
||||
~Game();
|
||||
// initialize game state (load all shaders/textures/levels)
|
||||
void Init();
|
||||
// game loop
|
||||
void ProcessInput(float dt);
|
||||
void Update(float dt);
|
||||
void Render();
|
||||
void DoCollisions();
|
||||
// reset
|
||||
void ResetLevel();
|
||||
void ResetPlayer();
|
||||
// powerups
|
||||
void SpawnPowerUps(GameObject &block);
|
||||
void UpdatePowerUps(float dt);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,481 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 <algorithm>
|
||||
|
||||
#include <irrklang/irrKlang.h>
|
||||
using namespace irrklang;
|
||||
|
||||
#include "game.h"
|
||||
#include "resource_manager.h"
|
||||
#include "sprite_renderer.h"
|
||||
#include "game_object.h"
|
||||
#include "ball_object.h"
|
||||
#include "particle_generator.h"
|
||||
#include "post_processor.h"
|
||||
|
||||
// Game-related State data
|
||||
SpriteRenderer *Renderer;
|
||||
GameObject *Player;
|
||||
BallObject *Ball;
|
||||
ParticleGenerator *Particles;
|
||||
PostProcessor *Effects;
|
||||
ISoundEngine *SoundEngine = createIrrKlangDevice();
|
||||
|
||||
float ShakeTime = 0.0f;
|
||||
|
||||
Game::Game(unsigned int width, unsigned int height)
|
||||
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete Renderer;
|
||||
delete Player;
|
||||
delete Ball;
|
||||
delete Particles;
|
||||
delete Effects;
|
||||
SoundEngine->drop();
|
||||
}
|
||||
|
||||
void Game::Init()
|
||||
{
|
||||
// load shaders
|
||||
ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
|
||||
ResourceManager::LoadShader("shaders/particle.vs", "shaders/particle.frag", nullptr, "particle");
|
||||
ResourceManager::LoadShader("shaders/post_processing.vs", "shaders/post_processing.frag", nullptr, "postprocessing");
|
||||
// configure shaders
|
||||
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(this->Width),
|
||||
static_cast<float>(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 textures
|
||||
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
|
||||
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
|
||||
ResourceManager::LoadTexture("textures/block.png", false, "block");
|
||||
ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid");
|
||||
ResourceManager::LoadTexture("textures/paddle.png", true, "paddle");
|
||||
ResourceManager::LoadTexture("textures/particle.png", true, "particle");
|
||||
ResourceManager::LoadTexture("textures/powerup_speed.png", true, "powerup_speed");
|
||||
ResourceManager::LoadTexture("textures/powerup_sticky.png", true, "powerup_sticky");
|
||||
ResourceManager::LoadTexture("textures/powerup_increase.png", true, "powerup_increase");
|
||||
ResourceManager::LoadTexture("textures/powerup_confuse.png", true, "powerup_confuse");
|
||||
ResourceManager::LoadTexture("textures/powerup_chaos.png", true, "powerup_chaos");
|
||||
ResourceManager::LoadTexture("textures/powerup_passthrough.png", true, "powerup_passthrough");
|
||||
// set render-specific controls
|
||||
Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
|
||||
Particles = new ParticleGenerator(ResourceManager::GetShader("particle"), ResourceManager::GetTexture("particle"), 500);
|
||||
Effects = new PostProcessor(ResourceManager::GetShader("postprocessing"), this->Width, this->Height);
|
||||
// load levels
|
||||
GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
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.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));
|
||||
glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -BALL_RADIUS * 2.0f);
|
||||
Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("face"));
|
||||
// audio
|
||||
SoundEngine->play2D("audio/breakout.mp3", true);
|
||||
}
|
||||
|
||||
void Game::Update(float dt)
|
||||
{
|
||||
// update objects
|
||||
Ball->Move(dt, this->Width);
|
||||
// check for collisions
|
||||
this->DoCollisions();
|
||||
// update particles
|
||||
Particles->Update(dt, *Ball, 2, glm::vec2(Ball->Radius / 2.0f));
|
||||
// update PowerUps
|
||||
this->UpdatePowerUps(dt);
|
||||
// reduce shake time
|
||||
if (ShakeTime > 0.0f)
|
||||
{
|
||||
ShakeTime -= dt;
|
||||
if (ShakeTime <= 0.0f)
|
||||
Effects->Shake = false;
|
||||
}
|
||||
// check loss condition
|
||||
if (Ball->Position.y >= this->Height) // did ball reach bottom edge?
|
||||
{
|
||||
this->ResetLevel();
|
||||
this->ResetPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::ProcessInput(float dt)
|
||||
{
|
||||
if (this->State == GAME_ACTIVE)
|
||||
{
|
||||
float velocity = PLAYER_VELOCITY * dt;
|
||||
// move playerboard
|
||||
if (this->Keys[GLFW_KEY_A])
|
||||
{
|
||||
if (Player->Position.x >= 0.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::Render()
|
||||
{
|
||||
if(this->State == GAME_ACTIVE)
|
||||
{
|
||||
// begin rendering to postprocessing framebuffer
|
||||
Effects->BeginRender();
|
||||
// draw background
|
||||
Renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f);
|
||||
// draw level
|
||||
this->Levels[this->Level].Draw(*Renderer);
|
||||
// draw player
|
||||
Player->Draw(*Renderer);
|
||||
// draw PowerUps
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
if (!powerUp.Destroyed)
|
||||
powerUp.Draw(*Renderer);
|
||||
// draw particles
|
||||
Particles->Draw();
|
||||
// draw ball
|
||||
Ball->Draw(*Renderer);
|
||||
// end rendering to postprocessing framebuffer
|
||||
Effects->EndRender();
|
||||
// render postprocessing quad
|
||||
Effects->Render(glfwGetTime());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::ResetLevel()
|
||||
{
|
||||
if (this->Level == 0)
|
||||
this->Levels[0].Load("levels/one.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 1)
|
||||
this->Levels[1].Load("levels/two.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 2)
|
||||
this->Levels[2].Load("levels/three.lvl", this->Width, this->Height / 2);
|
||||
else if (this->Level == 3)
|
||||
this->Levels[3].Load("levels/four.lvl", this->Width, this->Height / 2);
|
||||
}
|
||||
|
||||
void Game::ResetPlayer()
|
||||
{
|
||||
// reset player/ball stats
|
||||
Player->Size = PLAYER_SIZE;
|
||||
Player->Position = glm::vec2(this->Width / 2.0f - PLAYER_SIZE.x / 2.0f, this->Height - PLAYER_SIZE.y);
|
||||
Ball->Reset(Player->Position + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, -(BALL_RADIUS * 2.0f)), INITIAL_BALL_VELOCITY);
|
||||
// also disable all active powerups
|
||||
Effects->Chaos = Effects->Confuse = false;
|
||||
Ball->PassThrough = Ball->Sticky = false;
|
||||
Player->Color = glm::vec3(1.0f);
|
||||
Ball->Color = glm::vec3(1.0f);
|
||||
}
|
||||
|
||||
// powerups
|
||||
bool IsOtherPowerUpActive(std::vector<PowerUp> &powerUps, std::string type);
|
||||
|
||||
void Game::UpdatePowerUps(float dt)
|
||||
{
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
{
|
||||
powerUp.Position += powerUp.Velocity * dt;
|
||||
if (powerUp.Activated)
|
||||
{
|
||||
powerUp.Duration -= dt;
|
||||
|
||||
if (powerUp.Duration <= 0.0f)
|
||||
{
|
||||
// remove powerup from list (will later be removed)
|
||||
powerUp.Activated = false;
|
||||
// deactivate effects
|
||||
if (powerUp.Type == "sticky")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "sticky"))
|
||||
{ // only reset if no other PowerUp of type sticky is active
|
||||
Ball->Sticky = false;
|
||||
Player->Color = glm::vec3(1.0f);
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "pass-through")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "pass-through"))
|
||||
{ // only reset if no other PowerUp of type pass-through is active
|
||||
Ball->PassThrough = false;
|
||||
Ball->Color = glm::vec3(1.0f);
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "confuse")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "confuse"))
|
||||
{ // only reset if no other PowerUp of type confuse is active
|
||||
Effects->Confuse = false;
|
||||
}
|
||||
}
|
||||
else if (powerUp.Type == "chaos")
|
||||
{
|
||||
if (!IsOtherPowerUpActive(this->PowerUps, "chaos"))
|
||||
{ // only reset if no other PowerUp of type chaos is active
|
||||
Effects->Chaos = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove all PowerUps from vector that are destroyed AND !activated (thus either off the map or finished)
|
||||
// Note we use a lambda expression to remove each PowerUp which is destroyed and not activated
|
||||
this->PowerUps.erase(std::remove_if(this->PowerUps.begin(), this->PowerUps.end(),
|
||||
[](const PowerUp &powerUp) { return powerUp.Destroyed && !powerUp.Activated; }
|
||||
), this->PowerUps.end());
|
||||
}
|
||||
|
||||
bool ShouldSpawn(unsigned int chance)
|
||||
{
|
||||
unsigned int random = rand() % chance;
|
||||
return random == 0;
|
||||
}
|
||||
void Game::SpawnPowerUps(GameObject &block)
|
||||
{
|
||||
if (ShouldSpawn(75)) // 1 in 75 chance
|
||||
this->PowerUps.push_back(PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.Position, ResourceManager::GetTexture("powerup_speed")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.Position, ResourceManager::GetTexture("powerup_sticky")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.Position, ResourceManager::GetTexture("powerup_passthrough")));
|
||||
if (ShouldSpawn(75))
|
||||
this->PowerUps.push_back(PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.Position, ResourceManager::GetTexture("powerup_increase")));
|
||||
if (ShouldSpawn(15)) // Negative powerups should spawn more often
|
||||
this->PowerUps.push_back(PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_confuse")));
|
||||
if (ShouldSpawn(15))
|
||||
this->PowerUps.push_back(PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_chaos")));
|
||||
}
|
||||
|
||||
void ActivatePowerUp(PowerUp &powerUp)
|
||||
{
|
||||
if (powerUp.Type == "speed")
|
||||
{
|
||||
Ball->Velocity *= 1.2;
|
||||
}
|
||||
else if (powerUp.Type == "sticky")
|
||||
{
|
||||
Ball->Sticky = true;
|
||||
Player->Color = glm::vec3(1.0f, 0.5f, 1.0f);
|
||||
}
|
||||
else if (powerUp.Type == "pass-through")
|
||||
{
|
||||
Ball->PassThrough = 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 = true; // only activate if chaos wasn't already active
|
||||
}
|
||||
else if (powerUp.Type == "chaos")
|
||||
{
|
||||
if (!Effects->Confuse)
|
||||
Effects->Chaos = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOtherPowerUpActive(std::vector<PowerUp> &powerUps, std::string type)
|
||||
{
|
||||
// Check if another PowerUp of the same type is still active
|
||||
// in which case we don't disable its effect (yet)
|
||||
for (const PowerUp &powerUp : powerUps)
|
||||
{
|
||||
if (powerUp.Activated)
|
||||
if (powerUp.Type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// collision detection
|
||||
bool CheckCollision(GameObject &one, GameObject &two);
|
||||
Collision CheckCollision(BallObject &one, GameObject &two);
|
||||
Direction VectorDirection(glm::vec2 closest);
|
||||
|
||||
void Game::DoCollisions()
|
||||
{
|
||||
for (GameObject &box : this->Levels[this->Level].Bricks)
|
||||
{
|
||||
if (!box.Destroyed)
|
||||
{
|
||||
Collision collision = CheckCollision(*Ball, box);
|
||||
if (std::get<0>(collision)) // if collision is true
|
||||
{
|
||||
// destroy block if not solid
|
||||
if (!box.IsSolid)
|
||||
{
|
||||
box.Destroyed = true;
|
||||
this->SpawnPowerUps(box);
|
||||
SoundEngine->play2D("audio/bleep.mp3", false);
|
||||
}
|
||||
else
|
||||
{ // if block is solid, enable shake effect
|
||||
ShakeTime = 0.05f;
|
||||
Effects->Shake = true;
|
||||
SoundEngine->play2D("audio/solid.wav", false);
|
||||
}
|
||||
// collision resolution
|
||||
Direction dir = std::get<1>(collision);
|
||||
glm::vec2 diff_vector = std::get<2>(collision);
|
||||
if (!(Ball->PassThrough && !box.IsSolid)) // don't do collision resolution on non-solid bricks if pass-through is activated
|
||||
{
|
||||
if (dir == LEFT || dir == RIGHT) // horizontal collision
|
||||
{
|
||||
Ball->Velocity.x = -Ball->Velocity.x; // reverse horizontal velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.x);
|
||||
if (dir == LEFT)
|
||||
Ball->Position.x += penetration; // move ball to right
|
||||
else
|
||||
Ball->Position.x -= penetration; // move ball to left;
|
||||
}
|
||||
else // vertical collision
|
||||
{
|
||||
Ball->Velocity.y = -Ball->Velocity.y; // reverse vertical velocity
|
||||
// relocate
|
||||
float penetration = Ball->Radius - std::abs(diff_vector.y);
|
||||
if (dir == UP)
|
||||
Ball->Position.y -= penetration; // move ball bback up
|
||||
else
|
||||
Ball->Position.y += penetration; // move ball back down
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also check collisions on PowerUps and if so, activate them
|
||||
for (PowerUp &powerUp : this->PowerUps)
|
||||
{
|
||||
if (!powerUp.Destroyed)
|
||||
{
|
||||
// first check if powerup passed bottom edge, if so: keep as inactive and destroy
|
||||
if (powerUp.Position.y >= this->Height)
|
||||
powerUp.Destroyed = true;
|
||||
|
||||
if (CheckCollision(*Player, powerUp))
|
||||
{ // collided with player, now activate powerup
|
||||
ActivatePowerUp(powerUp);
|
||||
powerUp.Destroyed = true;
|
||||
powerUp.Activated = true;
|
||||
SoundEngine->play2D("audio/powerup.wav", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// and finally check collisions for player pad (unless stuck)
|
||||
Collision result = CheckCollision(*Ball, *Player);
|
||||
if (!Ball->Stuck && std::get<0>(result))
|
||||
{
|
||||
// check where it hit the board, and change velocity based on where it hit the board
|
||||
float centerBoard = Player->Position.x + Player->Size.x / 2.0f;
|
||||
float distance = (Ball->Position.x + Ball->Radius) - centerBoard;
|
||||
float percentage = distance / (Player->Size.x / 2.0f);
|
||||
// then move accordingly
|
||||
float strength = 2.0f;
|
||||
glm::vec2 oldVelocity = Ball->Velocity;
|
||||
Ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
|
||||
//Ball->Velocity.y = -Ball->Velocity.y;
|
||||
Ball->Velocity = glm::normalize(Ball->Velocity) * glm::length(oldVelocity); // keep speed consistent over both axes (multiply by length of old velocity, so total strength is not changed)
|
||||
// fix sticky paddle
|
||||
Ball->Velocity.y = -1.0f * abs(Ball->Velocity.y);
|
||||
|
||||
// if Sticky powerup is activated, also stick ball to paddle once new velocity vectors were calculated
|
||||
Ball->Stuck = Ball->Sticky;
|
||||
|
||||
SoundEngine->play2D("audio/bleep.wav", false);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
|
||||
{
|
||||
// collision x-axis?
|
||||
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
|
||||
two.Position.x + two.Size.x >= one.Position.x;
|
||||
// collision y-axis?
|
||||
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
|
||||
two.Position.y + two.Size.y >= one.Position.y;
|
||||
// collision only if on both axes
|
||||
return collisionX && collisionY;
|
||||
}
|
||||
|
||||
Collision CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
|
||||
{
|
||||
// get center point circle first
|
||||
glm::vec2 center(one.Position + one.Radius);
|
||||
// calculate AABB info (center, half-extents)
|
||||
glm::vec2 aabb_half_extents(two.Size.x / 2.0f, two.Size.y / 2.0f);
|
||||
glm::vec2 aabb_center(two.Position.x + aabb_half_extents.x, two.Position.y + aabb_half_extents.y);
|
||||
// get difference vector between both centers
|
||||
glm::vec2 difference = center - aabb_center;
|
||||
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
|
||||
// now that we know the the clamped values, add this to AABB_center and we get the value of box closest to circle
|
||||
glm::vec2 closest = aabb_center + clamped;
|
||||
// now retrieve vector between center circle and closest point AABB and check if length < radius
|
||||
difference = closest - center;
|
||||
|
||||
if (glm::length(difference) < one.Radius) // not <= since in that case a collision also occurs when object one exactly touches object two, which they are at the end of each collision resolution stage.
|
||||
return std::make_tuple(true, VectorDirection(difference), difference);
|
||||
else
|
||||
return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// calculates which direction a vector is facing (N,E,S or W)
|
||||
Direction VectorDirection(glm::vec2 target)
|
||||
{
|
||||
glm::vec2 compass[] = {
|
||||
glm::vec2(0.0f, 1.0f), // up
|
||||
glm::vec2(1.0f, 0.0f), // right
|
||||
glm::vec2(0.0f, -1.0f), // down
|
||||
glm::vec2(-1.0f, 0.0f) // left
|
||||
};
|
||||
float max = 0.0f;
|
||||
unsigned int best_match = -1;
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
float dot_product = glm::dot(glm::normalize(target), compass[i]);
|
||||
if (dot_product > max)
|
||||
{
|
||||
max = dot_product;
|
||||
best_match = i;
|
||||
}
|
||||
}
|
||||
return (Direction)best_match;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 "resource_manager.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
#include "stb_image.h"
|
||||
|
||||
// Instantiate static variables
|
||||
std::map<std::string, Texture2D> ResourceManager::Textures;
|
||||
std::map<std::string, Shader> ResourceManager::Shaders;
|
||||
|
||||
|
||||
Shader ResourceManager::LoadShader(const char *vShaderFile, const char *fShaderFile, const char *gShaderFile, std::string name)
|
||||
{
|
||||
Shaders[name] = loadShaderFromFile(vShaderFile, fShaderFile, gShaderFile);
|
||||
return Shaders[name];
|
||||
}
|
||||
|
||||
Shader ResourceManager::GetShader(std::string name)
|
||||
{
|
||||
return Shaders[name];
|
||||
}
|
||||
|
||||
Texture2D ResourceManager::LoadTexture(const char *file, bool alpha, std::string name)
|
||||
{
|
||||
Textures[name] = loadTextureFromFile(file, alpha);
|
||||
return Textures[name];
|
||||
}
|
||||
|
||||
Texture2D ResourceManager::GetTexture(std::string name)
|
||||
{
|
||||
return Textures[name];
|
||||
}
|
||||
|
||||
void ResourceManager::Clear()
|
||||
{
|
||||
// (properly) delete all shaders
|
||||
for (auto iter : Shaders)
|
||||
glDeleteProgram(iter.second.ID);
|
||||
// (properly) delete all textures
|
||||
for (auto iter : Textures)
|
||||
glDeleteTextures(1, &iter.second.ID);
|
||||
}
|
||||
|
||||
Shader ResourceManager::loadShaderFromFile(const char *vShaderFile, const char *fShaderFile, const char *gShaderFile)
|
||||
{
|
||||
// 1. retrieve the vertex/fragment source code from filePath
|
||||
std::string vertexCode;
|
||||
std::string fragmentCode;
|
||||
std::string geometryCode;
|
||||
try
|
||||
{
|
||||
// open files
|
||||
std::ifstream vertexShaderFile(vShaderFile);
|
||||
std::ifstream fragmentShaderFile(fShaderFile);
|
||||
std::stringstream vShaderStream, fShaderStream;
|
||||
// read file's buffer contents into streams
|
||||
vShaderStream << vertexShaderFile.rdbuf();
|
||||
fShaderStream << fragmentShaderFile.rdbuf();
|
||||
// close file handlers
|
||||
vertexShaderFile.close();
|
||||
fragmentShaderFile.close();
|
||||
// convert stream into string
|
||||
vertexCode = vShaderStream.str();
|
||||
fragmentCode = fShaderStream.str();
|
||||
// if geometry shader path is present, also load a geometry shader
|
||||
if (gShaderFile != nullptr)
|
||||
{
|
||||
std::ifstream geometryShaderFile(gShaderFile);
|
||||
std::stringstream gShaderStream;
|
||||
gShaderStream << geometryShaderFile.rdbuf();
|
||||
geometryShaderFile.close();
|
||||
geometryCode = gShaderStream.str();
|
||||
}
|
||||
}
|
||||
catch (std::exception e)
|
||||
{
|
||||
std::cout << "ERROR::SHADER: Failed to read shader files" << std::endl;
|
||||
}
|
||||
const char *vShaderCode = vertexCode.c_str();
|
||||
const char *fShaderCode = fragmentCode.c_str();
|
||||
const char *gShaderCode = geometryCode.c_str();
|
||||
// 2. now create shader object from source code
|
||||
Shader shader;
|
||||
shader.Compile(vShaderCode, fShaderCode, gShaderFile != nullptr ? gShaderCode : nullptr);
|
||||
return shader;
|
||||
}
|
||||
|
||||
Texture2D ResourceManager::loadTextureFromFile(const char *file, bool alpha)
|
||||
{
|
||||
// create texture object
|
||||
Texture2D texture;
|
||||
if (alpha)
|
||||
{
|
||||
texture.Internal_Format = GL_RGBA;
|
||||
texture.Image_Format = GL_RGBA;
|
||||
}
|
||||
// load image
|
||||
int width, height, nrChannels;
|
||||
unsigned char* data = stbi_load(file, &width, &height, &nrChannels, 0);
|
||||
// now generate texture
|
||||
texture.Generate(width, height, data);
|
||||
// and finally free image data
|
||||
stbi_image_free(data);
|
||||
return texture;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef RESOURCE_MANAGER_H
|
||||
#define RESOURCE_MANAGER_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
|
||||
|
||||
// A static singleton ResourceManager class that hosts several
|
||||
// functions to load Textures and Shaders. Each loaded texture
|
||||
// and/or shader is also stored for future reference by string
|
||||
// handles. All functions and resources are static and no
|
||||
// public constructor is defined.
|
||||
class ResourceManager
|
||||
{
|
||||
public:
|
||||
// resource storage
|
||||
static std::map<std::string, Shader> Shaders;
|
||||
static std::map<std::string, Texture2D> Textures;
|
||||
// loads (and generates) a shader program from file loading vertex, fragment (and geometry) shader's source code. If gShaderFile is not nullptr, it also loads a geometry shader
|
||||
static Shader LoadShader(const char *vShaderFile, const char *fShaderFile, const char *gShaderFile, std::string name);
|
||||
// retrieves a stored sader
|
||||
static Shader GetShader(std::string name);
|
||||
// loads (and generates) a texture from file
|
||||
static Texture2D LoadTexture(const char *file, bool alpha, std::string name);
|
||||
// retrieves a stored texture
|
||||
static Texture2D GetTexture(std::string name);
|
||||
// properly de-allocates all loaded resources
|
||||
static void Clear();
|
||||
private:
|
||||
// private constructor, that is we do not want any actual resource manager objects. Its members and functions should be publicly available (static).
|
||||
ResourceManager() { }
|
||||
// loads and generates a shader from file
|
||||
static Shader loadShaderFromFile(const char *vShaderFile, const char *fShaderFile, const char *gShaderFile = nullptr);
|
||||
// loads a single texture from file
|
||||
static Texture2D loadTextureFromFile(const char *file, bool alpha);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,137 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 "shader.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
Shader &Shader::Use()
|
||||
{
|
||||
glUseProgram(this->ID);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Shader::Compile(const char* vertexSource, const char* fragmentSource, const char* geometrySource)
|
||||
{
|
||||
unsigned int sVertex, sFragment, gShader;
|
||||
// vertex Shader
|
||||
sVertex = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(sVertex, 1, &vertexSource, NULL);
|
||||
glCompileShader(sVertex);
|
||||
checkCompileErrors(sVertex, "VERTEX");
|
||||
// fragment Shader
|
||||
sFragment = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(sFragment, 1, &fragmentSource, NULL);
|
||||
glCompileShader(sFragment);
|
||||
checkCompileErrors(sFragment, "FRAGMENT");
|
||||
// if geometry shader source code is given, also compile geometry shader
|
||||
if (geometrySource != nullptr)
|
||||
{
|
||||
gShader = glCreateShader(GL_GEOMETRY_SHADER);
|
||||
glShaderSource(gShader, 1, &geometrySource, NULL);
|
||||
glCompileShader(gShader);
|
||||
checkCompileErrors(gShader, "GEOMETRY");
|
||||
}
|
||||
// shader program
|
||||
this->ID = glCreateProgram();
|
||||
glAttachShader(this->ID, sVertex);
|
||||
glAttachShader(this->ID, sFragment);
|
||||
if (geometrySource != nullptr)
|
||||
glAttachShader(this->ID, gShader);
|
||||
glLinkProgram(this->ID);
|
||||
checkCompileErrors(this->ID, "PROGRAM");
|
||||
// delete the shaders as they're linked into our program now and no longer necessery
|
||||
glDeleteShader(sVertex);
|
||||
glDeleteShader(sFragment);
|
||||
if (geometrySource != nullptr)
|
||||
glDeleteShader(gShader);
|
||||
}
|
||||
|
||||
void Shader::SetFloat(const char *name, float value, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform1f(glGetUniformLocation(this->ID, name), value);
|
||||
}
|
||||
void Shader::SetInteger(const char *name, int value, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform1i(glGetUniformLocation(this->ID, name), value);
|
||||
}
|
||||
void Shader::SetVector2f(const char *name, float x, float y, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform2f(glGetUniformLocation(this->ID, name), x, y);
|
||||
}
|
||||
void Shader::SetVector2f(const char *name, const glm::vec2 &value, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform2f(glGetUniformLocation(this->ID, name), value.x, value.y);
|
||||
}
|
||||
void Shader::SetVector3f(const char *name, float x, float y, float z, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform3f(glGetUniformLocation(this->ID, name), x, y, z);
|
||||
}
|
||||
void Shader::SetVector3f(const char *name, const glm::vec3 &value, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform3f(glGetUniformLocation(this->ID, name), value.x, value.y, value.z);
|
||||
}
|
||||
void Shader::SetVector4f(const char *name, float x, float y, float z, float w, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform4f(glGetUniformLocation(this->ID, name), x, y, z, w);
|
||||
}
|
||||
void Shader::SetVector4f(const char *name, const glm::vec4 &value, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniform4f(glGetUniformLocation(this->ID, name), value.x, value.y, value.z, value.w);
|
||||
}
|
||||
void Shader::SetMatrix4(const char *name, const glm::mat4 &matrix, bool useShader)
|
||||
{
|
||||
if (useShader)
|
||||
this->Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(this->ID, name), 1, false, glm::value_ptr(matrix));
|
||||
}
|
||||
|
||||
|
||||
void Shader::checkCompileErrors(unsigned int object, std::string type)
|
||||
{
|
||||
int success;
|
||||
char infoLog[1024];
|
||||
if (type != "PROGRAM")
|
||||
{
|
||||
glGetShaderiv(object, GL_COMPILE_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glGetShaderInfoLog(object, 1024, NULL, infoLog);
|
||||
std::cout << "| ERROR::SHADER: Compile-time error: Type: " << type << "\n"
|
||||
<< infoLog << "\n -- --------------------------------------------------- -- "
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glGetProgramiv(object, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glGetProgramInfoLog(object, 1024, NULL, infoLog);
|
||||
std::cout << "| ERROR::Shader: Link-time error: Type: " << type << "\n"
|
||||
<< infoLog << "\n -- --------------------------------------------------- -- "
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef SHADER_H
|
||||
#define SHADER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
|
||||
// General purpsoe shader object. Compiles from file, generates
|
||||
// compile/link-time error messages and hosts several utility
|
||||
// functions for easy management.
|
||||
class Shader
|
||||
{
|
||||
public:
|
||||
// state
|
||||
unsigned int ID;
|
||||
// constructor
|
||||
Shader() { }
|
||||
// sets the current shader as active
|
||||
Shader &Use();
|
||||
// compiles the shader from given source code
|
||||
void Compile(const char *vertexSource, const char *fragmentSource, const char *geometrySource = nullptr); // note: geometry source code is optional
|
||||
// utility functions
|
||||
void SetFloat (const char *name, float value, bool useShader = false);
|
||||
void SetInteger (const char *name, int value, bool useShader = false);
|
||||
void SetVector2f (const char *name, float x, float y, bool useShader = false);
|
||||
void SetVector2f (const char *name, const glm::vec2 &value, bool useShader = false);
|
||||
void SetVector3f (const char *name, float x, float y, float z, bool useShader = false);
|
||||
void SetVector3f (const char *name, const glm::vec3 &value, bool useShader = false);
|
||||
void SetVector4f (const char *name, float x, float y, float z, float w, bool useShader = false);
|
||||
void SetVector4f (const char *name, const glm::vec4 &value, bool useShader = false);
|
||||
void SetMatrix4 (const char *name, const glm::mat4 &matrix, bool useShader = false);
|
||||
private:
|
||||
// checks if compilation or linking failed and if so, print the error logs
|
||||
void checkCompileErrors(unsigned int object, std::string type);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
#version 330 core
|
||||
in vec2 TexCoords;
|
||||
in vec4 ParticleColor;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sprite;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = (texture(sprite, TexCoords) * ParticleColor);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec4 vertex; // <vec2 position, vec2 texCoords>
|
||||
|
||||
out vec2 TexCoords;
|
||||
out vec4 ParticleColor;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform vec2 offset;
|
||||
uniform vec4 color;
|
||||
|
||||
void main()
|
||||
{
|
||||
float scale = 10.0f;
|
||||
TexCoords = vertex.zw;
|
||||
ParticleColor = color;
|
||||
gl_Position = projection * vec4((vertex.xy * scale) + offset, 0.0, 1.0);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#version 330 core
|
||||
in vec2 TexCoords;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D scene;
|
||||
uniform vec2 offsets[9];
|
||||
uniform int edge_kernel[9];
|
||||
uniform float blur_kernel[9];
|
||||
|
||||
uniform bool chaos;
|
||||
uniform bool confuse;
|
||||
uniform bool shake;
|
||||
|
||||
void main()
|
||||
{
|
||||
// zero out memory since an out variable is initialized with undefined values by default
|
||||
color = vec4(0.0f);
|
||||
|
||||
vec3 sample[9];
|
||||
// sample from texture offsets if using convolution matrix
|
||||
if(chaos || shake)
|
||||
for(int i = 0; i < 9; i++)
|
||||
sample[i] = vec3(texture(scene, TexCoords.st + offsets[i]));
|
||||
|
||||
// process effects
|
||||
if(chaos)
|
||||
{
|
||||
for(int i = 0; i < 9; i++)
|
||||
color += vec4(sample[i] * edge_kernel[i], 0.0f);
|
||||
color.a = 1.0f;
|
||||
}
|
||||
else if(confuse)
|
||||
{
|
||||
color = vec4(1.0 - texture(scene, TexCoords).rgb, 1.0);
|
||||
}
|
||||
else if(shake)
|
||||
{
|
||||
for(int i = 0; i < 9; i++)
|
||||
color += vec4(sample[i] * blur_kernel[i], 0.0f);
|
||||
color.a = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = texture(scene, TexCoords);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec4 vertex; // <vec2 position, vec2 texCoords>
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform bool chaos;
|
||||
uniform bool confuse;
|
||||
uniform bool shake;
|
||||
uniform float time;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(vertex.xy, 0.0f, 1.0f);
|
||||
vec2 texture = vertex.zw;
|
||||
if(chaos)
|
||||
{
|
||||
float strength = 0.3;
|
||||
vec2 pos = vec2(texture.x + sin(time) * strength, texture.y + cos(time) * strength);
|
||||
TexCoords = pos;
|
||||
}
|
||||
else if(confuse)
|
||||
{
|
||||
TexCoords = vec2(1.0 - texture.x, 1.0 - texture.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
TexCoords = texture;
|
||||
}
|
||||
if (shake)
|
||||
{
|
||||
float strength = 0.01;
|
||||
gl_Position.x += cos(time * 10) * strength;
|
||||
gl_Position.y += cos(time * 15) * strength;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#version 330 core
|
||||
in vec2 TexCoords;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sprite;
|
||||
uniform vec3 spriteColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
color = vec4(spriteColor, 1.0) * texture(sprite, TexCoords);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec4 vertex; // <vec2 position, vec2 texCoords>
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 model;
|
||||
// note that we're omitting the view matrix; the view never changes so we basically have an identity view matrix and can therefore omit it.
|
||||
uniform mat4 projection;
|
||||
|
||||
void main()
|
||||
{
|
||||
TexCoords = vertex.zw;
|
||||
gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 "sprite_renderer.h"
|
||||
|
||||
|
||||
SpriteRenderer::SpriteRenderer(Shader &shader)
|
||||
{
|
||||
this->shader = shader;
|
||||
this->initRenderData();
|
||||
}
|
||||
|
||||
SpriteRenderer::~SpriteRenderer()
|
||||
{
|
||||
glDeleteVertexArrays(1, &this->quadVAO);
|
||||
}
|
||||
|
||||
void SpriteRenderer::DrawSprite(Texture2D &texture, glm::vec2 position, glm::vec2 size, float rotate, glm::vec3 color)
|
||||
{
|
||||
// prepare transformations
|
||||
this->shader.Use();
|
||||
glm::mat4 model = glm::mat4(1.0f);
|
||||
model = glm::translate(model, glm::vec3(position, 0.0f)); // first translate (transformations are: scale happens first, then rotation, and then final translation happens; reversed order)
|
||||
|
||||
model = glm::translate(model, glm::vec3(0.5f * size.x, 0.5f * size.y, 0.0f)); // move origin of rotation to center of quad
|
||||
model = glm::rotate(model, glm::radians(rotate), glm::vec3(0.0f, 0.0f, 1.0f)); // then rotate
|
||||
model = glm::translate(model, glm::vec3(-0.5f * size.x, -0.5f * size.y, 0.0f)); // move origin back
|
||||
|
||||
model = glm::scale(model, glm::vec3(size, 1.0f)); // last scale
|
||||
|
||||
this->shader.SetMatrix4("model", model);
|
||||
|
||||
// render textured quad
|
||||
this->shader.SetVector3f("spriteColor", color);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
texture.Bind();
|
||||
|
||||
glBindVertexArray(this->quadVAO);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void SpriteRenderer::initRenderData()
|
||||
{
|
||||
// configure VAO/VBO
|
||||
unsigned int VBO;
|
||||
float vertices[] = {
|
||||
// pos // tex
|
||||
0.0f, 1.0f, 0.0f, 1.0f,
|
||||
1.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
|
||||
0.0f, 1.0f, 0.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 0.0f, 1.0f, 0.0f
|
||||
};
|
||||
|
||||
glGenVertexArrays(1, &this->quadVAO);
|
||||
glGenBuffers(1, &VBO);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(this->quadVAO);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef SPRITE_RENDERER_H
|
||||
#define SPRITE_RENDERER_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
|
||||
|
||||
class SpriteRenderer
|
||||
{
|
||||
public:
|
||||
// Constructor (inits shaders/shapes)
|
||||
SpriteRenderer(Shader &shader);
|
||||
// Destructor
|
||||
~SpriteRenderer();
|
||||
// Renders a defined quad textured with given sprite
|
||||
void DrawSprite(Texture2D &texture, glm::vec2 position, glm::vec2 size = glm::vec2(10.0f, 10.0f), float rotate = 0.0f, glm::vec3 color = glm::vec3(1.0f));
|
||||
private:
|
||||
// Render state
|
||||
Shader shader;
|
||||
unsigned int quadVAO;
|
||||
// Initializes and configures the quad's buffer and vertex attributes
|
||||
void initRenderData();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,2 +0,0 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
@@ -1,140 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 <iostream>
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "text_renderer.h"
|
||||
#include "resource_manager.h"
|
||||
|
||||
|
||||
TextRenderer::TextRenderer(unsigned int width, unsigned int height)
|
||||
{
|
||||
// load and configure shader
|
||||
this->TextShader = ResourceManager::LoadShader("shaders/text.vs", "shaders/text.frag", nullptr, "text");
|
||||
this->TextShader.SetMatrix4("projection", glm::ortho(0.0f, static_cast<float>(width), static_cast<float>(height), 0.0f), 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(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void TextRenderer::Load(std::string font, unsigned int fontSize)
|
||||
{
|
||||
// first clear the previously loaded Characters
|
||||
this->Characters.clear();
|
||||
// then initialize and load the FreeType library
|
||||
FT_Library ft;
|
||||
if (FT_Init_FreeType(&ft)) // all functions return a value different than 0 whenever an error occurred
|
||||
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: Failed to load font" << 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++) // lol see what I did there
|
||||
{
|
||||
// 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
|
||||
unsigned int 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<char, 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, float x, float y, float 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];
|
||||
|
||||
float xpos = x + ch.Bearing.x * scale;
|
||||
float ypos = y + (this->Characters['H'].Bearing.y - ch.Bearing.y) * scale;
|
||||
|
||||
float w = ch.Size.x * scale;
|
||||
float h = ch.Size.y * scale;
|
||||
// update VBO for each character
|
||||
float vertices[6][4] = {
|
||||
{ xpos, ypos + h, 0.0f, 1.0f },
|
||||
{ xpos + w, ypos, 1.0f, 0.0f },
|
||||
{ xpos, ypos, 0.0f, 0.0f },
|
||||
|
||||
{ xpos, ypos + h, 0.0f, 1.0f },
|
||||
{ xpos + w, ypos + h, 1.0f, 1.0f },
|
||||
{ xpos + w, ypos, 1.0f, 0.0f }
|
||||
};
|
||||
// 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);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef TEXT_RENDERER_H
|
||||
#define TEXT_RENDERER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
|
||||
|
||||
/// Holds all state information relevant to a character as loaded using FreeType
|
||||
struct Character {
|
||||
unsigned int TextureID; // ID handle of the glyph texture
|
||||
glm::ivec2 Size; // size of glyph
|
||||
glm::ivec2 Bearing; // offset from baseline to left/top of glyph
|
||||
unsigned int Advance; // horizontal offset to advance to next glyph
|
||||
};
|
||||
|
||||
|
||||
// A renderer class for rendering text displayed by a font loaded using the
|
||||
// FreeType library. A single font is loaded, processed into a list of Character
|
||||
// items for later rendering.
|
||||
class TextRenderer
|
||||
{
|
||||
public:
|
||||
// holds a list of pre-compiled Characters
|
||||
std::map<char, Character> Characters;
|
||||
// shader used for text rendering
|
||||
Shader TextShader;
|
||||
// constructor
|
||||
TextRenderer(unsigned int width, unsigned int height);
|
||||
// pre-compiles a list of characters from the given font
|
||||
void Load(std::string font, unsigned int fontSize);
|
||||
// renders a string of text using the precompiled list of characters
|
||||
void RenderText(std::string text, float x, float y, float scale, glm::vec3 color = glm::vec3(1.0f));
|
||||
private:
|
||||
// render state
|
||||
unsigned int VAO, VBO;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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 <iostream>
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
Texture2D::Texture2D()
|
||||
: Width(0), Height(0), Internal_Format(GL_RGB), Image_Format(GL_RGB), Wrap_S(GL_REPEAT), Wrap_T(GL_REPEAT), Filter_Min(GL_LINEAR), Filter_Max(GL_LINEAR)
|
||||
{
|
||||
glGenTextures(1, &this->ID);
|
||||
}
|
||||
|
||||
void Texture2D::Generate(unsigned int width, unsigned int height, unsigned char* data)
|
||||
{
|
||||
this->Width = width;
|
||||
this->Height = height;
|
||||
// create Texture
|
||||
glBindTexture(GL_TEXTURE_2D, this->ID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, this->Internal_Format, width, height, 0, this->Image_Format, GL_UNSIGNED_BYTE, data);
|
||||
// set Texture wrap and filter modes
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, this->Wrap_S);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, this->Wrap_T);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, this->Filter_Min);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, this->Filter_Max);
|
||||
// unbind texture
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Texture2D::Bind() const
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, this->ID);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*******************************************************************
|
||||
** 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.
|
||||
******************************************************************/
|
||||
#ifndef TEXTURE_H
|
||||
#define TEXTURE_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
// Texture2D is able to store and configure a texture in OpenGL.
|
||||
// It also hosts utility functions for easy management.
|
||||
class Texture2D
|
||||
{
|
||||
public:
|
||||
// holds the ID of the texture object, used for all texture operations to reference to this particlar texture
|
||||
unsigned int ID;
|
||||
// texture image dimensions
|
||||
unsigned int Width, Height; // width and height of loaded image in pixels
|
||||
// texture Format
|
||||
unsigned int Internal_Format; // format of texture object
|
||||
unsigned int Image_Format; // format of loaded image
|
||||
// texture configuration
|
||||
unsigned int Wrap_S; // wrapping mode on S axis
|
||||
unsigned int Wrap_T; // wrapping mode on T axis
|
||||
unsigned int Filter_Min; // filtering mode if texture pixels < screen pixels
|
||||
unsigned int Filter_Max; // filtering mode if texture pixels > screen pixels
|
||||
// constructor (sets default texture modes)
|
||||
Texture2D();
|
||||
// generates texture from image data
|
||||
void Generate(unsigned int width, unsigned int height, unsigned char* data);
|
||||
// binds the texture as the current active GL_TEXTURE_2D texture object
|
||||
void Bind() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 298 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 15 KiB |