diff --git a/include/game/agame/enemy.hpp b/include/game/agame/enemy.hpp index c4c08b6..9c43da6 100644 --- a/include/game/agame/enemy.hpp +++ b/include/game/agame/enemy.hpp @@ -8,5 +8,7 @@ namespace Game::AGame { GAME_ENTITY(Enemy) + public: + void onCollisionEnter(Object::Entity* other) override; END_GAME_ENTITY() } \ No newline at end of file diff --git a/include/object/components/boxcollider.hpp b/include/object/components/boxcollider.hpp index da6f66b..b0dca7f 100644 --- a/include/object/components/boxcollider.hpp +++ b/include/object/components/boxcollider.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace Game::Object::Components { struct BoxColliderBounds { @@ -21,10 +22,10 @@ namespace Game::Object::Components { void update(float deltaTime, Object::Entity* thisEntity) override; BoxColliderBounds getBounds() const { return mBounds; } - bool isColliding() const { return mIsColliding; } + bool isColliding() const { return !mCollidingWith.empty(); } private: - BoxColliderBounds mBounds; - bool mIsColliding = false; // Whether this collider is currently colliding with another collider; used to determine whether to call onCollisionEnter vs onCollisionStay, and to call onCollisionExit when collisions end + BoxColliderBounds mBounds{0.f, 0.f, 0.f, 0.f}; + std::unordered_set mCollidingWith; // Track collisions per-entity so enter/stay/exit callbacks remain correct with multiple colliders }; } \ No newline at end of file diff --git a/src/game/agame/enemy.cpp b/src/game/agame/enemy.cpp index e814e61..a69c36d 100644 --- a/src/game/agame/enemy.cpp +++ b/src/game/agame/enemy.cpp @@ -1,11 +1,19 @@ #include +#include namespace Game::AGame { void Enemy::start() { mZIndex = 20; + addComponent(); + LOG("Enemy started: " << getName()); } void Enemy::update(float deltaTime) { return; } + + void Enemy::onCollisionEnter(Object::Entity* other) { + LOG("Enemy '" << getName() << "' collided with '" << other->getName() << "' (onCollisionEnter); Killing myself now!"); + GameManager::setSharedData("enemyActiveCount", GameManager::getSharedData("enemyActiveCount") - 1); + } } \ No newline at end of file diff --git a/src/object/components/boxcollider.cpp b/src/object/components/boxcollider.cpp index 8fb9f15..07a966d 100644 --- a/src/object/components/boxcollider.cpp +++ b/src/object/components/boxcollider.cpp @@ -1,5 +1,8 @@ #include #include +#include + +#include namespace Game::Object::Components { BoxCollider::BoxCollider(const BoxCollider& other) : Component(other) { @@ -32,44 +35,68 @@ namespace Game::Object::Components { void BoxCollider::update(float deltaTime, Object::Entity* thisEntity) { // Collision detection and response logic can go here + (void)deltaTime; + + if (!thisEntity) { + return; + } // Make box bounds Transform* transform = thisEntity->getTransform(); - float halfWidth = transform->scaleX * UNIVERSAL_SCALE_COEFFICIENT * 0.5f; - float halfHeight = transform->scaleY * UNIVERSAL_SCALE_COEFFICIENT * 0.5f; - float left = transform->x - halfWidth; - float right = transform->x + halfWidth; - float top = transform->y - halfHeight; - float bottom = transform->y + halfHeight; + if (!transform) { + return; + } + + float width = 1.f; + float height = 1.f; + if (const auto tex = thisEntity->getTexture()) { + width = tex->getWidth() * transform->scaleX * UNIVERSAL_SCALE_COEFFICIENT; + height = tex->getHeight() * transform->scaleY * UNIVERSAL_SCALE_COEFFICIENT; + } else { + width = transform->scaleX * UNIVERSAL_SCALE_COEFFICIENT; + height = transform->scaleY * UNIVERSAL_SCALE_COEFFICIENT; + } + width = std::max(1.f, width); + height = std::max(1.f, height); + + // Transform position is used as top-left in rendering, so match that convention for collision bounds. + float left = transform->x; + float right = transform->x + width; + float top = transform->y; + float bottom = transform->y + height; mBounds = {left, right, top, bottom}; // Check for collisions with other entities that have box colliders // For simplicity, just check each + std::unordered_set currentCollisions; std::vector entities = State::GameState::getInstance().getEntitiesSnapshot(); for (Entity* other : entities) { - if (other == thisEntity) continue; // Don't check collision with self + if (!other || other == thisEntity || !other->isActive()) continue; // Don't check collision with self/inactive entities BoxCollider* otherCollider = other->getComponent(); - if (!otherCollider) continue; // No collider, skip + if (!otherCollider || !otherCollider->isActive()) continue; // No active collider, skip BoxColliderBounds otherBounds = otherCollider->getBounds(); - if (left < otherBounds.right && right > otherBounds.left && top < otherBounds.bottom && bottom > otherBounds.top) { + bool isOverlapping = left < otherBounds.right && right > otherBounds.left && top < otherBounds.bottom && bottom > otherBounds.top; + if (isOverlapping) { + currentCollisions.insert(other); // Collision detected - if (!mIsColliding) { - mIsColliding = true; + if (!mCollidingWith.contains(other)) { thisEntity->onCollisionEnter(other); - other->onCollisionEnter(thisEntity); + //other->onCollisionEnter(thisEntity); } else { thisEntity->onCollisionStay(other); - other->onCollisionStay(thisEntity); - } - } else { - // No collision - if (mIsColliding) { - mIsColliding = false; - thisEntity->onCollisionExit(other); - other->onCollisionExit(thisEntity); + //other->onCollisionStay(thisEntity); } } } + + for (Entity* wasColliding : mCollidingWith) { + if (!currentCollisions.contains(wasColliding)) { + thisEntity->onCollisionExit(wasColliding); + //wasColliding->onCollisionExit(thisEntity); + } + } + + mCollidingWith = std::move(currentCollisions); } } \ No newline at end of file