From fc2597302bce457f4937029a3e6c8c4f695381f1 Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Wed, 21 Dec 2016 21:49:10 +0200 Subject: [PATCH] Implemented some fixes to collider. Made pickables use collider. Partial implementation of stackable objects. --- src/components/basic/Collider.cpp | 22 ++++--- src/components/basic/Collider.h | 4 +- src/components/basic/InventoryItem.h | 3 + src/components/items/Pickable.h | 7 +- src/components/player/Player.cpp | 99 +++++++++++++++------------- src/components/player/Player.h | 4 +- src/controller/FarmlandsGame.cpp | 6 ++ src/controller/FarmlandsGame.h | 1 + src/model/GameObject.cpp | 98 ++++++++++++++++++++------- src/model/GameObject.h | 59 +++++++++++++---- src/resources/ResourceManager.cpp | 1 + src/storage/Parsers.cpp | 8 +++ 12 files changed, 221 insertions(+), 91 deletions(-) diff --git a/src/components/basic/Collider.cpp b/src/components/basic/Collider.cpp index 8dfea75..20a9cd8 100644 --- a/src/components/basic/Collider.cpp +++ b/src/components/basic/Collider.cpp @@ -67,7 +67,12 @@ void Collider::dump(unsigned level) for (unsigned i = 0; i < level; i++) std::cout<<" "; - std::cout << " .Component: Collider\n"; + std::cout << " .Component: Collider "; + std::cout << "x=" << collisionRect.x << " "; + std::cout << "y=" << collisionRect.y << " "; + std::cout << "w=" << collisionRect.w << " "; + std::cout << "h=" << collisionRect.h << " "; + std::cout << "solid=" << solid << "\n"; } void Collider::onInitialize() @@ -109,17 +114,18 @@ void Collider::onUpdateLogic() if (m_sprite) { auto it1 = qtree->find(this, oldBounds.x + oldBounds.w, oldBounds.y); - auto it2 = qtree->find(this, oldBounds.x, oldBounds.y + oldBounds.h); - auto it3 = qtree->find(this, oldBounds.x + oldBounds.w, oldBounds.y + oldBounds.h); - qtree->move(it1, m_lastBounds.x + m_lastBounds.w, m_lastBounds.y); + + auto it2 = qtree->find(this, oldBounds.x, oldBounds.y + oldBounds.h); qtree->move(it2, m_lastBounds.x, m_lastBounds.y + m_lastBounds.h); + + auto it3 = qtree->find(this, oldBounds.x + oldBounds.w, oldBounds.y + oldBounds.h); qtree->move(it3, m_lastBounds.x + m_lastBounds.w, m_lastBounds.y + m_lastBounds.h); } } } -Collider* Collider::checkCollision() +void Collider::checkCollisions(std::unordered_set& results) { auto qtree = GameState::current().colliderTree; Assert(qtree != nullptr, "Collider tree not set!"); @@ -128,11 +134,9 @@ Collider* Collider::checkCollision() for (auto it = qtree->lower_bound(searchArea); it != qtree->upper_bound(searchArea); it++) { - if (it->data != this && m_lastBounds.intersects(it->data->m_lastBounds)) - return it->data; + if (it->data != this && m_lastBounds.intersects(it->data->m_lastBounds) && results.count(it->data) == 0) + results.insert(it->data); } - - return nullptr; } bool Collider::canMove(float newX, float newY) diff --git a/src/components/basic/Collider.h b/src/components/basic/Collider.h index 38151b4..167c9ec 100644 --- a/src/components/basic/Collider.h +++ b/src/components/basic/Collider.h @@ -12,6 +12,8 @@ #include #include +#include + namespace farmlands { namespace components { namespace basic { @@ -35,7 +37,7 @@ namespace basic { /** * Checks if this object collides with another Collider object. */ - Collider* checkCollision(); + void checkCollisions(std::unordered_set& results); /** * Tests if this object can move to the given coordinates. diff --git a/src/components/basic/InventoryItem.h b/src/components/basic/InventoryItem.h index 05bac11..25dcb02 100644 --- a/src/components/basic/InventoryItem.h +++ b/src/components/basic/InventoryItem.h @@ -23,6 +23,9 @@ namespace basic { virtual void dump(unsigned level) override; size_t slot; + bool stackable; + unsigned stackSize; + unsigned maxStackSize; }; } /* namespace basic */ diff --git a/src/components/items/Pickable.h b/src/components/items/Pickable.h index 039bbc2..500db5e 100644 --- a/src/components/items/Pickable.h +++ b/src/components/items/Pickable.h @@ -27,11 +27,16 @@ namespace items { virtual ~Pickable() { } virtual model::Component* clone() override; virtual void dump(unsigned level) override; + + bool stackable; + unsigned maxStack; }; inline model::Component* Pickable::clone() { - return new Pickable(); + Pickable* clone = new Pickable(); + clone->stackable = stackable; + return clone; } inline void Pickable::dump(unsigned level) diff --git a/src/components/player/Player.cpp b/src/components/player/Player.cpp index 57efb41..1607bd4 100644 --- a/src/components/player/Player.cpp +++ b/src/components/player/Player.cpp @@ -127,7 +127,7 @@ void Player::onUpdateLogic() energy = clamp(energy, 0, maxEnergy); updateMovement(); - updatePickables(); + checkCollisions(); } void Player::onPreRender() @@ -293,20 +293,40 @@ void Player::updateMovement() // Compute movement positions float vx = Input::instance().getX() * velMultiplier; float vy = Input::instance().getY() * velMultiplier; - float newX = gameObject->transform.x + vx; - float newY = gameObject->transform.y + vy; - if ((vx || vy) && canMove(newX, newY)) + if (vx || vy) { - gameObject->transform.x = newX; - gameObject->transform.y = newY; + bool res = canMove(gameObject->transform.x + vx, gameObject->transform.y + vy); + if (!res && vx != 0) + { + // Try moving on the X axis + res = canMove(gameObject->transform.x + vx, gameObject->transform.y); + if (res) + vy = 0; + } + if (!res && vy != 0) + { + // Try moving on the Y axis + res = canMove(gameObject->transform.x, gameObject->transform.y + vy); + if (res) + vx = 0; + } - walking = true; - facingDirection = getDirection(vx, vy); + if (res) + { + float newX = gameObject->transform.x + vx; + float newY = gameObject->transform.y + vy; - lookX = newX; - lookY = newY; - move(&lookX, &lookY, facingDirection, PlayerLookDistance); + gameObject->transform.x = newX; + gameObject->transform.y = newY; + + walking = true; + facingDirection = getDirection(vx, vy); + + lookX = newX; + lookY = newY; + move(&lookX, &lookY, facingDirection, PlayerLookDistance); + } } } @@ -363,47 +383,36 @@ bool Player::canMove(float x, float y) return m_collider->canMove(x, y); } -void Player::updatePickables() +void Player::checkCollisions() { - // Don't do anything if inventory is full - if (m_inventory->emptySlots() <= 0) - return; + std::unordered_set objects; + m_collider->checkCollisions(objects); - std::vector toPickUp; - - for (auto it = m_pickables->childrenBegin(); it != m_pickables->childrenEnd(); it++) + for (auto collider : objects) { - GameObject* obj = *it; - Pickable* pickable = obj->component(); - - if (pickable) - { - if (checkCollision(gameObject, obj)) - { - // We can't modify the container now, so queue the item for picking up - toPickUp.push_back(obj); - } - // Compute distance from player -// float dist = distanceSq(gameObject->transform.x, gameObject->transform.y, obj->transform.x, obj->transform.y); - - } + if (collider->gameObject->haveComponent()) + pickUpObject(collider->gameObject); } +} - for (auto obj : toPickUp) - { - // Object no longer pickable - obj->destroyComponent(); +void Player::pickUpObject(GameObject* obj) +{ + Assert(obj != nullptr, "Can't pick up null."); + Assert(obj->haveComponent(), "Can't pick up a non-pickable."); - // Make object a child of player - m_pickables->removeChild(obj); - gameObject->addChild(obj); + // Object no longer pickable + obj->destroyComponent(); + obj->destroyComponent(); - // Add to inventory - m_inventory->add(obj); - obj->setEnabled(false); - obj->transform.x = 0; - obj->transform.y = 0; - } + // Make object a child of player + m_pickables->removeChild(obj); + gameObject->addChild(obj); + + // Add to inventory + m_inventory->add(obj); + obj->setEnabled(false); + obj->transform.x = 0; + obj->transform.y = 0; } } /* namespace player */ diff --git a/src/components/player/Player.h b/src/components/player/Player.h index 05d1833..d355ffa 100644 --- a/src/components/player/Player.h +++ b/src/components/player/Player.h @@ -77,8 +77,10 @@ namespace player { void handleActionEvents(SDL_Event& event); void performAction(model::GameObject* obj); + void checkCollisions(); + // Picking up - void updatePickables(); + void pickUpObject(model::GameObject* obj); basic::Collider* m_collider; diff --git a/src/controller/FarmlandsGame.cpp b/src/controller/FarmlandsGame.cpp index d815ce7..6251c96 100644 --- a/src/controller/FarmlandsGame.cpp +++ b/src/controller/FarmlandsGame.cpp @@ -96,6 +96,11 @@ void FarmlandsGame::onPostRender() GameState::current().scene->root.onPostRender(); } +void FarmlandsGame::onFrameEnded() +{ + GameState::current().scene->root.onFrameEnded(); +} + void FarmlandsGame::stop() { m_running = false; @@ -124,6 +129,7 @@ int FarmlandsGame::run() onPreRender(); onRender(); onPostRender(); + onFrameEnded(); } // Cleanup diff --git a/src/controller/FarmlandsGame.h b/src/controller/FarmlandsGame.h index 30887af..8172fac 100644 --- a/src/controller/FarmlandsGame.h +++ b/src/controller/FarmlandsGame.h @@ -30,6 +30,7 @@ namespace controller { void onPreRender(); void onRender(); void onPostRender(); + void onFrameEnded(); void stop(); diff --git a/src/model/GameObject.cpp b/src/model/GameObject.cpp index d88a736..44e010f 100644 --- a/src/model/GameObject.cpp +++ b/src/model/GameObject.cpp @@ -9,6 +9,8 @@ #include #include #include + +#include #include namespace farmlands { @@ -18,10 +20,12 @@ GameObject::GameObject() : name("unnamed"), visible(true), transform(this), - m_components(), - m_children(), + m_enabled(false), m_parent(nullptr), - m_enabled(false) + m_children(), + m_childRemoveQueue(), + m_components(), + m_componentRemoveQueue() { } @@ -105,42 +109,48 @@ void GameObject::addChild(GameObject* obj) GameObject* GameObject::removeChild(GameObject::ChildrenIterator it) { - m_children.erase(it); + ChildRemoveQueueItem item = + { + .data = *it, + .free = false + }; + m_childRemoveQueue.push_back(item); + (*it)->m_parent = nullptr; + return *it; } GameObject* GameObject::removeChild(GameObject* obj) { - for (auto it = m_children.begin(); it != m_children.end(); it++) + ChildRemoveQueueItem item = { - if (*it == obj) - { - m_children.erase(it); - obj->m_parent = nullptr; - return *it; - } - } + .data = obj, + .free = false + }; + m_childRemoveQueue.push_back(item); + obj->m_parent = nullptr; return nullptr; } void GameObject::destroyChild(GameObject::ChildrenIterator it) { - delete *it; - m_children.erase(it); + ChildRemoveQueueItem item = + { + .data = *it, + .free = true + }; + m_childRemoveQueue.push_back(item); } void GameObject::destroyChild(GameObject* obj) { - for (auto it = m_children.begin(); it != m_children.end(); it++) + ChildRemoveQueueItem item = { - if (*it == obj) - { - delete *it; - m_children.erase(it); - return; - } - } + .data = obj, + .free = true + }; + m_childRemoveQueue.push_back(item); } size_t GameObject::childrenSize() const @@ -264,6 +274,18 @@ void GameObject::onPostRender() for (auto child : m_children) if (child->m_enabled && child->visible) child->onPostRender(); + + // Process removals at end of frame + processRemovals(); +} + +void GameObject::onFrameEnded() +{ + // Call children + for (auto child : m_children) + child->onFrameEnded(); + + processRemovals(); } void GameObject::onEnable() @@ -332,6 +354,38 @@ void GameObject::dumpTree(unsigned level) child->dumpTree(level + 1); } +void GameObject::processRemovals() +{ + // Remove components/children at end of frame + for (auto item : m_childRemoveQueue) + { + auto it = std::find(m_children.begin(), m_children.end(), item.data); + if (it != m_children.end()) + { + m_children.erase(it); + if (item.free) + { + item.data->onDestroy(); + delete item.data; + } + } + } + m_childRemoveQueue.clear(); + + for (auto item : m_componentRemoveQueue) + { + m_components.erase(item.index); + + if (item.free) + { + item.data->onDestroy(); + delete item.data; + } + } + m_componentRemoveQueue.clear(); +} + } /* namespace model */ } /* namespace farmlands */ + diff --git a/src/model/GameObject.h b/src/model/GameObject.h index bba05c3..e0bd805 100644 --- a/src/model/GameObject.h +++ b/src/model/GameObject.h @@ -15,9 +15,10 @@ #include +#include #include -#include #include +#include namespace farmlands { namespace model { @@ -37,8 +38,11 @@ namespace model { // Constructors GameObject(); virtual ~GameObject(); + + // Cloneable virtual GameObject* clone() override; + // Static methods static GameObject* instantiate(GameObject* gameObject, std::string name, GameObject* parent); // Components API @@ -83,6 +87,7 @@ namespace model { void onPreRender(); void onRender(); void onPostRender(); + void onFrameEnded(); void onDestroy(); void onEnable(); @@ -98,18 +103,39 @@ namespace model { // Other properties std::string name; bool visible; + bool persistent; Transform transform; private: - // Components - ComponentContainer m_components; + struct ComponentRemoveQueueItem + { + std::type_index index; + Component* data; + bool free; + }; - // Tree - ChildrenContainer m_children; - GameObject* m_parent; + struct ChildRemoveQueueItem + { + GameObject* data; + bool free; + }; + + typedef std::vector ComponentRemoveQueue; + typedef std::vector ChildRemoveQueue; + + void processRemovals(); // Properties bool m_enabled; + + // Tree + GameObject* m_parent; + ChildrenContainer m_children; + ChildRemoveQueue m_childRemoveQueue; + + // Components + ComponentContainer m_components; + ComponentRemoveQueue m_componentRemoveQueue; }; template @@ -160,8 +186,14 @@ namespace model { auto it = m_components.find(typeIndex); if (it != m_components.end()) { - comp = dynamic_cast(it->second); - m_components.erase(it); + ComponentRemoveQueueItem item = + { + .index = typeIndex, + .data = it->second, + .free = false + }; + m_componentRemoveQueue.push_back(item); + // it->second->gameObject = nullptr; } return comp; @@ -176,10 +208,13 @@ namespace model { auto it = m_components.find(typeIndex); if (it != m_components.end()) { - T* comp = dynamic_cast(it->second); - m_components.erase(it); - - delete comp; + ComponentRemoveQueueItem item = + { + .index = typeIndex, + .data = it->second, + .free = true + }; + m_componentRemoveQueue.push_back(item); } } diff --git a/src/resources/ResourceManager.cpp b/src/resources/ResourceManager.cpp index 5b94022..d0488ef 100644 --- a/src/resources/ResourceManager.cpp +++ b/src/resources/ResourceManager.cpp @@ -107,6 +107,7 @@ void ResourceManager::loadGame() item->transform.x = x; item->transform.y = y; item->addComponent(new components::items::Pickable()); + item->addComponent(new components::basic::Collider()); } for (auto prefab : GameState::current().seedsPrefabs) diff --git a/src/storage/Parsers.cpp b/src/storage/Parsers.cpp index d90808a..ab8ddc9 100644 --- a/src/storage/Parsers.cpp +++ b/src/storage/Parsers.cpp @@ -196,6 +196,9 @@ components::basic::InventoryItem* parse (boost components::basic::InventoryItem* item = new components::basic::InventoryItem(); item->slot = root.get(".slot"); + item->stackable = root.get(".stackable", false); + item->stackSize = root.get(".stackSize", 1); + item->maxStackSize = root.get(".maxStackSize", 1); return item; } @@ -367,6 +370,10 @@ components::items::Pickable* parse (boost::property if (root.size() > 0 && root.front().first == "Pickaxe") root = root.front().second; + components::items::Pickable* pickable = new components::items::Pickable(); + pickable->stackable = root.get(".stackable", false); + pickable->maxStack = root.get(".maxStack", 1); + // Parse return new components::items::Pickable(); } @@ -581,6 +588,7 @@ model::GameObject* parse (boost::property_tree::ptree& root) model::GameObject* gameObj = new model::GameObject(); gameObj->name = root.get(".name"); gameObj->visible = root.get(".visible", true); + gameObj->persistent = root.get(".persistent", false); gameObj->setEnabled(root.get(".enabled", true)); for (auto child : root)