From 841d08ef95ab28ba7e0b20c604cd62b4646c2a12 Mon Sep 17 00:00:00 2001 From: Sara Date: Tue, 14 Oct 2025 13:05:43 +0200 Subject: [PATCH] feat: added reference implementation --- src/Balls.hpp | 140 +++++++++++++++++++++++++++++++++++++++ src/collision_crisis.cpp | 75 ++++++++++----------- 2 files changed, 175 insertions(+), 40 deletions(-) create mode 100644 src/Balls.hpp diff --git a/src/Balls.hpp b/src/Balls.hpp new file mode 100644 index 0000000..bbe900f --- /dev/null +++ b/src/Balls.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include + +#include + +struct Ball { + sf::CircleShape shape; + sf::Vector2f velocity; + + Ball(float x, float y, float radius, sf::Color color, float vx, float vy) { + shape.setRadius(radius); + shape.setPosition(sf::Vector2f(x, y)); + shape.setFillColor(color); + shape.setOrigin(sf::Vector2f(radius, radius)); // Center origin + velocity = sf::Vector2f(vx, vy); + } +}; + +class BallGame { +private: + // Create balls + std::vector balls; + std::random_device rd; + std::mt19937 gen; + std::uniform_real_distribution posDist; + std::uniform_real_distribution velDist; + std::uniform_int_distribution colorDist; + std::uniform_real_distribution radiusDist; + +public: + BallGame::BallGame() { + gen = std::mt19937(rd()); + posDist = std::uniform_real_distribution(5.0f, 795.0f); + velDist = std::uniform_real_distribution(-200.0f, 200.0f); + colorDist = std::uniform_int_distribution(0, 255); + radiusDist = std::uniform_real_distribution(2.5f, 2.5f); + + // Generate random balls + for (int i = 0; i < 2500; ++i) { + sf::Color randomColor(colorDist(gen), colorDist(gen), colorDist(gen)); + balls.emplace_back( + posDist(gen), posDist(gen), // position + radiusDist(gen), // radius + randomColor, // color + velDist(gen), velDist(gen) // velocity + ); + } + } + + void updateBalls(const sf::Vector2u& windowSize, float deltaTime) { + // Update positions + for (auto& ball : balls) { + ball.shape.move(ball.velocity * deltaTime); + } + + // Handle ball-to-ball collisions + for (size_t i = 0; i < balls.size(); ++i) { + for (size_t j = i + 1; j < balls.size(); ++j) { + Ball& ball1 = balls[i]; + Ball& ball2 = balls[j]; + + sf::Vector2f pos1 = ball1.shape.getPosition(); + sf::Vector2f pos2 = ball2.shape.getPosition(); + float radius1 = ball1.shape.getRadius(); + float radius2 = ball2.shape.getRadius(); + + // Calculate distance between centers + sf::Vector2f delta = pos2 - pos1; + float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y); + float minDistance = radius1 + radius2; + + if (distance < minDistance && distance > 0) { + // Normalize collision vector + sf::Vector2f normal = delta / distance; + + // Separate balls to prevent overlap + float overlap = minDistance - distance; + sf::Vector2f separation = normal * (overlap * 0.5f); + ball1.shape.setPosition(pos1 - separation); + ball2.shape.setPosition(pos2 + separation); + + // Calculate relative velocity + sf::Vector2f relativeVel = ball2.velocity - ball1.velocity; + float velAlongNormal = relativeVel.x * normal.x + relativeVel.y * normal.y; + + // Don't resolve if velocities are separating + if (velAlongNormal > 0) continue; + + // Apply collision response (elastic collision) + float restitution = 0.0f; // Bounce factor (0 = no bounce, 1 = perfect bounce) + float impulse = -(1 + restitution) * velAlongNormal; + + // Assume equal mass for simplicity + sf::Vector2f impulseVector = impulse * normal; + ball1.velocity -= impulseVector; + ball2.velocity += impulseVector; + } + } + } + + // Handle wall collisions + for (auto& ball : balls) { + sf::Vector2f pos = ball.shape.getPosition(); + float radius = ball.shape.getRadius(); + + // Bounce off walls + if (pos.x - radius <= 0 || pos.x + radius >= windowSize.x) { + ball.velocity.x = -ball.velocity.x; + // Clamp position to prevent sticking + if (pos.x - radius <= 0) { + ball.shape.setPosition(sf::Vector2f(radius, pos.y)); + } + else { + ball.shape.setPosition(sf::Vector2f(windowSize.x - radius, pos.y)); + } + } + + if (pos.y - radius <= 0 || pos.y + radius >= windowSize.y) { + ball.velocity.y = -ball.velocity.y; + // Clamp position to prevent sticking + if (pos.y - radius <= 0) { + ball.shape.setPosition(sf::Vector2f(pos.x, radius)); + } + else { + ball.shape.setPosition(sf::Vector2f(pos.x, windowSize.y - radius)); + } + } + } + } + + void drawBalls( sf::RenderWindow& window ) const + { + for (const auto& ball : balls) { + window.draw(ball.shape); + } + } +}; \ No newline at end of file diff --git a/src/collision_crisis.cpp b/src/collision_crisis.cpp index e6b3c2a..c257694 100644 --- a/src/collision_crisis.cpp +++ b/src/collision_crisis.cpp @@ -9,68 +9,63 @@ #include #include - void configure(AppConfig &config) { - config.window_title = "CHANGEME"; - config.frame_rate_limit = std::nullopt; - config.vsync = true; + config.window_title = "CHANGEME"; + config.frame_rate_limit = std::nullopt; + config.vsync = true; } void setup() { - ImGui::GetIO().ConfigFlags |= ( - ImGuiConfigFlags_NavEnableKeyboard - | ImGuiConfigFlags_NavEnableGamepad - | ImGuiConfigFlags_DockingEnable - ); - sf::View view{ get_window().getView() }; - view.setCenter( { 0.f, 0.f } ); - set_render_view(view); + ImGui::GetIO().ConfigFlags |= (ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_DockingEnable); + sf::View view{get_window().getView()}; + view.setCenter({0.f, 0.f}); + set_render_view(view); } void handle_input_event(sf::Event const &event) { } void handle_window_event(sf::Event const &event) { - if (sf::Event::Resized const *resized{ event.getIf()}) { - sf::View view{ get_window().getView() }; - view.setSize({ static_cast(resized->size.x), static_cast(resized->size.y) }); - set_render_view(view); - } + if (sf::Event::Resized const *resized{event.getIf()}) { + sf::View view{get_window().getView()}; + view.setSize({static_cast(resized->size.x), static_cast(resized->size.y)}); + set_render_view(view); + } } void loop(double delta) { } void draw_scene(sf::RenderTarget &target, sf::RenderStates const &states) { - static sf::RectangleShape rect{ { 300, 300 } }; - sf::RenderStates n_states{states}; - n_states.transform.translate({ -150, -150 }); - target.draw(rect, n_states); + static sf::RectangleShape rect{{300, 300}}; + sf::RenderStates n_states{states}; + n_states.transform.translate({-150, -150}); + target.draw(rect, n_states); } void draw_main_menu_bar() { - if (ImGui::BeginMenu("Edit")) { - if (ImGui::MenuItem("Menu item!")) { - std::println("Wahooo!!!"); - } - ImGui::EndMenu(); - } + if (ImGui::BeginMenu("Edit")) { + if (ImGui::MenuItem("Menu item!")) { + std::println("Wahooo!!!"); + } + ImGui::EndMenu(); + } } void draw_gui() { - // draw your GUI - ImGui::DockSpaceOverViewport(0, NULL, ImGuiDockNodeFlags_PassthruCentralNode); - if (ImGui::Begin("My Window")) { - ImGui::Text("A window with text and a button!!"); - if (ImGui::Button("My Button")) { - std::println("Yipeeee"); - } - ImGui::End(); - } - if (ImGui::Begin("Second Window :O")) { - ImGui::Text("A window with text!"); - ImGui::End(); - } + // draw your GUI + ImGui::DockSpaceOverViewport(0, NULL, ImGuiDockNodeFlags_PassthruCentralNode); + if (ImGui::Begin("My Window")) { + ImGui::Text("A window with text and a button!!"); + if (ImGui::Button("My Button")) { + std::println("Yipeeee"); + } + ImGui::End(); + } + if (ImGui::Begin("Second Window :O")) { + ImGui::Text("A window with text!"); + ImGui::End(); + } } void shutdown() {