341 lines
12 KiB
C++
341 lines
12 KiB
C++
#include "Game.h"
|
||
#include "Environment.h"
|
||
#include <iostream>
|
||
#ifdef __ANDROID__
|
||
#include <android/log.h>
|
||
#endif
|
||
|
||
#ifdef EMSCRIPTEN
|
||
#include <emscripten.h>
|
||
#include <emscripten/html5.h>
|
||
#endif
|
||
|
||
|
||
// For Emscripten the game is heap-allocated so it can be destroyed and
|
||
// re-created when the WebGL context is lost and restored (e.g. fullscreen).
|
||
// For Android and Desktop a plain global value is used (no context loss).
|
||
#ifdef EMSCRIPTEN
|
||
ZL::Game* g_game = nullptr;
|
||
//static SDL_Window* win_x = nullptr;
|
||
static SDL_GLContext glContext_x; // Не указатель, а сам объект (который суть void*)
|
||
|
||
#else
|
||
ZL::Game game;
|
||
#endif
|
||
|
||
|
||
#ifdef EMSCRIPTEN
|
||
void MainLoop() {
|
||
// SDL_GL_MakeCurrent тут не нужен каждый раз
|
||
/*glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||
SDL_GL_SwapWindow(ZL::Environment::window);*/
|
||
g_game->update();
|
||
}
|
||
#else
|
||
void MainLoop() {
|
||
game.update();
|
||
}
|
||
#endif
|
||
|
||
|
||
#ifdef EMSCRIPTEN
|
||
|
||
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
||
//delete g_game;
|
||
//g_game = nullptr;
|
||
return EM_TRUE;
|
||
}
|
||
|
||
EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
||
//g_game = new ZL::Game();
|
||
//g_game->setup();
|
||
return EM_TRUE;
|
||
}
|
||
|
||
static void applyResize(int logicalW, int logicalH) {
|
||
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
||
//double dpr = emscripten_get_device_pixel_ratio();
|
||
double dpr = 1; // low quality
|
||
|
||
// Вычисляем реальные физические пиксели
|
||
int physicalW = static_cast<int>(logicalW * dpr);
|
||
int physicalH = static_cast<int>(logicalH * dpr);
|
||
|
||
// Устанавливаем размер внутреннего буфера канваса
|
||
emscripten_set_canvas_element_size("#canvas", physicalW, physicalH);
|
||
|
||
// Сообщаем SDL о новом размере.
|
||
// ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
|
||
// для корректной работы последующих вызовов glViewport.
|
||
if (ZL::Environment::window) {
|
||
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
|
||
}
|
||
|
||
// Обновляем ваши внутренние переменные окружения
|
||
ZL::Environment::width = physicalW;
|
||
ZL::Environment::height = physicalH;
|
||
|
||
// Пушим событие, чтобы движок пересчитал матрицы проекции
|
||
SDL_Event e = {};
|
||
e.type = SDL_WINDOWEVENT;
|
||
e.window.event = SDL_WINDOWEVENT_RESIZED;
|
||
e.window.data1 = physicalW;
|
||
e.window.data2 = physicalH;
|
||
SDL_PushEvent(&e);
|
||
}
|
||
|
||
EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) {
|
||
// Use the event's window dimensions — querying the canvas element would
|
||
// return its old fixed size (e.g. 1280x720) before it has been resized.
|
||
applyResize(e->windowInnerWidth, e->windowInnerHeight);
|
||
return EM_FALSE;
|
||
}
|
||
|
||
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
|
||
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
|
||
// так как после перехода в FS именно он растягивается на весь экран.
|
||
double clientW, clientH;
|
||
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
|
||
applyResize(clientW, clientH);
|
||
return EM_FALSE;
|
||
}
|
||
|
||
int main(int argc, char* argv[]) {
|
||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); // Для WebGL 2.0
|
||
|
||
ZL::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||
glContext_x = SDL_GL_CreateContext(ZL::Environment::window);
|
||
|
||
SDL_GL_MakeCurrent(ZL::Environment::window, glContext_x);
|
||
|
||
g_game = new ZL::Game();
|
||
g_game->setup();
|
||
|
||
emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost);
|
||
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
|
||
|
||
// Keep Environment::width/height in sync when the canvas is resized.
|
||
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
|
||
|
||
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
|
||
// Получаем реальные размеры окна браузера на момент запуска
|
||
int canvasW = EM_ASM_INT({ return window.innerWidth; });
|
||
int canvasH = EM_ASM_INT({ return window.innerHeight; });
|
||
|
||
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
|
||
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
|
||
applyResize(canvasW, canvasH);
|
||
|
||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||
|
||
emscripten_set_main_loop(MainLoop, 0, 1);
|
||
|
||
|
||
return 0;
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
#ifdef __ANDROID__
|
||
|
||
extern "C" int SDL_main(int argc, char* argv[]) {
|
||
// Инициализация SDL перед получением информации о дисплее
|
||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError());
|
||
return 1;
|
||
}
|
||
|
||
SDL_DisplayMode displayMode;
|
||
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
|
||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
|
||
SDL_Quit();
|
||
return 1;
|
||
}
|
||
ZL::Environment::width = displayMode.w;
|
||
ZL::Environment::height = displayMode.h;
|
||
|
||
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
|
||
ZL::Environment::width, ZL::Environment::height);
|
||
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||
|
||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||
|
||
|
||
ZL::Environment::window = SDL_CreateWindow(
|
||
"Space Ship Game",
|
||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||
ZL::Environment::width, ZL::Environment::height,
|
||
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||
);
|
||
|
||
if (!ZL::Environment::window) {
|
||
__android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError());
|
||
SDL_Quit();
|
||
return 1;
|
||
}
|
||
|
||
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
||
if (!ctx) {
|
||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError());
|
||
SDL_DestroyWindow(ZL::Environment::window);
|
||
SDL_Quit();
|
||
return 1;
|
||
}
|
||
|
||
if (SDL_GL_MakeCurrent(ZL::Environment::window, ctx) != 0) {
|
||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError());
|
||
SDL_GL_DeleteContext(ctx);
|
||
SDL_DestroyWindow(ZL::Environment::window);
|
||
SDL_Quit();
|
||
return 1;
|
||
}
|
||
|
||
// Настройка VSync
|
||
if (SDL_GL_SetSwapInterval(1) < 0) {
|
||
__android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError());
|
||
}
|
||
|
||
// Настройка игры
|
||
try {
|
||
game.setup();
|
||
} catch (const std::exception &e) {
|
||
__android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what());
|
||
return 1;
|
||
}
|
||
|
||
while (!game.shouldExit()) {
|
||
game.update();
|
||
SDL_Delay(16);
|
||
}
|
||
|
||
return 0;
|
||
|
||
}
|
||
|
||
|
||
#endif
|
||
|
||
#ifdef WIN32_LEAN_AND_MEAN
|
||
|
||
int main(int argc, char *argv[]) {
|
||
try
|
||
{
|
||
|
||
|
||
constexpr int CONST_WIDTH = 1280;
|
||
constexpr int CONST_HEIGHT = 720;
|
||
|
||
ZL::Environment::width = CONST_WIDTH;
|
||
ZL::Environment::height = CONST_HEIGHT;
|
||
|
||
|
||
/*#ifdef EMSCRIPTEN
|
||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||
std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||
|
||
SDL_Window* win = SDL_CreateWindow("Space Ship Game",
|
||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||
CONST_WIDTH, CONST_HEIGHT,
|
||
SDL_WINDOW_OPENGL);
|
||
|
||
if (!win) {
|
||
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
SDL_GLContext glContext = SDL_GL_CreateContext(win);
|
||
if (!glContext) {
|
||
std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// Привязка контекста к окну — важно!
|
||
SDL_GL_MakeCurrent(win, glContext);
|
||
|
||
ZL::Environment::window = win;
|
||
|
||
g_game = new ZL::Game();
|
||
g_game->setup();
|
||
|
||
// Re-create the game object when the WebGL context is lost and restored
|
||
// (this happens e.g. when the user toggles fullscreen in the browser).
|
||
emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost);
|
||
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
|
||
|
||
// Keep Environment::width/height in sync when the canvas is resized.
|
||
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
|
||
|
||
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
|
||
// Получаем реальные размеры окна браузера на момент запуска
|
||
int canvasW = EM_ASM_INT({ return window.innerWidth; });
|
||
int canvasH = EM_ASM_INT({ return window.innerHeight; });
|
||
|
||
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
|
||
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
|
||
applyResize(canvasW, canvasH);
|
||
|
||
// 3. Создаем игру и вызываем setup (теперь проекция уже будет знать верный size)
|
||
g_game = new ZL::Game();
|
||
g_game->setup();
|
||
|
||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||
|
||
emscripten_set_main_loop(MainLoop, 0, 1);
|
||
#else*/
|
||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||
SDL_Log("SDL init failed: %s", SDL_GetError());
|
||
return 1;
|
||
}
|
||
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||
|
||
ZL::Environment::window = SDL_CreateWindow(
|
||
"Space Ship Game",
|
||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||
CONST_WIDTH, CONST_HEIGHT,
|
||
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||
);
|
||
|
||
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
||
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
|
||
|
||
game.setup();
|
||
|
||
while (!game.shouldExit()) {
|
||
game.update();
|
||
SDL_Delay(2);
|
||
}
|
||
//#endif
|
||
|
||
}
|
||
catch (const std::exception& e)
|
||
{
|
||
std::cout << e.what() << std::endl;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
#endif
|