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;