#include "game.h" #include "particleGenerator.h" #include "postprocessor.h" #include #include typedef std::tuple 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(this->Width), static_cast(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); }