Adapting web version
This commit is contained in:
parent
7e5aa22fee
commit
2728ca9646
@ -1,100 +1,76 @@
|
||||
<!doctypehtml>
|
||||
<html lang=en-us>
|
||||
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<meta content="text/html; charset=utf-8" http-equiv=Content-Type>
|
||||
<!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 {
|
||||
font-family: arial;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #000;
|
||||
body, html {
|
||||
margin: 0; padding: 0; width: 100%; height: 100%;
|
||||
overflow: hidden; background-color: #000;
|
||||
position: fixed; /* Предотвращает pull-to-refresh на Android */
|
||||
}
|
||||
|
||||
.emscripten {
|
||||
padding-right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block
|
||||
#canvas {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100vw; height: 100vh;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.emscripten {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
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
|
||||
/* Кнопка 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>
|
||||
</head>
|
||||
<body>
|
||||
<button id="fs-button">Fullscreen</button>
|
||||
<div id="status">Downloading...</div>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||
|
||||
<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>
|
||||
<script>
|
||||
var statusElement = document.getElementById("status");
|
||||
var canvas = document.getElementById("canvas");
|
||||
|
||||
</html>
|
||||
var Module = {
|
||||
canvas: canvas,
|
||||
setStatus: function(text) {
|
||||
statusElement.innerHTML = text;
|
||||
statusElement.style.display = text ? 'block' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
// Кнопка 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();
|
||||
}
|
||||
});
|
||||
|
||||
// Обработка ориентации
|
||||
window.addEventListener("orientationchange", function() {
|
||||
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
||||
// Ждем завершения анимации поворота.
|
||||
setTimeout(() => {
|
||||
// В Emscripten это вызовет ваш onWindowResized в C++
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}, 200);
|
||||
});
|
||||
|
||||
</script>
|
||||
<script async src="space-game001.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
resources/Cargo_Base_color_sRGB.png
(Stored with Git LFS)
BIN
resources/Cargo_Base_color_sRGB.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/DefaultMaterial_BaseColor_shine.png
(Stored with Git LFS)
BIN
resources/DefaultMaterial_BaseColor_shine.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/MainCharacter_Base_color_sRGB.png
(Stored with Git LFS)
BIN
resources/MainCharacter_Base_color_sRGB.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/game_over/Container.png
(Stored with Git LFS)
BIN
resources/game_over/Container.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/game_over/Filledbuttons.png
(Stored with Git LFS)
BIN
resources/game_over/Filledbuttons.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/game_over/FinalScore.png
(Stored with Git LFS)
BIN
resources/game_over/FinalScore.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/game_over/MissionFailed.png
(Stored with Git LFS)
BIN
resources/game_over/MissionFailed.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/game_over/Secondarybutton.png
(Stored with Git LFS)
BIN
resources/game_over/Secondarybutton.png
(Stored with Git LFS)
Binary file not shown.
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
85
src/Game.cpp
85
src/Game.cpp
@ -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>();
|
||||
networkClient->Connect("", 0);
|
||||
|
||||
#ifndef NETWORK
|
||||
auto localClient = dynamic_cast<ZL::LocalClient*>(networkClient.get());
|
||||
if (localClient) {
|
||||
ZL::ClientState st = Environment::shipState;
|
||||
auto localClient = new LocalClient;
|
||||
ClientState st = Environment::shipState;
|
||||
st.id = localClient->GetClientId();
|
||||
localClient->setLocalPlayerState(st);
|
||||
}
|
||||
#endif
|
||||
|
||||
networkClient = std::unique_ptr<INetworkClient>(localClient);
|
||||
networkClient->Connect("", 0);
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
67
src/main.cpp
67
src/main.cpp
@ -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;
|
||||
}
|
||||
|
||||
@ -227,6 +235,21 @@ int main(int argc, char *argv[]) {
|
||||
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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user