|
13 | 13 | // @see en/Building_a_Simple_Engine/Engine_Architecture/02_architectural_patterns.adoc |
14 | 14 |
|
15 | 15 | Engine::Engine() |
16 | | - : resourceManager(std::make_unique<ResourceManager>()), |
17 | | - modelLoader(std::make_unique<ModelLoader>()), |
18 | | - audioSystem(std::make_unique<AudioSystem>()), |
19 | | - physicsSystem(std::make_unique<PhysicsSystem>()), |
20 | | - imguiSystem(std::make_unique<ImGuiSystem>()) { |
| 16 | + : resourceManager(std::make_unique<ResourceManager>()) { |
21 | 17 | } |
22 | 18 |
|
23 | 19 | Engine::~Engine() { |
@@ -64,37 +60,25 @@ bool Engine::Initialize(const std::string& appName, int width, int height, bool |
64 | 60 | return false; |
65 | 61 | } |
66 | 62 |
|
67 | | - // Initialize model loader |
68 | | - if (!modelLoader->Initialize(renderer.get())) { |
69 | | - return false; |
70 | | - } |
71 | | - |
72 | | - // Connect model loader to renderer for light extraction |
73 | | - renderer->SetModelLoader(modelLoader.get()); |
74 | | - |
75 | | - // Initialize audio system |
76 | | - if (!audioSystem->Initialize(this, renderer.get())) { |
77 | | - return false; |
78 | | - } |
79 | | - |
80 | | - // Initialize a physics system |
81 | | - physicsSystem->SetRenderer(renderer.get()); |
| 63 | + try { |
| 64 | + // Model loader via constructor; also wire into renderer |
| 65 | + modelLoader = std::make_unique<ModelLoader>(renderer.get()); |
| 66 | + renderer->SetModelLoader(modelLoader.get()); |
82 | 67 |
|
83 | | - // Enable GPU acceleration for physics calculations to drastically speed up computations |
84 | | - physicsSystem->SetGPUAccelerationEnabled(true); |
| 68 | + // Audio system via constructor |
| 69 | + audioSystem = std::make_unique<AudioSystem>(this, renderer.get()); |
85 | 70 |
|
86 | | - if (!physicsSystem->Initialize()) { |
87 | | - return false; |
88 | | - } |
| 71 | + // Physics system via constructor (GPU enabled) |
| 72 | + physicsSystem = std::make_unique<PhysicsSystem>(renderer.get(), true); |
89 | 73 |
|
90 | | - // Initialize ImGui system |
91 | | - if (!imguiSystem->Initialize(renderer.get(), width, height)) { |
| 74 | + // ImGui via constructor, then connect audio system |
| 75 | + imguiSystem = std::make_unique<ImGuiSystem>(renderer.get(), width, height); |
| 76 | + imguiSystem->SetAudioSystem(audioSystem.get()); |
| 77 | + } catch (const std::exception& e) { |
| 78 | + std::cerr << "Subsystem initialization failed: " << e.what() << std::endl; |
92 | 79 | return false; |
93 | 80 | } |
94 | 81 |
|
95 | | - // Connect ImGui system to an audio system for UI controls |
96 | | - imguiSystem->SetAudioSystem(audioSystem.get()); |
97 | | - |
98 | 82 | // Generate ball material properties once at load time |
99 | 83 | GenerateBallMaterial(); |
100 | 84 |
|
@@ -132,9 +116,15 @@ void Engine::Run() { |
132 | 116 | // Update window title with FPS and frame time every second |
133 | 117 | if (fpsUpdateTimer >= 1.0f) { |
134 | 118 | uint64_t framesSinceLastUpdate = frameCount - lastFPSUpdateFrame; |
135 | | - currentFPS = framesSinceLastUpdate / fpsUpdateTimer; |
136 | | - // Average frame time in milliseconds over the last interval |
137 | | - double avgMs = (fpsUpdateTimer / static_cast<double>(framesSinceLastUpdate)) * 1000.0; |
| 119 | + double avgMs = 0.0; |
| 120 | + if (framesSinceLastUpdate > 0 && fpsUpdateTimer > 0.0f) { |
| 121 | + currentFPS = static_cast<float>(static_cast<double>(framesSinceLastUpdate) / static_cast<double>(fpsUpdateTimer)); |
| 122 | + avgMs = (fpsUpdateTimer / static_cast<double>(framesSinceLastUpdate)) * 1000.0; |
| 123 | + } else { |
| 124 | + // Avoid divide-by-zero; keep previous FPS and estimate avgMs from last delta |
| 125 | + currentFPS = std::max(currentFPS, 1.0f); |
| 126 | + avgMs = static_cast<double>(deltaTimeMs.count()); |
| 127 | + } |
138 | 128 |
|
139 | 129 | // Update window title with frame count, FPS, and frame time |
140 | 130 | std::string title = "Simple Engine - Frame: " + std::to_string(frameCount) + |
@@ -402,15 +392,19 @@ void Engine::Update(TimeDelta deltaTime) { |
402 | 392 | UpdateCameraControls(deltaTime); |
403 | 393 | } |
404 | 394 |
|
405 | | - // Update all entities |
| 395 | + // Update all entities (guard against null unique_ptrs) |
406 | 396 | for (auto& entity : entities) { |
407 | | - if (entity->IsActive()) { |
408 | | - entity->Update(deltaTime); |
409 | | - } |
| 397 | + if (!entity) { continue; } |
| 398 | + if (!entity->IsActive()) { continue; } |
| 399 | + entity->Update(deltaTime); |
410 | 400 | } |
411 | 401 | } |
412 | 402 |
|
413 | 403 | void Engine::Render() { |
| 404 | + // Ensure renderer is ready |
| 405 | + if (!renderer || !renderer->IsInitialized()) { |
| 406 | + return; |
| 407 | + } |
414 | 408 |
|
415 | 409 | // Check if we have an active camera |
416 | 410 | if (!activeCamera) { |
|
0 commit comments