Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# C++ Gravitation Simulation with using SDL2 Library.

## **Installing dependencies and building the project for the first time**.
This project created using WSL Ubuntu LTS on a windows platform. But before you clone and use make to build the project, you have to make some configuration if this is the first time using C/GCC/SDL in your machine.
This project created using WSL Ubuntu 22.04 LTS on a windows platform. But before you clone and use make to build the project, you have to make some configuration if this is the first time using C/GCC/SDL in your machine.


### **Dependencies**
Expand All @@ -26,4 +26,4 @@ You have to install LLVM for formatting your code. Refer to [this](https://llvm.
**THIS PROJECT IS CREATED USING UBUNTU 22.04LTS. SOMEHOW, IN OTHER UBUNTU RELEASES, I COULD NOT GET MY TOOLCHAIN SETTED UP TO BUILD THE PROJECT BECAUSE I AM NOT CAPABLE OF FIXING THOSE KIND OF PROBLEMS(FOR NOW).**

- (**PROBABLY ONLY FOR WSL USERS**)When SDL library tries to popup a textbox or etc. via using xServer over windows, it fails and gets a core dump fail. To fix this, you need to install the correct GUI package for your wsl. It is most probably gets fixed with ```sudo apt install zenity ``` but it may depend on the linux version and distribution you are currently using. For further questions, go and search it on Google, Stackoverflow or ask ChatGPT. Don't bother me.
- In systems other than Ubuntu 22.04LTS, some building problems might occur (for example, i could not build Logger class because i had to include each C++ standard library by hand, such as **`#include <array>`** **`#include <unordered_map>`**). If you know how to fix it, please feel free to contribute to this file.
- In systems other than Ubuntu 22.04LTS, some building problems might occur (for example, i could not build Logger class because i had to include each C++ standard library by hand, such as **`#include <array>`** **`#include <unordered_map>`**). If you know how to fix it, please feel free to contribute to this file.
31 changes: 21 additions & 10 deletions include/CollisionManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,35 @@
#include <cmath>
#include <memory>
#include <vector>
#include <unordered_map>

namespace game::engine
{
struct pair_hash
{
template<class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const
{
auto h1 = std::hash<T1>{}(pair.first);
auto h2 = std::hash<T2>{}(pair.second);
return h1 ^ (h2 << 1); // Combine hashes
}
};

class CollisionManager
{

using SpatialGrid =
std::unordered_map<std::pair<int, int>, std::vector<game::object::GameObject*>, pair_hash>;
using CollisionsObject =
std::vector<std::pair<game::object::GameObject*, game::object::GameObject*>>;

private:
std::vector<std::pair<game::object::GameObject*, game::object::GameObject*>>
m_active_collisions;
int m_cumulative_collision_count = 0;
const std::vector<std::pair<int, int>> neighborOffsets = {
{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 0}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};

public:
CollisionManager() = default;
Expand All @@ -24,16 +45,6 @@ class CollisionManager
const std::vector<std::pair<game::object::GameObject*, game::object::GameObject*>>&
get_active_collisions() const;
int get_collision_count() const;
struct pair_hash
{
template<class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const
{
auto h1 = std::hash<T1>{}(pair.first);
auto h2 = std::hash<T2>{}(pair.second);
return h1 ^ (h2 << 1); // Combine hashes
}
};
};
} // namespace game::engine

Expand Down
2 changes: 1 addition & 1 deletion include/Game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Game
{
return m_window_height;
}
GameState* p_gameState;
GameState m_gameState;

private:
void init();
Expand Down
12 changes: 8 additions & 4 deletions include/GameObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "ClassLogger.hpp"
#include "ObjectHelper.hpp"

#include "GameHelper.hpp"
#include <algorithm>
#include <cmath>
#include <memory>
Expand Down Expand Up @@ -40,6 +40,7 @@ class GameObject
m_pos = {0.0f, 0.0f};
m_acceleration = {0.0f, 0.0f};
m_restitution = 1;
m_is_stable = false;
};
virtual ~GameObject() {}

Expand All @@ -66,12 +67,14 @@ class GameObject
void setRestitution(double restitution);
void setColor(Color color);
void setColorState(ColorState colorState);
void setStability(bool stable);
bool getStability() const;
std::vector<Position> getLastPositions() const;
Force getForce() const;
double get_mass();
double getMass() const;

ObjectType get_type() const;
Color get_color() const;
ObjectType getType() const;
Color getColor() const;
Position getPosition() const;
Velocity getVelocity() const;
Acceleration getAcceleration() const;
Expand All @@ -86,6 +89,7 @@ class GameObject
double m_mass;
double m_restitution; // Coefficient of restitution (bounciness) for elastic/inelastic
// collisions. 1.0 for perfectly elastic, 0.0 for perfectly inelastic.
bool m_is_stable;
Force m_force;
ColorState m_color_state;
Color m_color;
Expand Down
2 changes: 2 additions & 0 deletions include/Logger.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once
#include <array>
#include <iostream>
#include <mutex>
#include <sstream>
#include <string>
#include <type_traits>
namespace utils
{
class Logger
Expand Down
150 changes: 87 additions & 63 deletions src/CollisionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,16 @@ namespace game::engine
void CollisionManager::resolve_collisions(
const std::vector<std::unique_ptr<game::object::GameObject>>& objects)
{
// Define the grid size
const float cellSize = 50.0f; // Adjust based on object size and game world

// A map to store objects by their grid cell
std::unordered_map<std::pair<int, int>, std::vector<game::object::GameObject*>, pair_hash>
spatialGrid;
SpatialGrid spatialGrid; // A map to store objects by their grid cell

// Helper lambda to compute grid cell for a position
auto getCell = [cellSize](const game::object::GameObject& obj)
{
int pos_x, pos_y;
switch (obj.get_type())
{
case game::object::helper::ObjectType::CIRCLE:
if (const auto* circle = dynamic_cast<const game::object::CircleObject*>(&obj))
{
pos_x = static_cast<int>(std::floor(circle->getPosition().getX() / cellSize));
pos_y = static_cast<int>(std::floor(circle->getPosition().getY() / cellSize));
}
break;
case game::object::helper::ObjectType::RECTANGLE:
if (const auto* rect = dynamic_cast<const game::object::RectObject*>(&obj))
{
pos_x = static_cast<int>(std::floor(
(rect->getPosition().getX() + rect->get_width() / 2) / cellSize));
pos_y = static_cast<int>(std::floor(
(rect->getPosition().getY() + rect->get_height() / 2) / cellSize));
}
break;
}

int cellX = static_cast<int>(std::floor(pos_x / cellSize));
int cellY = static_cast<int>(std::floor(pos_y / cellSize));
game::object::Position center = obj.getCenter();
int cellX = static_cast<int>(std::floor(center.getX() / cellSize));
int cellY = static_cast<int>(std::floor(center.getY() / cellSize));
return std::make_pair(cellX, cellY);
};

Expand All @@ -50,59 +27,106 @@ void CollisionManager::resolve_collisions(
}

// Temporary set to track current collisions
std::vector<std::pair<game::object::GameObject*, game::object::GameObject*>> currentCollisions;
CollisionsObject currentCollisions;

// Check collisions within each cell and neighboring cells
for (const auto& [cell, cellObjects] : spatialGrid)
{
const int cellX = cell.first;
const int cellY = cell.second;

// Iterate over the cell and its 8 neighbors
for (int dx = -1; dx <= 1; ++dx)
for (const auto& offset : this->neighborOffsets)
{
for (int dy = -1; dy <= 1; ++dy)
auto neighborCell =
std::make_pair(cell.first + offset.first, cell.second + offset.second);
if (spatialGrid.find(neighborCell) == spatialGrid.end())
{
auto neighborCell = std::make_pair(cellX + dx, cellY + dy);
if (spatialGrid.find(neighborCell) == spatialGrid.end())
continue;
continue;
}
const auto& neighborObjects = spatialGrid[neighborCell];

const auto& neighborObjects = spatialGrid[neighborCell];
// Check for collisions between objects in the current cell and neighbor cell
for (auto* obj1 : cellObjects)
{
for (auto* obj2 : neighborObjects)
{
if (obj1 >= obj2)
{
continue; // Skip self-collision and duplicate checks
}

if (obj1->is_colliding_with(*obj2))
{
currentCollisions.emplace_back(obj1, obj2);
m_cumulative_collision_count++;
}
else
{
m_active_collisions.erase(
std::remove_if(m_active_collisions.begin(), m_active_collisions.end(),
[&](const std::pair<game::object::GameObject*,
game::object::GameObject*>& pair)
{
return (pair.first == obj1 && pair.second == obj2) ||
(pair.first == obj2 && pair.second == obj1);
}),
m_active_collisions.end());
}
}
}
}
}

// Check for collisions between objects in the current cell and neighbor cell
for (auto* obj1 : cellObjects)
// Resolve Collisions
for (const auto& [obj1, obj2] : currentCollisions)
{
obj1->on_collision(*obj2);
obj2->on_collision(*obj1);
// Check for collisions between objects in the current cell and neighbor cell
for (auto* obj1 : cellObjects)
{
for (auto* obj2 : neighborObjects)
{
for (auto* obj2 : neighborObjects)
if (obj1 >= obj2)
{
if (obj1 == obj2)
continue; // Skip self-collision
if (obj1->is_colliding_with(*obj2))
{
obj1->on_collision(*obj2);
obj2->on_collision(*obj1);
// Track the colliding pair
currentCollisions.emplace_back(obj1, obj2);
m_cumulative_collision_count++;
}
else
continue; // Skip self-collision and duplicate checks
}

if (obj1->is_colliding_with(*obj2))
{
if(!obj2->getStability())
{
m_active_collisions.erase(
std::remove_if(
m_active_collisions.begin(), m_active_collisions.end(),
[&](const std::pair<game::object::GameObject*,
game::object::GameObject*>& pair)
{
return (pair.first == obj1 && pair.second == obj2) ||
(pair.first == obj2 && pair.second == obj1);
}),
m_active_collisions.end());
if(obj2->getVelocity().magnitude()< 2.5)
{
object::helper::Vector2D zeroSpeed{0,0};
obj2->setVelocity(zeroSpeed);
return;
}
}
currentCollisions.emplace_back(obj1, obj2);
m_cumulative_collision_count++;
}
else
{
m_active_collisions.erase(
std::remove_if(m_active_collisions.begin(), m_active_collisions.end(),
[&](const std::pair<game::object::GameObject*,
game::object::GameObject*>& pair)
{
return (pair.first == obj1 && pair.second == obj2) ||
(pair.first == obj2 && pair.second == obj1);
}),
m_active_collisions.end());
}
}
}
}
}

// Resolve Collisions
for (const auto& [obj1, obj2] : currentCollisions)
{
obj1->on_collision(*obj2);
obj2->on_collision(*obj1);
}

// Update active collisions
m_active_collisions = std::move(currentCollisions);
}
Expand Down Expand Up @@ -148,7 +172,7 @@ void CollisionManager::calculate_gravitational_force(
sqrt(d_square(obj2->getPosition().getX() - obj1->getPosition().getX()) +
d_square(obj2->getPosition().getY() - obj1->getPosition().getY()));
double g_force =
(GRAVITATIONAL_CONSTANT * obj1->get_mass() * obj2->get_mass()) / d_square(distance);
(GRAVITATIONAL_CONSTANT * obj1->getMass() * obj2->getMass()) / d_square(distance);
game::object::helper::Vector2D force{0, 0};

if (!distance)
Expand Down
8 changes: 4 additions & 4 deletions src/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ void Game::run()
while (m_sdl->isRunning())
{
m_sdl->update(); // Update SDL timing
m_sdl->handleEvents(*p_gameState);
m_sdl->render(gameObjects, *p_gameState); // Pass game objects to SDLHelper for rendering
m_sdl->handleEvents(m_gameState);
m_sdl->render(gameObjects, m_gameState); // Pass game objects to SDLHelper for rendering
m_sdl->renderCollisionHighlights(m_collisionManager.get_active_collisions());
// Fixed timestep for game logic
while (m_sdl->getAccumulator() >= m_LOGIC_TIMESTEP)
{
if (*p_gameState == game::GameState::PLAYING)
if (m_gameState == game::GameState::PLAYING)
{
update();
m_sdl->renderCollisionScoreboard(
Expand All @@ -95,7 +95,7 @@ void Game::run()
void Game::init()
{
gameObjects = std::vector<std::unique_ptr<game::object::GameObject>>();
*p_gameState = GameState::MENU;
m_gameState = GameState::MENU;
std::string loggerName = "sdl_logger";
m_sdl = std::make_unique<game::sdl::SDLHelper>("Game Title", m_window_width, m_window_height,
loggerName);
Expand Down
Loading