diff --git a/proj-web/space-game001plain.html b/proj-web/space-game001plain.html
index 17708db..72892cd 100644
--- a/proj-web/space-game001plain.html
+++ b/proj-web/space-game001plain.html
@@ -1,100 +1,76 @@
-
-
+
+
+
+
+
+ Space Game
+
+
+
+
+ Downloading...
+
-
-
-
- Space Game
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/resources/Cargo_Base_color_sRGB.png b/resources/Cargo_Base_color_sRGB.png
index 6ec9e8e..493514c 100644
--- a/resources/Cargo_Base_color_sRGB.png
+++ b/resources/Cargo_Base_color_sRGB.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6bd5d071ed94f2bd8ce3ab060136e9b93b04b06d5eabaffc7845a99d73faeb30
-size 2345111
+oid sha256:d8505521fa1598d9140e518deabcc7c20b226b90a7758e1b1ff5795c9a3b73a5
+size 2890059
diff --git a/resources/DefaultMaterial_BaseColor_shine.png b/resources/DefaultMaterial_BaseColor_shine.png
deleted file mode 100644
index 53e6f41..0000000
--- a/resources/DefaultMaterial_BaseColor_shine.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:26c118a182e269aebfa22684489836d8632b0b2cb6631be646c2678c18493463
-size 90539
diff --git a/resources/MainCharacter_Base_color_sRGB.png b/resources/MainCharacter_Base_color_sRGB.png
index 73d132c..9c074e3 100644
--- a/resources/MainCharacter_Base_color_sRGB.png
+++ b/resources/MainCharacter_Base_color_sRGB.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dc8c7262949a4f2f86d2cc47daf572d908294868d89f7577a771149c7e2e60e5
-size 1296067
+oid sha256:69a783d983e5356aa0559f0f333ed6a083d4e5c9cd6190bf68a28d122af66ec8
+size 2823280
diff --git a/resources/config/game_over.json b/resources/config/game_over.json
index 4fcac9d..6248579 100644
--- a/resources/config/game_over.json
+++ b/resources/config/game_over.json
@@ -1,93 +1,85 @@
{
- "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"
- }
- }
- ]
- }
+ "root": {
+ "type": "LinearLayout",
+ "orientation": "vertical",
+ "vertical_align": "center",
+ "horizontal_align": "center",
+ "spacing": 10,
+ "x": 0,
+ "y": 0,
+ "width": "match_parent",
+ "height": "match_parent",
+ "children": [
+ {
+ "type": "Button",
+ "name": "gameOverText",
+ "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",
+ "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",
+ "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",
+ "width": 600,
+ "height": 80,
+ "text": "0",
+ "fontSize": 36,
+ "color": [
+ 0,
+ 217,
+ 255,
+ 1
+ ],
+ "align": "center"
+ },
+ {
+ "type": "Button",
+ "name": "restartButton",
+ "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",
+ "width": 382,
+ "height": 56,
+ "textures": {
+ "normal": "resources/game_over/Secondarybutton.png",
+ "hover": "resources/game_over/Secondarybutton.png",
+ "pressed": "resources/game_over/Secondarybutton.png"
+ }
+ }
+ ]
+ }
}
\ No newline at end of file
diff --git a/resources/config/game_over_old.json b/resources/config/game_over_old.json
new file mode 100644
index 0000000..4fcac9d
--- /dev/null
+++ b/resources/config/game_over_old.json
@@ -0,0 +1,93 @@
+{
+ "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"
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/resources/config/main_menu.json b/resources/config/main_menu.json
index 1807e74..55bbb1f 100644
--- a/resources/config/main_menu.json
+++ b/resources/config/main_menu.json
@@ -1,141 +1,81 @@
{
"root": {
- "type": "FrameLayout",
- "x": 0,
- "y": 0,
- "width": 1280,
- "height": 720,
- "children": [
- {
- "type": "LinearLayout",
- "name": "settingsButtons",
- "orientation": "vertical",
- "spacing": 10,
- "x": 0,
- "y": 0,
- "width": 300,
- "height": 300,
- "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": {
+ "type": "LinearLayout",
+ "orientation": "vertical",
+ "vertical_align": "center",
+ "horizontal_align": "center",
+ "spacing": 10,
+ "x": 0,
+ "y": 0,
+ "width": "match_parent",
+ "height": "match_parent",
+ "children": [
+ {
+ "type": "Button",
+ "name": "titleBtn",
+ "width": 254,
+ "height": 35,
+ "textures": {
"normal": "resources/main_menu/title.png",
"hover": "resources/main_menu/title.png",
"pressed": "resources/main_menu/title.png"
- }
- },
- {
- "type": "Button",
- "name": "underlineBtn",
- "x": 516,
- "y": 465,
- "width": 168,
- "height": 44,
- "textures": {
+ }
+ },
+ {
+ "type": "Button",
+ "name": "underlineBtn",
+ "width": 168,
+ "height": 44,
+ "textures": {
"normal": "resources/main_menu/line.png",
"hover": "resources/main_menu/line.png",
"pressed": "resources/main_menu/line.png"
- }
- },
- {
- "type": "Button",
- "name": "subtitleBtn",
- "x": 528,
- "y": 455,
- "width": 144,
- "height": 11,
- "textures": {
+ }
+ },
+ {
+ "type": "Button",
+ "name": "subtitleBtn",
+ "width": 144,
+ "height": 11,
+ "textures": {
"normal": "resources/main_menu/subtitle.png",
"hover": "resources/main_menu/subtitle.png",
"pressed": "resources/main_menu/subtitle.png"
- }
- },
- {
- "type": "Button",
- "name": "singleButton",
- "x": 409,
- "y": 360,
- "width": 382,
- "height": 56,
- "textures": {
+ }
+ },
+ {
+ "type": "Button",
+ "name": "singleButton",
+ "width": 382,
+ "height": 56,
+ "textures": {
"normal": "resources/main_menu/single.png",
"hover": "resources/main_menu/single.png",
"pressed": "resources/main_menu/single.png"
- }
- },
- {
- "type": "Button",
- "name": "multiplayerButton",
- "x": 409,
- "y": 289,
- "width": 382,
- "height": 56,
- "textures": {
+ }
+ },
+ {
+ "type": "Button",
+ "name": "multiplayerButton",
+ "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": "multiplayerButton2",
- "x": 409,
- "y": 218,
- "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": {
+ }
+ },
+ {
+ "type": "Button",
+ "name": "versionLabel",
+ "width": 81,
+ "height": 9,
+ "textures": {
"normal": "resources/main_menu/version.png",
"hover": "resources/main_menu/version.png",
"pressed": "resources/main_menu/version.png"
- }
}
- ]
}
- ]
- }
+ ]
}
-
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/resources/config/ship_selection_menu.json b/resources/config/ship_selection_menu.json
index 7d953fe..1a305de 100644
--- a/resources/config/ship_selection_menu.json
+++ b/resources/config/ship_selection_menu.json
@@ -1,64 +1,59 @@
{
- "root": {
- "name": "shipSelectionRoot",
- "type": "node",
- "children": [
-
- {
- "type": "TextField",
- "name": "nicknameInput",
- "x": 400,
- "y": 150,
- "width": 400,
- "height": 50,
- "placeholder": "Enter your nickname",
- "fontPath": "resources/fonts/DroidSans.ttf",
- "fontSize": 16,
- "maxLength": 256,
- "color": [122, 156, 198, 1],
- "placeholderColor": [122, 156, 198, 1],
- "backgroundColor": [15, 29, 51, 1],
- "borderColor": [15, 29, 51, 1]
- },
- {
- "type": "Button",
- "name": "spaceshipButton",
- "x": 300,
- "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"
+ "root": {
+ "type": "LinearLayout",
+ "orientation": "vertical",
+ "vertical_align": "center",
+ "horizontal_align": "center",
+ "spacing": 10,
+ "x": 0,
+ "y": 0,
+ "width": "match_parent",
+ "height": "match_parent",
+ "children": [
+ {
+ "type": "LinearLayout",
+ "orientation": "horizontal",
+ "vertical_align": "center",
+ "horizontal_align": "center",
+ "spacing": 10,
+ "width": "match_parent",
+ "height": 260,
+ "children": [
+ {
+ "type": "Button",
+ "name": "spaceshipButton",
+ "width": 256,
+ "height": 256,
+ "textures": {
+ "normal": "resources/multiplayer_menu/ship_fighter.png",
+ "hover": "resources/multiplayer_menu/ship_fighter_pressed.png",
+ "pressed": "resources/multiplayer_menu/ship_fighter_pressed.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"
- }
- }
- ]
- }
- }
\ No newline at end of file
+ ]
+ }
+}
\ No newline at end of file
diff --git a/resources/config/ui.json b/resources/config/ui.json
index f8f1f7d..fa27292 100644
--- a/resources/config/ui.json
+++ b/resources/config/ui.json
@@ -1,194 +1,57 @@
{
"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": "FrameLayout",
+ "x": 0,
+ "y": 0,
+ "width": "match_parent",
+ "height": "match_parent",
+ "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": "Button",
+ "name": "shootButton",
+ "x": 0,
+ "y": 0,
+ "width": 150,
+ "height": 150,
+ "horizontal_gravity": "right",
+ "vertical_gravity": "bottom",
+ "textures": {
+ "normal": "resources/shoot_normal.png",
+ "hover": "resources/shoot_hover.png",
+ "pressed": "resources/shoot_pressed.png"
+ }
+ },
+ {
+ "type": "Button",
+ "name": "shootButton2",
+ "x": 0,
+ "y": 0,
+ "width": 150,
+ "height": 150,
+ "horizontal_gravity": "left",
+ "vertical_gravity": "bottom",
+ "textures": {
+ "normal": "resources/shoot_normal.png",
+ "hover": "resources/shoot_hover.png",
+ "pressed": "resources/shoot_pressed.png"
+ }
+ },
+ {
+ "type": "Slider",
+ "name": "velocitySlider",
+ "x": 10,
+ "y": 200,
+ "width": 80,
+ "height": 300,
+ "value": 0.0,
+ "orientation": "vertical",
+ "horizontal_gravity": "right",
+ "vertical_gravity": "bottom",
+ "textures": {
+ "track": "resources/velocitySliderTexture.png",
+ "knob": "resources/velocitySliderButton.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
- }
- ]
+ ]
}
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/resources/config/ui_old.json b/resources/config/ui_old.json
new file mode 100644
index 0000000..f8f1f7d
--- /dev/null
+++ b/resources/config/ui_old.json
@@ -0,0 +1,194 @@
+{
+ "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
+ }
+ ]
+ }
+ }
\ No newline at end of file
diff --git a/resources/game_over/Container.png b/resources/game_over/Container.png
index 226e276..e2af5b9 100644
--- a/resources/game_over/Container.png
+++ b/resources/game_over/Container.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d495f21543ab72e6a2cb5082507212616c666bef11bc99bd0447e6906a957836
-size 6709
+oid sha256:4292ab255136aeeff003e265bfde42bef4aabb092427bd68f9b2ac42d86916a1
+size 27198
diff --git a/resources/game_over/Filledbuttons.png b/resources/game_over/Filledbuttons.png
index 808344f..a9b2806 100644
--- a/resources/game_over/Filledbuttons.png
+++ b/resources/game_over/Filledbuttons.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f6283a169f71822d3c3f8b3c80369cd3ac25b70ef51d3f34685687b1d2819a1b
-size 2406
+oid sha256:436d9137c479b475d8bc753961986ea3f58b6a2439de6f83b5d707172c3f2ff9
+size 7976
diff --git a/resources/game_over/FinalScore.png b/resources/game_over/FinalScore.png
index 7ac0bbc..af06f07 100644
--- a/resources/game_over/FinalScore.png
+++ b/resources/game_over/FinalScore.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6bb6e8e59482729d6da84188c830842cf07e4e379076ba64d7916b8b0d45cf09
-size 1244
+oid sha256:ea4e2a8408fa1b68793fd8d81135902ff15ef2779ea898a99a9ef83ff6e147e8
+size 4142
diff --git a/resources/game_over/MissionFailed.png b/resources/game_over/MissionFailed.png
index 1a8731d..ec8d6c3 100644
--- a/resources/game_over/MissionFailed.png
+++ b/resources/game_over/MissionFailed.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:930565f3911bcb11904808fe67ca278c1460f42f7c7c7dcfffc2301f5c0d408c
-size 2678
+oid sha256:c2187818c1fbfb127f70c130f033aa7c16cc3b3a02a2ea59317413437f530c30
+size 9982
diff --git a/resources/game_over/Secondarybutton.png b/resources/game_over/Secondarybutton.png
index 5bb684a..37cdae0 100644
--- a/resources/game_over/Secondarybutton.png
+++ b/resources/game_over/Secondarybutton.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:00a1c3c32da992686febd87918801868d4af2ec99904ed3f80b0c8f58009c5b2
-size 1840
+oid sha256:113c524330190fbdf36f0f7b4ebfc03032170aff5011f8c17201533b52db872f
+size 5387
diff --git a/resources/multiplayer_menu/ship_cargo.png b/resources/multiplayer_menu/ship_cargo.png
new file mode 100644
index 0000000..88a6f68
--- /dev/null
+++ b/resources/multiplayer_menu/ship_cargo.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c86eb962f4abf04aacf7ebbf07433611e9afed42b0f7806fc892ae4a81b45b58
+size 13296
diff --git a/resources/multiplayer_menu/ship_cargo_pressed.png b/resources/multiplayer_menu/ship_cargo_pressed.png
new file mode 100644
index 0000000..30590cf
--- /dev/null
+++ b/resources/multiplayer_menu/ship_cargo_pressed.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97d66a6cd037fd75d6f78e72d37b14362683a0de38557470806db53d5e5675d9
+size 13694
diff --git a/resources/multiplayer_menu/ship_fighter.png b/resources/multiplayer_menu/ship_fighter.png
new file mode 100644
index 0000000..4c6ea5f
--- /dev/null
+++ b/resources/multiplayer_menu/ship_fighter.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:072bf292acb2760e1ce0ab6c783eca114614d95a9451ba3c86088eee9d824b43
+size 29780
diff --git a/resources/multiplayer_menu/ship_fighter_pressed.png b/resources/multiplayer_menu/ship_fighter_pressed.png
new file mode 100644
index 0000000..c0628eb
--- /dev/null
+++ b/resources/multiplayer_menu/ship_fighter_pressed.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7a120a716261b122f6a90a04d7a9bbf1c9582e8c9c114e633bc348c15f7d002
+size 29926
diff --git a/resources/spark2.png b/resources/spark2.png
new file mode 100644
index 0000000..b0e806f
--- /dev/null
+++ b/resources/spark2.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11cde0c85c95f91eb9ace65cead22a37ba80d215897f32a2d6be1410210c1acf
+size 2656
diff --git a/server/server.cpp b/server/server.cpp
index afce8e6..186a76a 100644
--- a/server/server.cpp
+++ b/server/server.cpp
@@ -62,7 +62,7 @@ struct Projectile {
uint64_t spawnMs = 0;
Eigen::Vector3f pos;
Eigen::Vector3f vel;
- float lifeMs = 5000.0f;
+ float lifeMs = PROJECTILE_LIFE;
};
struct BoxDestroyedInfo {
@@ -699,11 +699,12 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
}
}
+
+
// --- Tick: box-projectile collisions ---
{
std::lock_guard bm(g_boxes_mutex);
- const float projectileHitRadius = 5.0f;
- const float boxCollisionRadius = 2.0f;
+
std::vector> boxProjectileCollisions;
@@ -749,9 +750,6 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
std::lock_guard bm(g_boxes_mutex);
std::lock_guard lm(g_sessions_mutex);
- const float shipCollisionRadius = 15.0f;
- const float boxCollisionRadius = 2.0f;
-
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
if (g_serverBoxes[bi].destroyed) continue;
@@ -840,8 +838,8 @@ std::vector generateServerBoxes(int count) {
std::random_device rd;
std::mt19937 gen(rd());
- const float MIN_COORD = -100.0f;
- const float MAX_COORD = 100.0f;
+ const float MIN_COORD = -1000.0f;
+ const float MAX_COORD = 1000.0f;
const float MIN_DISTANCE = 3.0f;
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
const int MAX_ATTEMPTS = 1000;
diff --git a/src/Environment.cpp b/src/Environment.cpp
index 1fec925..9c41b4d 100644
--- a/src/Environment.cpp
+++ b/src/Environment.cpp
@@ -36,5 +36,25 @@ ClientState Environment::shipState;
const float Environment::CONST_Z_NEAR = 5.f;
const float Environment::CONST_Z_FAR = 5000.f;
+float Environment::projectionWidth = 1280.0f;
+float Environment::projectionHeight = 720.0f;
+
+void Environment::computeProjectionDimensions()
+{
+ if (width <= 0 || height <= 0) return;
+
+ const float refShortSide = 720.0f;
+ float aspect = (float)width / (float)height;
+
+ if (width >= height) {
+ // Landscape: fix height to 720, scale width to preserve aspect
+ projectionHeight = refShortSide;
+ projectionWidth = refShortSide * aspect;
+ } else {
+ // Portrait: fix width to 720, scale height to preserve aspect
+ projectionWidth = refShortSide;
+ projectionHeight = refShortSide / aspect;
+ }
+}
} // namespace ZL
diff --git a/src/Environment.h b/src/Environment.h
index 5d3c1ca..17125ac 100644
--- a/src/Environment.h
+++ b/src/Environment.h
@@ -35,8 +35,15 @@ public:
static const float CONST_Z_NEAR;
static const float CONST_Z_FAR;
+ // Virtual projection dimensions used for all 2D/UI rendering.
+ // These maintain the screen's actual aspect ratio but normalize the
+ // height to 720 (landscape) or width to 720 (portrait), giving a
+ // consistent coordinate space regardless of physical screen resolution.
+ static float projectionWidth;
+ static float projectionHeight;
-
+ // Call this once at startup and whenever the window is resized.
+ static void computeProjectionDimensions();
};
} // namespace ZL
diff --git a/src/Game.cpp b/src/Game.cpp
index 72a7b8c..82c81fd 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -84,6 +84,8 @@ namespace ZL
void Game::setup() {
glContext = SDL_GL_CreateContext(ZL::Environment::window);
+ Environment::computeProjectionDimensions();
+
ZL::BindOpenGlFunctions();
ZL::CheckGlError();
renderer.InitOpenGL();
@@ -100,7 +102,7 @@ namespace ZL
loadingTexture = std::make_unique(CreateTextureDataFromPng("resources/loading.png", CONST_ZIP_FILE));
#endif
- loadingMesh.data = CreateRect2D({ Environment::width * 0.5, Environment::height * 0.5 }, { Environment::width * 0.5, Environment::height*0.5 }, 3);
+ loadingMesh.data = CreateRect2D({ Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, { Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, 3);
loadingMesh.RefreshVBO();
#ifdef EMSCRIPTEN
@@ -142,17 +144,14 @@ namespace ZL
Environment::shipState.nickname = nickname;
Environment::shipState.shipType = shipType;
- networkClient = std::make_unique();
+ auto localClient = new LocalClient;
+ ClientState st = Environment::shipState;
+ st.id = localClient->GetClientId();
+ localClient->setLocalPlayerState(st);
+
+ networkClient = std::unique_ptr(localClient);
networkClient->Connect("", 0);
-#ifndef NETWORK
- auto localClient = dynamic_cast(networkClient.get());
- if (localClient) {
- ZL::ClientState st = Environment::shipState;
- st.id = localClient->GetClientId();
- localClient->setLocalPlayerState(st);
- }
-#endif
lastTickCount = 0;
spaceGameStarted = 1;
};
@@ -161,8 +160,7 @@ namespace ZL
Environment::shipState.nickname = nickname;
Environment::shipState.shipType = shipType;
- networkClient = std::make_unique();
-#ifdef NETWORK
+
#ifdef EMSCRIPTEN
networkClient = std::make_unique();
networkClient->Connect("localhost", 8081);
@@ -170,19 +168,6 @@ namespace ZL
networkClient = std::make_unique(taskManager.getIOContext());
networkClient->Connect("localhost", 8081);
#endif
-#else
- networkClient->Connect("", 0);
-#endif
-
-#ifndef NETWORK
- auto localClient = dynamic_cast(networkClient.get());
- if (localClient) {
- ZL::ClientState st = Environment::shipState;
- st.id = localClient->GetClientId();
- localClient->setLocalPlayerState(st);
- }
-#endif
-
if (networkClient) {
std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType);
@@ -265,8 +250,8 @@ namespace ZL
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
- float width = Environment::width;
- float height = Environment::height;
+ float width = Environment::projectionWidth;
+ float height = Environment::projectionHeight;
renderer.PushProjectionMatrix(
0, width,
@@ -351,16 +336,19 @@ namespace ZL
if (event.type == SDL_QUIT) {
Environment::exitGameLoop = true;
}
-#if SDL_VERSION_ATLEAST(2,0,5)
- else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
+
+
+ if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
// Обновляем размеры и сбрасываем кеш текстов, т.к. меши хранятся в пикселях
Environment::width = event.window.data1;
Environment::height = event.window.data2;
- std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
+ Environment::computeProjectionDimensions();
+ menuManager.uiManager.updateAllLayouts();
+ std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
space.clearTextRendererCache();
}
-#endif
+
#ifdef __ANDROID__
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
Environment::exitGameLoop = true;
@@ -369,22 +357,38 @@ namespace ZL
#ifdef __ANDROID__
if (event.type == SDL_FINGERDOWN) {
- int mx = static_cast(event.tfinger.x * Environment::width);
- int my = static_cast(event.tfinger.y * Environment::height);
+ int mx = static_cast(event.tfinger.x * Environment::projectionWidth);
+ int my = static_cast(event.tfinger.y * Environment::projectionHeight);
handleDown(mx, my);
}
else if (event.type == SDL_FINGERUP) {
- int mx = static_cast(event.tfinger.x * Environment::width);
- int my = static_cast(event.tfinger.y * Environment::height);
+ int mx = static_cast(event.tfinger.x * Environment::projectionWidth);
+ int my = static_cast(event.tfinger.y * Environment::projectionHeight);
handleUp(mx, my);
}
else if (event.type == SDL_FINGERMOTION) {
- int mx = static_cast(event.tfinger.x * Environment::width);
- int my = static_cast(event.tfinger.y * Environment::height);
+ int mx = static_cast(event.tfinger.x * Environment::projectionWidth);
+ int my = static_cast(event.tfinger.y * Environment::projectionHeight);
handleMotion(mx, my);
}
#else
- if (event.type == SDL_MOUSEBUTTONDOWN) {
+
+
+ if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
+ // Преобразуем экранные пиксели в проекционные единицы
+ int mx = static_cast((float)event.button.x / Environment::width * Environment::projectionWidth);
+ int my = static_cast((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((float)event.motion.x / Environment::width * Environment::projectionWidth);
+ int my = static_cast((float)event.motion.y / Environment::height * Environment::projectionHeight);
+ handleMotion(mx, my);
+ }
+
+ /*if (event.type == SDL_MOUSEBUTTONDOWN) {
int mx = event.button.x;
int my = event.button.y;
handleDown(mx, my);
@@ -398,7 +402,7 @@ namespace ZL
int mx = event.motion.x;
int my = event.motion.y;
handleMotion(mx, my);
- }
+ }*/
if (event.type == SDL_MOUSEWHEEL) {
static const float zoomstep = 2.0f;
@@ -480,7 +484,7 @@ namespace ZL
void Game::handleDown(int mx, int my)
{
int uiX = mx;
- int uiY = Environment::height - my;
+ int uiY = Environment::projectionHeight - my;
menuManager.uiManager.onMouseDown(uiX, uiY);
@@ -508,7 +512,7 @@ namespace ZL
void Game::handleUp(int mx, int my)
{
int uiX = mx;
- int uiY = Environment::height - my;
+ int uiY = Environment::projectionHeight - my;
menuManager.uiManager.onMouseUp(uiX, uiY);
@@ -523,7 +527,7 @@ namespace ZL
void Game::handleMotion(int mx, int my)
{
int uiX = mx;
- int uiY = Environment::height - my;
+ int uiY = Environment::projectionHeight - my;
menuManager.uiManager.onMouseMove(uiX, uiY);
diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp
index b87a72e..c5b072d 100644
--- a/src/MenuManager.cpp
+++ b/src/MenuManager.cpp
@@ -96,6 +96,7 @@ namespace ZL {
}
});
+
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
onFirePressed();
});
@@ -103,6 +104,7 @@ namespace ZL {
onFirePressed();
});
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
+
int newVel = roundf(value * 10);
if (newVel > 2)
{
@@ -183,8 +185,8 @@ namespace ZL {
}
});
- uiManager.setButtonCallback("multiplayerButton2", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
- /*std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
+ /*uiManager.setButtonCallback("multiplayerButton2", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
+ std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
uiManager.startAnimationOnNode("playButton", "buttonsExit");
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
@@ -219,7 +221,7 @@ namespace ZL {
}
else {
std::cerr << "Failed to load multiplayer menu\n";
- }*/
+ }
std::cerr << "Single button pressed: " << name << " -> open ship selection UI\n";
if (!shipSelectionRoot) {
std::cerr << "Failed to load ship selection UI\n";
@@ -255,7 +257,7 @@ namespace ZL {
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
Environment::exitGameLoop = true;
- });
+ });*/
}
void MenuManager::showGameOver(int score)
diff --git a/src/Projectile.cpp b/src/Projectile.cpp
index 419419d..9f98d4f 100644
--- a/src/Projectile.cpp
+++ b/src/Projectile.cpp
@@ -41,7 +41,7 @@ namespace ZL {
}
void Projectile::rebuildMesh(Renderer&) {
- float half = size * 0.5f;
+ float half = 10 * size * 0.5f;
mesh.data.PositionData.clear();
mesh.data.TexCoordData.clear();
diff --git a/src/Projectile.h b/src/Projectile.h
index 872177f..e72552e 100644
--- a/src/Projectile.h
+++ b/src/Projectile.h
@@ -3,6 +3,7 @@
#include "render/Renderer.h"
#include "render/TextureManager.h"
#include
+#include "SparkEmitter.h"
namespace ZL {
@@ -19,6 +20,8 @@ namespace ZL {
Vector3f getPosition() const { return pos; }
void deactivate() { active = false; }
+
+ SparkEmitter projectileEmitter;
private:
Vector3f pos;
Vector3f vel;
diff --git a/src/Space.cpp b/src/Space.cpp
index 146d7bc..3639912 100644
--- a/src/Space.cpp
+++ b/src/Space.cpp
@@ -159,15 +159,15 @@ namespace ZL
// В пределах экрана?
// (можно оставить, можно клампить)
- float sx = (ndc.x() * 0.5f + 0.5f) * Environment::width;
- float sy = (ndc.y() * 0.5f + 0.5f) * Environment::height;
+ float sx = (ndc.x() * 0.5f + 0.5f) * Environment::projectionWidth;
+ float sy = (ndc.y() * 0.5f + 0.5f) * Environment::projectionHeight;
outX = sx;
outY = sy;
// Можно отсеять те, что вне:
- if (sx < -200 || sx > Environment::width + 200) return false;
- if (sy < -200 || sy > Environment::height + 200) return false;
+ if (sx < -200 || sx > Environment::projectionWidth + 200) return false;
+ if (sy < -200 || sy > Environment::projectionHeight + 200) return false;
return true;
}
@@ -296,12 +296,12 @@ namespace ZL
cubemapTexture = std::make_shared(
std::array{
- CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
- CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
- CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
- CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
- CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE),
- CreateTextureDataFromPng("resources/sky/space_red.png", CONST_ZIP_FILE)
+ CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
+ CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
+ CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
+ CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
+ CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE),
+ CreateTextureDataFromPng("resources/sky/space1.png", CONST_ZIP_FILE)
});
@@ -335,6 +335,7 @@ namespace ZL
cargo.AssignFrom(cargoBase);
cargo.RefreshVBO();
+ //projectileTexture = std::make_shared(CreateTextureDataFromPng("resources/spark2.png", CONST_ZIP_FILE));
//Boxes
boxTexture = std::make_unique(CreateTextureDataFromPng("resources/box/box.png", CONST_ZIP_FILE));
@@ -362,7 +363,7 @@ namespace ZL
}
crosshairCfgLoaded = loadCrosshairConfig("resources/config/crosshair_config.json");
- std::cerr << "[Crosshair] loaded=" << crosshairCfgLoaded
+ std::cout << "[Crosshair] loaded=" << crosshairCfgLoaded
<< " enabled=" << crosshairCfg.enabled
<< " w=" << Environment::width << " h=" << Environment::height
<< " alpha=" << crosshairCfg.alpha
@@ -490,13 +491,25 @@ namespace ZL
glEnable(GL_BLEND);
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) {
if (p && p->isActive()) {
p->draw(renderer);
+ p->projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
}
}
+ glDisable(GL_BLEND);
- projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
+ renderer.DisableVertexAttribArray(vPositionName);
+ renderer.DisableVertexAttribArray(vTexCoordName);
+ renderer.shaderManager.PopShader();
+
+ //projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
if (shipAlive) {
renderer.PushMatrix();
@@ -859,8 +872,8 @@ namespace ZL
// если ничего не изменилось — не трогаем VBO
if (crosshairMeshValid &&
- crosshairLastW == Environment::width &&
- crosshairLastH == Environment::height &&
+ crosshairLastW == Environment::projectionWidth &&
+ crosshairLastH == Environment::projectionHeight &&
std::abs(crosshairLastAlpha - crosshairCfg.alpha) < 1e-6f &&
std::abs(crosshairLastThickness - crosshairCfg.thicknessPx) < 1e-6f &&
std::abs(crosshairLastGap - crosshairCfg.gapPx) < 1e-6f &&
@@ -869,18 +882,18 @@ namespace ZL
return;
}
- crosshairLastW = Environment::width;
- crosshairLastH = Environment::height;
+ crosshairLastW = Environment::projectionWidth;
+ crosshairLastH = Environment::projectionHeight;
crosshairLastAlpha = crosshairCfg.alpha;
crosshairLastThickness = crosshairCfg.thicknessPx;
crosshairLastGap = crosshairCfg.gapPx;
crosshairLastScaleMul = crosshairCfg.scaleMul;
- float cx = Environment::width * 0.5f;
- float cy = Environment::height * 0.5f;
+ float cx = Environment::projectionWidth * 0.5f;
+ float cy = Environment::projectionHeight * 0.5f;
// масштаб от reference (стандартно: по высоте)
- float scale = (crosshairCfg.refH > 0) ? (Environment::height / (float)crosshairCfg.refH) : 1.0f;
+ float scale = (crosshairCfg.refH > 0) ? (Environment::projectionHeight / (float)crosshairCfg.refH) : 1.0f;
scale *= crosshairCfg.scaleMul;
float thickness = crosshairCfg.thicknessPx * scale;
@@ -940,7 +953,7 @@ namespace ZL
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
renderer.shaderManager.PushShader("defaultColor");
- renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
+ renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
renderer.PushMatrix();
renderer.LoadIdentity();
@@ -1124,7 +1137,7 @@ namespace ZL
// Lead Indicator
// скорость пули (как в fireProjectiles)
- const float projectileSpeed = 60.0f;
+ const float projectileSpeed = PROJECTILE_VELOCITY;
// позиция вылета
Vector3f shooterPos = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f };
@@ -1187,15 +1200,15 @@ namespace ZL
// 4) Настройки стиля
Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный
- float thickness = 10.0f; // толщина линий (px)
+ float thickness = 2.0f; // толщина линий (px)
float z = 0.0f; // 2D слой
// 5) Если цель в кадре: рисуем скобки
if (onScreen)
{
// перевод NDC -> экран (в пикселях)
- float sx = (ndcX * 0.5f + 0.5f) * Environment::width;
- float sy = (ndcY * 0.5f + 0.5f) * Environment::height;
+ float sx = (ndcX * 0.5f + 0.5f) * Environment::projectionWidth;
+ float sy = (ndcY * 0.5f + 0.5f) * Environment::projectionHeight;
// анимация “снаружи внутрь”
// targetAcquireAnim растёт к 1, быстро (похоже на захват)
@@ -1234,7 +1247,7 @@ namespace ZL
glClear(GL_DEPTH_BUFFER_BIT);
renderer.shaderManager.PushShader("defaultColor");
- renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
+ renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
renderer.PushMatrix();
renderer.LoadIdentity();
@@ -1248,8 +1261,8 @@ namespace ZL
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) {
- float lx = (leadNdcX * 0.5f + 0.5f) * Environment::width;
- float ly = (leadNdcY * 0.5f + 0.5f) * Environment::height;
+ float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
+ float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
float distLead = (Environment::shipState.position - leadWorld).norm();
float r = 30.0f / (distLead * 0.01f + 1.0f);
@@ -1323,8 +1336,8 @@ namespace ZL
float edgeNdcX = dirX * k;
float edgeNdcY = dirY * k;
- float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::width;
- float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::height;
+ float edgeX = (edgeNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
+ float edgeY = (edgeNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
float bob = std::sin(t * 6.0f) * 6.0f;
edgeX += dirX * bob;
@@ -1367,7 +1380,7 @@ namespace ZL
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
renderer.shaderManager.PushShader("defaultColor");
- renderer.PushProjectionMatrix((float)Environment::width, (float)Environment::height, 0.f, 1.f);
+ renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, 0.f, 1.f);
renderer.PushMatrix();
renderer.LoadIdentity();
@@ -1540,22 +1553,25 @@ namespace ZL
}
}
- std::vector projCameraPoints;
+
for (const auto& p : projectiles) {
if (p && p->isActive()) {
Vector3f worldPos = p->getPosition();
Vector3f rel = worldPos - Environment::shipState.position;
Vector3f camPos = Environment::inverseShipMatrix * rel;
- projCameraPoints.push_back(camPos);
+ p->projectileEmitter.setEmissionPoints({ camPos });
+ p->projectileEmitter.emit();
+ p->projectileEmitter.update(static_cast(delta));
}
}
+ /*
if (!projCameraPoints.empty()) {
projectileEmitter.setEmissionPoints(projCameraPoints);
projectileEmitter.emit();
}
else {
projectileEmitter.setEmissionPoints(std::vector());
- }
+ }*/
std::vector shipCameraPoints;
for (const auto& lp : shipLocalEmissionPoints) {
@@ -1567,7 +1583,7 @@ namespace ZL
}
sparkEmitter.update(static_cast(delta));
- projectileEmitter.update(static_cast(delta));
+ //projectileEmitter.update(static_cast(delta));
explosionEmitter.update(static_cast(delta));
if (showExplosion) {
@@ -1696,8 +1712,8 @@ namespace ZL
Vector3f{ 1.5f, 0.9f - 6.f, 5.0f }
};
- const float projectileSpeed = 60.0f;
- const float lifeMs = 50000.0f;
+ const float projectileSpeed = PROJECTILE_VELOCITY;
+ const float lifeMs = PROJECTILE_LIFE;
const float size = 0.5f;
Vector3f localForward = { 0,0,-1 };
@@ -1710,6 +1726,7 @@ namespace ZL
for (auto& p : projectiles) {
if (!p->isActive()) {
p->init(worldPos, worldVel, lifeMs, size, projectileTexture, renderer);
+ p->projectileEmitter = SparkEmitter(projectileEmitter);
break;
}
}
@@ -1721,8 +1738,8 @@ namespace ZL
if (networkClient) {
auto pending = networkClient->getPendingProjectiles();
if (!pending.empty()) {
- const float projectileSpeed = 60.0f;
- const float lifeMs = 5000.0f;
+ const float projectileSpeed = PROJECTILE_VELOCITY;
+ const float lifeMs = PROJECTILE_LIFE;
const float size = 0.5f;
for (const auto& pi : pending) {
const std::vector localOffsets = {
@@ -1747,6 +1764,7 @@ namespace ZL
for (auto& p : projectiles) {
if (!p->isActive()) {
p->init(shotPos, baseVel, lifeMs, size, projectileTexture, renderer);
+ p->projectileEmitter = SparkEmitter(projectileEmitter);
break;
}
}
diff --git a/src/Space.h b/src/Space.h
index 894fa39..b38b98e 100644
--- a/src/Space.h
+++ b/src/Space.h
@@ -96,7 +96,7 @@ namespace ZL {
std::shared_ptr projectileTexture;
float projectileCooldownMs = 500.0f;
int64_t lastProjectileFireTime = 0;
- int maxProjectiles = 32;
+ int maxProjectiles = 500;
std::vector shipLocalEmissionPoints;
diff --git a/src/SparkEmitter.cpp b/src/SparkEmitter.cpp
index 05f686c..5f70710 100644
--- a/src/SparkEmitter.cpp
+++ b/src/SparkEmitter.cpp
@@ -25,6 +25,23 @@ namespace ZL {
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& positions, float rate)
: emissionPoints(positions), emissionRate(rate), isActive(true),
drawDataDirty(true), maxParticles(positions.size() * 100),
diff --git a/src/SparkEmitter.h b/src/SparkEmitter.h
index 3eed0aa..812b828 100644
--- a/src/SparkEmitter.h
+++ b/src/SparkEmitter.h
@@ -41,7 +41,7 @@ namespace ZL {
float biasX;
// Ranges (used when config supplies intervals)
- struct FloatRange { float min; float max; };
+ struct FloatRange { float min=0; float max=0; };
FloatRange speedRange; // XY speed
FloatRange zSpeedRange; // Z speed
FloatRange scaleRange;
@@ -55,6 +55,7 @@ namespace ZL {
public:
SparkEmitter();
+ SparkEmitter(const SparkEmitter& copyFrom);
SparkEmitter(const std::vector& positions, float rate = 100.0f);
SparkEmitter(const std::vector& positions,
std::shared_ptr tex,
diff --git a/src/UiManager.cpp b/src/UiManager.cpp
index 647d72f..f06e47f 100644
--- a/src/UiManager.cpp
+++ b/src/UiManager.cpp
@@ -184,21 +184,89 @@ namespace ZL {
std::shared_ptr parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
auto node = std::make_shared();
- if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get();
- if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get();
- if (j.contains("x")) node->rect.x = j["x"].get();
- if (j.contains("y")) node->rect.y = j["y"].get();
- if (j.contains("width")) node->rect.w = j["width"].get();
- if (j.contains("height")) node->rect.h = j["height"].get();
+ // 1. Определяем тип контейнера и ориентацию
+ std::string typeStr = j.value("type", "FrameLayout"); // По умолчанию FrameLayout
+ if (typeStr == "LinearLayout") {
+ node->layoutType = LayoutType::Linear;
+ }
+ else {
+ node->layoutType = LayoutType::Frame;
+ }
- if (j.contains("orientation") && j["orientation"].is_string()) node->orientation = j["orientation"].get();
- if (j.contains("spacing")) node->spacing = j["spacing"].get();
+ if (j.contains("name")) node->name = j["name"].get();
- if (node->type == "Button") {
+ // 2. Читаем размеры во временные "локальные" поля
+ // Это критически важно: мы не пишем сразу в 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();
+ }
+ }
+ 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();
+ }
+ }
+ else
+ {
+ node->height = 0.0f;
+ }
+
+ // 3. Параметры компоновки
+ if (j.contains("orientation")) {
+ std::string orient = j["orientation"].get();
+ 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();
+ 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();
+ 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();
btn->name = node->name;
- btn->rect = node->rect;
+ btn->rect = initialRect;
if (!j.contains("textures") || !j["textures"].is_object()) {
std::cerr << "UiManager: Button '" << btn->name << "' missing textures" << std::endl;
@@ -225,10 +293,10 @@ namespace ZL {
node->button = btn;
}
- else if (node->type == "Slider") {
+ else if (typeStr == "Slider") {
auto s = std::make_shared();
s->name = node->name;
- s->rect = node->rect;
+ s->rect = initialRect;
if (!j.contains("textures") || !j["textures"].is_object()) {
std::cerr << "UiManager: Slider '" << s->name << "' missing textures" << std::endl;
@@ -261,10 +329,10 @@ namespace ZL {
node->slider = s;
}
- else if (node->type == "TextField") {
+ else if (typeStr == "TextField") {
auto tf = std::make_shared();
tf->name = node->name;
- tf->rect = node->rect;
+ tf->rect = initialRect;
if (j.contains("placeholder")) tf->placeholder = j["placeholder"].get();
if (j.contains("fontPath")) tf->fontPath = j["fontPath"].get();
@@ -331,11 +399,11 @@ namespace ZL {
}
}
- if (node->type == "TextView") {
+ if (typeStr == "TextView") {
auto tv = std::make_shared();
- tv->name = node->name;
- tv->rect = node->rect;
+ tv->name = node->name;
+ tv->rect = initialRect;
if (j.contains("text")) tv->text = j["text"].get();
if (j.contains("fontPath")) tv->fontPath = j["fontPath"].get();
if (j.contains("fontSize")) tv->fontSize = j["fontSize"].get();
@@ -400,6 +468,7 @@ namespace ZL {
throw std::runtime_error("Failed to load UI file: " + path);
}
+
root = parseNode(j["root"], renderer, zipFile);
return root;
@@ -407,7 +476,14 @@ namespace ZL {
void UiManager::replaceRoot(std::shared_ptr newRoot) {
root = newRoot;
- layoutNode(root);
+ layoutNode(
+ root,
+ 0.0f, 0.0f, // parentX, parentY (экран начинается с 0,0)
+ Environment::projectionWidth, // parentW
+ Environment::projectionHeight, // parentH
+ root->localX, // finalLocalX
+ root->localY // finalLocalY
+ );
buttons.clear();
sliders.clear();
textViews.clear();
@@ -431,39 +507,170 @@ namespace ZL {
replaceRoot(newRoot);
}
-
- void UiManager::layoutNode(const std::shared_ptr& node) {
- for (auto& child : node->children) {
- child->rect.x += node->rect.x;
- child->rect.y += node->rect.y;
- }
+ void UiManager::layoutNode(const std::shared_ptr& node, float parentX, float parentY, float parentW, float parentH, float finalLocalX, float finalLocalY) {
- if (node->type == "LinearLayout") {
- std::string orient = node->orientation;
- std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
+ node->screenRect.w = (node->width < 0) ? parentW : node->width;
+ node->screenRect.h = (node->height < 0) ? parentH : node->height;
- float cursorX = node->rect.x;
- float cursorY = node->rect.y;
- for (auto& child : node->children) {
- if (orient == "horizontal") {
- child->rect.x = cursorX;
- child->rect.y = node->rect.y;
- cursorX += child->rect.w + node->spacing;
+ // ТЕПЕРЬ используем переданные координаты, а не node->localX напрямую
+ node->screenRect.x = parentX + finalLocalX;
+ node->screenRect.y = parentY + finalLocalY;
+
+ 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 {
- child->rect.x = node->rect.x;
- child->rect.y = cursorY;
- cursorY += child->rect.h + node->spacing;
+ totalContentWidth += node->children[i]->width;
+ if (i < node->children.size() - 1) totalContentWidth += node->spacing;
}
- layoutNode(child);
+ }
+
+ 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;
+
+ for (auto& child : node->children) {
+
+ float childW = (child->width < 0) ? currentW : child->width;
+ float childH = (child->height < 0) ? currentH : child->height;
+
+ 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 {
+ child->localX = cursorX;
+
+ // Вертикальное выравнивание внутри "строки" (Cross-axis alignment)
+ 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);
}
}
else {
for (auto& child : node->children) {
- layoutNode(child);
+ float childW = (child->width < 0) ? currentW : child->width;
+ 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& 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& node) {
@@ -657,7 +864,7 @@ namespace ZL {
}
void UiManager::draw(Renderer& renderer) {
- renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
+ renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, -1, 1);
renderer.PushMatrix();
renderer.LoadIdentity();
diff --git a/src/UiManager.h b/src/UiManager.h
index 05af33c..59743d2 100644
--- a/src/UiManager.h
+++ b/src/UiManager.h
@@ -31,6 +31,48 @@ namespace ZL {
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 {
std::string name;
UiRect rect;
@@ -111,21 +153,38 @@ namespace ZL {
};
struct UiNode {
- std::string type;
- UiRect rect;
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> children;
+
+ // Компоненты (только один из них обычно активен для ноды)
std::shared_ptr button;
std::shared_ptr slider;
std::shared_ptr textView;
std::shared_ptr textField;
- std::string orientation = "vertical";
- float spacing = 0.0f;
+ // Анимации
struct AnimStep {
std::string type;
float toX = 0.0f;
float toY = 0.0f;
+ float toScale = 1.0f; // Полезно добавить для UI
float durationMs = 0.0f;
std::string easing = "linear";
};
@@ -200,9 +259,11 @@ namespace ZL {
bool startAnimationOnNode(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 cb);
+ void updateAllLayouts();
private:
- void layoutNode(const std::shared_ptr& node);
+ void layoutNode(const std::shared_ptr& node, float parentX, float parentY, float parentW, float parentH, float finalLocalX, float finalLocalY);
+ void syncComponentRects(const std::shared_ptr& node);
void collectButtonsAndSliders(const std::shared_ptr& node);
struct ActiveAnim {
diff --git a/src/main.cpp b/src/main.cpp
index d1bdc1e..6b62092 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -44,20 +44,34 @@ EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void
return EM_TRUE;
}
-// Resize the canvas, notify SDL, and push a synthetic SDL_WINDOWEVENT_RESIZED
-// so Game::update()'s existing handler updates Environment::width/height and clears caches.
-static void applyResize(int w, int h) {
- if (w <= 0 || h <= 0) return;
- // Resize the actual WebGL canvas — without this the rendered pixels stay at
- // the original size no matter what Environment::width/height say.
- emscripten_set_canvas_element_size("#canvas", w, h);
- if (ZL::Environment::window)
- SDL_SetWindowSize(ZL::Environment::window, w, h);
+static void applyResize(int logicalW, int logicalH) {
+ // Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
+ double dpr = emscripten_get_device_pixel_ratio();
+
+ // Вычисляем реальные физические пиксели
+ int physicalW = static_cast(logicalW * dpr);
+ int physicalH = static_cast(logicalH * dpr);
+
+ // Устанавливаем размер внутреннего буфера канваса
+ emscripten_set_canvas_element_size("#canvas", physicalW, physicalH);
+
+ // Сообщаем SDL о новом размере.
+ // ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
+ // для корректной работы последующих вызовов glViewport.
+ if (ZL::Environment::window) {
+ SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
+ }
+
+ // Обновляем ваши внутренние переменные окружения
+ ZL::Environment::width = physicalW;
+ ZL::Environment::height = physicalH;
+
+ // Пушим событие, чтобы движок пересчитал матрицы проекции
SDL_Event e = {};
e.type = SDL_WINDOWEVENT;
e.window.event = SDL_WINDOWEVENT_RESIZED;
- e.window.data1 = w;
- e.window.data2 = h;
+ e.window.data1 = physicalW;
+ e.window.data2 = physicalH;
SDL_PushEvent(&e);
}
@@ -69,17 +83,11 @@ EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*u
}
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
- if (e->isFullscreen) {
- // e->screenWidth/screenHeight comes from screen.width/screen.height in JS,
- // which on mobile browsers returns physical pixels (e.g. 2340x1080),
- // causing the canvas to extend far off-screen. window.innerWidth/innerHeight
- // always gives CSS logical pixels and is correct on both desktop and mobile.
- int w = EM_ASM_INT({ return window.innerWidth; });
- int h = EM_ASM_INT({ return window.innerHeight; });
- applyResize(w, h);
- }
- // Exiting fullscreen: the browser fires a window resize event next,
- // which onWindowResized handles automatically.
+ // Вместо window.innerWidth, попробуйте запросить размер целевого элемента
+ // так как после перехода в FS именно он растягивается на весь экран.
+ double clientW, clientH;
+ emscripten_get_element_css_size("#canvas", &clientW, &clientH);
+ applyResize(clientW, clientH);
return EM_FALSE;
}
@@ -226,6 +234,21 @@ int main(int argc, char *argv[]) {
// Keep Environment::width/height in sync when the canvas is resized.
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
+
+ // 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
+ // Получаем реальные размеры окна браузера на момент запуска
+ int canvasW = EM_ASM_INT({ return window.innerWidth; });
+ int canvasH = EM_ASM_INT({ return window.innerHeight; });
+
+ // Вызываем вашу функцию — она сама применит DPR, выставит физический размер
+ // канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
+ applyResize(canvasW, canvasH);
+
+ // 3. Создаем игру и вызываем setup (теперь проекция уже будет знать верный size)
+ g_game = new ZL::Game();
+ g_game->setup();
+
+ SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
emscripten_set_main_loop(MainLoop, 0, 1);
#else
diff --git a/src/network/ClientState.h b/src/network/ClientState.h
index 2624679..5bbd8b3 100644
--- a/src/network/ClientState.h
+++ b/src/network/ClientState.h
@@ -27,6 +27,14 @@ constexpr long long SERVER_DELAY = 0; //ms
constexpr long long CLIENT_DELAY = 500; //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);
struct ClientState {
diff --git a/src/network/LocalClient.cpp b/src/network/LocalClient.cpp
index 0321249..1d43a78 100644
--- a/src/network/LocalClient.cpp
+++ b/src/network/LocalClient.cpp
@@ -21,8 +21,8 @@ namespace ZL {
std::random_device rd;
std::mt19937 gen(rd());
- const float MIN_COORD = -100.0f;
- const float MAX_COORD = 100.0f;
+ const float MIN_COORD = -1000.0f;
+ const float MAX_COORD = 1000.0f;
const float MIN_DISTANCE = 3.0f;
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
const int MAX_ATTEMPTS = 1000;
@@ -68,7 +68,7 @@ namespace ZL {
Eigen::Vector3f LocalClient::generateRandomPosition() {
std::random_device rd;
std::mt19937 gen(rd());
- std::uniform_real_distribution<> distrib(-500.0, 500.0);
+ std::uniform_real_distribution<> distrib(-5000.0, 5000.0);
return Eigen::Vector3f(
(float)distrib(gen),
@@ -238,11 +238,6 @@ namespace ZL {
auto now_ms = std::chrono::duration_cast(
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> boxProjectileCollisions;
for (size_t bi = 0; bi < serverBoxes.size(); ++bi) {
diff --git a/src/render/TextRenderer.cpp b/src/render/TextRenderer.cpp
index 585ca8d..73e2db1 100644
--- a/src/render/TextRenderer.cpp
+++ b/src/render/TextRenderer.cpp
@@ -362,9 +362,10 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
// 4. Рендеринг
r->shaderManager.PushShader(shaderName);
- // Матрица проекции (экрана)
- float W = (float)Environment::width;
- float H = (float)Environment::height;
+ // Матрица проекции — используем виртуальные проекционные размеры,
+ // чтобы координаты текста были независимы от физического разрешения экрана.
+ float W = Environment::projectionWidth;
+ float H = Environment::projectionHeight;
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
proj(0, 0) = 2.0f / W;
proj(1, 1) = 2.0f / H;