Implemented picking up objects and dropping them. Also, made transform part of GameObject instead of a separate component.

This commit is contained in:
Tiberiu Chibici 2016-12-14 23:32:39 +02:00
parent f8571b36bd
commit ddae4934ef
23 changed files with 237 additions and 180 deletions

View File

@ -32,6 +32,10 @@
money="50" />
</GameObject>
<GameObject name="Pickables">
<Transform />
</GameObject>
<!-- Debug object -->
<GameObject name="Debug">
<DebugController />

View File

@ -6,7 +6,6 @@
*/
#include <components/basic/Grid.h>
#include <components/basic/Transform.h>
#include <utils/Assert.h>
#include <utils/Exceptions.h>
@ -60,16 +59,8 @@ void Grid::onInitialize()
{
GameObject* obj = *it;
// Get transform
Transform* tr = obj->component<Transform>();
if (tr == nullptr)
{
std::cerr << "Grid: ignoring object " << obj->name << ": object has no transform.";
continue;
}
// Compute grid position(s)
Rect<int> bounds(tr->x, tr->y, roundf(tr->w), roundf(tr->h));
Rect<int> bounds(obj->transform.x, obj->transform.y, roundf(obj->transform.w), roundf(obj->transform.h));
if (!bounds.intersects(m_bounds))
{
std::cerr << "Grid: ignoring object " << obj->name << ": object outside allowed bounds.";
@ -139,11 +130,8 @@ void Grid::set(model::GameObject* obj, int x, int y, bool throwOnOverwrite)
}
m_grid[index] = obj;
// Set transform as well
Transform* transf = obj->component<Transform>();
transf->x = x;
transf->y = y;
obj->transform.x = x;
obj->transform.y = y;
}
} /* namespace basic */

View File

@ -142,7 +142,17 @@ void Inventory::setCapacity(size_t capacity)
m_capacity = capacity;
}
size_t Inventory::emptySlots() const
{
size_t count = 0;
for (size_t i = 0; i < m_capacity; i++)
if (m_items[i] == nullptr)
++count;
return count;
}
} /* namespace basic */
} /* namespace components */
} /* namespace farmlands */

View File

@ -65,6 +65,11 @@ namespace basic {
*/
size_t capacity() const { return m_capacity; }
/**
* Gets the number of empty slots.
*/
size_t emptySlots() const;
private:
size_t m_capacity;
model::GameObject** m_items;

View File

@ -6,7 +6,6 @@
*/
#include <GameState.h>
#include <components/basic/Transform.h>
#include <components/basic/Sprite.h>
#include <utils/Assert.h>

View File

@ -1,80 +0,0 @@
/*
* Transform.cpp
*
* Created on: Dec 2, 2016
* Author: tibi
*/
#include <model/GameObject.h>
#include <components/basic/Transform.h>
#include <iostream>
namespace farmlands {
namespace components {
namespace basic {
Transform::Transform()
: x(0), y(0),
w(0), h(0),
m_parent(nullptr)
{
}
Transform::~Transform()
{
}
model::Component* Transform::clone()
{
Transform* clone = new Transform();
clone->x = x;
clone->y = y;
clone->w = w;
clone->h = h;
return clone;
}
void Transform::onCreate()
{
if (gameObject->parent() != nullptr)
m_parent = gameObject->parent()->component<Transform>();
}
float Transform::globalX() const
{
return (m_parent) ? m_parent->globalX() + x : x;
}
float Transform::globalY() const
{
return (m_parent) ? m_parent->globalY() + y : y;
}
void Transform::setGlobalX(float x)
{
this->x = (m_parent) ? x - m_parent->globalX() : x;
}
void Transform::setGlobalY(float y)
{
this->y = (m_parent) ? y - m_parent->globalY() : y;
}
void Transform::dump(unsigned level)
{
for (unsigned i = 0; i < level; i++)
std::cout<<" ";
std::cout << " .Component: Transform ";
std::cout << "x="<<x<<" ";
std::cout << "y="<<y<<" ";
std::cout << "w="<<w<<" ";
std::cout << "h="<<h<<"\n";
}
}
}
} /* namespace farmlands */

View File

@ -6,6 +6,7 @@
*/
#include <GameState.h>
#include <components/items/Pickable.h>
#include <components/player/Player.h>
#include <input/Input.h>
#include <math/GameMath.h>
@ -26,6 +27,8 @@ static const float PlayerWalkVelocity = 2.0f; // The default velocity of the p
static const float PlayerRunVelocity = 4.0f; // The default velocity of the player when running (units/sec).
static const float PlayerAttackVelocity = 0.1f; // Movement speed when attacking.
static const float PickablePickTreshold = 0.7f;
/**
* Distance from player position to "look" position.
* This position is used for picking the cell which will be affected by the player's actions.
@ -54,9 +57,9 @@ Player::Player()
hp(100), maxHp(100),
energy(100), maxEnergy(100),
money(0),
m_transform(nullptr),
m_inventory(nullptr),
m_grid(nullptr)
m_grid(nullptr),
m_pickables(nullptr)
{
}
@ -101,15 +104,20 @@ void Player::dump(unsigned level)
void Player::onInitialize()
{
m_transform = gameObject->component<Transform>();
m_inventory = gameObject->component<Inventory>();
// Search for the object which has the grid
auto root = &GameState::current().scene->root;
// Search for the object which has the grid
auto gridIt = root->findByComponent<Grid>();
Assert(gridIt != root->childrenEnd(), "Cannot find grid component.");
m_grid = (*gridIt)->component<Grid>();
// Search for the object named "Pickables"
auto pickablesIt = root->findByName("Pickables");
Assert(pickablesIt != root->childrenEnd(), "Cannot find grid component.");
m_pickables = *pickablesIt;
initializeInventory();
}
@ -129,6 +137,7 @@ void Player::onUpdateLogic()
energy = clamp(energy, 0, maxEnergy);
updateMovement();
updatePickables();
}
void Player::onPreRender()
@ -165,10 +174,19 @@ void Player::handleInventoryDropEvent(SDL_Event& event)
if (inventoryDrop && selectedItemIndex >= 0)
{
// TODO: convert to 'pickable' item instead of destroying
// Remove object and add it to "pickables"
m_inventory->remove(static_cast<size_t>(selectedItemIndex));
gameObject->destroyChild(selectedItem);
gameObject->removeChild(selectedItem);
m_pickables->addChild(selectedItem);
// Make pickable
selectedItem->addComponent(new Pickable());
// Set location
translate(gameObject->transform.x, gameObject->transform.y, facingDirection, 2.0f,
&selectedItem->transform.x, &selectedItem->transform.y);
// Remove selection
selectedItemIndex = -1;
selectedItem = nullptr;
selectedWeapon = nullptr;
@ -213,17 +231,15 @@ void Player::preRenderSelectedItem()
// Set item position
if (selectedItem)
{
basic::Transform* itemTransf = selectedItem->component<basic::Transform>();
if (facingDirection & Direction::East)
{
itemTransf->x = 0.2f;
itemTransf->y = -0.8f;
selectedItem->transform.x = 0.2f;
selectedItem->transform.y = -0.8f;
}
else if (facingDirection & Direction::West)
{
itemTransf->x = -0.8f;
itemTransf->y = -0.8f;
selectedItem->transform.x = -0.8f;
selectedItem->transform.y = -0.8f;
}
}
}
@ -256,9 +272,9 @@ void Player::preRenderMovement()
sprite->animationVelocity = animVelocity;
// Set camera
Transform* cam = GameState::current().renderContext.cameraTransform();
cam->x = m_transform->x;
cam->y = m_transform->y - 1;
GameObject* cam = GameState::current().renderContext.cameraObj();
cam->transform.x = gameObject->transform.x;
cam->transform.y = gameObject->transform.y - 1;
}
void Player::updateMovement()
@ -279,13 +295,13 @@ void Player::updateMovement()
// Compute movement positions
float vx = Input::instance().getX() * velMultiplier;
float vy = Input::instance().getY() * velMultiplier;
float newX = m_transform->x + vx;
float newY = m_transform->y + vy;
float newX = gameObject->transform.x + vx;
float newY = gameObject->transform.y + vy;
if ((vx || vy) && canMove(newX, newY))
{
m_transform->x = newX;
m_transform->y = newY;
gameObject->transform.x = newX;
gameObject->transform.y = newY;
walking = true;
facingDirection = getDirection(vx, vy);
@ -343,6 +359,49 @@ void Player::performAction(model::GameObject* obj)
}
}
void Player::updatePickables()
{
// Don't do anything if inventory is full
if (m_inventory->emptySlots() <= 0)
return;
std::vector<GameObject*> toPickUp;
for (auto it = m_pickables->childrenBegin(); it != m_pickables->childrenEnd(); it++)
{
GameObject* obj = *it;
Pickable* pickable = obj->component<Pickable>();
if (pickable)
{
// Compute distance from player
float dist = distanceSq(gameObject->transform.x, gameObject->transform.y, obj->transform.x, obj->transform.y);
// Pick the object up
if (dist < PickablePickTreshold * PickablePickTreshold)
{
// We can't modify the container now, so queue the item for picking up
toPickUp.push_back(obj);
}
}
}
for (auto obj : toPickUp)
{
// Object no longer pickable
obj->destroyComponent<Pickable>();
// 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 */
} /* namespace components */

View File

@ -10,7 +10,6 @@
#include <components/basic/Grid.h>
#include <components/basic/Inventory.h>
#include <components/basic/Transform.h>
#include <components/items/Weapon.h>
#include <model/Component.h>
#include <model/Direction.h>
@ -18,6 +17,8 @@
#include <SDL2/SDL.h>
#include <vector>
namespace farmlands {
namespace components {
namespace player {
@ -75,9 +76,13 @@ namespace player {
void handleActionEvents(SDL_Event& event);
void performAction(model::GameObject* obj);
basic::Transform* m_transform;
// Picking up
void updatePickables();
basic::Inventory* m_inventory;
basic::Grid* m_grid;
model::GameObject* m_pickables;
};
} /* namespace player */

View File

@ -58,10 +58,10 @@ void MapRenderer::onRender()
float cellsOnScreenX = m_context->viewport.width / cellW;
float cellsOnScreenY = m_context->viewport.height / cellH;
int minCellX = floorf(m_context->cameraTransform()->x - cellsOnScreenX / 2);
int maxCellX = ceilf(m_context->cameraTransform()->x + cellsOnScreenX / 2);
int minCellY = floorf(m_context->cameraTransform()->y - cellsOnScreenY / 2);
int maxCellY = ceilf(m_context->cameraTransform()->y + cellsOnScreenY / 2);
int minCellX = floorf(m_context->cameraObj()->transform.x - cellsOnScreenX / 2);
int maxCellX = ceilf(m_context->cameraObj()->transform.x + cellsOnScreenX / 2);
int minCellY = floorf(m_context->cameraObj()->transform.y - cellsOnScreenY / 2);
int maxCellY = ceilf(m_context->cameraObj()->transform.y + cellsOnScreenY / 2);
// Clamp cell positions
minCellX = clamp(minCellX, 0, (int)m_map->width - 1);

View File

@ -10,7 +10,6 @@
#include <model/Component.h>
#include <components/basic/Camera.h>
#include <components/basic/Transform.h>
#include <components/Map.h>
#include <graphics/RenderContext.h>

View File

@ -6,7 +6,6 @@
*/
#include <components/basic/Camera.h>
#include <components/basic/Transform.h>
#include <graphics/RenderContext.h>
#include <model/GameObject.h>
@ -19,25 +18,25 @@ namespace graphics {
float RenderContext::xToWorld(float x)
{
float cellW = viewport.pixelsPerUnitX * m_camera->scale;
return (x - viewport.width / 2) / cellW + m_cameraTransform->x;
return (x - viewport.width / 2) / cellW + m_cameraObj->transform.x;
}
float RenderContext::yToWorld(float y)
{
float cellH = viewport.pixelsPerUnitY * m_camera->scale;
return (y - viewport.height / 2) / cellH + m_cameraTransform->y;
return (y - viewport.height / 2) / cellH + m_cameraObj->transform.y;
}
float RenderContext::xToScreen(float x)
{
float cellW = viewport.pixelsPerUnitX * m_camera->scale;
return (x - m_cameraTransform->x) * cellW + viewport.width / 2;
return (x - m_cameraObj->transform.x) * cellW + viewport.width / 2;
}
float RenderContext::yToScreen(float y)
{
float cellH = viewport.pixelsPerUnitY * m_camera->scale;
return (y - m_cameraTransform->y) * cellH + viewport.height / 2;
return (y - m_cameraObj->transform.y) * cellH + viewport.height / 2;
}
bool RenderContext::visible(SDL_Rect& rect)
@ -49,7 +48,6 @@ bool RenderContext::visible(SDL_Rect& rect)
void RenderContext::setCamera(GameObject* camera)
{
m_cameraObj = camera;
m_cameraTransform = camera->component<Transform>();
m_camera = camera->component<Camera>();
}

View File

@ -8,6 +8,7 @@
#ifndef GRAPHICS_RENDERCONTEXT_H_
#define GRAPHICS_RENDERCONTEXT_H_
#include <model/Transform.h>
#include <model/Viewport.h>
#include <SDL2/SDL.h>
@ -32,8 +33,6 @@ namespace graphics {
inline model::GameObject* cameraObj() { return m_cameraObj; }
inline components::basic::Camera* camera() { return m_camera; }
inline components::basic::Transform* cameraTransform() { return m_cameraTransform; }
void setCamera(model::GameObject* camera);
/**
@ -44,7 +43,6 @@ namespace graphics {
private:
model::GameObject* m_cameraObj;
components::basic::Transform* m_cameraTransform;
components::basic::Camera* m_camera;
};

View File

@ -23,7 +23,6 @@ namespace graphics {
SpriteRenderer::SpriteRenderer()
: m_context(nullptr),
m_transform(nullptr),
m_sprite(nullptr)
{
}
@ -40,14 +39,13 @@ model::Component* SpriteRenderer::clone()
void SpriteRenderer::onInitialize()
{
m_context = &GameState::current().renderContext;
m_transform = gameObject->component<components::basic::Transform>();
m_sprite = gameObject->component<Sprite>();
}
void SpriteRenderer::onRender()
{
float posX = m_context->xToScreen(m_transform->globalX());
float posY = m_context->yToScreen(m_transform->globalY());
float posX = m_context->xToScreen(gameObject->transform.globalX());
float posY = m_context->yToScreen(gameObject->transform.globalY());
float w = m_sprite->currentFrame().width * m_context->viewport.pixelsPerUnitX;
float h = m_sprite->currentFrame().height * m_context->viewport.pixelsPerUnitY;

View File

@ -11,7 +11,6 @@
#include <model/Component.h>
#include <graphics/RenderContext.h>
#include <components/basic/Sprite.h>
#include <components/basic/Transform.h>
#include <SDL2/SDL.h>
@ -35,7 +34,6 @@ namespace graphics {
// Private fields
graphics::RenderContext* m_context;
components::basic::Transform* m_transform;
components::basic::Sprite* m_sprite;
};

View File

@ -6,6 +6,7 @@
*/
#include <math/GameMath.h>
#include <cmath>
namespace farmlands {
@ -36,4 +37,16 @@ void translate(float x, float y, model::Direction direction, float distance, flo
*outY = y + dy * distance;
}
void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed)
{
float angle = atan2f(towardsX - *x, towardsY - *y);
*x += cosf(angle) * speed;
*y += sinf(angle) * speed;
}
float distanceSq(float x0, float y0, float x1, float y1)
{
return (x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1);
}
}

View File

@ -24,7 +24,9 @@ namespace farmlands {
return value;
}
void translate(float x, float y, model::Direction direction, float distance, float* outX, float *outY);
void translate(float x, float y, model::Direction direction, float distance, float* outX, float* outY);
void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed);
float distanceSq(float x0, float y0, float x1, float y1);
}
#endif /* MATH_GAMEMATH_H_ */

View File

@ -17,6 +17,7 @@ namespace model {
GameObject::GameObject()
: name("unnamed"),
visible(true),
transform(this),
m_components(),
m_children(),
m_parent(nullptr),
@ -115,6 +116,7 @@ GameObject* GameObject::removeChild(GameObject* obj)
if (*it == obj)
{
m_children.erase(it);
obj->m_parent = nullptr;
return *it;
}
}

View File

@ -8,6 +8,7 @@
#ifndef GAMEOBJECT_H_
#define GAMEOBJECT_H_
#include <model/Transform.h>
#include <utils/Assert.h>
#include <utils/ICloneable.h>
#include <utils/INonAssignable.h>
@ -97,6 +98,7 @@ namespace model {
// Other properties
std::string name;
bool visible;
Transform transform;
private:
// Components
@ -169,7 +171,16 @@ namespace model {
void GameObject::destroyComponent()
{
std::type_index typeIndex(typeid(T));
m_components.erase(typeIndex);
T* comp = nullptr;
auto it = m_components.find(typeIndex);
if (it != m_components.end())
{
T* comp = dynamic_cast<T*>(it->second);
m_components.erase(it);
delete comp;
}
}
template <typename T>

58
src/model/Transform.cpp Normal file
View File

@ -0,0 +1,58 @@
/*
* Transform.cpp
*
* Created on: Dec 2, 2016
* Author: tibi
*/
#include <model/GameObject.h>
#include <model/Transform.h>
#include <iostream>
namespace farmlands {
namespace model {
Transform::Transform(GameObject* obj)
: x(0), y(0),
w(0), h(0),
gameObject(obj)
{
}
float Transform::globalX() const
{
if (gameObject->parent())
return gameObject->parent()->transform.globalX() + x;
return x;
}
float Transform::globalY() const
{
if (gameObject->parent())
return gameObject->parent()->transform.globalY() + y;
return y;
}
void Transform::setGlobalX(float x)
{
if (gameObject->parent())
this->x = x - gameObject->parent()->transform.globalX();
this->x = x;
}
void Transform::setGlobalY(float y)
{
if (gameObject->parent())
this->y = y - gameObject->parent()->transform.globalY();
this->y = y;
}
}
} /* namespace farmlands */

View File

@ -8,22 +8,15 @@
#ifndef BASE_TRANSFORM_H_
#define BASE_TRANSFORM_H_
#include <model/Component.h>
namespace farmlands {
namespace components {
namespace basic {
namespace model {
class Transform: public model::Component
class GameObject;
class Transform
{
public:
Transform();
virtual ~Transform();
virtual model::Component* clone() override;
virtual void dump(unsigned level) override;
virtual void onCreate();
Transform(GameObject* obj);
// Getters
float globalX() const;
@ -37,11 +30,9 @@ namespace basic {
float x, y;
float w, h;
private:
Transform* m_parent;
GameObject* gameObject;
};
}
}
} /* namespace farmlands */

View File

@ -95,11 +95,18 @@ void ResourceManager::loadGame()
model::GameObject* player = *root->findByName("Player");
components::basic::Inventory* inventory = player->component<components::basic::Inventory>();
model::GameObject* pickables = *root->findByName("Pickables");
// Give player all items
for (auto prefab : GameState::current().itemPrefabs)
{
model::GameObject* item = model::GameObject::instantiate(prefab, "inv item", player);
inventory->add(item);
float x = player->transform.x + 15 * GameState::current().random.getFloat();
float y = player->transform.y + 15 * GameState::current().random.getFloat();
model::GameObject* item = model::GameObject::instantiate(prefab, "Pickable item", pickables);
item->transform.x = x;
item->transform.y = y;
item->addComponent(new components::items::Pickable());
}
for (auto prefab : GameState::current().seedsPrefabs)

View File

@ -242,22 +242,6 @@ components::basic::SpriteState* parse<components::basic::SpriteState> (boost::pr
return spriteState;
}
template <>
components::basic::Transform* parse<components::basic::Transform> (boost::property_tree::ptree& root)
{
// Ensure we are on the correct node (property tree seems to add root of its own)
if (root.size() > 0 && root.front().first == "Transform")
root = root.front().second;
components::basic::Transform* transform = new components::basic::Transform();
transform->x = root.get<float>("<xmlattr>.x", 0.0f);
transform->y = root.get<float>("<xmlattr>.y", 0.0f);
transform->w = root.get<float>("<xmlattr>.w", 1.0f);
transform->h = root.get<float>("<xmlattr>.h", 1.0f);
return transform;
}
template <>
components::DebugController* parse<components::DebugController> (boost::property_tree::ptree& root)
{
@ -541,6 +525,18 @@ graphics::SpriteRenderer* parse<graphics::SpriteRenderer> (boost::property_tree:
/****** Model ******/
void parseTransform (model::Transform& transform, boost::property_tree::ptree& root)
{
// Ensure we are on the correct node (property tree seems to add root of its own)
if (root.size() > 0 && root.front().first == "Transform")
root = root.front().second;
transform.x = root.get<float>("<xmlattr>.x", 0.0f);
transform.y = root.get<float>("<xmlattr>.y", 0.0f);
transform.w = root.get<float>("<xmlattr>.w", 1.0f);
transform.h = root.get<float>("<xmlattr>.h", 1.0f);
}
template <>
model::GameObject* parse<model::GameObject> (boost::property_tree::ptree& root)
{
@ -559,6 +555,9 @@ model::GameObject* parse<model::GameObject> (boost::property_tree::ptree& root)
if (child.first == "GameObject")
gameObj->addChild(parse<model::GameObject>(child.second));
else if (child.first == "Transform")
parseTransform(gameObj->transform, child.second);
// Components::basic
else if (child.first == "Camera")
gameObj->addComponent(parse<components::basic::Camera>(child.second));
@ -575,9 +574,6 @@ model::GameObject* parse<model::GameObject> (boost::property_tree::ptree& root)
else if (child.first == "Sprite")
gameObj->addComponent(parse<components::basic::Sprite>(child.second));
else if (child.first == "Transform")
gameObj->addComponent(parse<components::basic::Transform>(child.second));
// Components::items
else if (child.first == "Axe")
gameObj->addComponent(parse<components::items::Axe>(child.second));

View File

@ -13,7 +13,6 @@
#include <components/basic/Inventory.h>
#include <components/basic/InventoryItem.h>
#include <components/basic/Sprite.h>
#include <components/basic/Transform.h>
#include <components/DebugController.h>
#include <components/environment/GameTime.h>
#include <components/environment/Weather.h>
@ -68,9 +67,6 @@ namespace storage {
template <>
components::basic::SpriteState* parse<components::basic::SpriteState> (boost::property_tree::ptree& root);
template <>
components::basic::Transform* parse<components::basic::Transform> (boost::property_tree::ptree& root);
template <>
components::DebugController* parse<components::DebugController> (boost::property_tree::ptree& root);