Compare commits
No commits in common. "78046e5e2d3cf397191c185b2140b27e38f7ab98" and "7e5aa22fee56cdeef5a6accd7b4344134b7c42d5" have entirely different histories.
78046e5e2d
...
7e5aa22fee
@ -1,76 +1,100 @@
|
|||||||
<!DOCTYPE html>
|
<!doctypehtml>
|
||||||
<html lang="en-us">
|
<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>
|
|
||||||
|
|
||||||
<script>
|
<head>
|
||||||
var statusElement = document.getElementById("status");
|
<meta charset=utf-8>
|
||||||
var canvas = document.getElementById("canvas");
|
<meta content="text/html; charset=utf-8" http-equiv=Content-Type>
|
||||||
|
<title>Space Game</title>
|
||||||
var Module = {
|
<style>
|
||||||
canvas: canvas,
|
body {
|
||||||
setStatus: function(text) {
|
font-family: arial;
|
||||||
statusElement.innerHTML = text;
|
margin: 0;
|
||||||
statusElement.style.display = text ? 'block' : 'none';
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #000;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Кнопка Fullscreen
|
.emscripten {
|
||||||
document.getElementById('fs-button').addEventListener('click', function() {
|
padding-right: 0;
|
||||||
if (!document.fullscreenElement) {
|
margin-left: auto;
|
||||||
document.documentElement.requestFullscreen().catch(e => {
|
margin-right: auto;
|
||||||
console.error(`Error attempting to enable full-screen mode: ${e.message}`);
|
display: block
|
||||||
});
|
|
||||||
} else {
|
|
||||||
document.exitFullscreen();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Обработка ориентации
|
div.emscripten {
|
||||||
window.addEventListener("orientationchange", function() {
|
text-align: center
|
||||||
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
}
|
||||||
// Ждем завершения анимации поворота.
|
|
||||||
setTimeout(() => {
|
|
||||||
// В Emscripten это вызовет ваш onWindowResized в C++
|
|
||||||
window.dispatchEvent(new Event('resize'));
|
|
||||||
}, 200);
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
div.emscripten_border {
|
||||||
<script async src="space-game001.js"></script>
|
border: 1px solid #000
|
||||||
</body>
|
}
|
||||||
</html>
|
|
||||||
|
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>
|
||||||
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)
Normal file
BIN
resources/DefaultMaterial_BaseColor_shine.png
(Stored with Git LFS)
Normal file
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.
@ -1,85 +1,93 @@
|
|||||||
{
|
{
|
||||||
"root": {
|
"root": {
|
||||||
"type": "LinearLayout",
|
"type": "LinearLayout",
|
||||||
"orientation": "vertical",
|
"orientation": "vertical",
|
||||||
"vertical_align": "center",
|
"align": "center",
|
||||||
"horizontal_align": "center",
|
"x": 0,
|
||||||
"spacing": 10,
|
"y": 0,
|
||||||
"x": 0,
|
"width": 1920,
|
||||||
"y": 0,
|
"height": 1080,
|
||||||
"width": "match_parent",
|
"background": {
|
||||||
"height": "match_parent",
|
"color": [0, 0, 0, 0.7]
|
||||||
"children": [
|
},
|
||||||
{
|
"children": [
|
||||||
"type": "Button",
|
{
|
||||||
"name": "gameOverText",
|
"type": "Button",
|
||||||
"width": 327,
|
"name": "gameOverText",
|
||||||
"height": 26,
|
"x": 476.5,
|
||||||
"textures": {
|
"y": 500,
|
||||||
"normal": "resources/game_over/MissionFailed.png",
|
"width": 327,
|
||||||
"hover": "resources/game_over/MissionFailed.png",
|
"height": 26,
|
||||||
"pressed": "resources/game_over/MissionFailed.png"
|
"textures": {
|
||||||
}
|
"normal": "resources/game_over/MissionFailed.png",
|
||||||
},
|
"hover": "resources/game_over/MissionFailed.png",
|
||||||
{
|
"pressed": "resources/game_over/MissionFailed.png"
|
||||||
"type": "Button",
|
}
|
||||||
"name": "underlineBtn",
|
},
|
||||||
"width": 168,
|
{
|
||||||
"height": 44,
|
"type": "Button",
|
||||||
"textures": {
|
"name": "underlineBtn",
|
||||||
"normal": "resources/game_over/Container.png",
|
"x": 556,
|
||||||
"hover": "resources/game_over/Container.png",
|
"y": 465,
|
||||||
"pressed": "resources/game_over/Container.png"
|
"width": 168,
|
||||||
}
|
"height": 44,
|
||||||
},
|
"textures": {
|
||||||
{
|
"normal": "resources/game_over/Container.png",
|
||||||
"type": "Button",
|
"hover": "resources/game_over/Container.png",
|
||||||
"name": "finalscore",
|
"pressed": "resources/game_over/Container.png"
|
||||||
"width": 87,
|
}
|
||||||
"height": 9,
|
},
|
||||||
"textures": {
|
{
|
||||||
"normal": "resources/game_over/FinalScore.png",
|
"type": "Button",
|
||||||
"hover": "resources/game_over/FinalScore.png",
|
"name": "finalscore",
|
||||||
"pressed": "resources/game_over/FinalScore.png"
|
"x": 596.5,
|
||||||
}
|
"y": 436,
|
||||||
},
|
"width": 87,
|
||||||
{
|
"height": 9,
|
||||||
"type": "TextView",
|
"textures": {
|
||||||
"name": "scoreText",
|
"normal": "resources/game_over/FinalScore.png",
|
||||||
"width": 600,
|
"hover": "resources/game_over/FinalScore.png",
|
||||||
"height": 80,
|
"pressed": "resources/game_over/FinalScore.png"
|
||||||
"text": "0",
|
}
|
||||||
"fontSize": 36,
|
},
|
||||||
"color": [
|
{
|
||||||
0,
|
"type": "TextView",
|
||||||
217,
|
"name": "scoreText",
|
||||||
255,
|
"x": 350,
|
||||||
1
|
"y": 356,
|
||||||
],
|
"width": 600,
|
||||||
"align": "center"
|
"height": 80,
|
||||||
},
|
"text": "0",
|
||||||
{
|
"fontSize": 36,
|
||||||
"type": "Button",
|
"color": [0, 217, 255, 1],
|
||||||
"name": "restartButton",
|
"align": "center"
|
||||||
"width": 382,
|
},
|
||||||
"height": 56,
|
{
|
||||||
"textures": {
|
"type": "Button",
|
||||||
"normal": "resources/game_over/Filledbuttons.png",
|
"name": "restartButton",
|
||||||
"hover": "resources/game_over/Filledbuttons.png",
|
"x": 449,
|
||||||
"pressed": "resources/game_over/Filledbuttons.png"
|
"y": 308,
|
||||||
}
|
"width": 382,
|
||||||
},
|
"height": 56,
|
||||||
{
|
"textures": {
|
||||||
"type": "Button",
|
"normal": "resources/game_over/Filledbuttons.png",
|
||||||
"name": "gameOverExitButton",
|
"hover": "resources/game_over/Filledbuttons.png",
|
||||||
"width": 382,
|
"pressed": "resources/game_over/Filledbuttons.png"
|
||||||
"height": 56,
|
}
|
||||||
"textures": {
|
},
|
||||||
"normal": "resources/game_over/Secondarybutton.png",
|
{
|
||||||
"hover": "resources/game_over/Secondarybutton.png",
|
"type": "Button",
|
||||||
"pressed": "resources/game_over/Secondarybutton.png"
|
"name": "gameOverExitButton",
|
||||||
}
|
"x": 449,
|
||||||
}
|
"y": 240,
|
||||||
]
|
"width": 382,
|
||||||
}
|
"height": 56,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/game_over/Secondarybutton.png",
|
||||||
|
"hover": "resources/game_over/Secondarybutton.png",
|
||||||
|
"pressed": "resources/game_over/Secondarybutton.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,93 +0,0 @@
|
|||||||
{
|
|
||||||
"root": {
|
|
||||||
"type": "LinearLayout",
|
|
||||||
"orientation": "vertical",
|
|
||||||
"align": "center",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"width": 1920,
|
|
||||||
"height": 1080,
|
|
||||||
"background": {
|
|
||||||
"color": [0, 0, 0, 0.7]
|
|
||||||
},
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "gameOverText",
|
|
||||||
"x": 476.5,
|
|
||||||
"y": 500,
|
|
||||||
"width": 327,
|
|
||||||
"height": 26,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/game_over/MissionFailed.png",
|
|
||||||
"hover": "resources/game_over/MissionFailed.png",
|
|
||||||
"pressed": "resources/game_over/MissionFailed.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "underlineBtn",
|
|
||||||
"x": 556,
|
|
||||||
"y": 465,
|
|
||||||
"width": 168,
|
|
||||||
"height": 44,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/game_over/Container.png",
|
|
||||||
"hover": "resources/game_over/Container.png",
|
|
||||||
"pressed": "resources/game_over/Container.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "finalscore",
|
|
||||||
"x": 596.5,
|
|
||||||
"y": 436,
|
|
||||||
"width": 87,
|
|
||||||
"height": 9,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/game_over/FinalScore.png",
|
|
||||||
"hover": "resources/game_over/FinalScore.png",
|
|
||||||
"pressed": "resources/game_over/FinalScore.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "TextView",
|
|
||||||
"name": "scoreText",
|
|
||||||
"x": 350,
|
|
||||||
"y": 356,
|
|
||||||
"width": 600,
|
|
||||||
"height": 80,
|
|
||||||
"text": "0",
|
|
||||||
"fontSize": 36,
|
|
||||||
"color": [0, 217, 255, 1],
|
|
||||||
"align": "center"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "restartButton",
|
|
||||||
"x": 449,
|
|
||||||
"y": 308,
|
|
||||||
"width": 382,
|
|
||||||
"height": 56,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/game_over/Filledbuttons.png",
|
|
||||||
"hover": "resources/game_over/Filledbuttons.png",
|
|
||||||
"pressed": "resources/game_over/Filledbuttons.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "gameOverExitButton",
|
|
||||||
"x": 449,
|
|
||||||
"y": 240,
|
|
||||||
"width": 382,
|
|
||||||
"height": 56,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/game_over/Secondarybutton.png",
|
|
||||||
"hover": "resources/game_over/Secondarybutton.png",
|
|
||||||
"pressed": "resources/game_over/Secondarybutton.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,81 +1,141 @@
|
|||||||
{
|
{
|
||||||
"root": {
|
"root": {
|
||||||
"type": "LinearLayout",
|
"type": "FrameLayout",
|
||||||
"orientation": "vertical",
|
"x": 0,
|
||||||
"vertical_align": "center",
|
"y": 0,
|
||||||
"horizontal_align": "center",
|
"width": 1280,
|
||||||
"spacing": 10,
|
"height": 720,
|
||||||
"x": 0,
|
"children": [
|
||||||
"y": 0,
|
{
|
||||||
"width": "match_parent",
|
"type": "LinearLayout",
|
||||||
"height": "match_parent",
|
"name": "settingsButtons",
|
||||||
"children": [
|
"orientation": "vertical",
|
||||||
{
|
"spacing": 10,
|
||||||
"type": "Button",
|
"x": 0,
|
||||||
"name": "titleBtn",
|
"y": 0,
|
||||||
"width": 254,
|
"width": 300,
|
||||||
"height": 35,
|
"height": 300,
|
||||||
"textures": {
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "langButton",
|
||||||
|
"x": 1100,
|
||||||
|
"y": 580,
|
||||||
|
"width": 142,
|
||||||
|
"height": 96,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/main_menu/lang.png",
|
||||||
|
"hover": "resources/main_menu/lang.png",
|
||||||
|
"pressed": "resources/main_menu/lang.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "titleBtn",
|
||||||
|
"x": 473,
|
||||||
|
"y": 500,
|
||||||
|
"width": 254,
|
||||||
|
"height": 35,
|
||||||
|
"textures": {
|
||||||
"normal": "resources/main_menu/title.png",
|
"normal": "resources/main_menu/title.png",
|
||||||
"hover": "resources/main_menu/title.png",
|
"hover": "resources/main_menu/title.png",
|
||||||
"pressed": "resources/main_menu/title.png"
|
"pressed": "resources/main_menu/title.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "underlineBtn",
|
"name": "underlineBtn",
|
||||||
"width": 168,
|
"x": 516,
|
||||||
"height": 44,
|
"y": 465,
|
||||||
"textures": {
|
"width": 168,
|
||||||
|
"height": 44,
|
||||||
|
"textures": {
|
||||||
"normal": "resources/main_menu/line.png",
|
"normal": "resources/main_menu/line.png",
|
||||||
"hover": "resources/main_menu/line.png",
|
"hover": "resources/main_menu/line.png",
|
||||||
"pressed": "resources/main_menu/line.png"
|
"pressed": "resources/main_menu/line.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "subtitleBtn",
|
"name": "subtitleBtn",
|
||||||
"width": 144,
|
"x": 528,
|
||||||
"height": 11,
|
"y": 455,
|
||||||
"textures": {
|
"width": 144,
|
||||||
|
"height": 11,
|
||||||
|
"textures": {
|
||||||
"normal": "resources/main_menu/subtitle.png",
|
"normal": "resources/main_menu/subtitle.png",
|
||||||
"hover": "resources/main_menu/subtitle.png",
|
"hover": "resources/main_menu/subtitle.png",
|
||||||
"pressed": "resources/main_menu/subtitle.png"
|
"pressed": "resources/main_menu/subtitle.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "singleButton",
|
"name": "singleButton",
|
||||||
"width": 382,
|
"x": 409,
|
||||||
"height": 56,
|
"y": 360,
|
||||||
"textures": {
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"textures": {
|
||||||
"normal": "resources/main_menu/single.png",
|
"normal": "resources/main_menu/single.png",
|
||||||
"hover": "resources/main_menu/single.png",
|
"hover": "resources/main_menu/single.png",
|
||||||
"pressed": "resources/main_menu/single.png"
|
"pressed": "resources/main_menu/single.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "multiplayerButton",
|
"name": "multiplayerButton",
|
||||||
"width": 382,
|
"x": 409,
|
||||||
"height": 56,
|
"y": 289,
|
||||||
"textures": {
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"textures": {
|
||||||
"normal": "resources/main_menu/multi.png",
|
"normal": "resources/main_menu/multi.png",
|
||||||
"hover": "resources/main_menu/multi.png",
|
"hover": "resources/main_menu/multi.png",
|
||||||
"pressed": "resources/main_menu/multi.png"
|
"pressed": "resources/main_menu/multi.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "versionLabel",
|
"name": "multiplayerButton2",
|
||||||
"width": 81,
|
"x": 409,
|
||||||
"height": 9,
|
"y": 218,
|
||||||
"textures": {
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/main_menu/multi.png",
|
||||||
|
"hover": "resources/main_menu/multi.png",
|
||||||
|
"pressed": "resources/main_menu/multi.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "exitButton",
|
||||||
|
"x": 409,
|
||||||
|
"y": 147,
|
||||||
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/main_menu/exit.png",
|
||||||
|
"hover": "resources/main_menu/exit.png",
|
||||||
|
"pressed": "resources/main_menu/exit.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "versionLabel",
|
||||||
|
"x": 559.5,
|
||||||
|
"y": 99,
|
||||||
|
"width": 81,
|
||||||
|
"height": 9,
|
||||||
|
"textures": {
|
||||||
"normal": "resources/main_menu/version.png",
|
"normal": "resources/main_menu/version.png",
|
||||||
"hover": "resources/main_menu/version.png",
|
"hover": "resources/main_menu/version.png",
|
||||||
"pressed": "resources/main_menu/version.png"
|
"pressed": "resources/main_menu/version.png"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@ -1,59 +1,64 @@
|
|||||||
{
|
{
|
||||||
"root": {
|
"root": {
|
||||||
"type": "LinearLayout",
|
"name": "shipSelectionRoot",
|
||||||
"orientation": "vertical",
|
"type": "node",
|
||||||
"vertical_align": "center",
|
"children": [
|
||||||
"horizontal_align": "center",
|
|
||||||
"spacing": 10,
|
{
|
||||||
"x": 0,
|
"type": "TextField",
|
||||||
"y": 0,
|
"name": "nicknameInput",
|
||||||
"width": "match_parent",
|
"x": 400,
|
||||||
"height": "match_parent",
|
"y": 150,
|
||||||
"children": [
|
"width": 400,
|
||||||
{
|
"height": 50,
|
||||||
"type": "LinearLayout",
|
"placeholder": "Enter your nickname",
|
||||||
"orientation": "horizontal",
|
"fontPath": "resources/fonts/DroidSans.ttf",
|
||||||
"vertical_align": "center",
|
"fontSize": 16,
|
||||||
"horizontal_align": "center",
|
"maxLength": 256,
|
||||||
"spacing": 10,
|
"color": [122, 156, 198, 1],
|
||||||
"width": "match_parent",
|
"placeholderColor": [122, 156, 198, 1],
|
||||||
"height": 260,
|
"backgroundColor": [15, 29, 51, 1],
|
||||||
"children": [
|
"borderColor": [15, 29, 51, 1]
|
||||||
{
|
},
|
||||||
"type": "Button",
|
{
|
||||||
"name": "spaceshipButton",
|
"type": "Button",
|
||||||
"width": 256,
|
"name": "spaceshipButton",
|
||||||
"height": 256,
|
"x": 300,
|
||||||
"textures": {
|
"y": 320,
|
||||||
"normal": "resources/multiplayer_menu/ship_fighter.png",
|
"width": 200,
|
||||||
"hover": "resources/multiplayer_menu/ship_fighter_pressed.png",
|
"height": 80,
|
||||||
"pressed": "resources/multiplayer_menu/ship_fighter_pressed.png"
|
"textures": {
|
||||||
}
|
"normal": "resources/multiplayer_menu/JoinServer.png",
|
||||||
},
|
"hover": "resources/multiplayer_menu/JoinServer.png",
|
||||||
{
|
"pressed": "resources/multiplayer_menu/JoinServer.png"
|
||||||
"type": "Button",
|
|
||||||
"name": "cargoshipButton",
|
|
||||||
"width": 256,
|
|
||||||
"height": 256,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/multiplayer_menu/ship_cargo.png",
|
|
||||||
"hover": "resources/multiplayer_menu/ship_cargo_pressed.png",
|
|
||||||
"pressed": "resources/multiplayer_menu/ship_cargo_pressed.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "backButton",
|
|
||||||
"width": 382,
|
|
||||||
"height": 56,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/multiplayer_menu/Backbutton.png",
|
|
||||||
"hover": "resources/multiplayer_menu/Backbutton.png",
|
|
||||||
"pressed": "resources/multiplayer_menu/Backbutton.png"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
}
|
{
|
||||||
}
|
"type": "Button",
|
||||||
|
"name": "cargoshipButton",
|
||||||
|
"x": 700,
|
||||||
|
"y": 320,
|
||||||
|
"width": 200,
|
||||||
|
"height": 80,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/multiplayer_menu/JoinServer.png",
|
||||||
|
"hover": "resources/multiplayer_menu/JoinServer.png",
|
||||||
|
"pressed": "resources/multiplayer_menu/JoinServer.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "backButton",
|
||||||
|
"x": 449,
|
||||||
|
"y": 280,
|
||||||
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/multiplayer_menu/Backbutton.png",
|
||||||
|
"hover": "resources/multiplayer_menu/Backbutton.png",
|
||||||
|
"pressed": "resources/multiplayer_menu/Backbutton.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,57 +1,194 @@
|
|||||||
{
|
{
|
||||||
"root": {
|
"root": {
|
||||||
"type": "FrameLayout",
|
"type": "FrameLayout",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"width": "match_parent",
|
"width": 1280,
|
||||||
"height": "match_parent",
|
"height": 720,
|
||||||
"children": [
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "leftPanel",
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"width": 320,
|
||||||
|
"height": 400,
|
||||||
|
"children": [
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "LinearLayout",
|
||||||
"name": "shootButton",
|
"name": "mainButtons",
|
||||||
"x": 0,
|
"orientation": "vertical",
|
||||||
"y": 0,
|
"spacing": 10,
|
||||||
"width": 150,
|
"x": 0,
|
||||||
"height": 150,
|
"y": 0,
|
||||||
"horizontal_gravity": "right",
|
"width": 300,
|
||||||
"vertical_gravity": "bottom",
|
"height": 300,
|
||||||
"textures": {
|
"children": [
|
||||||
"normal": "resources/shoot_normal.png",
|
{
|
||||||
"hover": "resources/shoot_hover.png",
|
"type": "Button",
|
||||||
"pressed": "resources/shoot_pressed.png"
|
"name": "playButton",
|
||||||
}
|
"x": -1000,
|
||||||
},
|
"y": 500,
|
||||||
{
|
"width": 200,
|
||||||
"type": "Button",
|
"height": 50,
|
||||||
"name": "shootButton2",
|
"animations": {
|
||||||
"x": 0,
|
"buttonsExit": {
|
||||||
"y": 0,
|
"repeat": false,
|
||||||
"width": 150,
|
"steps": [
|
||||||
"height": 150,
|
{
|
||||||
"horizontal_gravity": "left",
|
"type": "move",
|
||||||
"vertical_gravity": "bottom",
|
"to": [
|
||||||
"textures": {
|
-400,
|
||||||
"normal": "resources/shoot_normal.png",
|
0
|
||||||
"hover": "resources/shoot_hover.png",
|
],
|
||||||
"pressed": "resources/shoot_pressed.png"
|
"duration": 1.0,
|
||||||
}
|
"easing": "easein"
|
||||||
},
|
}
|
||||||
{
|
]
|
||||||
"type": "Slider",
|
}
|
||||||
"name": "velocitySlider",
|
},
|
||||||
"x": 10,
|
"textures": {
|
||||||
"y": 200,
|
"normal": "./resources/sand2.png",
|
||||||
"width": 80,
|
"hover": "./resources/sand2.png",
|
||||||
"height": 300,
|
"pressed": "./resources/sand2.png"
|
||||||
"value": 0.0,
|
}
|
||||||
"orientation": "vertical",
|
},
|
||||||
"horizontal_gravity": "right",
|
{
|
||||||
"vertical_gravity": "bottom",
|
"type": "Button",
|
||||||
"textures": {
|
"name": "settingsButton",
|
||||||
"track": "resources/velocitySliderTexture.png",
|
"x": -1000,
|
||||||
"knob": "resources/velocitySliderButton.png"
|
"y": 400,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "wait",
|
||||||
|
"duration": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/sand2.png",
|
||||||
|
"hover": "./resources/sand2.png",
|
||||||
|
"pressed": "./resources/sand2.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "exitButton",
|
||||||
|
"x": -1000,
|
||||||
|
"y": 300,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "wait",
|
||||||
|
"duration": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bgScroll": {
|
||||||
|
"repeat": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
1280,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 5.0,
|
||||||
|
"easing": "linear"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/sand2.png",
|
||||||
|
"hover": "./resources/sand2.png",
|
||||||
|
"pressed": "./resources/sand2.png"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Slider",
|
||||||
|
"name": "velocitySlider",
|
||||||
|
"x": 1140,
|
||||||
|
"y": 300,
|
||||||
|
"width": 50,
|
||||||
|
"height": 300,
|
||||||
|
"value": 0.0,
|
||||||
|
"orientation": "vertical",
|
||||||
|
"textures": {
|
||||||
|
"track": "resources/velocitySliderTexture.png",
|
||||||
|
"knob": "resources/velocitySliderButton.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "shootButton",
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"width": 100,
|
||||||
|
"height": 100,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/shoot_normal.png",
|
||||||
|
"hover": "resources/shoot_hover.png",
|
||||||
|
"pressed": "resources/shoot_pressed.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "shootButton2",
|
||||||
|
"x": 1000,
|
||||||
|
"y": 100,
|
||||||
|
"width": 100,
|
||||||
|
"height": 100,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/shoot_normal.png",
|
||||||
|
"hover": "resources/shoot_hover.png",
|
||||||
|
"pressed": "resources/shoot_pressed.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TextView",
|
||||||
|
"name": "velocityText",
|
||||||
|
"x": 10,
|
||||||
|
"y": 10,
|
||||||
|
"width": 200,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Velocity: 0",
|
||||||
|
"fontSize": 24,
|
||||||
|
"color": [1.0, 1.0, 1.0, 1.0],
|
||||||
|
"centered": false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,194 +0,0 @@
|
|||||||
{
|
|
||||||
"root": {
|
|
||||||
"type": "FrameLayout",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"width": 1280,
|
|
||||||
"height": 720,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "FrameLayout",
|
|
||||||
"name": "leftPanel",
|
|
||||||
"x": 100,
|
|
||||||
"y": 100,
|
|
||||||
"width": 320,
|
|
||||||
"height": 400,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "LinearLayout",
|
|
||||||
"name": "mainButtons",
|
|
||||||
"orientation": "vertical",
|
|
||||||
"spacing": 10,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"width": 300,
|
|
||||||
"height": 300,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "playButton",
|
|
||||||
"x": -1000,
|
|
||||||
"y": 500,
|
|
||||||
"width": 200,
|
|
||||||
"height": 50,
|
|
||||||
"animations": {
|
|
||||||
"buttonsExit": {
|
|
||||||
"repeat": false,
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"type": "move",
|
|
||||||
"to": [
|
|
||||||
-400,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"duration": 1.0,
|
|
||||||
"easing": "easein"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"textures": {
|
|
||||||
"normal": "./resources/sand2.png",
|
|
||||||
"hover": "./resources/sand2.png",
|
|
||||||
"pressed": "./resources/sand2.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "settingsButton",
|
|
||||||
"x": -1000,
|
|
||||||
"y": 400,
|
|
||||||
"width": 200,
|
|
||||||
"height": 50,
|
|
||||||
"animations": {
|
|
||||||
"buttonsExit": {
|
|
||||||
"repeat": false,
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"type": "wait",
|
|
||||||
"duration": 0.5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "move",
|
|
||||||
"to": [
|
|
||||||
-400,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"duration": 1.0,
|
|
||||||
"easing": "easein"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"textures": {
|
|
||||||
"normal": "./resources/sand2.png",
|
|
||||||
"hover": "./resources/sand2.png",
|
|
||||||
"pressed": "./resources/sand2.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "exitButton",
|
|
||||||
"x": -1000,
|
|
||||||
"y": 300,
|
|
||||||
"width": 200,
|
|
||||||
"height": 50,
|
|
||||||
"animations": {
|
|
||||||
"buttonsExit": {
|
|
||||||
"repeat": false,
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"type": "wait",
|
|
||||||
"duration": 1.0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "move",
|
|
||||||
"to": [
|
|
||||||
-400,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"duration": 1.0,
|
|
||||||
"easing": "easein"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bgScroll": {
|
|
||||||
"repeat": true,
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"type": "move",
|
|
||||||
"to": [
|
|
||||||
1280,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"duration": 5.0,
|
|
||||||
"easing": "linear"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"textures": {
|
|
||||||
"normal": "./resources/sand2.png",
|
|
||||||
"hover": "./resources/sand2.png",
|
|
||||||
"pressed": "./resources/sand2.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Slider",
|
|
||||||
"name": "velocitySlider",
|
|
||||||
"x": 1140,
|
|
||||||
"y": 300,
|
|
||||||
"width": 50,
|
|
||||||
"height": 300,
|
|
||||||
"value": 0.0,
|
|
||||||
"orientation": "vertical",
|
|
||||||
"textures": {
|
|
||||||
"track": "resources/velocitySliderTexture.png",
|
|
||||||
"knob": "resources/velocitySliderButton.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "shootButton",
|
|
||||||
"x": 100,
|
|
||||||
"y": 100,
|
|
||||||
"width": 100,
|
|
||||||
"height": 100,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/shoot_normal.png",
|
|
||||||
"hover": "resources/shoot_hover.png",
|
|
||||||
"pressed": "resources/shoot_pressed.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "shootButton2",
|
|
||||||
"x": 1000,
|
|
||||||
"y": 100,
|
|
||||||
"width": 100,
|
|
||||||
"height": 100,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/shoot_normal.png",
|
|
||||||
"hover": "resources/shoot_hover.png",
|
|
||||||
"pressed": "resources/shoot_pressed.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "TextView",
|
|
||||||
"name": "velocityText",
|
|
||||||
"x": 10,
|
|
||||||
"y": 10,
|
|
||||||
"width": 200,
|
|
||||||
"height": 40,
|
|
||||||
"text": "Velocity: 0",
|
|
||||||
"fontSize": 24,
|
|
||||||
"color": [1.0, 1.0, 1.0, 1.0],
|
|
||||||
"centered": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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.
BIN
resources/multiplayer_menu/ship_cargo.png
(Stored with Git LFS)
BIN
resources/multiplayer_menu/ship_cargo.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/multiplayer_menu/ship_cargo_pressed.png
(Stored with Git LFS)
BIN
resources/multiplayer_menu/ship_cargo_pressed.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/multiplayer_menu/ship_fighter.png
(Stored with Git LFS)
BIN
resources/multiplayer_menu/ship_fighter.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/multiplayer_menu/ship_fighter_pressed.png
(Stored with Git LFS)
BIN
resources/multiplayer_menu/ship_fighter_pressed.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/spark2.png
(Stored with Git LFS)
BIN
resources/spark2.png
(Stored with Git LFS)
Binary file not shown.
@ -23,15 +23,6 @@ namespace http = beast::http;
|
|||||||
namespace websocket = beast::websocket;
|
namespace websocket = beast::websocket;
|
||||||
namespace net = boost::asio;
|
namespace net = boost::asio;
|
||||||
using tcp = net::ip::tcp;
|
using tcp = net::ip::tcp;
|
||||||
static constexpr float kWorldZOffset = 45000.0f;
|
|
||||||
static const Eigen::Vector3f kWorldOffset(0.0f, 0.0f, kWorldZOffset);
|
|
||||||
|
|
||||||
static constexpr float kShipRadius = 15.0f;
|
|
||||||
static constexpr float kSpawnShipMargin = 25.0f;
|
|
||||||
static constexpr float kSpawnBoxMargin = 15.0f;
|
|
||||||
static constexpr float kSpawnZJitter = 60.0f;
|
|
||||||
|
|
||||||
Eigen::Vector3f PickSafeSpawnPos(int forPlayerId);
|
|
||||||
|
|
||||||
struct DeathInfo {
|
struct DeathInfo {
|
||||||
int targetId = -1;
|
int targetId = -1;
|
||||||
@ -62,7 +53,7 @@ struct Projectile {
|
|||||||
uint64_t spawnMs = 0;
|
uint64_t spawnMs = 0;
|
||||||
Eigen::Vector3f pos;
|
Eigen::Vector3f pos;
|
||||||
Eigen::Vector3f vel;
|
Eigen::Vector3f vel;
|
||||||
float lifeMs = PROJECTILE_LIFE;
|
float lifeMs = 5000.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BoxDestroyedInfo {
|
struct BoxDestroyedInfo {
|
||||||
@ -101,10 +92,6 @@ class Session : public std::enable_shared_from_this<Session> {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ClientStateInterval timedClientStates;
|
ClientStateInterval timedClientStates;
|
||||||
bool joined_ = false;
|
|
||||||
|
|
||||||
bool hasReservedSpawn_ = false;
|
|
||||||
Eigen::Vector3f reservedSpawn_ = Eigen::Vector3f(0.0f, 0.0f, kWorldZOffset);
|
|
||||||
|
|
||||||
std::string nickname = "Player";
|
std::string nickname = "Player";
|
||||||
int shipType = 0;
|
int shipType = 0;
|
||||||
@ -115,9 +102,6 @@ public:
|
|||||||
|
|
||||||
int get_id() const { return id_; }
|
int get_id() const { return id_; }
|
||||||
|
|
||||||
bool hasSpawnReserved() const { return hasReservedSpawn_; }
|
|
||||||
const Eigen::Vector3f& reservedSpawn() const { return reservedSpawn_; }
|
|
||||||
|
|
||||||
bool fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const {
|
bool fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const {
|
||||||
if (timedClientStates.canFetchClientStateAtTime(targetTime)) {
|
if (timedClientStates.canFetchClientStateAtTime(targetTime)) {
|
||||||
outState = timedClientStates.fetchClientStateAtTime(targetTime);
|
outState = timedClientStates.fetchClientStateAtTime(targetTime);
|
||||||
@ -205,11 +189,7 @@ public:
|
|||||||
timer->expires_after(std::chrono::milliseconds(100));
|
timer->expires_after(std::chrono::milliseconds(100));
|
||||||
timer->async_wait([self = shared_from_this(), timer](const boost::system::error_code& ec) {
|
timer->async_wait([self = shared_from_this(), timer](const boost::system::error_code& ec) {
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
auto now_tp = std::chrono::system_clock::now();
|
self->send_message("ID:" + std::to_string(self->id_));
|
||||||
uint64_t now_ms = static_cast<uint64_t>(
|
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(now_tp.time_since_epoch()).count());
|
|
||||||
|
|
||||||
self->send_message("ID:" + std::to_string(self->id_) + ":" + std::to_string(now_ms));
|
|
||||||
self->do_read();
|
self->do_read();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -301,45 +281,7 @@ private:
|
|||||||
|
|
||||||
this->nickname = nick;
|
this->nickname = nick;
|
||||||
this->shipType = sType;
|
this->shipType = sType;
|
||||||
this->joined_ = true;
|
|
||||||
|
|
||||||
auto now_tp = std::chrono::system_clock::now();
|
|
||||||
uint64_t now_ms = static_cast<uint64_t>(
|
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(now_tp.time_since_epoch()).count());
|
|
||||||
|
|
||||||
Eigen::Vector3f spawnPos = PickSafeSpawnPos(id_);
|
|
||||||
this->hasReservedSpawn_ = true;
|
|
||||||
this->reservedSpawn_ = spawnPos;
|
|
||||||
|
|
||||||
ClientState st;
|
|
||||||
st.id = id_;
|
|
||||||
st.position = spawnPos;
|
|
||||||
st.rotation = Eigen::Matrix3f::Identity();
|
|
||||||
st.currentAngularVelocity = Eigen::Vector3f::Zero();
|
|
||||||
st.velocity = 0.0f;
|
|
||||||
st.selectedVelocity = 0;
|
|
||||||
st.discreteMag = 0.0f;
|
|
||||||
st.discreteAngle = -1;
|
|
||||||
st.lastUpdateServerTime = now_tp;
|
|
||||||
st.nickname = this->nickname;
|
|
||||||
st.shipType = this->shipType;
|
|
||||||
|
|
||||||
timedClientStates.add_state(st);
|
|
||||||
|
|
||||||
this->send_message(
|
|
||||||
"SPAWN:" + std::to_string(id_) + ":" + std::to_string(now_ms) + ":" + st.formPingMessageContent()
|
|
||||||
);
|
|
||||||
|
|
||||||
std::string eventMsg =
|
|
||||||
"EVENT:" + std::to_string(id_) + ":UPD:" + std::to_string(now_ms) + ":" + st.formPingMessageContent();
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
|
||||||
for (auto& session : g_sessions) {
|
|
||||||
if (session->get_id() == id_) continue;
|
|
||||||
session->send_message(eventMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout << "Server: Player " << id_ << " joined as [" << nick << "] shipType=" << sType << std::endl;
|
std::cout << "Server: Player " << id_ << " joined as [" << nick << "] shipType=" << sType << std::endl;
|
||||||
|
|
||||||
std::string info = "PLAYERINFO:" + std::to_string(id_) + ":" + nick + ":" + std::to_string(sType);
|
std::string info = "PLAYERINFO:" + std::to_string(id_) + ":" + nick + ":" + std::to_string(sType);
|
||||||
@ -356,16 +298,12 @@ private:
|
|||||||
for (auto& session : g_sessions) {
|
for (auto& session : g_sessions) {
|
||||||
if (session->get_id() == this->id_) continue;
|
if (session->get_id() == this->id_) continue;
|
||||||
std::string otherInfo = "PLAYERINFO:" + std::to_string(session->get_id()) + ":" + session->nickname + ":" + std::to_string(session->shipType);
|
std::string otherInfo = "PLAYERINFO:" + std::to_string(session->get_id()) + ":" + session->nickname + ":" + std::to_string(session->shipType);
|
||||||
|
// Отправляем именно новому клиенту
|
||||||
this->send_message(otherInfo);
|
this->send_message(otherInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == "UPD") {
|
else if (type == "UPD") {
|
||||||
if (!joined_) {
|
|
||||||
std::cout << "Server: Ignoring UPD before JOIN from " << id_ << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> gd(g_dead_mutex);
|
std::lock_guard<std::mutex> gd(g_dead_mutex);
|
||||||
if (g_dead_players.find(id_) != g_dead_players.end()) {
|
if (g_dead_players.find(id_) != g_dead_players.end()) {
|
||||||
@ -400,13 +338,7 @@ private:
|
|||||||
|
|
||||||
ClientState st;
|
ClientState st;
|
||||||
st.id = id_;
|
st.id = id_;
|
||||||
|
st.position = Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
|
||||||
Eigen::Vector3f spawnPos = PickSafeSpawnPos(id_);
|
|
||||||
st.position = spawnPos;
|
|
||||||
|
|
||||||
this->hasReservedSpawn_ = true;
|
|
||||||
this->reservedSpawn_ = spawnPos;
|
|
||||||
|
|
||||||
st.rotation = Eigen::Matrix3f::Identity();
|
st.rotation = Eigen::Matrix3f::Identity();
|
||||||
st.currentAngularVelocity = Eigen::Vector3f::Zero();
|
st.currentAngularVelocity = Eigen::Vector3f::Zero();
|
||||||
st.velocity = 0.0f;
|
st.velocity = 0.0f;
|
||||||
@ -418,9 +350,7 @@ private:
|
|||||||
st.shipType = this->shipType;
|
st.shipType = this->shipType;
|
||||||
|
|
||||||
timedClientStates.add_state(st);
|
timedClientStates.add_state(st);
|
||||||
this->send_message(
|
|
||||||
"SPAWN:" + std::to_string(id_) + ":" + std::to_string(now_ms) + ":" + st.formPingMessageContent()
|
|
||||||
);
|
|
||||||
std::string respawnMsg = "RESPAWN_ACK:" + std::to_string(id_);
|
std::string respawnMsg = "RESPAWN_ACK:" + std::to_string(id_);
|
||||||
broadcastToAll(respawnMsg);
|
broadcastToAll(respawnMsg);
|
||||||
|
|
||||||
@ -506,71 +436,6 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Eigen::Vector3f PickSafeSpawnPos(int forPlayerId)
|
|
||||||
{
|
|
||||||
static thread_local std::mt19937 rng{ std::random_device{}() };
|
|
||||||
|
|
||||||
std::scoped_lock lock(g_boxes_mutex, g_sessions_mutex, g_dead_mutex);
|
|
||||||
|
|
||||||
auto isSafe = [&](const Eigen::Vector3f& pWorld) -> bool
|
|
||||||
{
|
|
||||||
for (const auto& box : g_serverBoxes) {
|
|
||||||
if (box.destroyed) continue;
|
|
||||||
|
|
||||||
Eigen::Vector3f boxWorld = box.position + kWorldOffset;
|
|
||||||
float minDist = kShipRadius + box.collisionRadius + kSpawnBoxMargin;
|
|
||||||
|
|
||||||
if ((pWorld - boxWorld).squaredNorm() < minDist * minDist)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& s : g_sessions) {
|
|
||||||
int pid = s->get_id();
|
|
||||||
if (pid == forPlayerId) continue;
|
|
||||||
if (g_dead_players.count(pid)) continue;
|
|
||||||
|
|
||||||
Eigen::Vector3f otherPos;
|
|
||||||
if (!s->timedClientStates.timedStates.empty()) {
|
|
||||||
otherPos = s->timedClientStates.timedStates.back().position;
|
|
||||||
}
|
|
||||||
else if (s->hasSpawnReserved()) {
|
|
||||||
otherPos = s->reservedSpawn();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float minDist = (kShipRadius * 2.0f) + kSpawnShipMargin;
|
|
||||||
if ((pWorld - otherPos).squaredNorm() < minDist * minDist)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const float radii[] = { 150.f, 250.f, 400.f, 650.f, 1000.f, 1600.f };
|
|
||||||
|
|
||||||
for (float r : radii) {
|
|
||||||
std::uniform_real_distribution<float> dxy(-r, r);
|
|
||||||
std::uniform_real_distribution<float> dz(-kSpawnZJitter, kSpawnZJitter);
|
|
||||||
|
|
||||||
for (int attempt = 0; attempt < 250; ++attempt) {
|
|
||||||
Eigen::Vector3f cand(
|
|
||||||
dxy(rng),
|
|
||||||
dxy(rng),
|
|
||||||
kWorldZOffset + dz(rng)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isSafe(cand))
|
|
||||||
return cand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int a = (forPlayerId % 10);
|
|
||||||
int b = ((forPlayerId / 10) % 10);
|
|
||||||
return Eigen::Vector3f(600.0f + a * 100.0f, -600.0f + b * 100.0f, kWorldZOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void broadcastToAll(const std::string& message) {
|
void broadcastToAll(const std::string& message) {
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
||||||
for (const auto& session : g_sessions) {
|
for (const auto& session : g_sessions) {
|
||||||
@ -699,12 +564,11 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- Tick: box-projectile collisions ---
|
// --- Tick: box-projectile collisions ---
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> bm(g_boxes_mutex);
|
std::lock_guard<std::mutex> bm(g_boxes_mutex);
|
||||||
|
const float projectileHitRadius = 5.0f;
|
||||||
|
const float boxCollisionRadius = 2.0f;
|
||||||
|
|
||||||
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
|
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
|
||||||
|
|
||||||
@ -750,6 +614,9 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
|
|||||||
std::lock_guard<std::mutex> bm(g_boxes_mutex);
|
std::lock_guard<std::mutex> bm(g_boxes_mutex);
|
||||||
std::lock_guard<std::mutex> lm(g_sessions_mutex);
|
std::lock_guard<std::mutex> lm(g_sessions_mutex);
|
||||||
|
|
||||||
|
const float shipCollisionRadius = 15.0f;
|
||||||
|
const float boxCollisionRadius = 2.0f;
|
||||||
|
|
||||||
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
|
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
|
||||||
if (g_serverBoxes[bi].destroyed) continue;
|
if (g_serverBoxes[bi].destroyed) continue;
|
||||||
|
|
||||||
@ -838,8 +705,8 @@ std::vector<ServerBox> generateServerBoxes(int count) {
|
|||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 gen(rd());
|
std::mt19937 gen(rd());
|
||||||
|
|
||||||
const float MIN_COORD = -1000.0f;
|
const float MIN_COORD = -100.0f;
|
||||||
const float MAX_COORD = 1000.0f;
|
const float MAX_COORD = 100.0f;
|
||||||
const float MIN_DISTANCE = 3.0f;
|
const float MIN_DISTANCE = 3.0f;
|
||||||
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
|
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
|
||||||
const int MAX_ATTEMPTS = 1000;
|
const int MAX_ATTEMPTS = 1000;
|
||||||
|
|||||||
@ -36,25 +36,5 @@ ClientState Environment::shipState;
|
|||||||
const float Environment::CONST_Z_NEAR = 5.f;
|
const float Environment::CONST_Z_NEAR = 5.f;
|
||||||
const float Environment::CONST_Z_FAR = 5000.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
|
} // namespace ZL
|
||||||
|
|||||||
@ -35,15 +35,8 @@ public:
|
|||||||
static const float CONST_Z_NEAR;
|
static const float CONST_Z_NEAR;
|
||||||
static const float CONST_Z_FAR;
|
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
|
} // namespace ZL
|
||||||
|
|||||||
112
src/Game.cpp
112
src/Game.cpp
@ -16,7 +16,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef NETWORK
|
#ifdef NETWORK
|
||||||
#include "network/WebSocketClientBase.h"
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
#include "network/WebSocketClientEmscripten.h"
|
#include "network/WebSocketClientEmscripten.h"
|
||||||
#else
|
#else
|
||||||
@ -84,8 +83,6 @@ namespace ZL
|
|||||||
void Game::setup() {
|
void Game::setup() {
|
||||||
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
||||||
|
|
||||||
Environment::computeProjectionDimensions();
|
|
||||||
|
|
||||||
ZL::BindOpenGlFunctions();
|
ZL::BindOpenGlFunctions();
|
||||||
ZL::CheckGlError();
|
ZL::CheckGlError();
|
||||||
renderer.InitOpenGL();
|
renderer.InitOpenGL();
|
||||||
@ -102,7 +99,7 @@ namespace ZL
|
|||||||
loadingTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/loading.png", CONST_ZIP_FILE));
|
loadingTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/loading.png", CONST_ZIP_FILE));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
loadingMesh.data = CreateRect2D({ Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, { Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, 3);
|
loadingMesh.data = CreateRect2D({ Environment::width * 0.5, Environment::height * 0.5 }, { Environment::width * 0.5, Environment::height*0.5 }, 3);
|
||||||
loadingMesh.RefreshVBO();
|
loadingMesh.RefreshVBO();
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
@ -144,14 +141,17 @@ namespace ZL
|
|||||||
Environment::shipState.nickname = nickname;
|
Environment::shipState.nickname = nickname;
|
||||||
Environment::shipState.shipType = shipType;
|
Environment::shipState.shipType = shipType;
|
||||||
|
|
||||||
auto localClient = new LocalClient;
|
networkClient = std::make_unique<LocalClient>();
|
||||||
ClientState st = Environment::shipState;
|
|
||||||
st.id = localClient->GetClientId();
|
|
||||||
localClient->setLocalPlayerState(st);
|
|
||||||
|
|
||||||
networkClient = std::unique_ptr<INetworkClient>(localClient);
|
|
||||||
networkClient->Connect("", 0);
|
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;
|
lastTickCount = 0;
|
||||||
spaceGameStarted = 1;
|
spaceGameStarted = 1;
|
||||||
};
|
};
|
||||||
@ -160,7 +160,8 @@ namespace ZL
|
|||||||
Environment::shipState.nickname = nickname;
|
Environment::shipState.nickname = nickname;
|
||||||
Environment::shipState.shipType = shipType;
|
Environment::shipState.shipType = shipType;
|
||||||
|
|
||||||
|
networkClient = std::make_unique<LocalClient>();
|
||||||
|
#ifdef NETWORK
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
networkClient = std::make_unique<WebSocketClientEmscripten>();
|
networkClient = std::make_unique<WebSocketClientEmscripten>();
|
||||||
networkClient->Connect("localhost", 8081);
|
networkClient->Connect("localhost", 8081);
|
||||||
@ -168,6 +169,19 @@ namespace ZL
|
|||||||
networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext());
|
networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext());
|
||||||
networkClient->Connect("localhost", 8081);
|
networkClient->Connect("localhost", 8081);
|
||||||
#endif
|
#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) {
|
if (networkClient) {
|
||||||
std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType);
|
std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType);
|
||||||
@ -250,8 +264,8 @@ namespace ZL
|
|||||||
renderer.EnableVertexAttribArray(vPositionName);
|
renderer.EnableVertexAttribArray(vPositionName);
|
||||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
float width = Environment::projectionWidth;
|
float width = Environment::width;
|
||||||
float height = Environment::projectionHeight;
|
float height = Environment::height;
|
||||||
|
|
||||||
renderer.PushProjectionMatrix(
|
renderer.PushProjectionMatrix(
|
||||||
0, width,
|
0, width,
|
||||||
@ -336,19 +350,16 @@ namespace ZL
|
|||||||
if (event.type == SDL_QUIT) {
|
if (event.type == SDL_QUIT) {
|
||||||
Environment::exitGameLoop = true;
|
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::width = event.window.data1;
|
||||||
Environment::height = event.window.data2;
|
Environment::height = event.window.data2;
|
||||||
Environment::computeProjectionDimensions();
|
|
||||||
menuManager.uiManager.updateAllLayouts();
|
|
||||||
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
|
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
|
||||||
|
|
||||||
space.clearTextRendererCache();
|
space.clearTextRendererCache();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
||||||
Environment::exitGameLoop = true;
|
Environment::exitGameLoop = true;
|
||||||
@ -357,38 +368,22 @@ namespace ZL
|
|||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (event.type == SDL_FINGERDOWN) {
|
if (event.type == SDL_FINGERDOWN) {
|
||||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
int mx = static_cast<int>(event.tfinger.x * Environment::width);
|
||||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
int my = static_cast<int>(event.tfinger.y * Environment::height);
|
||||||
handleDown(mx, my);
|
handleDown(mx, my);
|
||||||
}
|
}
|
||||||
else if (event.type == SDL_FINGERUP) {
|
else if (event.type == SDL_FINGERUP) {
|
||||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
int mx = static_cast<int>(event.tfinger.x * Environment::width);
|
||||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
int my = static_cast<int>(event.tfinger.y * Environment::height);
|
||||||
handleUp(mx, my);
|
handleUp(mx, my);
|
||||||
}
|
}
|
||||||
else if (event.type == SDL_FINGERMOTION) {
|
else if (event.type == SDL_FINGERMOTION) {
|
||||||
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
int mx = static_cast<int>(event.tfinger.x * Environment::width);
|
||||||
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
int my = static_cast<int>(event.tfinger.y * Environment::height);
|
||||||
handleMotion(mx, my);
|
handleMotion(mx, my);
|
||||||
}
|
}
|
||||||
#else
|
#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 mx = event.button.x;
|
||||||
int my = event.button.y;
|
int my = event.button.y;
|
||||||
handleDown(mx, my);
|
handleDown(mx, my);
|
||||||
@ -402,7 +397,7 @@ namespace ZL
|
|||||||
int mx = event.motion.x;
|
int mx = event.motion.x;
|
||||||
int my = event.motion.y;
|
int my = event.motion.y;
|
||||||
handleMotion(mx, my);
|
handleMotion(mx, my);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if (event.type == SDL_MOUSEWHEEL) {
|
if (event.type == SDL_MOUSEWHEEL) {
|
||||||
static const float zoomstep = 2.0f;
|
static const float zoomstep = 2.0f;
|
||||||
@ -448,31 +443,6 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
networkClient->Poll();
|
networkClient->Poll();
|
||||||
#ifdef NETWORK
|
|
||||||
auto* wsBase = dynamic_cast<ZL::WebSocketClientBase*>(networkClient.get());
|
|
||||||
if (wsBase) {
|
|
||||||
auto spawns = wsBase->getPendingSpawns();
|
|
||||||
for (auto& st : spawns) {
|
|
||||||
if (st.id == wsBase->getClientId()) {
|
|
||||||
// применяем к локальному кораблю
|
|
||||||
ZL::Environment::shipState.position = st.position;
|
|
||||||
ZL::Environment::shipState.rotation = st.rotation;
|
|
||||||
|
|
||||||
// обнуляем движение чтобы не было рывков
|
|
||||||
ZL::Environment::shipState.currentAngularVelocity = Eigen::Vector3f::Zero();
|
|
||||||
ZL::Environment::shipState.velocity = 0.0f;
|
|
||||||
ZL::Environment::shipState.selectedVelocity = 0;
|
|
||||||
ZL::Environment::shipState.discreteMag = 0.0f;
|
|
||||||
ZL::Environment::shipState.discreteAngle = -1;
|
|
||||||
|
|
||||||
std::cout << "Game: Applied SPAWN at "
|
|
||||||
<< st.position.x() << ", "
|
|
||||||
<< st.position.y() << ", "
|
|
||||||
<< st.position.z() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
mainThreadHandler.processMainThreadTasks();
|
mainThreadHandler.processMainThreadTasks();
|
||||||
|
|
||||||
@ -484,7 +454,7 @@ namespace ZL
|
|||||||
void Game::handleDown(int mx, int my)
|
void Game::handleDown(int mx, int my)
|
||||||
{
|
{
|
||||||
int uiX = mx;
|
int uiX = mx;
|
||||||
int uiY = Environment::projectionHeight - my;
|
int uiY = Environment::height - my;
|
||||||
|
|
||||||
menuManager.uiManager.onMouseDown(uiX, uiY);
|
menuManager.uiManager.onMouseDown(uiX, uiY);
|
||||||
|
|
||||||
@ -512,7 +482,7 @@ namespace ZL
|
|||||||
void Game::handleUp(int mx, int my)
|
void Game::handleUp(int mx, int my)
|
||||||
{
|
{
|
||||||
int uiX = mx;
|
int uiX = mx;
|
||||||
int uiY = Environment::projectionHeight - my;
|
int uiY = Environment::height - my;
|
||||||
|
|
||||||
menuManager.uiManager.onMouseUp(uiX, uiY);
|
menuManager.uiManager.onMouseUp(uiX, uiY);
|
||||||
|
|
||||||
@ -527,7 +497,7 @@ namespace ZL
|
|||||||
void Game::handleMotion(int mx, int my)
|
void Game::handleMotion(int mx, int my)
|
||||||
{
|
{
|
||||||
int uiX = mx;
|
int uiX = mx;
|
||||||
int uiY = Environment::projectionHeight - my;
|
int uiY = Environment::height - my;
|
||||||
|
|
||||||
menuManager.uiManager.onMouseMove(uiX, uiY);
|
menuManager.uiManager.onMouseMove(uiX, uiY);
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,6 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
|
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
|
||||||
onFirePressed();
|
onFirePressed();
|
||||||
});
|
});
|
||||||
@ -104,7 +103,6 @@ namespace ZL {
|
|||||||
onFirePressed();
|
onFirePressed();
|
||||||
});
|
});
|
||||||
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
||||||
|
|
||||||
int newVel = roundf(value * 10);
|
int newVel = roundf(value * 10);
|
||||||
if (newVel > 2)
|
if (newVel > 2)
|
||||||
{
|
{
|
||||||
@ -185,8 +183,8 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/*uiManager.setButtonCallback("multiplayerButton2", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
|
uiManager.setButtonCallback("multiplayerButton2", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
|
||||||
std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
|
/*std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
|
||||||
|
|
||||||
uiManager.startAnimationOnNode("playButton", "buttonsExit");
|
uiManager.startAnimationOnNode("playButton", "buttonsExit");
|
||||||
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
|
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
|
||||||
@ -221,7 +219,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cerr << "Failed to load multiplayer menu\n";
|
std::cerr << "Failed to load multiplayer menu\n";
|
||||||
}
|
}*/
|
||||||
std::cerr << "Single button pressed: " << name << " -> open ship selection UI\n";
|
std::cerr << "Single button pressed: " << name << " -> open ship selection UI\n";
|
||||||
if (!shipSelectionRoot) {
|
if (!shipSelectionRoot) {
|
||||||
std::cerr << "Failed to load ship selection UI\n";
|
std::cerr << "Failed to load ship selection UI\n";
|
||||||
@ -257,7 +255,7 @@ namespace ZL {
|
|||||||
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
|
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
|
||||||
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
|
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
|
||||||
Environment::exitGameLoop = true;
|
Environment::exitGameLoop = true;
|
||||||
});*/
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::showGameOver(int score)
|
void MenuManager::showGameOver(int score)
|
||||||
|
|||||||
@ -41,7 +41,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Projectile::rebuildMesh(Renderer&) {
|
void Projectile::rebuildMesh(Renderer&) {
|
||||||
float half = 10 * size * 0.5f;
|
float half = size * 0.5f;
|
||||||
|
|
||||||
mesh.data.PositionData.clear();
|
mesh.data.PositionData.clear();
|
||||||
mesh.data.TexCoordData.clear();
|
mesh.data.TexCoordData.clear();
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "SparkEmitter.h"
|
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
@ -20,8 +19,6 @@ namespace ZL {
|
|||||||
|
|
||||||
Vector3f getPosition() const { return pos; }
|
Vector3f getPosition() const { return pos; }
|
||||||
void deactivate() { active = false; }
|
void deactivate() { active = false; }
|
||||||
|
|
||||||
SparkEmitter projectileEmitter;
|
|
||||||
private:
|
private:
|
||||||
Vector3f pos;
|
Vector3f pos;
|
||||||
Vector3f vel;
|
Vector3f vel;
|
||||||
|
|||||||
@ -159,15 +159,15 @@ namespace ZL
|
|||||||
|
|
||||||
// В пределах экрана?
|
// В пределах экрана?
|
||||||
// (можно оставить, можно клампить)
|
// (можно оставить, можно клампить)
|
||||||
float sx = (ndc.x() * 0.5f + 0.5f) * Environment::projectionWidth;
|
float sx = (ndc.x() * 0.5f + 0.5f) * Environment::width;
|
||||||
float sy = (ndc.y() * 0.5f + 0.5f) * Environment::projectionHeight;
|
float sy = (ndc.y() * 0.5f + 0.5f) * Environment::height;
|
||||||
|
|
||||||
outX = sx;
|
outX = sx;
|
||||||
outY = sy;
|
outY = sy;
|
||||||
|
|
||||||
// Можно отсеять те, что вне:
|
// Можно отсеять те, что вне:
|
||||||
if (sx < -200 || sx > Environment::projectionWidth + 200) return false;
|
if (sx < -200 || sx > Environment::width + 200) return false;
|
||||||
if (sy < -200 || sy > Environment::projectionHeight + 200) return false;
|
if (sy < -200 || sy > Environment::height + 200) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -296,12 +296,12 @@ namespace ZL
|
|||||||
|
|
||||||
cubemapTexture = std::make_shared<Texture>(
|
cubemapTexture = std::make_shared<Texture>(
|
||||||
std::array<TextureDataStruct, 6>{
|
std::array<TextureDataStruct, 6>{
|
||||||
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
|
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
|
||||||
CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE)
|
CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -335,7 +335,6 @@ namespace ZL
|
|||||||
cargo.AssignFrom(cargoBase);
|
cargo.AssignFrom(cargoBase);
|
||||||
cargo.RefreshVBO();
|
cargo.RefreshVBO();
|
||||||
|
|
||||||
//projectileTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/spark2.png", CONST_ZIP_FILE));
|
|
||||||
|
|
||||||
//Boxes
|
//Boxes
|
||||||
boxTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/box/box.png", CONST_ZIP_FILE));
|
boxTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/box/box.png", CONST_ZIP_FILE));
|
||||||
@ -363,7 +362,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
crosshairCfgLoaded = loadCrosshairConfig("resources/config/crosshair_config.json");
|
crosshairCfgLoaded = loadCrosshairConfig("resources/config/crosshair_config.json");
|
||||||
std::cout << "[Crosshair] loaded=" << crosshairCfgLoaded
|
std::cerr << "[Crosshair] loaded=" << crosshairCfgLoaded
|
||||||
<< " enabled=" << crosshairCfg.enabled
|
<< " enabled=" << crosshairCfg.enabled
|
||||||
<< " w=" << Environment::width << " h=" << Environment::height
|
<< " w=" << Environment::width << " h=" << Environment::height
|
||||||
<< " alpha=" << crosshairCfg.alpha
|
<< " alpha=" << crosshairCfg.alpha
|
||||||
@ -491,25 +490,13 @@ namespace ZL
|
|||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader("default");
|
|
||||||
renderer.RenderUniform1i(textureUniformName, 0);
|
|
||||||
renderer.EnableVertexAttribArray(vPositionName);
|
|
||||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
||||||
for (const auto& p : projectiles) {
|
for (const auto& p : projectiles) {
|
||||||
if (p && p->isActive()) {
|
if (p && p->isActive()) {
|
||||||
p->draw(renderer);
|
p->draw(renderer);
|
||||||
p->projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glDisable(GL_BLEND);
|
|
||||||
|
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
|
||||||
renderer.shaderManager.PopShader();
|
|
||||||
|
|
||||||
//projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
|
||||||
|
|
||||||
if (shipAlive) {
|
if (shipAlive) {
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
@ -872,8 +859,8 @@ namespace ZL
|
|||||||
|
|
||||||
// если ничего не изменилось — не трогаем VBO
|
// если ничего не изменилось — не трогаем VBO
|
||||||
if (crosshairMeshValid &&
|
if (crosshairMeshValid &&
|
||||||
crosshairLastW == Environment::projectionWidth &&
|
crosshairLastW == Environment::width &&
|
||||||
crosshairLastH == Environment::projectionHeight &&
|
crosshairLastH == Environment::height &&
|
||||||
std::abs(crosshairLastAlpha - crosshairCfg.alpha) < 1e-6f &&
|
std::abs(crosshairLastAlpha - crosshairCfg.alpha) < 1e-6f &&
|
||||||
std::abs(crosshairLastThickness - crosshairCfg.thicknessPx) < 1e-6f &&
|
std::abs(crosshairLastThickness - crosshairCfg.thicknessPx) < 1e-6f &&
|
||||||
std::abs(crosshairLastGap - crosshairCfg.gapPx) < 1e-6f &&
|
std::abs(crosshairLastGap - crosshairCfg.gapPx) < 1e-6f &&
|
||||||
@ -882,18 +869,18 @@ namespace ZL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
crosshairLastW = Environment::projectionWidth;
|
crosshairLastW = Environment::width;
|
||||||
crosshairLastH = Environment::projectionHeight;
|
crosshairLastH = Environment::height;
|
||||||
crosshairLastAlpha = crosshairCfg.alpha;
|
crosshairLastAlpha = crosshairCfg.alpha;
|
||||||
crosshairLastThickness = crosshairCfg.thicknessPx;
|
crosshairLastThickness = crosshairCfg.thicknessPx;
|
||||||
crosshairLastGap = crosshairCfg.gapPx;
|
crosshairLastGap = crosshairCfg.gapPx;
|
||||||
crosshairLastScaleMul = crosshairCfg.scaleMul;
|
crosshairLastScaleMul = crosshairCfg.scaleMul;
|
||||||
|
|
||||||
float cx = Environment::projectionWidth * 0.5f;
|
float cx = Environment::width * 0.5f;
|
||||||
float cy = Environment::projectionHeight * 0.5f;
|
float cy = Environment::height * 0.5f;
|
||||||
|
|
||||||
// масштаб от reference (стандартно: по высоте)
|
// масштаб от reference (стандартно: по высоте)
|
||||||
float scale = (crosshairCfg.refH > 0) ? (Environment::projectionHeight / (float)crosshairCfg.refH) : 1.0f;
|
float scale = (crosshairCfg.refH > 0) ? (Environment::height / (float)crosshairCfg.refH) : 1.0f;
|
||||||
scale *= crosshairCfg.scaleMul;
|
scale *= crosshairCfg.scaleMul;
|
||||||
|
|
||||||
float thickness = crosshairCfg.thicknessPx * scale;
|
float thickness = crosshairCfg.thicknessPx * scale;
|
||||||
@ -953,7 +940,7 @@ namespace ZL
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader("defaultColor");
|
renderer.shaderManager.PushShader("defaultColor");
|
||||||
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
|
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
@ -1137,7 +1124,7 @@ namespace ZL
|
|||||||
|
|
||||||
// Lead Indicator
|
// Lead Indicator
|
||||||
// скорость пули (как в fireProjectiles)
|
// скорость пули (как в fireProjectiles)
|
||||||
const float projectileSpeed = PROJECTILE_VELOCITY;
|
const float projectileSpeed = 60.0f;
|
||||||
|
|
||||||
// позиция вылета
|
// позиция вылета
|
||||||
Vector3f shooterPos = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f };
|
Vector3f shooterPos = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f };
|
||||||
@ -1200,15 +1187,15 @@ namespace ZL
|
|||||||
|
|
||||||
// 4) Настройки стиля
|
// 4) Настройки стиля
|
||||||
Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный
|
Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный
|
||||||
float thickness = 2.0f; // толщина линий (px)
|
float thickness = 10.0f; // толщина линий (px)
|
||||||
float z = 0.0f; // 2D слой
|
float z = 0.0f; // 2D слой
|
||||||
|
|
||||||
// 5) Если цель в кадре: рисуем скобки
|
// 5) Если цель в кадре: рисуем скобки
|
||||||
if (onScreen)
|
if (onScreen)
|
||||||
{
|
{
|
||||||
// перевод NDC -> экран (в пикселях)
|
// перевод NDC -> экран (в пикселях)
|
||||||
float sx = (ndcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
float sx = (ndcX * 0.5f + 0.5f) * Environment::width;
|
||||||
float sy = (ndcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
float sy = (ndcY * 0.5f + 0.5f) * Environment::height;
|
||||||
|
|
||||||
// анимация “снаружи внутрь”
|
// анимация “снаружи внутрь”
|
||||||
// targetAcquireAnim растёт к 1, быстро (похоже на захват)
|
// targetAcquireAnim растёт к 1, быстро (похоже на захват)
|
||||||
@ -1247,7 +1234,7 @@ namespace ZL
|
|||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader("defaultColor");
|
renderer.shaderManager.PushShader("defaultColor");
|
||||||
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
|
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
@ -1261,8 +1248,8 @@ namespace ZL
|
|||||||
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
|
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
|
||||||
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
|
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
|
||||||
if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) {
|
if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) {
|
||||||
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::width;
|
||||||
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::height;
|
||||||
|
|
||||||
float distLead = (Environment::shipState.position - leadWorld).norm();
|
float distLead = (Environment::shipState.position - leadWorld).norm();
|
||||||
float r = 30.0f / (distLead * 0.01f + 1.0f);
|
float r = 30.0f / (distLead * 0.01f + 1.0f);
|
||||||
@ -1336,8 +1323,8 @@ namespace ZL
|
|||||||
float edgeNdcX = dirX * k;
|
float edgeNdcX = dirX * k;
|
||||||
float edgeNdcY = dirY * k;
|
float edgeNdcY = dirY * k;
|
||||||
|
|
||||||
float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::width;
|
||||||
float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::height;
|
||||||
|
|
||||||
float bob = std::sin(t * 6.0f) * 6.0f;
|
float bob = std::sin(t * 6.0f) * 6.0f;
|
||||||
edgeX += dirX * bob;
|
edgeX += dirX * bob;
|
||||||
@ -1380,7 +1367,7 @@ namespace ZL
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
renderer.shaderManager.PushShader("defaultColor");
|
renderer.shaderManager.PushShader("defaultColor");
|
||||||
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
|
renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
@ -1553,25 +1540,22 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Vector3f> projCameraPoints;
|
||||||
for (const auto& p : projectiles) {
|
for (const auto& p : projectiles) {
|
||||||
if (p && p->isActive()) {
|
if (p && p->isActive()) {
|
||||||
Vector3f worldPos = p->getPosition();
|
Vector3f worldPos = p->getPosition();
|
||||||
Vector3f rel = worldPos - Environment::shipState.position;
|
Vector3f rel = worldPos - Environment::shipState.position;
|
||||||
Vector3f camPos = Environment::inverseShipMatrix * rel;
|
Vector3f camPos = Environment::inverseShipMatrix * rel;
|
||||||
p->projectileEmitter.setEmissionPoints({ camPos });
|
projCameraPoints.push_back(camPos);
|
||||||
p->projectileEmitter.emit();
|
|
||||||
p->projectileEmitter.update(static_cast<float>(delta));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if (!projCameraPoints.empty()) {
|
if (!projCameraPoints.empty()) {
|
||||||
projectileEmitter.setEmissionPoints(projCameraPoints);
|
projectileEmitter.setEmissionPoints(projCameraPoints);
|
||||||
projectileEmitter.emit();
|
projectileEmitter.emit();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||||
}*/
|
}
|
||||||
|
|
||||||
std::vector<Vector3f> shipCameraPoints;
|
std::vector<Vector3f> shipCameraPoints;
|
||||||
for (const auto& lp : shipLocalEmissionPoints) {
|
for (const auto& lp : shipLocalEmissionPoints) {
|
||||||
@ -1583,7 +1567,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
sparkEmitter.update(static_cast<float>(delta));
|
sparkEmitter.update(static_cast<float>(delta));
|
||||||
//projectileEmitter.update(static_cast<float>(delta));
|
projectileEmitter.update(static_cast<float>(delta));
|
||||||
|
|
||||||
explosionEmitter.update(static_cast<float>(delta));
|
explosionEmitter.update(static_cast<float>(delta));
|
||||||
if (showExplosion) {
|
if (showExplosion) {
|
||||||
@ -1712,8 +1696,8 @@ namespace ZL
|
|||||||
Vector3f{ 1.5f, 0.9f - 6.f, 5.0f }
|
Vector3f{ 1.5f, 0.9f - 6.f, 5.0f }
|
||||||
};
|
};
|
||||||
|
|
||||||
const float projectileSpeed = PROJECTILE_VELOCITY;
|
const float projectileSpeed = 60.0f;
|
||||||
const float lifeMs = PROJECTILE_LIFE;
|
const float lifeMs = 50000.0f;
|
||||||
const float size = 0.5f;
|
const float size = 0.5f;
|
||||||
|
|
||||||
Vector3f localForward = { 0,0,-1 };
|
Vector3f localForward = { 0,0,-1 };
|
||||||
@ -1726,7 +1710,6 @@ namespace ZL
|
|||||||
for (auto& p : projectiles) {
|
for (auto& p : projectiles) {
|
||||||
if (!p->isActive()) {
|
if (!p->isActive()) {
|
||||||
p->init(worldPos, worldVel, lifeMs, size, projectileTexture, renderer);
|
p->init(worldPos, worldVel, lifeMs, size, projectileTexture, renderer);
|
||||||
p->projectileEmitter = SparkEmitter(projectileEmitter);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1738,8 +1721,8 @@ namespace ZL
|
|||||||
if (networkClient) {
|
if (networkClient) {
|
||||||
auto pending = networkClient->getPendingProjectiles();
|
auto pending = networkClient->getPendingProjectiles();
|
||||||
if (!pending.empty()) {
|
if (!pending.empty()) {
|
||||||
const float projectileSpeed = PROJECTILE_VELOCITY;
|
const float projectileSpeed = 60.0f;
|
||||||
const float lifeMs = PROJECTILE_LIFE;
|
const float lifeMs = 5000.0f;
|
||||||
const float size = 0.5f;
|
const float size = 0.5f;
|
||||||
for (const auto& pi : pending) {
|
for (const auto& pi : pending) {
|
||||||
const std::vector<Vector3f> localOffsets = {
|
const std::vector<Vector3f> localOffsets = {
|
||||||
@ -1764,7 +1747,6 @@ namespace ZL
|
|||||||
for (auto& p : projectiles) {
|
for (auto& p : projectiles) {
|
||||||
if (!p->isActive()) {
|
if (!p->isActive()) {
|
||||||
p->init(shotPos, baseVel, lifeMs, size, projectileTexture, renderer);
|
p->init(shotPos, baseVel, lifeMs, size, projectileTexture, renderer);
|
||||||
p->projectileEmitter = SparkEmitter(projectileEmitter);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,7 +96,7 @@ namespace ZL {
|
|||||||
std::shared_ptr<Texture> projectileTexture;
|
std::shared_ptr<Texture> projectileTexture;
|
||||||
float projectileCooldownMs = 500.0f;
|
float projectileCooldownMs = 500.0f;
|
||||||
int64_t lastProjectileFireTime = 0;
|
int64_t lastProjectileFireTime = 0;
|
||||||
int maxProjectiles = 500;
|
int maxProjectiles = 32;
|
||||||
std::vector<Vector3f> shipLocalEmissionPoints;
|
std::vector<Vector3f> shipLocalEmissionPoints;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -25,23 +25,6 @@ namespace ZL {
|
|||||||
sparkQuad.data = VertexDataStruct();
|
sparkQuad.data = VertexDataStruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
SparkEmitter::SparkEmitter(const SparkEmitter& copyFrom)
|
|
||||||
: particles(copyFrom.particles), emissionPoints(copyFrom.emissionPoints),
|
|
||||||
lastEmissionTime(copyFrom.lastEmissionTime), emissionRate(copyFrom.emissionRate),
|
|
||||||
isActive(copyFrom.isActive), drawPositions(copyFrom.drawPositions),
|
|
||||||
drawTexCoords(copyFrom.drawTexCoords), drawDataDirty(copyFrom.drawDataDirty),
|
|
||||||
sparkQuad(copyFrom.sparkQuad), texture(copyFrom.texture),
|
|
||||||
maxParticles(copyFrom.maxParticles), particleSize(copyFrom.particleSize),
|
|
||||||
biasX(copyFrom.biasX), speedRange(copyFrom.speedRange),
|
|
||||||
zSpeedRange(copyFrom.zSpeedRange),
|
|
||||||
scaleRange(copyFrom.scaleRange),
|
|
||||||
lifeTimeRange(copyFrom.lifeTimeRange),
|
|
||||||
shaderProgramName(copyFrom.shaderProgramName),
|
|
||||||
configured(copyFrom.configured), useWorldSpace(copyFrom.useWorldSpace)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
|
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
|
||||||
: emissionPoints(positions), emissionRate(rate), isActive(true),
|
: emissionPoints(positions), emissionRate(rate), isActive(true),
|
||||||
drawDataDirty(true), maxParticles(positions.size() * 100),
|
drawDataDirty(true), maxParticles(positions.size() * 100),
|
||||||
|
|||||||
@ -41,7 +41,7 @@ namespace ZL {
|
|||||||
float biasX;
|
float biasX;
|
||||||
|
|
||||||
// Ranges (used when config supplies intervals)
|
// Ranges (used when config supplies intervals)
|
||||||
struct FloatRange { float min=0; float max=0; };
|
struct FloatRange { float min; float max; };
|
||||||
FloatRange speedRange; // XY speed
|
FloatRange speedRange; // XY speed
|
||||||
FloatRange zSpeedRange; // Z speed
|
FloatRange zSpeedRange; // Z speed
|
||||||
FloatRange scaleRange;
|
FloatRange scaleRange;
|
||||||
@ -55,7 +55,6 @@ namespace ZL {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
SparkEmitter();
|
SparkEmitter();
|
||||||
SparkEmitter(const SparkEmitter& copyFrom);
|
|
||||||
SparkEmitter(const std::vector<Vector3f>& positions, float rate = 100.0f);
|
SparkEmitter(const std::vector<Vector3f>& positions, float rate = 100.0f);
|
||||||
SparkEmitter(const std::vector<Vector3f>& positions,
|
SparkEmitter(const std::vector<Vector3f>& positions,
|
||||||
std::shared_ptr<Texture> tex,
|
std::shared_ptr<Texture> tex,
|
||||||
|
|||||||
@ -184,89 +184,21 @@ namespace ZL {
|
|||||||
|
|
||||||
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
|
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
|
||||||
auto node = std::make_shared<UiNode>();
|
auto node = std::make_shared<UiNode>();
|
||||||
|
if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get<std::string>();
|
||||||
|
if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get<std::string>();
|
||||||
|
|
||||||
// 1. Определяем тип контейнера и ориентацию
|
if (j.contains("x")) node->rect.x = j["x"].get<float>();
|
||||||
std::string typeStr = j.value("type", "FrameLayout"); // По умолчанию FrameLayout
|
if (j.contains("y")) node->rect.y = j["y"].get<float>();
|
||||||
if (typeStr == "LinearLayout") {
|
if (j.contains("width")) node->rect.w = j["width"].get<float>();
|
||||||
node->layoutType = LayoutType::Linear;
|
if (j.contains("height")) node->rect.h = j["height"].get<float>();
|
||||||
}
|
|
||||||
else {
|
|
||||||
node->layoutType = LayoutType::Frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("name")) node->name = j["name"].get<std::string>();
|
if (j.contains("orientation") && j["orientation"].is_string()) node->orientation = j["orientation"].get<std::string>();
|
||||||
|
if (j.contains("spacing")) node->spacing = j["spacing"].get<float>();
|
||||||
|
|
||||||
// 2. Читаем размеры во временные "локальные" поля
|
if (node->type == "Button") {
|
||||||
// Это критически важно: мы не пишем сразу в screenRect,
|
|
||||||
// так как LinearLayout их пересчитает.
|
|
||||||
node->localX = j.value("x", 0.0f);
|
|
||||||
node->localY = j.value("y", 0.0f);
|
|
||||||
if (j.contains("width")) {
|
|
||||||
if (j["width"].is_string() && j["width"] == "match_parent") {
|
|
||||||
node->width = -1.0f; // Наш маркер для match_parent
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node->width = j["width"].get<float>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
node->width = 0.0f;
|
|
||||||
}
|
|
||||||
if (j.contains("height")) {
|
|
||||||
if (j["height"].is_string() && j["height"] == "match_parent") {
|
|
||||||
node->height = -1.0f; // Наш маркер для match_parent
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node->height = j["height"].get<float>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
node->height = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Параметры компоновки
|
|
||||||
if (j.contains("orientation")) {
|
|
||||||
std::string orient = j["orientation"].get<std::string>();
|
|
||||||
node->orientation = (orient == "horizontal") ? Orientation::Horizontal : Orientation::Vertical;
|
|
||||||
}
|
|
||||||
node->spacing = j.value("spacing", 0.0f);
|
|
||||||
|
|
||||||
if (j.contains("horizontal_align")) {
|
|
||||||
std::string halign = j["horizontal_align"];
|
|
||||||
if (halign == "center") node->layoutSettings.hAlign = HorizontalAlign::Center;
|
|
||||||
else if (halign == "right") node->layoutSettings.hAlign = HorizontalAlign::Right;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("vertical_align")) {
|
|
||||||
std::string valign = j["vertical_align"];
|
|
||||||
if (valign == "center") node->layoutSettings.vAlign = VerticalAlign::Center;
|
|
||||||
else if (valign == "bottom") node->layoutSettings.vAlign = VerticalAlign::Bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("horizontal_gravity")) {
|
|
||||||
std::string hg = j["horizontal_gravity"].get<std::string>();
|
|
||||||
if (hg == "right") node->layoutSettings.hGravity = HorizontalGravity::Right;
|
|
||||||
else node->layoutSettings.hGravity = HorizontalGravity::Left;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Читаем Vertical Gravity
|
|
||||||
if (j.contains("vertical_gravity")) {
|
|
||||||
std::string vg = j["vertical_gravity"].get<std::string>();
|
|
||||||
if (vg == "bottom") node->layoutSettings.vGravity = VerticalGravity::Bottom;
|
|
||||||
else node->layoutSettings.vGravity = VerticalGravity::Top;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Подготавливаем базовый rect для компонентов (кнопок и т.д.)
|
|
||||||
// На этапе парсинга мы даем им "желаемый" размер
|
|
||||||
UiRect initialRect = { node->localX, node->localY, node->width, node->height };
|
|
||||||
|
|
||||||
|
|
||||||
if (typeStr == "Button") {
|
|
||||||
auto btn = std::make_shared<UiButton>();
|
auto btn = std::make_shared<UiButton>();
|
||||||
btn->name = node->name;
|
btn->name = node->name;
|
||||||
btn->rect = initialRect;
|
btn->rect = node->rect;
|
||||||
|
|
||||||
if (!j.contains("textures") || !j["textures"].is_object()) {
|
if (!j.contains("textures") || !j["textures"].is_object()) {
|
||||||
std::cerr << "UiManager: Button '" << btn->name << "' missing textures" << std::endl;
|
std::cerr << "UiManager: Button '" << btn->name << "' missing textures" << std::endl;
|
||||||
@ -293,10 +225,10 @@ namespace ZL {
|
|||||||
|
|
||||||
node->button = btn;
|
node->button = btn;
|
||||||
}
|
}
|
||||||
else if (typeStr == "Slider") {
|
else if (node->type == "Slider") {
|
||||||
auto s = std::make_shared<UiSlider>();
|
auto s = std::make_shared<UiSlider>();
|
||||||
s->name = node->name;
|
s->name = node->name;
|
||||||
s->rect = initialRect;
|
s->rect = node->rect;
|
||||||
|
|
||||||
if (!j.contains("textures") || !j["textures"].is_object()) {
|
if (!j.contains("textures") || !j["textures"].is_object()) {
|
||||||
std::cerr << "UiManager: Slider '" << s->name << "' missing textures" << std::endl;
|
std::cerr << "UiManager: Slider '" << s->name << "' missing textures" << std::endl;
|
||||||
@ -329,10 +261,10 @@ namespace ZL {
|
|||||||
|
|
||||||
node->slider = s;
|
node->slider = s;
|
||||||
}
|
}
|
||||||
else if (typeStr == "TextField") {
|
else if (node->type == "TextField") {
|
||||||
auto tf = std::make_shared<UiTextField>();
|
auto tf = std::make_shared<UiTextField>();
|
||||||
tf->name = node->name;
|
tf->name = node->name;
|
||||||
tf->rect = initialRect;
|
tf->rect = node->rect;
|
||||||
|
|
||||||
if (j.contains("placeholder")) tf->placeholder = j["placeholder"].get<std::string>();
|
if (j.contains("placeholder")) tf->placeholder = j["placeholder"].get<std::string>();
|
||||||
if (j.contains("fontPath")) tf->fontPath = j["fontPath"].get<std::string>();
|
if (j.contains("fontPath")) tf->fontPath = j["fontPath"].get<std::string>();
|
||||||
@ -399,11 +331,11 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeStr == "TextView") {
|
if (node->type == "TextView") {
|
||||||
auto tv = std::make_shared<UiTextView>();
|
auto tv = std::make_shared<UiTextView>();
|
||||||
|
|
||||||
tv->name = node->name;
|
tv->name = node->name;
|
||||||
tv->rect = initialRect;
|
tv->rect = node->rect;
|
||||||
|
|
||||||
if (j.contains("text")) tv->text = j["text"].get<std::string>();
|
if (j.contains("text")) tv->text = j["text"].get<std::string>();
|
||||||
if (j.contains("fontPath")) tv->fontPath = j["fontPath"].get<std::string>();
|
if (j.contains("fontPath")) tv->fontPath = j["fontPath"].get<std::string>();
|
||||||
if (j.contains("fontSize")) tv->fontSize = j["fontSize"].get<int>();
|
if (j.contains("fontSize")) tv->fontSize = j["fontSize"].get<int>();
|
||||||
@ -468,7 +400,6 @@ namespace ZL {
|
|||||||
throw std::runtime_error("Failed to load UI file: " + path);
|
throw std::runtime_error("Failed to load UI file: " + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
root = parseNode(j["root"], renderer, zipFile);
|
root = parseNode(j["root"], renderer, zipFile);
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
@ -476,14 +407,7 @@ namespace ZL {
|
|||||||
|
|
||||||
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
|
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
|
||||||
root = newRoot;
|
root = newRoot;
|
||||||
layoutNode(
|
layoutNode(root);
|
||||||
root,
|
|
||||||
0.0f, 0.0f, // parentX, parentY (экран начинается с 0,0)
|
|
||||||
Environment::projectionWidth, // parentW
|
|
||||||
Environment::projectionHeight, // parentH
|
|
||||||
root->localX, // finalLocalX
|
|
||||||
root->localY // finalLocalY
|
|
||||||
);
|
|
||||||
buttons.clear();
|
buttons.clear();
|
||||||
sliders.clear();
|
sliders.clear();
|
||||||
textViews.clear();
|
textViews.clear();
|
||||||
@ -508,169 +432,38 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node, float parentX, float parentY, float parentW, float parentH, float finalLocalX, float finalLocalY) {
|
|
||||||
|
|
||||||
node->screenRect.w = (node->width < 0) ? parentW : node->width;
|
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node) {
|
||||||
node->screenRect.h = (node->height < 0) ? parentH : node->height;
|
for (auto& child : node->children) {
|
||||||
|
child->rect.x += node->rect.x;
|
||||||
|
child->rect.y += node->rect.y;
|
||||||
|
}
|
||||||
|
|
||||||
// ТЕПЕРЬ используем переданные координаты, а не node->localX напрямую
|
if (node->type == "LinearLayout") {
|
||||||
node->screenRect.x = parentX + finalLocalX;
|
std::string orient = node->orientation;
|
||||||
node->screenRect.y = parentY + finalLocalY;
|
std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
|
||||||
|
|
||||||
float currentW = node->screenRect.w;
|
|
||||||
float currentH = node->screenRect.h;
|
|
||||||
|
|
||||||
if (node->layoutType == LayoutType::Linear) {
|
|
||||||
float totalContentWidth = 0;
|
|
||||||
float totalContentHeight = 0;
|
|
||||||
|
|
||||||
// Предварительный расчет занимаемого места всеми детьми
|
|
||||||
for (size_t i = 0; i < node->children.size(); ++i) {
|
|
||||||
if (node->orientation == Orientation::Vertical) {
|
|
||||||
totalContentHeight += node->children[i]->height;
|
|
||||||
if (i < node->children.size() - 1) totalContentHeight += node->spacing;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
totalContentWidth += node->children[i]->width;
|
|
||||||
if (i < node->children.size() - 1) totalContentWidth += node->spacing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float startX = 0;
|
|
||||||
float startY = currentH;
|
|
||||||
|
|
||||||
if (node->orientation == Orientation::Vertical) {
|
|
||||||
if (node->layoutSettings.vAlign == VerticalAlign::Center) {
|
|
||||||
startY = (currentH + totalContentHeight) / 2.0f;
|
|
||||||
}
|
|
||||||
else if (node->layoutSettings.vAlign == VerticalAlign::Bottom) {
|
|
||||||
startY = totalContentHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Горизонтальное выравнивание всего блока
|
|
||||||
if (node->orientation == Orientation::Horizontal) {
|
|
||||||
if (node->layoutSettings.hAlign == HorizontalAlign::Center) {
|
|
||||||
startX = (currentW - totalContentWidth) / 2.0f;
|
|
||||||
}
|
|
||||||
else if (node->layoutSettings.hAlign == HorizontalAlign::Right) {
|
|
||||||
startX = currentW - totalContentWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float cursorX = startX;
|
|
||||||
float cursorY = startY;
|
|
||||||
|
|
||||||
|
float cursorX = node->rect.x;
|
||||||
|
float cursorY = node->rect.y;
|
||||||
for (auto& child : node->children) {
|
for (auto& child : node->children) {
|
||||||
|
if (orient == "horizontal") {
|
||||||
float childW = (child->width < 0) ? currentW : child->width;
|
child->rect.x = cursorX;
|
||||||
float childH = (child->height < 0) ? currentH : child->height;
|
child->rect.y = node->rect.y;
|
||||||
|
cursorX += child->rect.w + node->spacing;
|
||||||
if (node->orientation == Orientation::Vertical) {
|
|
||||||
cursorY -= childH; // используем вычисленный childH
|
|
||||||
|
|
||||||
float childX = 0;
|
|
||||||
float freeSpaceX = currentW - childW;
|
|
||||||
if (node->layoutSettings.hAlign == HorizontalAlign::Center) childX = freeSpaceX / 2.0f;
|
|
||||||
else if (node->layoutSettings.hAlign == HorizontalAlign::Right) childX = freeSpaceX;
|
|
||||||
|
|
||||||
child->localX = childX;
|
|
||||||
child->localY = cursorY;
|
|
||||||
cursorY -= node->spacing;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
child->localX = cursorX;
|
child->rect.x = node->rect.x;
|
||||||
|
child->rect.y = cursorY;
|
||||||
// Вертикальное выравнивание внутри "строки" (Cross-axis alignment)
|
cursorY += child->rect.h + node->spacing;
|
||||||
float childY = 0;
|
|
||||||
float freeSpaceY = currentH - childH;
|
|
||||||
|
|
||||||
if (node->layoutSettings.vAlign == VerticalAlign::Center) {
|
|
||||||
childY = freeSpaceY / 2.0f;
|
|
||||||
}
|
|
||||||
else if (node->layoutSettings.vAlign == VerticalAlign::Top) {
|
|
||||||
childY = freeSpaceY; // Прижимаем к верхнему краю (т.к. Y растет вверх)
|
|
||||||
}
|
|
||||||
else if (node->layoutSettings.vAlign == VerticalAlign::Bottom) {
|
|
||||||
childY = 0; // Прижимаем к нижнему краю
|
|
||||||
}
|
|
||||||
|
|
||||||
child->localY = childY;
|
|
||||||
|
|
||||||
// Сдвигаем курсор вправо для следующего элемента
|
|
||||||
cursorX += childW + node->spacing;
|
|
||||||
}
|
}
|
||||||
layoutNode(child, node->screenRect.x, node->screenRect.y, currentW, currentH, child->localX, child->localY);
|
layoutNode(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (auto& child : node->children) {
|
for (auto& child : node->children) {
|
||||||
float childW = (child->width < 0) ? currentW : child->width;
|
layoutNode(child);
|
||||||
float childH = (child->height < 0) ? currentH : child->height;
|
|
||||||
|
|
||||||
float fLX = child->localX;
|
|
||||||
float fLY = child->localY;
|
|
||||||
|
|
||||||
if (child->layoutSettings.hGravity == HorizontalGravity::Right) {
|
|
||||||
fLX = currentW - childW - child->localX;
|
|
||||||
}
|
|
||||||
if (child->layoutSettings.vGravity == VerticalGravity::Top) {
|
|
||||||
fLY = currentH - childH - child->localY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Передаем рассчитанные fLX, fLY в рекурсию
|
|
||||||
layoutNode(child, node->screenRect.x, node->screenRect.y, currentW, currentH, fLX, fLY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем меши визуальных компонентов
|
|
||||||
syncComponentRects(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UiManager::syncComponentRects(const std::shared_ptr<UiNode>& node) {
|
|
||||||
if (!node) return;
|
|
||||||
|
|
||||||
// 1. Обновляем кнопку
|
|
||||||
if (node->button) {
|
|
||||||
node->button->rect = node->screenRect;
|
|
||||||
// Если у кнопки есть анимационные смещения, они учитываются внутри buildMesh
|
|
||||||
// или при рендеринге через Uniform-переменные матрицы модели.
|
|
||||||
node->button->buildMesh();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Обновляем слайдер
|
|
||||||
if (node->slider) {
|
|
||||||
node->slider->rect = node->screenRect;
|
|
||||||
node->slider->buildTrackMesh();
|
|
||||||
node->slider->buildKnobMesh();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Обновляем текстовое поле (TextView)
|
|
||||||
if (node->textView) {
|
|
||||||
node->textView->rect = node->screenRect;
|
|
||||||
// Если в TextView реализован кэш меша для текста, его нужно обновить здесь
|
|
||||||
// node->textView->rebuildText();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Обновляем поле ввода (TextField)
|
|
||||||
if (node->textField) {
|
|
||||||
node->textField->rect = node->screenRect;
|
|
||||||
// Аналогично для курсора и фонового меша
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UiManager::updateAllLayouts() {
|
|
||||||
if (!root) return;
|
|
||||||
|
|
||||||
// Запускаем расчет от корня, передавая размеры экрана как "родительские"
|
|
||||||
layoutNode(
|
|
||||||
root,
|
|
||||||
0.0f, 0.0f, // parentX, parentY (экран начинается с 0,0)
|
|
||||||
Environment::projectionWidth, // parentW
|
|
||||||
Environment::projectionHeight, // parentH
|
|
||||||
root->localX, // finalLocalX
|
|
||||||
root->localY // finalLocalY
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::collectButtonsAndSliders(const std::shared_ptr<UiNode>& node) {
|
void UiManager::collectButtonsAndSliders(const std::shared_ptr<UiNode>& node) {
|
||||||
@ -864,7 +657,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::draw(Renderer& renderer) {
|
void UiManager::draw(Renderer& renderer) {
|
||||||
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, -1, 1);
|
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
|
|||||||
@ -31,48 +31,6 @@ namespace ZL {
|
|||||||
Pressed
|
Pressed
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LayoutType {
|
|
||||||
Frame, // Позиционирование по X, Y
|
|
||||||
Linear // Автоматическое позиционирование
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Orientation {
|
|
||||||
Vertical,
|
|
||||||
Horizontal
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class HorizontalAlign {
|
|
||||||
Left,
|
|
||||||
Center,
|
|
||||||
Right
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class VerticalAlign {
|
|
||||||
Top,
|
|
||||||
Center,
|
|
||||||
Bottom
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class HorizontalGravity {
|
|
||||||
Left,
|
|
||||||
Right
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class VerticalGravity {
|
|
||||||
Bottom, // Обычно в OpenGL Y растет вверх, так что низ - это 0
|
|
||||||
Top
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// В структуру или класс, отвечающий за LinearLayout (вероятно, это свойства UiNode)
|
|
||||||
struct LayoutSettings {
|
|
||||||
HorizontalAlign hAlign = HorizontalAlign::Left;
|
|
||||||
VerticalAlign vAlign = VerticalAlign::Top;
|
|
||||||
|
|
||||||
HorizontalGravity hGravity = HorizontalGravity::Left;
|
|
||||||
VerticalGravity vGravity = VerticalGravity::Top;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UiButton {
|
struct UiButton {
|
||||||
std::string name;
|
std::string name;
|
||||||
UiRect rect;
|
UiRect rect;
|
||||||
@ -153,38 +111,21 @@ namespace ZL {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct UiNode {
|
struct UiNode {
|
||||||
|
std::string type;
|
||||||
|
UiRect rect;
|
||||||
std::string name;
|
std::string name;
|
||||||
LayoutType layoutType = LayoutType::Frame;
|
|
||||||
Orientation orientation = Orientation::Vertical;
|
|
||||||
float spacing = 0.0f;
|
|
||||||
|
|
||||||
LayoutSettings layoutSettings;
|
|
||||||
|
|
||||||
// Внутренние вычисленные координаты для OpenGL
|
|
||||||
// Именно их мы передаем в Vertex Buffer при buildMesh()
|
|
||||||
UiRect screenRect;
|
|
||||||
|
|
||||||
// Данные из JSON (желаемые размеры и смещения)
|
|
||||||
float localX = 0;
|
|
||||||
float localY = 0;
|
|
||||||
float width = 0;
|
|
||||||
float height = 0;
|
|
||||||
|
|
||||||
// Иерархия
|
|
||||||
std::vector<std::shared_ptr<UiNode>> children;
|
std::vector<std::shared_ptr<UiNode>> children;
|
||||||
|
|
||||||
// Компоненты (только один из них обычно активен для ноды)
|
|
||||||
std::shared_ptr<UiButton> button;
|
std::shared_ptr<UiButton> button;
|
||||||
std::shared_ptr<UiSlider> slider;
|
std::shared_ptr<UiSlider> slider;
|
||||||
std::shared_ptr<UiTextView> textView;
|
std::shared_ptr<UiTextView> textView;
|
||||||
std::shared_ptr<UiTextField> textField;
|
std::shared_ptr<UiTextField> textField;
|
||||||
|
std::string orientation = "vertical";
|
||||||
|
float spacing = 0.0f;
|
||||||
|
|
||||||
// Анимации
|
|
||||||
struct AnimStep {
|
struct AnimStep {
|
||||||
std::string type;
|
std::string type;
|
||||||
float toX = 0.0f;
|
float toX = 0.0f;
|
||||||
float toY = 0.0f;
|
float toY = 0.0f;
|
||||||
float toScale = 1.0f; // Полезно добавить для UI
|
|
||||||
float durationMs = 0.0f;
|
float durationMs = 0.0f;
|
||||||
std::string easing = "linear";
|
std::string easing = "linear";
|
||||||
};
|
};
|
||||||
@ -259,11 +200,9 @@ namespace ZL {
|
|||||||
bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
||||||
bool stopAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
bool stopAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
||||||
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
|
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
|
||||||
void updateAllLayouts();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void layoutNode(const std::shared_ptr<UiNode>& node, float parentX, float parentY, float parentW, float parentH, float finalLocalX, float finalLocalY);
|
void layoutNode(const std::shared_ptr<UiNode>& node);
|
||||||
void syncComponentRects(const std::shared_ptr<UiNode>& node);
|
|
||||||
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
|
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
|
||||||
|
|
||||||
struct ActiveAnim {
|
struct ActiveAnim {
|
||||||
|
|||||||
67
src/main.cpp
67
src/main.cpp
@ -44,34 +44,20 @@ EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void
|
|||||||
return EM_TRUE;
|
return EM_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void applyResize(int logicalW, int logicalH) {
|
// Resize the canvas, notify SDL, and push a synthetic SDL_WINDOWEVENT_RESIZED
|
||||||
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
// so Game::update()'s existing handler updates Environment::width/height and clears caches.
|
||||||
double dpr = emscripten_get_device_pixel_ratio();
|
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
|
||||||
int physicalW = static_cast<int>(logicalW * dpr);
|
// the original size no matter what Environment::width/height say.
|
||||||
int physicalH = static_cast<int>(logicalH * dpr);
|
emscripten_set_canvas_element_size("#canvas", w, h);
|
||||||
|
if (ZL::Environment::window)
|
||||||
// Устанавливаем размер внутреннего буфера канваса
|
SDL_SetWindowSize(ZL::Environment::window, w, h);
|
||||||
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 = {};
|
SDL_Event e = {};
|
||||||
e.type = SDL_WINDOWEVENT;
|
e.type = SDL_WINDOWEVENT;
|
||||||
e.window.event = SDL_WINDOWEVENT_RESIZED;
|
e.window.event = SDL_WINDOWEVENT_RESIZED;
|
||||||
e.window.data1 = physicalW;
|
e.window.data1 = w;
|
||||||
e.window.data2 = physicalH;
|
e.window.data2 = h;
|
||||||
SDL_PushEvent(&e);
|
SDL_PushEvent(&e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,11 +69,17 @@ EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*u
|
|||||||
}
|
}
|
||||||
|
|
||||||
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
|
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
|
||||||
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
|
if (e->isFullscreen) {
|
||||||
// так как после перехода в FS именно он растягивается на весь экран.
|
// e->screenWidth/screenHeight comes from screen.width/screen.height in JS,
|
||||||
double clientW, clientH;
|
// which on mobile browsers returns physical pixels (e.g. 2340x1080),
|
||||||
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
|
// causing the canvas to extend far off-screen. window.innerWidth/innerHeight
|
||||||
applyResize(clientW, clientH);
|
// 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.
|
||||||
return EM_FALSE;
|
return EM_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,21 +227,6 @@ int main(int argc, char *argv[]) {
|
|||||||
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
||||||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
|
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);
|
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||||
#else
|
#else
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||||
|
|||||||
@ -27,14 +27,6 @@ constexpr long long SERVER_DELAY = 0; //ms
|
|||||||
constexpr long long CLIENT_DELAY = 500; //ms
|
constexpr long long CLIENT_DELAY = 500; //ms
|
||||||
constexpr long long CUTOFF_TIME = 5000; //ms
|
constexpr long long CUTOFF_TIME = 5000; //ms
|
||||||
|
|
||||||
constexpr float PROJECTILE_VELOCITY = 600.f;
|
|
||||||
constexpr float PROJECTILE_LIFE = 15000.f; //ms
|
|
||||||
|
|
||||||
const float projectileHitRadius = 1.5f * 5;
|
|
||||||
const float boxCollisionRadius = 2.0f * 5;
|
|
||||||
const float shipCollisionRadius = 15.0f * 5;
|
|
||||||
const float npcCollisionRadius = 5.0f * 5;
|
|
||||||
|
|
||||||
uint32_t fnv1a_hash(const std::string& data);
|
uint32_t fnv1a_hash(const std::string& data);
|
||||||
|
|
||||||
struct ClientState {
|
struct ClientState {
|
||||||
|
|||||||
@ -21,8 +21,8 @@ namespace ZL {
|
|||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 gen(rd());
|
std::mt19937 gen(rd());
|
||||||
|
|
||||||
const float MIN_COORD = -1000.0f;
|
const float MIN_COORD = -100.0f;
|
||||||
const float MAX_COORD = 1000.0f;
|
const float MAX_COORD = 100.0f;
|
||||||
const float MIN_DISTANCE = 3.0f;
|
const float MIN_DISTANCE = 3.0f;
|
||||||
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
|
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
|
||||||
const int MAX_ATTEMPTS = 1000;
|
const int MAX_ATTEMPTS = 1000;
|
||||||
@ -68,7 +68,7 @@ namespace ZL {
|
|||||||
Eigen::Vector3f LocalClient::generateRandomPosition() {
|
Eigen::Vector3f LocalClient::generateRandomPosition() {
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 gen(rd());
|
std::mt19937 gen(rd());
|
||||||
std::uniform_real_distribution<> distrib(-5000.0, 5000.0);
|
std::uniform_real_distribution<> distrib(-500.0, 500.0);
|
||||||
|
|
||||||
return Eigen::Vector3f(
|
return Eigen::Vector3f(
|
||||||
(float)distrib(gen),
|
(float)distrib(gen),
|
||||||
@ -238,6 +238,11 @@ namespace ZL {
|
|||||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
const float projectileHitRadius = 1.5f;
|
||||||
|
const float boxCollisionRadius = 2.0f;
|
||||||
|
const float shipCollisionRadius = 15.0f;
|
||||||
|
const float npcCollisionRadius = 5.0f;
|
||||||
|
|
||||||
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
|
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
|
||||||
|
|
||||||
for (size_t bi = 0; bi < serverBoxes.size(); ++bi) {
|
for (size_t bi = 0; bi < serverBoxes.size(); ++bi) {
|
||||||
|
|||||||
@ -190,28 +190,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.rfind("SPAWN:", 0) == 0) {
|
|
||||||
// SPAWN:playerId:serverTime:<14 полей>
|
|
||||||
if (parts.size() >= 3 + 14) {
|
|
||||||
try {
|
|
||||||
int pid = std::stoi(parts[1]);
|
|
||||||
uint64_t serverTime = std::stoull(parts[2]);
|
|
||||||
|
|
||||||
ClientState st;
|
|
||||||
st.id = pid;
|
|
||||||
std::chrono::system_clock::time_point tp{ std::chrono::milliseconds(serverTime) };
|
|
||||||
st.lastUpdateServerTime = tp;
|
|
||||||
|
|
||||||
// данные начинаются с parts[3]
|
|
||||||
st.handle_full_sync(parts, 3);
|
|
||||||
|
|
||||||
pendingSpawns_.push_back(st);
|
|
||||||
std::cout << "Client: SPAWN received for player " << pid << std::endl;
|
|
||||||
}
|
|
||||||
catch (...) {}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (msg.rfind("EVENT:", 0) == 0) {
|
if (msg.rfind("EVENT:", 0) == 0) {
|
||||||
//auto parts = split(msg, ':');
|
//auto parts = split(msg, ':');
|
||||||
if (parts.size() < 5) return; // EVENT:ID:TYPE:TIME:DATA...
|
if (parts.size() < 5) return; // EVENT:ID:TYPE:TIME:DATA...
|
||||||
@ -355,12 +334,6 @@ namespace ZL {
|
|||||||
copy.swap(pendingBoxDestructions_);
|
copy.swap(pendingBoxDestructions_);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ClientState> WebSocketClientBase::getPendingSpawns() {
|
|
||||||
std::vector<ClientState> copy;
|
|
||||||
copy.swap(pendingSpawns_);
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -22,7 +22,6 @@ namespace ZL {
|
|||||||
std::vector<BoxDestroyedInfo> pendingBoxDestructions_;
|
std::vector<BoxDestroyedInfo> pendingBoxDestructions_;
|
||||||
int clientId = -1;
|
int clientId = -1;
|
||||||
int64_t timeOffset = 0;
|
int64_t timeOffset = 0;
|
||||||
std::vector<ClientState> pendingSpawns_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int GetClientId() const override { return clientId; }
|
int GetClientId() const override { return clientId; }
|
||||||
@ -49,8 +48,6 @@ namespace ZL {
|
|||||||
std::vector<DeathInfo> getPendingDeaths() override;
|
std::vector<DeathInfo> getPendingDeaths() override;
|
||||||
std::vector<int> getPendingRespawns() override;
|
std::vector<int> getPendingRespawns() override;
|
||||||
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override;
|
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override;
|
||||||
std::vector<ClientState> getPendingSpawns();
|
|
||||||
int getClientId() const { return clientId; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -362,10 +362,9 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
|||||||
// 4. Рендеринг
|
// 4. Рендеринг
|
||||||
r->shaderManager.PushShader(shaderName);
|
r->shaderManager.PushShader(shaderName);
|
||||||
|
|
||||||
// Матрица проекции — используем виртуальные проекционные размеры,
|
// Матрица проекции (экрана)
|
||||||
// чтобы координаты текста были независимы от физического разрешения экрана.
|
float W = (float)Environment::width;
|
||||||
float W = Environment::projectionWidth;
|
float H = (float)Environment::height;
|
||||||
float H = Environment::projectionHeight;
|
|
||||||
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
|
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
|
||||||
proj(0, 0) = 2.0f / W;
|
proj(0, 0) = 2.0f / W;
|
||||||
proj(1, 1) = 2.0f / H;
|
proj(1, 1) = 2.0f / H;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user