diff --git a/docs/USAGE.md b/docs/USAGE.md index 329c919..7d829c7 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -9,6 +9,11 @@ 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)); + +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 b8e892e..0170198 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,24 @@ 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); + void debugRender(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/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 new file mode 100644 index 0000000..08a979c --- /dev/null +++ b/engine/include/velos/physics/aabb_collider.h @@ -0,0 +1,21 @@ +#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); + void debugRender(Renderer& renderer) const override; + + 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..91a152f --- /dev/null +++ b/engine/include/velos/physics/circle_collider.h @@ -0,0 +1,21 @@ +#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; + + 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 new file mode 100644 index 0000000..d596ac4 --- /dev/null +++ b/engine/include/velos/physics/collider.h @@ -0,0 +1,23 @@ +#pragma once +#include "../math/transform.h" +#include "../graphics/renderer.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; + + 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 c8b7ae2..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); @@ -19,4 +24,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/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 new file mode 100644 index 0000000..caff0aa --- /dev/null +++ b/engine/src/physics/aabb_collider.cpp @@ -0,0 +1,26 @@ +#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) { + 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 new file mode 100644 index 0000000..9246627 --- /dev/null +++ b/engine/src/physics/circle_collider.cpp @@ -0,0 +1,20 @@ +#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; + } + + 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 d66b15f..295b68f 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,9 @@ int main() { entity->addShape(rect); entity->transform().position = {200, 200}; + 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(); stationary_entity->addShape(circle); @@ -69,5 +73,8 @@ int main() { engine.attachCamera(*entity); engine.camera()->setFollowMode(velos::CameraFollowMode::Smooth); + // For debugging collision zones + // engine.setDebugRender(true); + engine.run(); }