replace physics loop with fixed-step loop

This commit is contained in:
2026-02-22 00:27:56 +01:00
parent cc4f8ce865
commit 05172d0d8f
4 changed files with 26 additions and 27 deletions

View File

@ -4,7 +4,6 @@
#include <raylib.h> #include <raylib.h>
#define PRINT_TIMINGS #define PRINT_TIMINGS
#define VERLET_UPDATE
// #define WEB // #define WEB
// Window // Window
@ -19,7 +18,6 @@ constexpr int MENU_ROWS = 3;
constexpr int MENU_COLS = 3; constexpr int MENU_COLS = 3;
// Camera Controls // Camera Controls
constexpr float SIM_SPEED = 4.0;
constexpr float CAMERA_FOV = 120.0; constexpr float CAMERA_FOV = 120.0;
constexpr float CAMERA_DISTANCE = 20.0; constexpr float CAMERA_DISTANCE = 20.0;
constexpr float MIN_CAMERA_DISTANCE = 2.0; constexpr float MIN_CAMERA_DISTANCE = 2.0;
@ -31,7 +29,8 @@ constexpr float PAN_MULTIPLIER = 10.0;
constexpr float ROT_SPEED = 1.0; constexpr float ROT_SPEED = 1.0;
// Physics Engine // Physics Engine
constexpr int UPDATES_PER_FRAME = 1; constexpr float SIM_SPEED = 4.0;
constexpr float TIMESTEP = 1.0 / 60; // Do 60 physics updates per second
constexpr float MASS = 1.0; constexpr float MASS = 1.0;
constexpr float SPRING_CONSTANT = 5.0; constexpr float SPRING_CONSTANT = 5.0;
constexpr float DAMPENING_CONSTANT = 1.0; constexpr float DAMPENING_CONSTANT = 1.0;

View File

@ -129,8 +129,6 @@ public:
auto CalculateRepulsionForces() -> void; auto CalculateRepulsionForces() -> void;
auto EulerUpdate(float delta_time) -> void;
auto VerletUpdate(float delta_time) -> void; auto VerletUpdate(float delta_time) -> void;
auto InvalidateGrid() -> void; auto InvalidateGrid() -> void;

View File

@ -63,9 +63,13 @@ auto main(int argc, char *argv[]) -> int {
std::chrono::duration<double, std::milli>(0); std::chrono::duration<double, std::milli>(0);
std::chrono::duration<double, std::milli> render_time_accumulator = std::chrono::duration<double, std::milli> render_time_accumulator =
std::chrono::duration<double, std::milli>(0); std::chrono::duration<double, std::milli>(0);
int time_measure_count = 0; int loop_count = 0;
#endif #endif
float timestep_accumulator = 0.0;
int update_accumulator = 0;
while (!WindowShouldClose()) { while (!WindowShouldClose()) {
timestep_accumulator += GetFrameTime();
// Input update // Input update
state.previous_state = state.current_state; state.previous_state = state.current_state;
input.HandleInput(); input.HandleInput();
@ -76,16 +80,17 @@ auto main(int argc, char *argv[]) -> int {
std::chrono::high_resolution_clock::time_point ps = std::chrono::high_resolution_clock::time_point ps =
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
#endif #endif
for (int i = 0; i < UPDATES_PER_FRAME; ++i) {
while (timestep_accumulator > TIMESTEP) {
mass_springs.ClearForces(); mass_springs.ClearForces();
mass_springs.CalculateSpringForces(); mass_springs.CalculateSpringForces();
mass_springs.CalculateRepulsionForces(); mass_springs.CalculateRepulsionForces();
#ifdef VERLET_UPDATE mass_springs.VerletUpdate(TIMESTEP * SIM_SPEED);
mass_springs.VerletUpdate(GetFrameTime() / UPDATES_PER_FRAME * SIM_SPEED);
#else timestep_accumulator -= TIMESTEP;
mass_springs.EulerUpdate(GetFrameTime() * SIM_SPEED); update_accumulator++;
#endif
} }
#ifdef PRINT_TIMINGS #ifdef PRINT_TIMINGS
std::chrono::high_resolution_clock::time_point pe = std::chrono::high_resolution_clock::time_point pe =
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
@ -97,6 +102,7 @@ auto main(int argc, char *argv[]) -> int {
std::chrono::high_resolution_clock::time_point rs = std::chrono::high_resolution_clock::time_point rs =
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
#endif #endif
renderer.UpdateCamera(mass_springs, state.current_state); renderer.UpdateCamera(mass_springs, state.current_state);
renderer.UpdateTextureSizes(); renderer.UpdateTextureSizes();
renderer.DrawMassSprings(mass_springs, state.current_state, renderer.DrawMassSprings(mass_springs, state.current_state,
@ -107,23 +113,26 @@ auto main(int argc, char *argv[]) -> int {
renderer.DrawMenu(mass_springs, state.current_preset, state.current_state, renderer.DrawMenu(mass_springs, state.current_preset, state.current_state,
state.winning_states); state.winning_states);
renderer.DrawTextures(); renderer.DrawTextures();
#ifdef PRINT_TIMINGS #ifdef PRINT_TIMINGS
std::chrono::high_resolution_clock::time_point re = std::chrono::high_resolution_clock::time_point re =
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
render_time_accumulator += re - rs; render_time_accumulator += re - rs;
time_measure_count++; loop_count++;
if (GetTime() - last_print_time > 10.0) { if (GetTime() - last_print_time > 10.0) {
std::cout << " - Physics time avg: " std::cout << " - Physics time avg: "
<< physics_time_accumulator / time_measure_count << "." << physics_time_accumulator / loop_count << "." << std::endl;
<< std::endl; std::cout << " - Render time avg: "
std::cout << " - Render time avg: " << render_time_accumulator / loop_count << "." << std::endl;
<< render_time_accumulator / time_measure_count << "." std::cout << " - Physics updates avg: "
<< std::endl; << static_cast<float>(update_accumulator) / loop_count
<< "x per frame." << std::endl;
last_print_time = GetTime(); last_print_time = GetTime();
physics_time_accumulator = std::chrono::duration<double, std::milli>(0); physics_time_accumulator = std::chrono::duration<double, std::milli>(0);
render_time_accumulator = std::chrono::duration<double, std::milli>(0); render_time_accumulator = std::chrono::duration<double, std::milli>(0);
time_measure_count = 0; loop_count = 0;
update_accumulator = 0;
} }
#endif #endif
} }

View File

@ -226,13 +226,6 @@ auto MassSpringSystem::CalculateRepulsionForces() -> void {
} }
} }
auto MassSpringSystem::EulerUpdate(float delta_time) -> void {
for (auto &[state, mass] : masses) {
mass.CalculateVelocity(delta_time);
mass.CalculatePosition(delta_time);
}
}
auto MassSpringSystem::VerletUpdate(float delta_time) -> void { auto MassSpringSystem::VerletUpdate(float delta_time) -> void {
for (auto &[state, mass] : masses) { for (auto &[state, mass] : masses) {
mass.VerletUpdate(delta_time); mass.VerletUpdate(delta_time);