380 lines
12 KiB
C++
Executable File
380 lines
12 KiB
C++
Executable File
#ifndef SHMUP_HPP
|
|
#define SHMUP_HPP
|
|
|
|
#include <open_engine.hpp>
|
|
|
|
#include <fstream>
|
|
#include <dlfcn.h>
|
|
#include <glm/ext/matrix_transform.hpp>
|
|
#include <glm/glm.hpp>
|
|
#include <random>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
#include <vector>
|
|
|
|
enum class GameState
|
|
{
|
|
Playing,
|
|
Paused,
|
|
GameOver
|
|
};
|
|
|
|
class Shmup : public OpenEngine::Layer
|
|
{
|
|
public:
|
|
Shmup()
|
|
: 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");
|
|
}
|
|
|
|
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 MovePlayerShip()
|
|
{
|
|
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;
|
|
}
|
|
|
|
bool MovePlayerShipDiscrete(OpenEngine::KeyPressedEvent& event)
|
|
{
|
|
if (state == GameState::Playing) {
|
|
double delta = OpenEngine::Time::DeltaTime();
|
|
if (event.GetKeyCode() == OpenEngine::KeyCode::Right) {
|
|
tr1.position.x += 0.1f;
|
|
}
|
|
if (event.GetKeyCode() == OpenEngine::KeyCode::Left) {
|
|
tr1.position.x -= 0.1f;
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MoveEntities(double time_step) {
|
|
double delta = time_step;
|
|
|
|
// Updating enemy positions
|
|
for (auto& enemy : enemies) {
|
|
enemy.position.y -= 0.3 * delta;
|
|
if (state == GameState::Playing && (enemy.position.y <= -1.1 || CheckCollision(
|
|
GetBounds(enemy.position, enemy.size),
|
|
GetBounds(tr1.position, tr1.size))))
|
|
lives--;
|
|
}
|
|
|
|
// Updating friendly bullets positions
|
|
for (auto& bullet : bullets) {
|
|
bullet.position.y += 1.1 * delta;
|
|
}
|
|
}
|
|
|
|
void UpdateEntity()
|
|
{
|
|
double delta = OpenEngine::Time::DeltaTime();
|
|
|
|
MoveEntities(delta);
|
|
|
|
// Deletes enemies offscreen
|
|
std::erase_if(enemies, [](const auto& enemy) {
|
|
return enemy.position.y <= -1.1f;
|
|
});
|
|
// Deletes bullets offscreen
|
|
std::erase_if(bullets, [](const auto& bullet) {
|
|
return bullet.position.y >= 1.2f;
|
|
});
|
|
|
|
// Lowers lives upon collision between player and enemy
|
|
for (auto& enemy : enemies) {
|
|
if (state == GameState::Playing) {
|
|
if (enemy.position.y <= -1.1)
|
|
lives--;
|
|
|
|
if (CheckCollision(
|
|
GetBounds(enemy.position, enemy.size),
|
|
GetBounds(tr1.position, tr1.size)))
|
|
lives = 0;
|
|
}
|
|
}
|
|
|
|
// Deletes enemy and bullet and increases score after collision
|
|
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 Render()
|
|
{
|
|
OE_PROFILE_FUNCTION()
|
|
{
|
|
OE_PROFILE_SCOPE("Setting up Rendering");
|
|
OpenEngine::RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 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();
|
|
|
|
}
|
|
|
|
void OnUpdate() override
|
|
{
|
|
float delta = OpenEngine::Time::DeltaTime();
|
|
|
|
Render();
|
|
|
|
UpdateEntity();
|
|
|
|
if (state == GameState::Playing) {
|
|
if (spawn_cooldown >= spawn_min_time) {
|
|
//SpawnEnemyRandom();
|
|
ReadMap();
|
|
spawn_cooldown = 0;
|
|
if (spawn_min_time > 0.5)
|
|
spawn_min_time -= 0.01;
|
|
}
|
|
|
|
//MovePlayerShip();
|
|
|
|
if (shooting)
|
|
SpawnBullet();
|
|
|
|
shot_cooldown += delta;
|
|
spawn_cooldown += delta;
|
|
}
|
|
|
|
if (lives <= 0)
|
|
state = GameState::GameOver;
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
tr1 = {glm::vec3(0.0f, -0.9f, -0.1f), glm::vec3(0.1f), 0.0f};
|
|
|
|
score = 0;
|
|
lives = 5;
|
|
|
|
bullets.clear();
|
|
enemies.clear();
|
|
|
|
state = GameState::Playing;
|
|
}
|
|
|
|
void SpawnEnemyRandom()
|
|
{
|
|
static std::uniform_int_distribution<int> dist{-8, 8};
|
|
|
|
SpawnEnemy(dist(rd));
|
|
};
|
|
|
|
void SpawnEnemy(int x)
|
|
{
|
|
OpenEngine::Transform enemy_tr({{x/10.0f, 1.2f, 0.1f}, glm::vec3(0.1f), 180.0f});
|
|
enemies.emplace_back(enemy_tr);
|
|
};
|
|
|
|
void LoadMap(const char* path = "assets/maps/lvl1.txt")
|
|
{
|
|
std::string buffer;
|
|
|
|
std::ifstream map_file(path);
|
|
|
|
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 (state == GameState::GameOver && 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<OpenEngine::KeyPressedEvent>(BIND_EVENT_FN(Shmup::ProcessKeyPressedEvents));
|
|
dispatcher.Dispatch<OpenEngine::KeyPressedEvent>(BIND_EVENT_FN(Shmup::MovePlayerShipDiscrete));
|
|
dispatcher.Dispatch<OpenEngine::KeyReleasedEvent>(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");
|
|
//}
|
|
|
|
GameState GetGameState() { return state; };
|
|
int GetScore() { return score; };
|
|
int GetLives() { return lives; };
|
|
|
|
private:
|
|
OpenEngine::Ref<OpenEngine::Texture2D> ship;
|
|
OpenEngine::Ref<OpenEngine::Texture2D> enemy_ship;
|
|
OpenEngine::Ref<OpenEngine::Texture2D> bullet_tx;
|
|
OpenEngine::Transform tr1 = {glm::vec3(0.0f, -0.9f, -0.1f), glm::vec3(0.1f), 0.0f};
|
|
std::vector<OpenEngine::Transform> 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<std::string> map;
|
|
|
|
std::random_device rd;
|
|
std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
|
|
|
|
OpenEngine::OrthographicCameraController camera;
|
|
GameState state = GameState::Playing;
|
|
|
|
//OpenEngine::Ref<Overlay> overlay;
|
|
};
|
|
|
|
#endif // SHMUP_HPP
|