From 6bdf7a1e242747a10d12b8dc7dfacb0fc52cd987 Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Sat, 24 Jan 2026 22:35:01 +0200 Subject: [PATCH 1/3] Implement colliders Signed-off-by: Mihai Dumitrescu --- engine/include/velos/physics/aabb_collider.h | 20 +++++++++++++++++ .../include/velos/physics/circle_collider.h | 18 +++++++++++++++ engine/include/velos/physics/collider.h | 20 +++++++++++++++++ engine/src/physics/aabb_collider.cpp | 22 +++++++++++++++++++ engine/src/physics/circle_collider.cpp | 16 ++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 engine/include/velos/physics/aabb_collider.h create mode 100644 engine/include/velos/physics/circle_collider.h create mode 100644 engine/include/velos/physics/collider.h create mode 100644 engine/src/physics/aabb_collider.cpp create mode 100644 engine/src/physics/circle_collider.cpp diff --git a/engine/include/velos/physics/aabb_collider.h b/engine/include/velos/physics/aabb_collider.h new file mode 100644 index 0000000..9e1b610 --- /dev/null +++ b/engine/include/velos/physics/aabb_collider.h @@ -0,0 +1,20 @@ +#pragma once +#include "collider.h" + +namespace velos { + class AABBCollider: public Collider { + public: + AABBCollider(const Vec2& size, const Vec2& offset = {0.f, 0.f}) + : m_size(size), m_offset(offset) {} + + ColliderType type() const override; + void computeWorld(const Transform& transform) override; + bool containsPoint(const Vec2& point) const override; + + void setSize(const Vec2 size) const; + + private: + Vec2 m_size; + Vec2 m_offset; + }; +} diff --git a/engine/include/velos/physics/circle_collider.h b/engine/include/velos/physics/circle_collider.h new file mode 100644 index 0000000..40fb0b1 --- /dev/null +++ b/engine/include/velos/physics/circle_collider.h @@ -0,0 +1,18 @@ +#pragma once +#include "collider.h" + +namespace velos { + class CircleCollider: public Collider { + public: + CircleCollider(float radius, const Vec2& offset = {0.f, 0.f}) + : m_radius(radius), m_offset(offset) {} + + ColliderType type() const override; + void computeWorld(const Transform& transform) override; + bool containsPoint(const Vec2& point) const override; + + private: + float m_radius; + Vec2 m_offset; + }; +} diff --git a/engine/include/velos/physics/collider.h b/engine/include/velos/physics/collider.h new file mode 100644 index 0000000..9fc10c8 --- /dev/null +++ b/engine/include/velos/physics/collider.h @@ -0,0 +1,20 @@ +#pragma once +#include "../math/transform.h" + +namespace velos { + enum class ColliderType { + AABB, + Circle + }; + + class Collider { + public: + virtual ~Collider() = default; + + virtual ColliderType type() const = 0; + virtual void computeWorld(const Transform& transform) = 0; + virtual bool containsPoint(const Vec2& worldPoint) const = 0; + protected: + Vec2 m_worldPosition{0.f, 0.f}; + }; +} diff --git a/engine/src/physics/aabb_collider.cpp b/engine/src/physics/aabb_collider.cpp new file mode 100644 index 0000000..195073b --- /dev/null +++ b/engine/src/physics/aabb_collider.cpp @@ -0,0 +1,22 @@ +#include "velos/physics/aabb_collider.h" + +namespace velos { + ColliderType AABBCollider::type() const { + return ColliderType::AABB; + } + + void AABBCollider::computeWorld(const Transform& transform) { + m_worldPosition = transform.position + m_offset; + } + + bool AABBCollider::containsPoint(const Vec2& point) const { + return point.x >= m_worldPosition.x && + point.x <= m_worldPosition.x + m_size.x && + point.y >= m_worldPosition.y && + point.y <= m_worldPosition.y + m_size.y; + } + + void AABBCollider::setSize(const Vec2 size) const { + m_size = size; + } +} diff --git a/engine/src/physics/circle_collider.cpp b/engine/src/physics/circle_collider.cpp new file mode 100644 index 0000000..2ed2aee --- /dev/null +++ b/engine/src/physics/circle_collider.cpp @@ -0,0 +1,16 @@ +#include "velos/physics/circle_collider.h" + +namespace velos { + ColliderType CircleCollider::type() const { + return ColliderType::Circle; + } + + void CircleCollider::computeWorld(const Transform& transform) { + m_worldPosition = transform.position + m_offset; + } + + bool CircleCollider::containsPoint(const Vec2& point) const { + Vec2 delta = point - m_worldPosition; + return delta.lengthSquared() <= m_radius * m_radius; + } +} From e7c171a34eb458659ab85131b9a248827e616611 Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Mon, 26 Jan 2026 15:26:30 +0200 Subject: [PATCH 2/3] Add collision zones in entity Signed-off-by: Mihai Dumitrescu --- docs/USAGE.md | 3 +++ engine/include/velos/entity/entity.h | 9 ++++++++- engine/src/entity/entity.cpp | 13 +++++++++++++ examples/sandbox/src/main.cpp | 5 +++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index 329c919..55e8d86 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -9,6 +9,9 @@ auto rect = std::make_shared(velos::Vec2{200.f, 200.f}, velos: auto entity = std::make_shared(); entity->addShape(rect); engine.entityManager()->addEntity(entity); + +auto collider = std::make_unique(); +entity->addCollider(std::move(collider)); ``` ## Moving and Transforming entities diff --git a/engine/include/velos/entity/entity.h b/engine/include/velos/entity/entity.h index b8e892e..5e57a7f 100644 --- a/engine/include/velos/entity/entity.h +++ b/engine/include/velos/entity/entity.h @@ -2,6 +2,7 @@ #include #include #include "../graphics/shapes/shape.h" +#include "../physics/collider.h" #include "../graphics/renderer.h" #include "../math/transform.h" @@ -9,17 +10,23 @@ namespace velos { class Entity { public: Entity() = default; + virtual ~Entity() = default; - virtual void update(float dt) {}; + virtual void update(float dt) { updateColliders(); } virtual void render(Renderer& renderer); bool containsPoint(const Vec2& worldPos) const; void addShape(std::shared_ptr shape) { m_shapes.push_back(shape); } void move(float dx, float dy); + void addCollider(std::unique_ptr collider); + const std::vector>& colliders() const; + void updateColliders(); + Transform& transform() { return m_transform; } private: Transform m_transform; std::vector> m_shapes; + std::vector> m_colliders; }; } diff --git a/engine/src/entity/entity.cpp b/engine/src/entity/entity.cpp index c8b7ae2..12a386a 100644 --- a/engine/src/entity/entity.cpp +++ b/engine/src/entity/entity.cpp @@ -19,4 +19,17 @@ namespace velos { m_transform.position.x += dx; m_transform.position.y += dy; } + + void Entity::addCollider(std::unique_ptr collider) { + m_colliders.push_back(std::move(collider)); + } + + const std::vector>& Entity::colliders() const { + return m_colliders; + } + + void Entity::updateColliders() { + for (auto& collider: m_colliders) + collider->computeWorld(m_transform); + } } diff --git a/examples/sandbox/src/main.cpp b/examples/sandbox/src/main.cpp index d66b15f..8af1cb5 100644 --- a/examples/sandbox/src/main.cpp +++ b/examples/sandbox/src/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include int main() { velos::EngineConfig config; @@ -29,6 +30,10 @@ int main() { entity->addShape(rect); entity->transform().position = {200, 200}; + auto collider = std::make_unique(); + collider->m_size = {50.f, 50.f}; + entity->addCollider(std::move(collider)); + auto stationary_entity = std::make_shared(); stationary_entity->addShape(circle); From 31d6698c39693219bbcab78a2c07fbaf2c619680 Mon Sep 17 00:00:00 2001 From: Mihai Dumitrescu Date: Wed, 28 Jan 2026 13:50:26 +0200 Subject: [PATCH 3/3] Add collision zone APIs Signed-off-by: Mihai Dumitrescu --- docs/USAGE.md | 2 ++ engine/include/velos/app/engine.h | 2 ++ engine/include/velos/entity/entity.h | 1 + engine/include/velos/entity/entity_manager.h | 3 +++ engine/include/velos/physics/aabb_collider.h | 3 ++- engine/include/velos/physics/circle_collider.h | 3 +++ engine/include/velos/physics/collider.h | 3 +++ engine/src/app/engine.cpp | 5 +++++ engine/src/entity/entity.cpp | 5 +++++ engine/src/entity/entity_manager.cpp | 10 +++++++++- engine/src/physics/aabb_collider.cpp | 6 +++++- engine/src/physics/circle_collider.cpp | 4 ++++ examples/sandbox/src/main.cpp | 6 ++++-- 13 files changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index 55e8d86..7d829c7 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -12,6 +12,8 @@ engine.entityManager()->addEntity(entity); auto collider = std::make_unique(); entity->addCollider(std::move(collider)); + +engine.setDebugRender(true); // For debugging collision zones ``` ## Moving and Transforming entities diff --git a/engine/include/velos/app/engine.h b/engine/include/velos/app/engine.h index d71ae8e..6dd30f2 100644 --- a/engine/include/velos/app/engine.h +++ b/engine/include/velos/app/engine.h @@ -31,6 +31,8 @@ namespace velos { void attachCamera(Entity& entity); void detachCamera(); + void setDebugRender(bool state); + static Engine* instance() { return s_instance; } Camera* camera() { return m_camera.get(); } EntityManager* entityManager() { return m_entityManager.get(); } diff --git a/engine/include/velos/entity/entity.h b/engine/include/velos/entity/entity.h index 5e57a7f..0170198 100644 --- a/engine/include/velos/entity/entity.h +++ b/engine/include/velos/entity/entity.h @@ -14,6 +14,7 @@ namespace velos { virtual void update(float dt) { updateColliders(); } virtual void render(Renderer& renderer); + void debugRender(Renderer& renderer); bool containsPoint(const Vec2& worldPos) const; void addShape(std::shared_ptr shape) { m_shapes.push_back(shape); } diff --git a/engine/include/velos/entity/entity_manager.h b/engine/include/velos/entity/entity_manager.h index 970733d..41f4434 100644 --- a/engine/include/velos/entity/entity_manager.h +++ b/engine/include/velos/entity/entity_manager.h @@ -10,8 +10,11 @@ namespace velos { void update(float dt); void render(Renderer& renderer); + void setDebugRender(bool state); + std::vector> entities() { return m_entities; } private: std::vector> m_entities; + bool m_debugRender = false; }; } diff --git a/engine/include/velos/physics/aabb_collider.h b/engine/include/velos/physics/aabb_collider.h index 9e1b610..08a979c 100644 --- a/engine/include/velos/physics/aabb_collider.h +++ b/engine/include/velos/physics/aabb_collider.h @@ -11,7 +11,8 @@ namespace velos { void computeWorld(const Transform& transform) override; bool containsPoint(const Vec2& point) const override; - void setSize(const Vec2 size) const; + void setSize(const Vec2 size); + void debugRender(Renderer& renderer) const override; private: Vec2 m_size; diff --git a/engine/include/velos/physics/circle_collider.h b/engine/include/velos/physics/circle_collider.h index 40fb0b1..91a152f 100644 --- a/engine/include/velos/physics/circle_collider.h +++ b/engine/include/velos/physics/circle_collider.h @@ -11,6 +11,9 @@ namespace velos { void computeWorld(const Transform& transform) override; bool containsPoint(const Vec2& point) const override; + void setRadius(float radius); + void debugRender(Renderer& renderer) const override; + private: float m_radius; Vec2 m_offset; diff --git a/engine/include/velos/physics/collider.h b/engine/include/velos/physics/collider.h index 9fc10c8..d596ac4 100644 --- a/engine/include/velos/physics/collider.h +++ b/engine/include/velos/physics/collider.h @@ -1,5 +1,6 @@ #pragma once #include "../math/transform.h" +#include "../graphics/renderer.h" namespace velos { enum class ColliderType { @@ -14,6 +15,8 @@ namespace velos { virtual ColliderType type() const = 0; virtual void computeWorld(const Transform& transform) = 0; virtual bool containsPoint(const Vec2& worldPoint) const = 0; + + virtual void debugRender(Renderer& renderer) const = 0; protected: Vec2 m_worldPosition{0.f, 0.f}; }; diff --git a/engine/src/app/engine.cpp b/engine/src/app/engine.cpp index ba90875..9f78b2e 100644 --- a/engine/src/app/engine.cpp +++ b/engine/src/app/engine.cpp @@ -49,6 +49,7 @@ namespace velos { m_running = false; } + void Engine::addEventListener(EventType type, EventCallback callback) { m_eventListeners[type].push_back(std::move(callback)); } @@ -101,4 +102,8 @@ namespace velos { void Engine::detachCamera() { m_camera->unfollow(); } + + void Engine::setDebugRender(bool state) { + entityManager()->setDebugRender(state); + } } diff --git a/engine/src/entity/entity.cpp b/engine/src/entity/entity.cpp index 12a386a..9189fc6 100644 --- a/engine/src/entity/entity.cpp +++ b/engine/src/entity/entity.cpp @@ -6,6 +6,11 @@ namespace velos { shape->render(renderer, m_transform); } + void Entity::debugRender(Renderer& renderer) { + for (auto& collider: m_colliders) + collider->debugRender(renderer); + } + bool Entity::containsPoint(const Vec2& worldPos) const { for (auto& shape: m_shapes) { Vec2 local = m_transform.applyInverse(worldPos); diff --git a/engine/src/entity/entity_manager.cpp b/engine/src/entity/entity_manager.cpp index e784eed..416156e 100644 --- a/engine/src/entity/entity_manager.cpp +++ b/engine/src/entity/entity_manager.cpp @@ -7,7 +7,15 @@ namespace velos { } void EntityManager::render(Renderer& renderer) { - for (auto& entity: m_entities) + for (auto& entity: m_entities) { entity->render(renderer); + + if (m_debugRender) + entity->debugRender(renderer); + } + } + + void EntityManager::setDebugRender(bool state) { + m_debugRender = state; } } diff --git a/engine/src/physics/aabb_collider.cpp b/engine/src/physics/aabb_collider.cpp index 195073b..caff0aa 100644 --- a/engine/src/physics/aabb_collider.cpp +++ b/engine/src/physics/aabb_collider.cpp @@ -16,7 +16,11 @@ namespace velos { point.y <= m_worldPosition.y + m_size.y; } - void AABBCollider::setSize(const Vec2 size) const { + void AABBCollider::setSize(const Vec2 size) { m_size = size; } + + void AABBCollider::debugRender(Renderer& renderer) const { + renderer.drawRect(m_worldPosition, m_size, Color{117, 145, 166, 100}); + } } diff --git a/engine/src/physics/circle_collider.cpp b/engine/src/physics/circle_collider.cpp index 2ed2aee..9246627 100644 --- a/engine/src/physics/circle_collider.cpp +++ b/engine/src/physics/circle_collider.cpp @@ -13,4 +13,8 @@ namespace velos { Vec2 delta = point - m_worldPosition; return delta.lengthSquared() <= m_radius * m_radius; } + + void CircleCollider::debugRender(Renderer& renderer) const { + renderer.drawCircle(m_worldPosition, m_radius, Color{117, 145, 166, 100}); + } } diff --git a/examples/sandbox/src/main.cpp b/examples/sandbox/src/main.cpp index 8af1cb5..295b68f 100644 --- a/examples/sandbox/src/main.cpp +++ b/examples/sandbox/src/main.cpp @@ -30,8 +30,7 @@ int main() { entity->addShape(rect); entity->transform().position = {200, 200}; - auto collider = std::make_unique(); - collider->m_size = {50.f, 50.f}; + auto collider = std::make_unique(size, velos::Vec2{0.f, 0.f} - size / 2.f); entity->addCollider(std::move(collider)); auto stationary_entity = std::make_shared(); @@ -74,5 +73,8 @@ int main() { engine.attachCamera(*entity); engine.camera()->setFollowMode(velos::CameraFollowMode::Smooth); + // For debugging collision zones + // engine.setDebugRender(true); + engine.run(); }