Adapting web version

This commit is contained in:
Vladislav Khorev committed 2026-02-28 20:37:47 +03:00
1 parent 7e5aa22fee
commit 2728ca9646
16 files changed
+234 -207

No files matched your search

+70 -94
View File
@@ -1,100 +1,76 @@
<!doctypehtml>
<html lang=en-us>
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Space Game</title>
<style>
body, html {
margin: 0; padding: 0; width: 100%; height: 100%;
overflow: hidden; background-color: #000;
position: fixed; /* Предотвращает pull-to-refresh на Android */
}
#canvas {
display: block;
position: absolute;
top: 0; left: 0;
width: 100vw; height: 100vh;
border: none;
}
/* Кнопка Fullscreen */
#fs-button {
position: absolute;
top: 10px; right: 10px;
padding: 10px;
z-index: 10;
background: rgba(255,255,255,0.3);
color: white; border: 1px solid white;
cursor: pointer;
font-family: sans-serif;
border-radius: 5px;
}
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
</style>
</head>
<body>
<button id="fs-button">Fullscreen</button>
<div id="status">Downloading...</div>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
<head>
<meta charset=utf-8>
<meta content="text/html; charset=utf-8" http-equiv=Content-Type>
<title>Space Game</title>
<style>
body {
font-family: arial;
margin: 0;
overflow: hidden;
padding: 0;
min-height: 100vh;
background-color: #000;
<script>
var statusElement = document.getElementById("status");
var canvas = document.getElementById("canvas");
var Module = {
canvas: canvas,
setStatus: function(text) {
statusElement.innerHTML = text;
statusElement.style.display = text ? 'block' : 'none';
}
};
.emscripten {
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block
// Кнопка Fullscreen
document.getElementById('fs-button').addEventListener('click', function() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(e => {
console.error(`Error attempting to enable full-screen mode: ${e.message}`);
});
} else {
document.exitFullscreen();
}
});
div.emscripten {
text-align: center
}
// Обработка ориентации
window.addEventListener("orientationchange", function() {
// Chrome на Android обновляет innerWidth/Height не мгновенно.
// Ждем завершения анимации поворота.
setTimeout(() => {
// В Emscripten это вызовет ваш onWindowResized в C++
window.dispatchEvent(new Event('resize'));
}, 200);
});
div.emscripten_border {
border: 1px solid #000
}
canvas.emscripten {
border: 0 none;
background-color: #000;
width: 90%;
height: 100vh;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0)
}
to {
-webkit-transform: rotate(360deg)
}
}
@-moz-keyframes rotation {
from {
-moz-transform: rotate(0)
}
to {
-moz-transform: rotate(360deg)
}
}
@-o-keyframes rotation {
from {
-o-transform: rotate(0)
}
to {
-o-transform: rotate(360deg)
}
}
@keyframes rotation {
from {
transform: rotate(0)
}
to {
transform: rotate(360deg)
}
}
#status {
display: none;
}
#progress {
height: 20px;
width: 300px
}
</style>
</head>
<body>
<div class=emscripten id=status></div>
<div class=emscripten><progress hidden id=progress max=100 value=0></progress></div>
<div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault()
tabindex=-1></canvas></div>
<script>var statusElement = document.getElementById("status"), progressElement = document.getElementById("progress"), spinnerElement = document.getElementById("spinner"), Module = { print: function () { var e = document.getElementById("output"); return e && (e.value = ""), function (t) { arguments.length > 1 && (t = Array.prototype.slice.call(arguments).join(" ")), console.log(t), e && (e.value += t + "\n", e.scrollTop = e.scrollHeight) } }(), canvas: (() => { var e = document.getElementById("canvas"); return e.addEventListener("webglcontextlost", (e => { alert("WebGL context lost. You will need to reload the page."), e.preventDefault() }), !1), e })(), setStatus: e => { if (Module.setStatus.last || (Module.setStatus.last = { time: Date.now(), text: "" }), e !== Module.setStatus.last.text) { var t = e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/), n = Date.now(); t && n - Module.setStatus.last.time < 30 || (Module.setStatus.last.time = n, Module.setStatus.last.text = e, t ? (e = t[1], progressElement.value = 100 * parseInt(t[2]), progressElement.max = 100 * parseInt(t[4]), progressElement.hidden = !1, spinnerElement.hidden = !1) : (progressElement.value = null, progressElement.max = null, progressElement.hidden = !0, e || (spinnerElement.style.display = "none")), statusElement.innerHTML = e) } }, totalDependencies: 0, monitorRunDependencies: e => { this.totalDependencies = Math.max(this.totalDependencies, e), Module.setStatus(e ? "Preparing... (" + (this.totalDependencies - e) + "/" + this.totalDependencies + ")" : "All downloads complete.") } }; Module.setStatus("Downloading..."), window.onerror = e => { Module.setStatus("Exception thrown, see JavaScript console"), spinnerElement.style.display = "none", Module.setStatus = e => { e && console.error("[post-exception status] " + e) } }</script>
<script async src=space-game001.js></script>
</body>
</html>
</script>
<script async src="space-game001.js"></script>
</body>
</html>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+20
View File
@@ -36,5 +36,25 @@ ClientState Environment::shipState;
const float Environment::CONST_Z_NEAR = 5.f;
const float Environment::CONST_Z_FAR = 5000.f;
float Environment::projectionWidth = 1280.0f;
float Environment::projectionHeight = 720.0f;
void Environment::computeProjectionDimensions()
{
if (width <= 0 || height <= 0) return;
const float refShortSide = 720.0f;
float aspect = (float)width / (float)height;
if (width >= height) {
// Landscape: fix height to 720, scale width to preserve aspect
projectionHeight = refShortSide;
projectionWidth = refShortSide * aspect;
} else {
// Portrait: fix width to 720, scale height to preserve aspect
projectionWidth = refShortSide;
projectionHeight = refShortSide / aspect;
}
}
} // namespace ZL
+8 -1
View File
@@ -35,8 +35,15 @@ public:
static const float CONST_Z_NEAR;
static const float CONST_Z_FAR;
// Virtual projection dimensions used for all 2D/UI rendering.
// These maintain the screen's actual aspect ratio but normalize the
// height to 720 (landscape) or width to 720 (portrait), giving a
// consistent coordinate space regardless of physical screen resolution.
static float projectionWidth;
static float projectionHeight;
// Call this once at startup and whenever the window is resized.
static void computeProjectionDimensions();
};
} // namespace ZL
+45 -42
View File
@@ -83,6 +83,8 @@ namespace ZL
void Game::setup() {
glContext = SDL_GL_CreateContext(ZL::Environment::window);
Environment::computeProjectionDimensions();
ZL::BindOpenGlFunctions();
ZL::CheckGlError();
renderer.InitOpenGL();
@@ -99,7 +101,7 @@ namespace ZL
loadingTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/loading.png", CONST_ZIP_FILE));
#endif
loadingMesh.data = CreateRect2D({ Environment::width * 0.5, Environment::height * 0.5 }, { Environment::width * 0.5, Environment::height*0.5 }, 3);
loadingMesh.data = CreateRect2D({ Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, { Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, 3);
loadingMesh.RefreshVBO();
#ifdef EMSCRIPTEN
@@ -141,17 +143,14 @@ namespace ZL
Environment::shipState.nickname = nickname;
Environment::shipState.shipType = shipType;
networkClient = std::make_unique<LocalClient>();
auto localClient = new LocalClient;
ClientState st = Environment::shipState;
st.id = localClient->GetClientId();
localClient->setLocalPlayerState(st);
networkClient = std::unique_ptr<INetworkClient>(localClient);
networkClient->Connect("", 0);
#ifndef NETWORK
auto localClient = dynamic_cast<ZL::LocalClient*>(networkClient.get());
if (localClient) {
ZL::ClientState st = Environment::shipState;
st.id = localClient->GetClientId();
localClient->setLocalPlayerState(st);
}
#endif
lastTickCount = 0;
spaceGameStarted = 1;
};
@@ -160,8 +159,7 @@ namespace ZL
Environment::shipState.nickname = nickname;
Environment::shipState.shipType = shipType;
networkClient = std::make_unique<LocalClient>();
#ifdef NETWORK
#ifdef EMSCRIPTEN
networkClient = std::make_unique<WebSocketClientEmscripten>();
networkClient->Connect("localhost", 8081);
@@ -169,19 +167,6 @@ namespace ZL
networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext());
networkClient->Connect("localhost", 8081);
#endif
#else
networkClient->Connect("", 0);
#endif
#ifndef NETWORK
auto localClient = dynamic_cast<ZL::LocalClient*>(networkClient.get());
if (localClient) {
ZL::ClientState st = Environment::shipState;
st.id = localClient->GetClientId();
localClient->setLocalPlayerState(st);
}
#endif
if (networkClient) {
std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType);
@@ -264,8 +249,8 @@ namespace ZL
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
float width = Environment::width;
float height = Environment::height;
float width = Environment::projectionWidth;
float height = Environment::projectionHeight;
renderer.PushProjectionMatrix(
0, width,
@@ -350,16 +335,18 @@ namespace ZL
if (event.type == SDL_QUIT) {
Environment::exitGameLoop = true;
}
#if SDL_VERSION_ATLEAST(2,0,5)
else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
// Обновляем размеры и сбрасываем кеш текстов, т.к. меши хранятся в пикселях
Environment::width = event.window.data1;
Environment::height = event.window.data2;
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
Environment::computeProjectionDimensions();
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
space.clearTextRendererCache();
}
#endif
#ifdef __ANDROID__
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
Environment::exitGameLoop = true;
@@ -368,22 +355,38 @@ namespace ZL
#ifdef __ANDROID__
if (event.type == SDL_FINGERDOWN) {
int mx = static_cast<int>(event.tfinger.x * Environment::width);
int my = static_cast<int>(event.tfinger.y * Environment::height);
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
handleDown(mx, my);
}
else if (event.type == SDL_FINGERUP) {
int mx = static_cast<int>(event.tfinger.x * Environment::width);
int my = static_cast<int>(event.tfinger.y * Environment::height);
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
handleUp(mx, my);
}
else if (event.type == SDL_FINGERMOTION) {
int mx = static_cast<int>(event.tfinger.x * Environment::width);
int my = static_cast<int>(event.tfinger.y * Environment::height);
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
handleMotion(mx, my);
}
#else
if (event.type == SDL_MOUSEBUTTONDOWN) {
if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
// Преобразуем экранные пиксели в проекционные единицы
int mx = static_cast<int>((float)event.button.x / Environment::width * Environment::projectionWidth);
int my = static_cast<int>((float)event.button.y / Environment::height * Environment::projectionHeight);
if (event.type == SDL_MOUSEBUTTONDOWN) handleDown(mx, my);
else handleUp(mx, my);
}
else if (event.type == SDL_MOUSEMOTION) {
int mx = static_cast<int>((float)event.motion.x / Environment::width * Environment::projectionWidth);
int my = static_cast<int>((float)event.motion.y / Environment::height * Environment::projectionHeight);
handleMotion(mx, my);
}
/*if (event.type == SDL_MOUSEBUTTONDOWN) {
int mx = event.button.x;
int my = event.button.y;
handleDown(mx, my);
@@ -397,7 +400,7 @@ namespace ZL
int mx = event.motion.x;
int my = event.motion.y;
handleMotion(mx, my);
}
}*/
if (event.type == SDL_MOUSEWHEEL) {
static const float zoomstep = 2.0f;
@@ -454,7 +457,7 @@ namespace ZL
void Game::handleDown(int mx, int my)
{
int uiX = mx;
int uiY = Environment::height - my;
int uiY = Environment::projectionHeight - my;
menuManager.uiManager.onMouseDown(uiX, uiY);
@@ -482,7 +485,7 @@ namespace ZL
void Game::handleUp(int mx, int my)
{
int uiX = mx;
int uiY = Environment::height - my;
int uiY = Environment::projectionHeight - my;
menuManager.uiManager.onMouseUp(uiX, uiY);
@@ -497,7 +500,7 @@ namespace ZL
void Game::handleMotion(int mx, int my)
{
int uiX = mx;
int uiY = Environment::height - my;
int uiY = Environment::projectionHeight - my;
menuManager.uiManager.onMouseMove(uiX, uiY);
+27 -27
View File
@@ -159,15 +159,15 @@ namespace ZL
// В пределах экрана?
// (можно оставить, можно клампить)
float sx = (ndc.x() * 0.5f + 0.5f) * Environment::width;
float sy = (ndc.y() * 0.5f + 0.5f) * Environment::height;
float sx = (ndc.x() * 0.5f + 0.5f) * Environment::projectionWidth;
float sy = (ndc.y() * 0.5f + 0.5f) * Environment::projectionHeight;
outX = sx;
outY = sy;
// Можно отсеять те, что вне:
if (sx < -200 || sx > Environment::width + 200) return false;
if (sy < -200 || sy > Environment::height + 200) return false;
if (sx < -200 || sx > Environment::projectionWidth + 200) return false;
if (sy < -200 || sy > Environment::projectionHeight + 200) return false;
return true;
}
@@ -296,12 +296,12 @@ namespace ZL
cubemapTexture = std::make_shared<Texture>(
std::array<TextureDataStruct, 6>{
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE)
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE)
});
@@ -859,8 +859,8 @@ namespace ZL
// если ничего не изменилось — не трогаем VBO
if (crosshairMeshValid &&
crosshairLastW == Environment::width &&
crosshairLastH == Environment::height &&
crosshairLastW == Environment::projectionWidth &&
crosshairLastH == Environment::projectionHeight &&
std::abs(crosshairLastAlpha - crosshairCfg.alpha) < 1e-6f &&
std::abs(crosshairLastThickness - crosshairCfg.thicknessPx) < 1e-6f &&
std::abs(crosshairLastGap - crosshairCfg.gapPx) < 1e-6f &&
@@ -869,18 +869,18 @@ namespace ZL
return;
}
crosshairLastW = Environment::width;
crosshairLastH = Environment::height;
crosshairLastW = Environment::projectionWidth;
crosshairLastH = Environment::projectionHeight;
crosshairLastAlpha = crosshairCfg.alpha;
crosshairLastThickness = crosshairCfg.thicknessPx;
crosshairLastGap = crosshairCfg.gapPx;
crosshairLastScaleMul = crosshairCfg.scaleMul;
float cx = Environment::width * 0.5f;
float cy = Environment::height * 0.5f;
float cx = Environment::projectionWidth * 0.5f;
float cy = Environment::projectionHeight * 0.5f;
// масштаб от reference (стандартно: по высоте)
float scale = (crosshairCfg.refH > 0) ? (Environment::height / (float)crosshairCfg.refH) : 1.0f;
float scale = (crosshairCfg.refH > 0) ? (Environment::projectionHeight / (float)crosshairCfg.refH) : 1.0f;
scale *= crosshairCfg.scaleMul;
float thickness = crosshairCfg.thicknessPx * scale;
@@ -940,7 +940,7 @@ namespace ZL
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
renderer.shaderManager.PushShader("defaultColor");
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
renderer.PushMatrix();
renderer.LoadIdentity();
@@ -1187,15 +1187,15 @@ namespace ZL
// 4) Настройки стиля
Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный
float thickness = 10.0f; // толщина линий (px)
float thickness = 2.0f; // толщина линий (px)
float z = 0.0f; // 2D слой
// 5) Если цель в кадре: рисуем скобки
if (onScreen)
{
// перевод NDC -> экран (в пикселях)
float sx = (ndcX * 0.5f + 0.5f) * Environment::width;
float sy = (ndcY * 0.5f + 0.5f) * Environment::height;
float sx = (ndcX * 0.5f + 0.5f) * Environment::projectionWidth;
float sy = (ndcY * 0.5f + 0.5f) * Environment::projectionHeight;
// анимация “снаружи внутрь”
// targetAcquireAnim растёт к 1, быстро (похоже на захват)
@@ -1234,7 +1234,7 @@ namespace ZL
glClear(GL_DEPTH_BUFFER_BIT);
renderer.shaderManager.PushShader("defaultColor");
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
renderer.PushMatrix();
renderer.LoadIdentity();
@@ -1248,8 +1248,8 @@ namespace ZL
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) {
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::width;
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::height;
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
float distLead = (Environment::shipState.position - leadWorld).norm();
float r = 30.0f / (distLead * 0.01f + 1.0f);
@@ -1323,8 +1323,8 @@ namespace ZL
float edgeNdcX = dirX * k;
float edgeNdcY = dirY * k;
float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::width;
float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::height;
float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
float bob = std::sin(t * 6.0f) * 6.0f;
edgeX += dirX * bob;
@@ -1367,7 +1367,7 @@ namespace ZL
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
renderer.shaderManager.PushShader("defaultColor");
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
renderer.PushMatrix();
renderer.LoadIdentity();
+1 -1
View File
@@ -657,7 +657,7 @@ namespace ZL {
}
void UiManager::draw(Renderer& renderer) {
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, -1, 1);
renderer.PushMatrix();
renderer.LoadIdentity();
+45 -22
View File
@@ -44,20 +44,34 @@ EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void
return EM_TRUE;
}
// Resize the canvas, notify SDL, and push a synthetic SDL_WINDOWEVENT_RESIZED
// so Game::update()'s existing handler updates Environment::width/height and clears caches.
static void applyResize(int w, int h) {
if (w <= 0 || h <= 0) return;
// Resize the actual WebGL canvas — without this the rendered pixels stay at
// the original size no matter what Environment::width/height say.
emscripten_set_canvas_element_size("#canvas", w, h);
if (ZL::Environment::window)
SDL_SetWindowSize(ZL::Environment::window, w, h);
static void applyResize(int logicalW, int logicalH) {
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
double dpr = emscripten_get_device_pixel_ratio();
// Вычисляем реальные физические пиксели
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 = w;
e.window.data2 = h;
e.window.data1 = physicalW;
e.window.data2 = physicalH;
SDL_PushEvent(&e);
}
@@ -69,17 +83,11 @@ EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*u
}
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
if (e->isFullscreen) {
// e->screenWidth/screenHeight comes from screen.width/screen.height in JS,
// which on mobile browsers returns physical pixels (e.g. 2340x1080),
// causing the canvas to extend far off-screen. window.innerWidth/innerHeight
// always gives CSS logical pixels and is correct on both desktop and mobile.
int w = EM_ASM_INT({ return window.innerWidth; });
int h = EM_ASM_INT({ return window.innerHeight; });
applyResize(w, h);
}
// Exiting fullscreen: the browser fires a window resize event next,
// which onWindowResized handles automatically.
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
// так как после перехода в FS именно он растягивается на весь экран.
double clientW, clientH;
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
applyResize(clientW, clientH);
return EM_FALSE;
}
@@ -226,6 +234,21 @@ int main(int argc, char *argv[]) {
// 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
+4 -3
View File
@@ -362,9 +362,10 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
// 4. Рендеринг
r->shaderManager.PushShader(shaderName);
// Матрица проекции (экрана)
float W = (float)Environment::width;
float H = (float)Environment::height;
// Матрица проекции — используем виртуальные проекционные размеры,
// чтобы координаты текста были независимы от физического разрешения экрана.
float W = Environment::projectionWidth;
float H = Environment::projectionHeight;
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
proj(0, 0) = 2.0f / W;
proj(1, 1) = 2.0f / H;