From 250f0963c840aa8147d24d80c6df4fe1a74cdc74 Mon Sep 17 00:00:00 2001 From: Jan Mrna Date: Fri, 10 Oct 2025 19:00:08 +0200 Subject: [PATCH] Basic collisions --- cpp/src/entities.cpp | 19 +++++++++++++ cpp/src/entities.hpp | 8 ++++-- cpp/src/gameloop.cpp | 10 +++++-- cpp/src/math.hpp | 8 +++++- cpp/src/pathfindingdemo.cpp | 55 +++++++++++++++++++++++++++++++++++-- cpp/src/pathfindingdemo.hpp | 5 ++++ 6 files changed, 98 insertions(+), 7 deletions(-) diff --git a/cpp/src/entities.cpp b/cpp/src/entities.cpp index a162d25..453b003 100644 --- a/cpp/src/entities.cpp +++ b/cpp/src/entities.cpp @@ -68,6 +68,25 @@ std::optional Entity::GetMoveTarget() return {}; } +bool Entity::CollidesWith(const Entity& other) const +{ + const auto& A = *this; + const auto& B = other; + + auto position_A = A.GetPosition(); + auto position_B = B.GetPosition(); + auto distance_sq = position_A.DistanceSquared(position_B); + auto collision_distance_sq = + A.GetCollisionRadiusSquared() + + B.GetCollisionRadiusSquared() + + 2 * A.GetCollisionRadius() * B.GetCollisionRadius(); + if (distance_sq < collision_distance_sq) + { + return true; + } + return false; +} + Player::Player() { LOG_DEBUG("."); if (m_Sprite == nullptr) { diff --git a/cpp/src/entities.hpp b/cpp/src/entities.hpp index 430e23b..587f36d 100644 --- a/cpp/src/entities.hpp +++ b/cpp/src/entities.hpp @@ -63,6 +63,10 @@ public: void SetPath(pathfinder::Path& path) { m_Path = path; } std::optional GetMoveTarget(); + bool CollidesWith(const Entity& other) const; + + bool IsCollisionBoxVisible() const { return m_CollisionBoxVisible; } + protected: WorldPos m_Position; WorldPos m_ActualVelocity; @@ -71,7 +75,7 @@ protected: private: bool m_FlagExpired = false; - static constexpr float m_CollisionRadiusSq = 1000.0f; + bool m_CollisionBoxVisible = true; }; class Player final : public Entity { @@ -83,7 +87,7 @@ public: constexpr Entity::Type GetType() const override { return Entity::Type::PLAYER; } - constexpr float GetCollisionRadius() const override { return 50.0f; } + constexpr float GetCollisionRadius() const override { return 25.0f; } bool IsMovable() const override { return true; } bool IsCollidable() const override { return true; } diff --git a/cpp/src/gameloop.cpp b/cpp/src/gameloop.cpp index 5a12f4e..329d4a6 100644 --- a/cpp/src/gameloop.cpp +++ b/cpp/src/gameloop.cpp @@ -43,8 +43,14 @@ void GameLoop::Draw() { // draw all the entities (player etc) for (auto &entity : m_Game->GetEntities()) { const auto &camera = m_Game->GetCamera(); - m_Window->DrawSprite(camera.WorldToWindow(entity->GetPosition()), - entity->GetSprite(), camera.GetZoom()); + auto entity_pos_window = camera.WorldToWindow(entity->GetPosition()); + m_Window->DrawSprite(entity_pos_window, + entity->GetSprite(), + camera.GetZoom()); + if (entity->IsCollisionBoxVisible()) + { + m_Window->DrawCircle(entity_pos_window, entity->GetCollisionRadius()); + } } } diff --git a/cpp/src/math.hpp b/cpp/src/math.hpp index fbd0682..235bdf9 100644 --- a/cpp/src/math.hpp +++ b/cpp/src/math.hpp @@ -165,6 +165,12 @@ public: return (a - b).Length(); } + T DistanceSquared(const vec &b) const { + const vec &a = *this; + return (a - b).LengthSquared(); + } + + // // In-place vector operations // @@ -394,4 +400,4 @@ public: private: std::array m_Array; -}; \ No newline at end of file +}; diff --git a/cpp/src/pathfindingdemo.cpp b/cpp/src/pathfindingdemo.cpp index e013ea0..46ccd2b 100644 --- a/cpp/src/pathfindingdemo.cpp +++ b/cpp/src/pathfindingdemo.cpp @@ -65,6 +65,15 @@ void PathFindingDemo::CreateMap() { player2->SetPosition(m_Map.TileToWorld(TilePos{50, 20})); AddEntity(player2); + for (int i = 0; i < 1; i++) + { + for (int j = 0; j < 10; j++) + { + auto p = std::make_shared(); + p->SetPosition(m_Map.TileToWorld(TilePos{10+5*i, 40+5*j})); + AddEntity(p); + } + } // select everything - TODO this is just temporary for testing for (const auto& entity : m_Entities) @@ -79,6 +88,30 @@ WorldPos PathFindingDemo::GetRandomPosition() const { +const std::vector& PathFindingDemo::GetEntityCollisions() +{ + static std::vector m_Collisions; + m_Collisions.clear(); + + for (const auto &entity_A : m_Entities) + { + for (const auto &entity_B : m_Entities) + { + if (entity_A == entity_B) + continue; + if (!entity_A->IsCollidable() || !entity_B->IsCollidable()) + continue; + if (entity_A->CollidesWith(*entity_B)) + { + // handle collision logic + m_Collisions.emplace_back(Collision(entity_A, entity_B)); + } + } + } + return m_Collisions; +} + + // Update entity positions, handle collisions void PathFindingDemo::UpdateWorld() { @@ -100,8 +133,26 @@ void PathFindingDemo::UpdateWorld() { } entity->SetActualVelocity(velocity * tile_velocity_coeff); - - // handle collisions - these may modify the velocity + for (const auto& collision : GetEntityCollisions()) + { + // TODO this loop is quite "hot", is it good idea to use weak_ptr and promote it? + auto weak_A = std::get<0>(collision); + auto weak_B = std::get<1>(collision); + auto A = weak_A.lock(); + auto B = weak_B.lock(); + if (A == nullptr || B == nullptr) + { + continue; + } + if (!A->IsMovable()) + continue; + // modify actual speed + // LOG_DEBUG("Collision: A is ", A, ", B is ", B); + auto AB = B->GetPosition() - A->GetPosition(); + A->ZeroActualVelocityInDirection(AB); + // handle logic + // TODO + } // update the position entity->Update(time_delta); diff --git a/cpp/src/pathfindingdemo.hpp b/cpp/src/pathfindingdemo.hpp index 8a3d9c5..ea34769 100644 --- a/cpp/src/pathfindingdemo.hpp +++ b/cpp/src/pathfindingdemo.hpp @@ -12,6 +12,8 @@ #include "pathfinder/base.hpp" #include "camera.hpp" +using Collision = std::pair, std::weak_ptr>; + class PathFindingDemo { public: PathFindingDemo(int width, int height); @@ -33,7 +35,10 @@ public: void HandleActions(const std::vector &actions); WorldPos GetRandomPosition() const; + private: + const std::vector& GetEntityCollisions(); + bool m_ExitRequested = false; Map m_Map; Camera m_Camera;