MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
defender_intro.cpp
Go to the documentation of this file.
1#include "defender_window.hpp"
2
3#include <algorithm>
4#include <cmath>
5#include <format>
6#include <string>
7
8#include <glm/ext/matrix_clip_space.hpp>
9#include <glm/ext/matrix_transform.hpp>
10#include <glm/gtc/quaternion.hpp>
11
12namespace defender {
13
14 void DefenderWindow::update_ship(float dt) {
15 ship.prev_position = ship.position;
16
17 if (reverse_pressed) {
18 reverse_pressed = false;
19 ship_forward_direction *= -1.0f;
20 }
21
22 constexpr float horizontal_acceleration = 58.0f;
23 constexpr float horizontal_drag = 2.4f;
24 const bool boost_active = mode == GameMode::Playing && (boost_pressed || controller_boost_pressed);
25 const bool propulsion_active = mode == GameMode::Playing && (propulsion_pressed || controller_propulsion_pressed || boost_active);
26 const float max_horizontal_speed = ship.max_speed * (boost_active ? 2.0f : 1.0f);
27 if (propulsion_active) {
28 ship.velocity.x += ship_forward_direction * horizontal_acceleration * dt;
29 } else {
30 ship.velocity.x = std::lerp(ship.velocity.x, 0.0f, 1.0f - std::exp(-dt * horizontal_drag));
31 }
32 ship.velocity.x = std::clamp(ship.velocity.x, -max_horizontal_speed, max_horizontal_speed);
33 ship.current_speed = std::abs(ship.velocity.x);
34
35 float vertical_input = 0.0f;
36 if (up_pressed && !down_pressed) {
37 vertical_input = 1.0f;
38 } else if (down_pressed && !up_pressed) {
39 vertical_input = -1.0f;
40 } else {
41 vertical_input = controller_vertical_input;
42 }
43
44 constexpr float vertical_speed = 19.0f;
45 constexpr float vertical_response = 12.0f;
46 const float target_vertical_velocity = vertical_input * vertical_speed;
47 ship.velocity.y = std::lerp(ship.velocity.y, target_vertical_velocity, 1.0f - std::exp(-dt * vertical_response));
48 ship.velocity.z = 0.0f;
49 ship.position += ship.velocity * dt;
50 ship.position.x = wrap_world_x(ship.position.x);
51 ship.position.y = std::clamp(ship.position.y, WORLD_BOTTOM, playable_world_top);
52 ship.position.z = 0.0f;
53
54 const float bank = std::clamp(-ship.velocity.y * 2.0f, -22.0f, 22.0f);
55 const float pitch = std::clamp(ship.velocity.y * 0.9f, -12.0f, 12.0f);
56 ship.rotation.x = pitch;
57 ship.rotation.y = ship_forward_direction > 0.0f ? -90.0f : 90.0f;
58 const float thrust_bank = propulsion_active ? ship_forward_direction * -7.0f : 0.0f;
59 ship.rotation.z = bank + thrust_bank;
60
61 if (ship.fire_cooldown > 0) {
62 --ship.fire_cooldown;
63 }
64 if (fire_pressed) {
65 fire_pressed = false;
66 if (ship.fire_cooldown <= 0) {
67 fire_projectile();
68 ship.fire_cooldown = FIRE_COOLDOWN;
69 }
70 }
71 }
72
73 void DefenderWindow::update_barrel_roll(float dt) {
74 const bool ship_visible_mode = mode == GameMode::IntroFadeIn || mode == GameMode::Countdown || mode == GameMode::Playing;
75 if (!ship_visible_mode || game_over || ship_respawning) {
76 barrel_roll_progress = 0.0f;
77 barrel_roll_angle = 0.0f;
78 return;
79 }
80
81 float roll_input = 0.0f;
82 if ((roll_left_pressed || controller_roll_left_pressed) && !(roll_right_pressed || controller_roll_right_pressed)) {
83 roll_input = -1.0f;
84 } else if ((roll_right_pressed || controller_roll_right_pressed) && !(roll_left_pressed || controller_roll_left_pressed)) {
85 roll_input = 1.0f;
86 }
87 if (roll_input != 0.0f) {
88 barrel_roll_direction = roll_input;
89 }
90
91 if (roll_input == 0.0f && barrel_roll_progress <= 0.0f) {
92 barrel_roll_angle = 0.0f;
93 return;
94 }
95
96 const float roll_speed = 360.0f / BARREL_ROLL_DURATION;
97 barrel_roll_progress += roll_speed * dt;
98 if (barrel_roll_progress >= 360.0f) {
99 barrel_roll_progress = (roll_input != 0.0f) ? std::fmod(barrel_roll_progress, 360.0f) : 0.0f;
100 }
101 barrel_roll_angle = barrel_roll_direction * barrel_roll_progress;
102 }
103
104 glm::mat4 DefenderWindow::build_ship_model_matrix() {
105 const glm::quat yaw = glm::angleAxis(glm::radians(ship.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
106 const glm::quat pitch = glm::angleAxis(glm::radians(ship.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
107 const glm::quat bank = glm::angleAxis(glm::radians(ship.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
108 const glm::quat barrel_roll = glm::angleAxis(glm::radians(barrel_roll_angle), glm::vec3(0.0f, 0.0f, 1.0f));
109 const glm::quat orientation = yaw * pitch * bank * barrel_roll;
110
111 glm::mat4 model(1.0f);
112 model = glm::translate(model, nearest_world_position(ship.position, camera_position.x));
113 model *= glm::mat4_cast(orientation);
114 model = glm::scale(model, glm::vec3(SHIP_MODEL_SCALE * ship_model.modelRenderScale()));
115 model = glm::translate(model, ship_model.modelCenterOffset());
116 return model;
117 }
118
119 glm::mat4 DefenderWindow::build_asteroid_model_matrix(const Asteroid &asteroid, const mxvk::VKAbstractModel &model_resource) const {
120 const glm::quat yaw = glm::angleAxis(glm::radians(asteroid.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
121 const glm::quat pitch = glm::angleAxis(glm::radians(asteroid.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
122 const glm::quat roll = glm::angleAxis(glm::radians(asteroid.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
123
124 glm::mat4 model(1.0f);
125 model = glm::translate(model, nearest_world_position(asteroid.position, camera_position.x));
126 model *= glm::mat4_cast(yaw * pitch * roll);
127 model = glm::scale(model, glm::vec3(asteroid.scale * model_resource.modelRenderScale()));
128 model = glm::translate(model, model_resource.modelCenterOffset());
129 return model;
130 }
131
132 void DefenderWindow::reset_intro_screen() {
133 mode = GameMode::Intro;
134 intro_fade = 1.0f;
135 intro_last_update_ms = SDL_GetTicks();
136 intro_fade_in_start_ms = 0;
137 if (intro_rain != nullptr) {
138 intro_rain->set_opacity(1.0f);
139 intro_rain->reset();
140 }
141 }
142
143 void DefenderWindow::start_launch_countdown() {
144 mode = GameMode::Countdown;
145 countdown_timer = 0;
146 countdown_duration = 1000;
147 countdown_start_ms = SDL_GetTicks();
148 countdown_number = 3;
149 launch_timer = 0;
150 launch_duration = 1000;
151 launch_start_ms = 0;
152 }
153
154 void DefenderWindow::start_intro_fade_in() {
155 start_launch_countdown();
157 intro_fade_in_start_ms = SDL_GetTicks();
158 log_game("Intro faded out. Launch countdown started.");
159 }
160
161 void DefenderWindow::draw_intro(const VkExtent2D &extent) {
162 if (intro_sprite == nullptr) {
163 start_launch_countdown();
164 return;
165 }
166
167 const Uint32 current_ms = SDL_GetTicks();
168 if ((current_ms - intro_last_update_ms) > 35U) {
169 intro_last_update_ms = current_ms;
170 intro_fade -= 0.01f;
171 }
172
173 if (intro_fade <= 0.0f) {
174 intro_fade = 0.0f;
175 if (intro_rain != nullptr) {
176 intro_rain->set_opacity(0.0f);
177 }
178 start_intro_fade_in();
179 return;
180 }
181
182 intro_sprite->setShaderParams(static_cast<float>(current_ms) / 1000.0f, 0.0f, 0.0f, intro_fade);
183 intro_sprite->drawSpriteRect(0, 0, static_cast<int>(extent.width), static_cast<int>(extent.height));
184 if (intro_rain != nullptr) {
185 intro_rain->set_opacity(intro_fade);
186 }
187 printText("Press Enter", 24, static_cast<int>(extent.height) - 52, {255, 230, 80, 255});
188 }
189
190 void DefenderWindow::draw_intro_fade_in(const VkExtent2D &extent) {
191 const Uint32 elapsed_ms = SDL_GetTicks() - intro_fade_in_start_ms;
192 const float progress = std::clamp(static_cast<float>(elapsed_ms) / static_cast<float>(INTRO_FADE_IN_DURATION_MS), 0.0f, 1.0f);
193 const float alpha = 1.0f - progress;
194 if (alpha <= 0.0f) {
195 mode = GameMode::Countdown;
196 return;
197 }
198
199 draw_fade_overlay(extent, alpha);
200 }
201
202 void DefenderWindow::draw_fade_overlay(const VkExtent2D &extent, float alpha) {
203 if (fade_overlay_sprite == nullptr) {
204 return;
205 }
206
207 alpha = std::clamp(alpha, 0.0f, 1.0f);
208 if (alpha <= 0.0f) {
209 return;
210 }
211
212 fade_overlay_sprite->setShaderParams(0.0f, 0.0f, 0.0f, alpha);
213 fade_overlay_sprite->drawSpriteRect(0, 0, static_cast<int>(extent.width), static_cast<int>(extent.height));
214 }
215
216 void DefenderWindow::update_camera_scroll(float aspect) {
217 const float visible_half_height = std::tan(glm::radians(25.0f)) * CAMERA_DISTANCE;
218 const float visible_half_width = visible_half_height * std::max(0.1f, aspect);
219 const float scroll_limit = visible_half_width * CAMERA_SCROLL_EDGE_FRACTION;
220 const bool propulsion_active = mode == GameMode::Playing && propulsion_pressed;
221 const float lead_offset = propulsion_active ? ship_forward_direction * visible_half_width * 0.18f : 0.0f;
222 const float desired_camera_x = wrap_world_x(ship.position.x + lead_offset);
223 const float relative_x = wrapped_delta_x(ship.position.x, camera_center_x);
224
225 if (relative_x > scroll_limit) {
226 camera_center_x = wrap_world_x(ship.position.x - scroll_limit);
227 } else if (relative_x < -scroll_limit) {
228 camera_center_x = wrap_world_x(ship.position.x + scroll_limit);
229 }
230
231 if (propulsion_active) {
232 camera_center_x = wrap_world_x(camera_center_x + wrapped_delta_x(desired_camera_x, camera_center_x) * 0.04f);
233 }
234 }
235
236 void DefenderWindow::update_camera_position(float dt) {
237 const float target_x = nearest_world_x(camera_center_x, camera_position.x);
238 const float blend = 1.0f - std::exp(-dt * 10.0f);
239 camera_position.x = wrap_world_x(camera_position.x + (target_x - camera_position.x) * blend);
240 camera_position.y = CAMERA_HEIGHT;
241 camera_position.z = CAMERA_DISTANCE;
242 }
243
244 void DefenderWindow::update_playable_world_top(const VkExtent2D &extent) {
245 if (extent.height == 0U) {
246 return;
247 }
248 const float visible_half_height = std::tan(glm::radians(25.0f)) * CAMERA_DISTANCE;
249 const float top_fraction = static_cast<float>(GAME_VIEWPORT_TOP) / static_cast<float>(extent.height);
250 playable_world_top = CAMERA_HEIGHT + (0.5f - top_fraction) * 2.0f * visible_half_height;
251 }
252
253 void DefenderWindow::update_countdown() {
254 const Uint32 now = SDL_GetTicks();
255 const Uint32 elapsed = now - countdown_start_ms;
256 countdown_timer = elapsed % countdown_duration;
257
258 if (elapsed < (countdown_duration * 3U)) {
259 countdown_number = 3 - static_cast<int>(elapsed / countdown_duration);
260 launch_start_ms = 0;
261 launch_timer = 0;
262 return;
263 }
264
265 countdown_number = 0;
266 if (launch_start_ms == 0U) {
267 launch_start_ms = now;
268 }
269 launch_timer = std::min<Uint32>(now - launch_start_ms, launch_duration);
270 if (launch_timer >= launch_duration) {
271 mode = GameMode::Playing;
272 if (!level_active) {
273 start_level();
274 }
275 reverse_pressed = false;
276 propulsion_pressed = false;
277 fire_pressed = false;
278#if defined(MXVK_WITH_MIXER) || defined(WITH_MIXER)
279 play_sound(takeoff_sound);
280#endif
281 log_game("Mission started.");
282 }
283 }
284
285 void DefenderWindow::draw_countdown(VkCommandBuffer cmd, uint32_t image_index, const VkExtent2D &extent, const glm::mat4 &view, const glm::mat4 &projection) {
286 update_countdown();
287
288 star_field.update(0.0f, camera_position, elapsed_seconds);
289 star_sprite->updateCamera(image_index, view, projection);
290 star_field.draw();
291 star_sprite->render(cmd, image_index);
292 star_sprite->clearQueue();
293
294 if (ship.visible && !ship_respawning) {
295 mxvk::UniformBufferObject ubo{};
296 ubo.model = build_ship_model_matrix();
297 ubo.view = view;
298 ubo.proj = projection;
299 ship_model.updateUBO(image_index, ubo);
300 ship_model.render(cmd, image_index, false);
301 }
302
303 if (countdown_number > 0) {
304 const std::string number = std::format("{}", countdown_number);
305 int text_w = 0;
306 int text_h = 0;
307 if (!getTextDimensions(number, text_w, text_h, countdown_font)) {
308 text_w = 64;
309 text_h = 64;
310 }
311 const bool show_number = ((countdown_timer / 167U) % 2U) == 0U;
312 if (show_number) {
313 printText(number,
314 static_cast<int>(extent.width) / 2 - text_w / 2,
315 static_cast<int>(extent.height) / 2 - text_h / 2,
316 {255, 255, 255, 255},
317 countdown_font);
318 }
319 } else {
320 const std::string launch = "MISSION START!";
321 int text_w = 0;
322 int text_h = 0;
323 if (!getTextDimensions(launch, text_w, text_h, countdown_font)) {
324 text_w = 440;
325 text_h = 48;
326 }
327 printText(launch,
328 static_cast<int>(extent.width) / 2 - text_w / 2,
329 static_cast<int>(extent.height) / 2 - text_h / 2,
330 {0, 255, 255, 255},
331 countdown_font);
332 }
333 }
334
335 void DefenderWindow::update_fps_counter(float delta_seconds) {
336 fps_accumulator += delta_seconds;
337 ++fps_frame_count;
338 if (fps_accumulator < 0.25f) {
339 return;
340 }
341
342 const float fps = static_cast<float>(fps_frame_count) / fps_accumulator;
343 fps_text = std::format("FPS: {:.1f}", fps);
344 fps_accumulator = 0.0f;
345 fps_frame_count = 0;
346 }
347
348 void DefenderWindow::draw_hud(const VkExtent2D &extent) {
349 const SDL_Color white{255, 255, 255, 255};
350 const SDL_Color yellow{255, 230, 80, 255};
351 const SDL_Color red{255, 70, 60, 255};
352 printText("Score: " + std::to_string(score), 24, 22, white, hud_font);
353 printText("Level: " + std::to_string(level), 24, 82, yellow, hud_font);
354 printText("Lives: " + std::to_string(lives), 24, 142, lives <= 1 ? red : white, hud_font);
355 draw_scanner(extent);
356 if (show_fps_counter) {
357 int fps_w = 0;
358 int fps_h = 0;
359 if (!getTextDimensions(fps_text, fps_w, fps_h)) {
360 fps_w = 120;
361 }
362 const int fps_x = std::max(24, static_cast<int>(extent.width) - fps_w - 24);
363 printText(fps_text, fps_x, 22, white);
364 }
365
366 if (!game_over) {
367 return;
368 }
369
370 const std::string title = "GAME OVER";
371 const std::string final_score = "Final Score: " + std::to_string(score);
372 const std::string prompt = "Press Enter to start a new game";
373 int title_w = 0;
374 int title_h = 0;
375 int score_w = 0;
376 int score_h = 0;
377 int prompt_w = 0;
378 int prompt_h = 0;
379 if (!getTextDimensions(title, title_w, title_h)) {
380 title_w = 180;
381 }
382 if (!getTextDimensions(final_score, score_w, score_h)) {
383 score_w = 220;
384 }
385 if (!getTextDimensions(prompt, prompt_w, prompt_h)) {
386 prompt_w = 360;
387 }
388 const int center_x = static_cast<int>(extent.width) / 2;
389 const int center_y = static_cast<int>(extent.height) / 2;
390 printText(title, center_x - title_w / 2, center_y - 52, red);
391 printText(final_score, center_x - score_w / 2, center_y - 12, yellow);
392 printText(prompt, center_x - prompt_w / 2, center_y + 28, white);
393 }
394
395 void DefenderWindow::draw_scanner(const VkExtent2D &extent) {
396 constexpr int SCANNER_BORDER = 2;
397 const int scanner_width = std::min(760, std::max(420, static_cast<int>(extent.width) - 320));
398 const int scanner_x = (static_cast<int>(extent.width) - scanner_width) / 2;
399 const int scanner_y = RADAR_TOP;
400 const int inner_x = scanner_x + SCANNER_BORDER;
401 const int inner_y = scanner_y + SCANNER_BORDER;
402 const int inner_width = scanner_width - SCANNER_BORDER * 2;
403 const int inner_height = RADAR_HEIGHT - SCANNER_BORDER * 2;
404 const auto draw_rect = [this](int x, int y, int width, int height, const glm::vec4 &color) {
405 fade_overlay_sprite->setShaderParams(color.r, color.g, color.b, color.a);
406 fade_overlay_sprite->drawSpriteRect(x, y, width, height);
407 };
408 const auto x_for = [inner_x, inner_width, this](float world_x) {
409 const float normalized = 0.5f + wrapped_delta_x(world_x, camera_center_x) / WORLD_WIDTH;
410 return inner_x + std::clamp(static_cast<int>(normalized * static_cast<float>(inner_width - 1)), 0, inner_width - 1);
411 };
412 const auto y_for = [this](float world_y) {
413 const float normalized = (playable_world_top - std::clamp(world_y, WORLD_BOTTOM, playable_world_top)) / (playable_world_top - WORLD_BOTTOM);
414 return inner_y + std::clamp(static_cast<int>(normalized * static_cast<float>(inner_height - 1)), 0, inner_height - 1);
415 };
416
417 const std::string title = "RADAR";
418 int title_width = 0;
419 int ignored_height = 0;
420 if (!getTextDimensions(title, title_width, ignored_height)) {
421 title_width = 180;
422 }
423 printText(title, static_cast<int>(extent.width) / 2 - title_width / 2, 6, {120, 220, 255, 255});
424
425 draw_rect(scanner_x, scanner_y, scanner_width, RADAR_HEIGHT, {0.02f, 0.05f, 0.09f, 0.94f});
426 draw_rect(scanner_x, scanner_y, scanner_width, SCANNER_BORDER, {0.32f, 0.72f, 0.88f, 0.95f});
427 draw_rect(scanner_x, scanner_y + RADAR_HEIGHT - SCANNER_BORDER, scanner_width, SCANNER_BORDER, {0.32f, 0.72f, 0.88f, 0.95f});
428 draw_rect(scanner_x, scanner_y, SCANNER_BORDER, RADAR_HEIGHT, {0.32f, 0.72f, 0.88f, 0.95f});
429 draw_rect(scanner_x + scanner_width - SCANNER_BORDER, scanner_y, SCANNER_BORDER, RADAR_HEIGHT, {0.32f, 0.72f, 0.88f, 0.95f});
430 for (int x = inner_x; x < inner_x + inner_width; x += 2) {
431 const float progress = static_cast<float>(x - inner_x) / static_cast<float>(inner_width - 1);
432 const float world_x = wrap_world_x(camera_center_x + (progress - 0.5f) * WORLD_WIDTH);
433 draw_rect(x, y_for(terrain_height(world_x)), 2, 2, {0.20f, 0.62f, 0.36f, 0.9f});
434 }
435
436 const float visible_half_height = std::tan(glm::radians(25.0f)) * CAMERA_DISTANCE;
437 const float visible_half_width = visible_half_height * std::max(0.1f, static_cast<float>(extent.width) / static_cast<float>(extent.height));
438 const int bracket_half_width = std::max(1, static_cast<int>(visible_half_width / WORLD_WIDTH * static_cast<float>(inner_width)));
439 const int scanner_center_x = inner_x + inner_width / 2;
440 const int left_bracket_x = scanner_center_x - bracket_half_width;
441 const int right_bracket_x = scanner_center_x + bracket_half_width;
442 constexpr int BRACKET_ARM_LENGTH = 10;
443 draw_rect(left_bracket_x, inner_y, 2, inner_height, {0.25f, 0.85f, 1.0f, 0.9f});
444 draw_rect(left_bracket_x, inner_y, BRACKET_ARM_LENGTH, 2, {0.25f, 0.85f, 1.0f, 0.9f});
445 draw_rect(left_bracket_x, inner_y + inner_height - 2, BRACKET_ARM_LENGTH, 2, {0.25f, 0.85f, 1.0f, 0.9f});
446 draw_rect(right_bracket_x, inner_y, 2, inner_height, {0.25f, 0.85f, 1.0f, 0.9f});
447 draw_rect(right_bracket_x - BRACKET_ARM_LENGTH + 2, inner_y, BRACKET_ARM_LENGTH, 2, {0.25f, 0.85f, 1.0f, 0.9f});
448 draw_rect(right_bracket_x - BRACKET_ARM_LENGTH + 2, inner_y + inner_height - 2, BRACKET_ARM_LENGTH, 2, {0.25f, 0.85f, 1.0f, 0.9f});
449
450 for (const Ufo &ufo : ufos) {
451 if (!ufo.active) {
452 continue;
453 }
454 if (ufo.sprite_set == UfoSpriteSet::Alien) {
455 draw_rect(x_for(ufo.position.x) - 2, y_for(ufo.position.y) - 2, 5, 5, {1.0f, 0.30f, 0.75f, 1.0f});
456 } else {
457 draw_rect(x_for(ufo.position.x) - 2, y_for(ufo.position.y) - 1, 5, 3, {0.25f, 1.0f, 0.46f, 1.0f});
458 }
459 }
460
461 for (const Asteroid &asteroid : asteroids) {
462 if (asteroid.active) {
463 draw_rect(x_for(asteroid.position.x) - 1, y_for(asteroid.position.y) - 1, 3, 3, {1.0f, 0.58f, 0.18f, 1.0f});
464 }
465 }
466 for (const space::Projectile &projectile : projectiles) {
467 if (projectile.active) {
468 draw_rect(x_for(projectile.position.x), y_for(projectile.position.y), 2, 2, {1.0f, 0.95f, 0.38f, 1.0f});
469 }
470 }
471 if (ship.visible && !ship_respawning && static_cast<int>(elapsed_seconds * 8.0f) % 2 == 0) {
472 draw_rect(x_for(ship.position.x) - 3, y_for(ship.position.y) - 3, 7, 7, {1.0f, 1.0f, 1.0f, 1.0f});
473 }
474 }
475
476} // namespace defender
float modelRenderScale() const
Access the computed render scale used for normalization.
glm::vec3 modelCenterOffset() const
Access the computed center offset used for normalization.
bool getTextDimensions(const std::string &text, int &width, int &height)
Measure text dimensions in pixels.
Definition mxvk.cpp:3069
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering during the current frame.
Definition mxvk.cpp:3018
constexpr int RADAR_TOP
glm::vec3 nearest_world_position(glm::vec3 position, float origin_x)
float nearest_world_x(float target, float origin)
float wrapped_delta_x(float target, float origin)
constexpr float WORLD_BOTTOM
float wrap_world_x(float x)
float terrain_height(float world_x)
constexpr float SHIP_MODEL_SCALE
constexpr int FIRE_COOLDOWN
constexpr float CAMERA_SCROLL_EDGE_FRACTION
constexpr float CAMERA_DISTANCE
constexpr int RADAR_HEIGHT
constexpr int GAME_VIEWPORT_TOP
constexpr float WORLD_WIDTH
constexpr float CAMERA_HEIGHT
bool active
Definition space.cpp:69
float x
Definition space.cpp:66
float y
Definition space.cpp:66