#ifndef SHMUP_HPP #define SHMUP_HPP #include #include #include "imgui.h" #include "overlay.hpp" #include #include #include #include #include #include #include #include class Shmup : public OpenEngine::Layer { public: Shmup(OpenEngine::Ref& overlay) : OpenEngine::Layer("shmup"), overlay(overlay), camera((float)OpenEngine::Application::Get().GetWindow().GetWidth() / OpenEngine::Application::Get().GetWindow().GetHeight(), 1.0f) { } ~Shmup() {}; void OnAttach() override { OE_PROFILE_FUNCTION(); { OE_PROFILE_SCOPE("Texture2D Creation"); ship = OpenEngine::Texture2D::Create("assets/textures/shmup/ship.png"); enemy_ship = OpenEngine::Texture2D::Create("assets/textures/shmup/enemy_ship.png"); bullet_tx = OpenEngine::Texture2D::Create("assets/textures/shmup/bullet.png"); } tr1 = {glm::vec3(0.0f, -0.9f, 1.0f), glm::vec3(0.1f), 0.0f}; LoadMap(); } void OnDetach() override { } struct Bounds { glm::vec2 min; // Bottom-Left glm::vec2 max; // Top-Right }; Bounds GetBounds(glm::vec2 position, glm::vec2 scale) { Bounds b; // Because the original vertices are -0.5 to 0.5, // the distance from center to edge is exactly scale * 0.5 glm::vec2 halfScale = scale * 0.5f; b.min = position - halfScale; b.max = position + halfScale; return b; } bool CheckCollision(const Bounds& a, const Bounds& b) { bool overlapX = a.max.x >= b.min.x && b.max.x >= a.min.x; bool overlapY = a.max.y >= b.min.y && b.max.y >= a.min.y; return overlapX && overlapY; } void MoveShip() { double delta = OpenEngine::Time::DeltaTime(); if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Up)) { tr1.position.y += 1 * delta; } if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Down)) { tr1.position.y -= 1 * delta; } if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Right)) { tr1.position.x += 1 * delta; } if (OpenEngine::Input::IsKeyPressed(OpenEngine::KeyCode::Left)) { tr1.position.x -= 1 * delta; } if (tr1.position.y <= -0.9) tr1.position.y = -0.9; if (tr1.position.y >= -0.3) tr1.position.y = -0.3; if (tr1.position.x <= -0.8) tr1.position.x = -0.8; if (tr1.position.x >= 0.8) tr1.position.x = 0.8; } void UpdateEntity() { double delta = OpenEngine::Time::DeltaTime(); for (auto& enemy : enemies) { enemy.position.y -= 0.3 * delta; if (enemy.position.y <= -1.1 || CheckCollision( GetBounds(enemy.position, enemy.size), GetBounds(tr1.position, tr1.size))){ lives--; } } for (auto& bullet : bullets) { bullet.position.y += 1.1 * delta; } std::erase_if(enemies, [](const auto& enemy) { return enemy.position.y <= -1.1f; }); std::erase_if(bullets, [](const auto& bullet) { return bullet.position.y >= 1.2f; }); for (auto& bullet : bullets) { for (auto& enemy : enemies) { Bounds bullet_bounds = GetBounds(bullet.position, bullet.size); Bounds enemy_bounds = GetBounds(enemy.position, enemy.size); if (CheckCollision(bullet_bounds, enemy_bounds)) { std::erase_if(bullets, [&](const OpenEngine::Transform& _bullet) { return _bullet.position == bullet.position; }); std::erase_if(enemies, [&](const auto& _enemy) { return _enemy.position == enemy.position; }); score++; } } } } void GameOver() { paused = true; auto& window = OpenEngine::Application::Get().GetWindow(); int width = window.GetWidth(); int height = window.GetHeight(); enemies.clear(); bullets.clear(); float x = width / 2.0f; float y = height / 2.0f; overlay->RenderTextCentered("Game Over!", x, y, 1.0f, glm::vec3(0.8f, 0.2f, 0.3f)); }; void OnUpdate() override { float delta = OpenEngine::Time::DeltaTime(); OE_PROFILE_FUNCTION() { OE_PROFILE_SCOPE("Setting up Rendering"); //OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f}); OpenEngine::RenderCommand::SetClearColor({1.0f, 1.0f, 1.0f, 1.0f}); OpenEngine::RenderCommand::Clear(); OpenEngine::Renderer2D::BeginScene(camera.GetCamera()); } { OE_PROFILE_SCOPE("Drawing Quads"); OpenEngine::Renderer2D::DrawQuad(tr1, ship); for (auto& enemy : enemies) OpenEngine::Renderer2D::DrawQuad(enemy, enemy_ship); for (auto& bullet : bullets) OpenEngine::Renderer2D::DrawQuad(bullet, bullet_tx); } OpenEngine::Renderer2D::EndScene(); if (!paused) { if (spawn_cooldown >= spawn_min_time) { SpawnEnemyRandom(); //ReadMap(); spawn_cooldown = 0; if (spawn_min_time > 0.5) spawn_min_time -= 0.01; } MoveShip(); UpdateEntity(); if (shooting) SpawnBullet(); for (auto& enemy : enemies) { if (CheckCollision(GetBounds(enemy.position, enemy.size), GetBounds(tr1.position, tr1.size))) game_over = true; } shot_cooldown += delta; spawn_cooldown += delta; } if (lives <= 0) game_over = true; if (game_over) GameOver(); std::string s_score = "Score: " + std::to_string(score); std::string s_lives = std::to_string(lives) + " lives"; overlay->RenderText(s_score, 10, OpenEngine::Application::Get().GetWindow().GetHeight() - 50, 1.0f, {0.8f, 0.2f, 0.3f}); overlay->RenderText( s_lives, OpenEngine::Application::Get().GetWindow().GetWidth() - 220, OpenEngine::Application::Get().GetWindow().GetHeight() - 50, 1.0f, {0.8f, 0.2f, 0.3f}); } void Reset() { tr1 = {glm::vec3(0.0f, -0.9f, 1.0f), glm::vec3(0.1f), 0.0f}; score = 0; lives = 5; game_over = false; paused = false; } void SpawnEnemyRandom() { static std::uniform_int_distribution dist{-8, 8}; OpenEngine::Transform enemy_tr({glm::vec3(dist(rd)/10.0f, 1.2f, -0.1f), glm::vec3(0.1f), 180.0f}); enemies.emplace_back(enemy_tr); }; void SpawnEnemy(int x) { OpenEngine::Transform enemy_tr({{x/10.0f, 1.2f, 0.0f}, glm::vec3(0.1f), 180.0f}); enemies.emplace_back(enemy_tr); }; void LoadMap() { std::string buffer; std::ifstream map_file("assets/maps/lvl1.txt"); while (std::getline(map_file, buffer)) { map.emplace_back(buffer); } }; void ReadMap() { static int line_n = 0; if (line_n >= map.size()) return; auto line = map[line_n]; int position = -8; for (int i = 0; i < line.length(); i++) { if (line[i] == '#') SpawnEnemy(position); position++; } line_n++; }; void SpawnBullet() { if (shot_cooldown >= 0.2) { OpenEngine::Transform bullet_tr; bullet_tr.size = glm::vec3(0.03f); bullet_tr.position.x = tr1.position.x; bullet_tr.position.y = tr1.position.y + 0.1; bullets.emplace_back(bullet_tr); shot_cooldown = 0; } } bool ProcessKeyPressedEvents(OpenEngine::KeyPressedEvent& event) { if (event.GetKeyCode() == OpenEngine::KeyCode::Space) { shooting = true; } if (game_over && event.GetKeyCode() == OpenEngine::KeyCode::Enter) { Reset(); } return false; }; bool ProcessKeyReleased(OpenEngine::KeyReleasedEvent& event) { if (event.GetKeyCode() == OpenEngine::KeyCode::Space) shooting = false; return false; } void OnEvent(OpenEngine::Event& event) override { OE_PROFILE_FUNCTION(); OpenEngine::EventDispatcher dispatcher(event); dispatcher.Dispatch(BIND_EVENT_FN(Shmup::ProcessKeyPressedEvents)); dispatcher.Dispatch(BIND_EVENT_FN(Shmup::ProcessKeyReleased)); { OE_PROFILE_SCOPE("Camera OnEvent"); camera.OnEvent(event); } } void OnImGuiRender() override { ImGuiIO io = ImGui::GetIO(); static auto m_Font = io.Fonts->AddFontFromFileTTF("/usr/share/fonts/TTF/JetBrainsMono-Regular.ttf", 120.0f); ImGui::GetForegroundDrawList()->AddText(m_Font, 48.0f, {5.0f, 5.0f}, 0xffffffff, "test"); } private: //glm::vec3 square_pos = glm::vec3(0.0f); //glm::vec4 square_color = glm::vec4(1.0f); bool paused = false; bool game_over = false; //float color[4] = {0.5f, 0.3f, 0.4f, 1.0f}; OpenEngine::Ref ship; OpenEngine::Ref enemy_ship; OpenEngine::Ref bullet_tx; OpenEngine::Transform tr1; std::vector enemies, bullets; int score = 0; int lives = 5; bool shooting = false; float spawn_cooldown = 0, shot_cooldown = 0; float spawn_min_time = 1.0f; std::vector map; std::random_device rd; std::mt19937 e{rd()}; // or std::default_random_engine e{rd()}; //std::unordered_map bindings; OpenEngine::OrthographicCameraController camera; OpenEngine::Ref overlay; //std::vector profiling_results; }; #endif // SHMUP_HPP