#include "Game.h" #include "Environment.h" #include #ifdef __ANDROID__ #include #endif #ifdef EMSCRIPTEN #include #include #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(logicalW * dpr); int physicalH = static_cast(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