Implemented some fixes to collider. Made pickables use collider. Partial implementation of stackable objects.

This commit is contained in:
Tiberiu Chibici 2016-12-21 21:49:10 +02:00
parent a7af100122
commit fc2597302b
12 changed files with 221 additions and 91 deletions

View File

@ -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<Collider*>& 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)

View File

@ -12,6 +12,8 @@
#include <model/Component.h>
#include <utils/QTree.h>
#include <unordered_set>
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<Collider*>& results);
/**
* Tests if this object can move to the given coordinates.

View File

@ -23,6 +23,9 @@ namespace basic {
virtual void dump(unsigned level) override;
size_t slot;
bool stackable;
unsigned stackSize;
unsigned maxStackSize;
};
} /* namespace basic */

View File

@ -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)

View File

@ -127,7 +127,7 @@ void Player::onUpdateLogic()
energy = clamp(energy, 0, maxEnergy);
updateMovement();
updatePickables();
checkCollisions();
}
void Player::onPreRender()
@ -293,11 +293,30 @@ void Player::updateMovement()
// Compute movement positions
float vx = Input::instance().getX() * velMultiplier;
float vy = Input::instance().getY() * velMultiplier;
if (vx || vy)
{
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;
}
if (res)
{
float newX = gameObject->transform.x + vx;
float newY = gameObject->transform.y + vy;
if ((vx || vy) && canMove(newX, newY))
{
gameObject->transform.x = newX;
gameObject->transform.y = newY;
@ -309,6 +328,7 @@ void Player::updateMovement()
move(&lookX, &lookY, facingDirection, PlayerLookDistance);
}
}
}
Direction Player::getDirection(float vx, float vy)
{
@ -363,36 +383,26 @@ 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<Collider*> objects;
m_collider->checkCollisions(objects);
std::vector<GameObject*> toPickUp;
for (auto it = m_pickables->childrenBegin(); it != m_pickables->childrenEnd(); it++)
for (auto collider : objects)
{
GameObject* obj = *it;
Pickable* pickable = obj->component<Pickable>();
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<Pickable>())
pickUpObject(collider->gameObject);
}
}
for (auto obj : toPickUp)
void Player::pickUpObject(GameObject* obj)
{
Assert(obj != nullptr, "Can't pick up null.");
Assert(obj->haveComponent<Pickable>(), "Can't pick up a non-pickable.");
// Object no longer pickable
obj->destroyComponent<Pickable>();
obj->destroyComponent<Collider>();
// Make object a child of player
m_pickables->removeChild(obj);
@ -404,7 +414,6 @@ void Player::updatePickables()
obj->transform.x = 0;
obj->transform.y = 0;
}
}
} /* namespace player */
} /* namespace components */

View File

@ -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;

View File

@ -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

View File

@ -30,6 +30,7 @@ namespace controller {
void onPreRender();
void onRender();
void onPostRender();
void onFrameEnded();
void stop();

View File

@ -9,6 +9,8 @@
#include <model/GameObject.h>
#include <model/Component.h>
#include <model/Transform.h>
#include <algorithm>
#include <iostream>
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);
.data = obj,
.free = false
};
m_childRemoveQueue.push_back(item);
obj->m_parent = nullptr;
return *it;
}
}
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 */

View File

@ -15,9 +15,10 @@
#include <SDL2/SDL.h>
#include <queue>
#include <typeindex>
#include <vector>
#include <unordered_map>
#include <vector>
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<ComponentRemoveQueueItem> ComponentRemoveQueue;
typedef std::vector<ChildRemoveQueueItem> 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 <typename T>
@ -160,8 +186,14 @@ namespace model {
auto it = m_components.find(typeIndex);
if (it != m_components.end())
{
comp = dynamic_cast<T*>(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<T*>(it->second);
m_components.erase(it);
delete comp;
ComponentRemoveQueueItem item =
{
.index = typeIndex,
.data = it->second,
.free = true
};
m_componentRemoveQueue.push_back(item);
}
}

View File

@ -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)

View File

@ -196,6 +196,9 @@ components::basic::InventoryItem* parse<components::basic::InventoryItem> (boost
components::basic::InventoryItem* item = new components::basic::InventoryItem();
item->slot = root.get<size_t>("<xmlattr>.slot");
item->stackable = root.get<bool>("<xmlattr>.stackable", false);
item->stackSize = root.get<unsigned>("<xmlattr>.stackSize", 1);
item->maxStackSize = root.get<unsigned>("<xmlattr>.maxStackSize", 1);
return item;
}
@ -367,6 +370,10 @@ components::items::Pickable* parse<components::items::Pickable> (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<bool>("<xmlattr>.stackable", false);
pickable->maxStack = root.get<unsigned>("<xmlattr>.maxStack", 1);
// Parse
return new components::items::Pickable();
}
@ -581,6 +588,7 @@ model::GameObject* parse<model::GameObject> (boost::property_tree::ptree& root)
model::GameObject* gameObj = new model::GameObject();
gameObj->name = root.get<std::string>("<xmlattr>.name");
gameObj->visible = root.get<bool>("<xmlattr>.visible", true);
gameObj->persistent = root.get<bool>("<xmlattr>.persistent", false);
gameObj->setEnabled(root.get<bool>("<xmlattr>.enabled", true));
for (auto child : root)