Я работаю над игровым движком, настроенным со следующей структурой:
- Engine (dll)
- Engine Gui (. Exe) зависит от Engine.dll и Game.dll
- Игра (dll / .exe) зависит от Engine.dll
Движок содержит точку входа (класс приложения), но создание точки входа происходит либо в Game или Engine Gui в зависимости от того, отлажена ли конфигурация сборки.
Поскольку точка входа одинакова как для Engine GUI, так и для Game, и мне нужны дополнительные функции для Engine Gui I использовал макросы, чтобы добавить эту функциональность, сохраняя чистоту сборки Game.
Пока не так много макросов, но я боюсь, что если я продолжу добавлять дополнительные функции для Engine GUI, это сделает класс грязный и нечитаемый.
Вот как это выглядит сейчас:
//Application.h
#ifndef CHEETAH_ENGINE_CORE_APPLICATION_H_
#define CHEETAH_ENGINE_CORE_APPLICATION_H_
#include "Core.h"
#include "Window.h"
#include "UpdateLayerQueue.h"
#include "../Events/ApplicationEvents.h"
#include "ImGUILayer.h"
namespace cheetah
{
class CH_API Application
{
public:
Application();
virtual ~Application() = default;
void run();
void onEvent(Event& event);
bool onWindowClose(WindowCloseEvent& event);
bool onWindowResize(WindowResizeEvent& event);
void pushLayer(UpdateLayer* layer);
void pushOverlay(UpdateLayer* overlay);
void exit();
static Window& getWindow();
static Application& getApplication();
private:
bool m_isRunning = true;
UpdateLayerQueue m_updateLayerQueue;
std::unique_ptr<Window> m_window;
static Application* s_instance;
#ifdef DEBUG
public:
void setImGUILayer(ImGUILayer* layer);
void test();
private:
ImGUILayer* m_imGUILayer = nullptr;
#endif // DEBUG
};
}
#endif // !CHEETAH_ENGINE_CORE_APPLICATION_H_
//Application.cpp
#include "Application.h"
#include "Events/EventDispatcher.h"
#include "Events/ApplicationEvents.h"
#include "Core/Time.h"
#include "Input/Input.h"
#include "Renderer/Renderer.h"
#include "Metrics/MemoryMetrics.h"
#include <iostream>
namespace cheetah
{
Application* Application::s_instance = nullptr;
Application::Application()
{
s_instance = this;
m_window = std::unique_ptr<Window>((Window::create()));
m_window->setEventCallBack(std::bind(&Application::onEvent, this, std::placeholders::_1));
Renderer::init();
}
void Application::run()
{
while (m_isRunning)
{
Time::getInstance()->setTime();
Time::getInstance()->setDeltaTime();
Time::getInstance()->setLastFrameTime();
m_updateLayerQueue.onUpdate(Time::getDeltaTimeMsec());
#ifdef DEBUG
if (m_imGUILayer != nullptr)
{
m_imGUILayer->begin();
m_updateLayerQueue.onImGUIRender();
m_imGUILayer->end();
}
MemoryMetrics::reset();
#endif // DEBUG
m_window->onUpdate();
}
}
// events
void Application::onEvent(Event& event)
{
EventDispatcher dispatcher(event);
dispatcher.dispatch<WindowCloseEvent>(EventTypes::WindowClose, std::bind(&Application::onWindowClose, this, std::placeholders::_1));
dispatcher.dispatch<WindowResizeEvent>(EventTypes::WindowResize, std::bind(&Application::onWindowResize, this, std::placeholders::_1));
m_updateLayerQueue.onEvent(event);
}
bool Application::onWindowResize(WindowResizeEvent& event)
{
Renderer::setViewPort(0, 0, event.m_width, event.m_height);
return true;
}
bool Application::onWindowClose(WindowCloseEvent& event)
{
this->exit();
return true;
}
void Application::exit()
{
m_isRunning = false;
Renderer::shutDown();
}
// layers
void Application::pushLayer(UpdateLayer* layer)
{
m_updateLayerQueue.pushLayer(layer);
}
void Application::pushOverlay(UpdateLayer* overlay)
{
m_updateLayerQueue.pushOverlay(overlay);
}
Application& Application::getApplication()
{
return *s_instance;
}
Window& Application::getWindow()
{
return *Application::getApplication().m_window;
}
#ifdef DEBUG
void Application::setImGUILayer(ImGUILayer* layer)
{
m_imGUILayer = layer;
this->pushOverlay(m_imGUILayer);
}
#endif // DEBUG
}
В настоящее время в качестве решения я думаю о создании отдельного файла "ApplicationDebug "и в зависимости от конфигурации сборки В сборку можно включить либо обычное приложение, либо файл ApplicationDebug (я использую CMake).
У этого есть свои недостатки, потому что мне по-прежнему нужны макросы для включения нужного файла, и потому что у меня теперь есть два файла, мне нужно было бы добавить функции и поддерживать их, и что, если мне нужна конфигурация отладки специально для Game или Engine, я бы добавил еще один дополнительный файл?
Каков будет лучший выбор в этом вопросе, или оба варианта неправильны, и есть ли лучшее решение?