Implemented some fixes to collider. Made pickables use collider. Partial implementation of stackable objects.
This commit is contained in:
parent
a7af100122
commit
fc2597302b
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -23,6 +23,9 @@ namespace basic {
|
||||
virtual void dump(unsigned level) override;
|
||||
|
||||
size_t slot;
|
||||
bool stackable;
|
||||
unsigned stackSize;
|
||||
unsigned maxStackSize;
|
||||
};
|
||||
|
||||
} /* namespace basic */
|
||||
|
@ -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)
|
||||
|
@ -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<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)
|
||||
{
|
||||
// Object no longer pickable
|
||||
obj->destroyComponent<Pickable>();
|
||||
void Player::pickUpObject(GameObject* obj)
|
||||
{
|
||||
Assert(obj != nullptr, "Can't pick up null.");
|
||||
Assert(obj->haveComponent<Pickable>(), "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<Pickable>();
|
||||
obj->destroyComponent<Collider>();
|
||||
|
||||
// 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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -30,6 +30,7 @@ namespace controller {
|
||||
void onPreRender();
|
||||
void onRender();
|
||||
void onPostRender();
|
||||
void onFrameEnded();
|
||||
|
||||
void stop();
|
||||
|
||||
|
@ -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);
|
||||
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 */
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user