1
0
Files
BreakOut/src/game.cpp
2022-10-20 15:40:28 +02:00

308 lines
9.9 KiB
C++

#include "game.h"
#include "particleGenerator.h"
#include "postprocessor.h"
#include <iostream>
#include <tuple>
typedef std::tuple<bool, Direction,glm::vec2> Collision;
ParticleGenerator* Particles;
SpriteRenderer* Renderer;
PostProcessor* effects;
// 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);
GameObject* player;
// 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;
BallObject* ball;
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;
}
// AABB with AABB collision detection
bool CheckCollisions(GameObject& one, GameObject& two)
{
// Collision on the X-Axis
bool CollisionX = one.Position.x + one.Size.x >= two.Position.x &&
two.Position.x + two.Size.x >= one.Position.x;
// Colision on the y-Axis
bool CollisionY = one.Position.y + one.Size.y >= two.Position.y &&
two.Position.y + two.Size.y >= one.Position.y;
return CollisionX && CollisionY;
}
// AABB with circle collision detection
Collision CheckCollisions(BallObject& one, GameObject& two)
{
// Calculate the closest point on two's border to the center point of one
glm::vec2 center (one.Position + one.Radius);
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 );
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
glm::vec2 closest = aabb_center + clamped;
// Calculate the distance and check if it is less then two's radius
difference = closest - center;
if( glm::length(difference) < one.Radius)
{
return std::make_tuple(true, VectorDirection(difference), difference);
} else
{
return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f));
}
}
Game::~Game()
{
delete Renderer;
delete player;
delete ball;
delete Particles;
delete effects;
}
Game::Game(unsigned int width, unsigned int height)
: State(GAME_ACTIVE), Keys(), Width(width), Height(height)
{
}
void Game::Init()
{
// Load shaders
ResourceManager::LoadShader("shader.vs", "shader.fs", nullptr, "sprite");
ResourceManager::LoadShader("particle.vs", "particle.fs", nullptr, "particle");
ResourceManager::LoadShader("postprocessing.vs", "postprocessing.fs", 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);
// 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 textures
ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
ResourceManager::LoadTexture("textures/background.jpg", false, "background");
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");
// 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;
glm::vec2 playerPos = glm::vec2(this->Width /2 - 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.0 - BALL_RADIUS, -BALL_RADIUS * 2.0f);
ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("face"));
}
float ShakeTime = 0.0f;
void Game::DoCollisions()
{
for(GameObject& box: this->Levels[this->Level].Bricks)
{
if(!box.Destroyed)
{
Collision collision = CheckCollisions(*ball, box);
if(std::get<0>(collision)) // If we have collided
{
if(!box.IsSolid)
{
box.Destroyed = true;
}
else
{
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 )
{
ball->Velocity.x = -ball->Velocity.x;
// relocate
float penetration = ball->Radius - std::abs(diff_vector.x);
if(dir == LEFT)
ball->Position.x += penetration;
else
ball->Position.x -= penetration;
}
else
{
ball->Velocity.y = -ball->Velocity.y;
float penetration = ball->Radius - std::abs(diff_vector.y);
if(dir == UP)
ball->Position.y -= penetration;
else
ball->Position.y += penetration;
}
}
}
}
Collision result = CheckCollisions(*ball, *player);
if(!ball->Stuck && std::get<0>(result)) // if we collided with player paddle
{
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);
float strength = 2.0f;
glm::vec2 oldVelocity = ball->Velocity;
ball->Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
ball->Velocity.y = -1.0f * abs(ball->Velocity.y);
ball->Velocity = glm::normalize(ball->Velocity) * glm::length(oldVelocity);
}
}
void Game::ProcessInput(float dt)
{
if(this->State == GAME_ACTIVE)
{
float velocity = PLAYER_VELOCITY * dt;
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::Update(float dt)
{
ball->Move(dt, this->Width);
this->DoCollisions();
Particles->Update(dt, *ball, 2, glm::vec2(ball->Radius /2.0f));
if(ShakeTime > 0.0f)
{
ShakeTime -= dt;
if (ShakeTime <= 0.0f)
effects->Shake = false;
}
if(ball->Position.y >= this->Height)
{
this->ResetLevel();
this->ResetPlayer();
}
}
void Game::Render()
{
if(this->State == GAME_ACTIVE)
{
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);
effects->EndRender();
effects->Render(glfwGetTime());
}
}
void Game::ResetLevel()
{
if(this->Level == 0)
this->Levels[0].Load("levels/one.lvl", this->Width, this->Height /2 );
if(this->Level == 1)
this->Levels[1].Load("levels/two.lvl", this->Width, this->Height /2 );
if(this->Level == 2)
this->Levels[2].Load("levels/three.lvl", this->Width, this->Height /2 );
if(this->Level == 3)
this->Levels[3].Load("levels/four.lvl", this->Width, this->Height /2 );
}
void Game::ResetPlayer()
{
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);
}