Implemented collisions.
This commit is contained in:
parent
42f0d4125b
commit
a7af100122
@ -26,6 +26,9 @@
|
|||||||
<Transform x="120" y="100" />
|
<Transform x="120" y="100" />
|
||||||
<Sprite src="sprites/Player.sprite" />
|
<Sprite src="sprites/Player.sprite" />
|
||||||
<SpriteRenderer />
|
<SpriteRenderer />
|
||||||
|
<Collider x="0.2" y="0.75"
|
||||||
|
w="0.6" h="0.25"
|
||||||
|
solid="true" />
|
||||||
<Inventory capacity="30" />
|
<Inventory capacity="30" />
|
||||||
<Player hp="100" maxHp="100"
|
<Player hp="100" maxHp="100"
|
||||||
energy="100" maxEnergy="100"
|
energy="100" maxEnergy="100"
|
||||||
@ -45,6 +48,24 @@
|
|||||||
<Transform x="125" y="110" />
|
<Transform x="125" y="110" />
|
||||||
<Sprite src="sprites/Player.sprite" />
|
<Sprite src="sprites/Player.sprite" />
|
||||||
<SpriteRenderer />
|
<SpriteRenderer />
|
||||||
|
<Collider x="0" y="0.5"
|
||||||
|
w="1" h="0.5"
|
||||||
|
solid="true" />
|
||||||
|
<MovingNpcController />
|
||||||
|
</GameObject>
|
||||||
|
|
||||||
|
<GameObject name="something">
|
||||||
|
<Transform x="130.5" y="100.3" />
|
||||||
|
<Sprite w="5" h="3"
|
||||||
|
anchorX="0" anchorY="0">
|
||||||
|
<State name="Normal">
|
||||||
|
<Frame tileSet="tilesets/Ground.png" cell="15" duration="1" />
|
||||||
|
</State>
|
||||||
|
</Sprite>
|
||||||
|
<SpriteRenderer />
|
||||||
|
<Collider solid="true"
|
||||||
|
x="0" y="0.5"
|
||||||
|
w="1" h="0.5" />
|
||||||
</GameObject>
|
</GameObject>
|
||||||
|
|
||||||
</Scene>
|
</Scene>
|
@ -8,11 +8,13 @@
|
|||||||
#ifndef MODEL_GAMESTATE_H_
|
#ifndef MODEL_GAMESTATE_H_
|
||||||
#define MODEL_GAMESTATE_H_
|
#define MODEL_GAMESTATE_H_
|
||||||
|
|
||||||
|
#include <components/basic/Collider.h>
|
||||||
|
#include <graphics/RenderContext.h>
|
||||||
#include <model/GameObject.h>
|
#include <model/GameObject.h>
|
||||||
#include <model/Scene.h>
|
#include <model/Scene.h>
|
||||||
#include <model/Configuration.h>
|
#include <model/Configuration.h>
|
||||||
#include <graphics/RenderContext.h>
|
|
||||||
#include <resources/ResourceManager.h>
|
#include <resources/ResourceManager.h>
|
||||||
|
#include <utils/QTree.h>
|
||||||
#include <utils/Random.h>
|
#include <utils/Random.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -42,6 +44,8 @@ namespace farmlands {
|
|||||||
std::vector<model::GameObject*> seedsPrefabs;
|
std::vector<model::GameObject*> seedsPrefabs;
|
||||||
std::vector<model::GameObject*> plantsPrefabs;
|
std::vector<model::GameObject*> plantsPrefabs;
|
||||||
|
|
||||||
|
utils::QTree<components::basic::Collider*>* colliderTree;
|
||||||
|
|
||||||
// Timing
|
// Timing
|
||||||
float time;
|
float time;
|
||||||
uint32_t date;
|
uint32_t date;
|
||||||
|
187
src/components/basic/Collider.cpp
Normal file
187
src/components/basic/Collider.cpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Collider.cpp
|
||||||
|
*
|
||||||
|
* Created on: Dec 15, 2016
|
||||||
|
* Author: tibi
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <GameState.h>
|
||||||
|
#include <components/basic/Collider.h>
|
||||||
|
#include <model/GameObject.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace farmlands::utils;
|
||||||
|
|
||||||
|
namespace farmlands {
|
||||||
|
namespace components {
|
||||||
|
namespace basic {
|
||||||
|
|
||||||
|
static const float CollisionCheckDistance = 4.0f;
|
||||||
|
|
||||||
|
|
||||||
|
Collider::Collider()
|
||||||
|
: collisionRect(0, 0, 1, 1),
|
||||||
|
solid(false),
|
||||||
|
m_sprite(nullptr),
|
||||||
|
m_lastBounds(0, 0, 0, 0),
|
||||||
|
m_lastX(0), m_lastY(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Collider::~Collider()
|
||||||
|
{
|
||||||
|
auto qtree = GameState::current().colliderTree;
|
||||||
|
if (!qtree)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remove self from the qtree
|
||||||
|
auto it0 = qtree->find(this, m_lastBounds.x, m_lastBounds.y);
|
||||||
|
|
||||||
|
// Object may have never been initialized, so it is possible that it is not in the qtree
|
||||||
|
if (it0 != qtree->end())
|
||||||
|
{
|
||||||
|
qtree->erase(it0);
|
||||||
|
|
||||||
|
if (m_sprite)
|
||||||
|
{
|
||||||
|
auto it1 = qtree->find(this, m_lastBounds.x + m_lastBounds.w, m_lastBounds.y);
|
||||||
|
qtree->erase(it1);
|
||||||
|
auto it2 = qtree->find(this, m_lastBounds.x, m_lastBounds.y + m_lastBounds.h);
|
||||||
|
qtree->erase(it2);
|
||||||
|
auto it3 = qtree->find(this, m_lastBounds.x + m_lastBounds.w, m_lastBounds.y + m_lastBounds.h);
|
||||||
|
qtree->erase(it3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model::Component* Collider::clone()
|
||||||
|
{
|
||||||
|
Collider* clone = new Collider();
|
||||||
|
clone->collisionRect = collisionRect;
|
||||||
|
clone->solid = solid;
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Collider::dump(unsigned level)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < level; i++)
|
||||||
|
std::cout<<" ";
|
||||||
|
|
||||||
|
std::cout << " .Component: Collider\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Collider::onInitialize()
|
||||||
|
{
|
||||||
|
auto qtree = GameState::current().colliderTree;
|
||||||
|
Assert(qtree != nullptr, "Collider tree not set!");
|
||||||
|
|
||||||
|
// Obtain sprite
|
||||||
|
m_sprite = gameObject->component<Sprite>();
|
||||||
|
recomputeBounds();
|
||||||
|
|
||||||
|
qtree->insert(this, m_lastBounds.x, m_lastBounds.y);
|
||||||
|
if (m_sprite)
|
||||||
|
{
|
||||||
|
qtree->insert(this, m_lastBounds.x + m_lastBounds.w, m_lastBounds.y);
|
||||||
|
qtree->insert(this, m_lastBounds.x, m_lastBounds.y + m_lastBounds.h);
|
||||||
|
qtree->insert(this, m_lastBounds.x + m_lastBounds.w, m_lastBounds.y + m_lastBounds.h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Collider::onUpdateLogic()
|
||||||
|
{
|
||||||
|
auto qtree = GameState::current().colliderTree;
|
||||||
|
Assert(qtree != nullptr, "Collider tree not set!");
|
||||||
|
|
||||||
|
// Update position if object moved.
|
||||||
|
// As an optimization, we ignore sprite shape changes (i.e. scale) because
|
||||||
|
// we expect that to be used for animation.
|
||||||
|
bool moved = (m_lastX != gameObject->transform.x || m_lastY != gameObject->transform.y);
|
||||||
|
|
||||||
|
if (moved)
|
||||||
|
{
|
||||||
|
RectF oldBounds = m_lastBounds;
|
||||||
|
recomputeBounds();
|
||||||
|
|
||||||
|
auto it0 = qtree->find(this, oldBounds.x, oldBounds.y);
|
||||||
|
qtree->move(it0, m_lastBounds.x, m_lastBounds.y);
|
||||||
|
|
||||||
|
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);
|
||||||
|
qtree->move(it2, m_lastBounds.x, m_lastBounds.y + m_lastBounds.h);
|
||||||
|
qtree->move(it3, m_lastBounds.x + m_lastBounds.w, m_lastBounds.y + m_lastBounds.h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collider* Collider::checkCollision()
|
||||||
|
{
|
||||||
|
auto qtree = GameState::current().colliderTree;
|
||||||
|
Assert(qtree != nullptr, "Collider tree not set!");
|
||||||
|
|
||||||
|
RectF searchArea(m_lastX - CollisionCheckDistance, m_lastY - CollisionCheckDistance, 2 * CollisionCheckDistance, 2 * CollisionCheckDistance);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Collider::canMove(float newX, float newY)
|
||||||
|
{
|
||||||
|
auto qtree = GameState::current().colliderTree;
|
||||||
|
Assert(qtree != nullptr, "Collider tree not set!");
|
||||||
|
|
||||||
|
// Both objects must be solid to register a collision
|
||||||
|
if (!solid)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Compute moved bounds
|
||||||
|
float offX = m_lastBounds.x - m_lastX;
|
||||||
|
float offY = m_lastBounds.y - m_lastY;
|
||||||
|
RectF movedBounds(newX + offX, newY + offY, m_lastBounds.w, m_lastBounds.h);
|
||||||
|
|
||||||
|
// Search for objects
|
||||||
|
RectF searchArea(newX - CollisionCheckDistance, newY - CollisionCheckDistance, 2 * CollisionCheckDistance, 2 * CollisionCheckDistance);
|
||||||
|
for (auto it = qtree->lower_bound(searchArea); it != qtree->upper_bound(searchArea); it++)
|
||||||
|
{
|
||||||
|
if (it->data != this && it->data->solid && movedBounds.intersects(it->data->m_lastBounds))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Collider::recomputeBounds()
|
||||||
|
{
|
||||||
|
m_lastX = gameObject->transform.x;
|
||||||
|
m_lastY = gameObject->transform.y;
|
||||||
|
|
||||||
|
if (m_sprite)
|
||||||
|
{
|
||||||
|
RectF spriteBounds = m_sprite->boundaries();
|
||||||
|
m_lastBounds.x = spriteBounds.x + spriteBounds.w * collisionRect.x;
|
||||||
|
m_lastBounds.y = spriteBounds.y + spriteBounds.h * collisionRect.y;
|
||||||
|
m_lastBounds.w = spriteBounds.w * collisionRect.w;
|
||||||
|
m_lastBounds.h = spriteBounds.h * collisionRect.h;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_lastBounds.x = m_lastX;
|
||||||
|
m_lastBounds.y = m_lastY;
|
||||||
|
m_lastBounds.w = 0;
|
||||||
|
m_lastBounds.h = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace basic */
|
||||||
|
} /* namespace components */
|
||||||
|
} /* namespace farmlands */
|
65
src/components/basic/Collider.h
Normal file
65
src/components/basic/Collider.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Collider.h
|
||||||
|
*
|
||||||
|
* Created on: Dec 15, 2016
|
||||||
|
* Author: tibi
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMPONENTS_BASIC_COLLIDER_H_
|
||||||
|
#define COMPONENTS_BASIC_COLLIDER_H_
|
||||||
|
|
||||||
|
#include <components/basic/Sprite.h>
|
||||||
|
#include <model/Component.h>
|
||||||
|
#include <utils/QTree.h>
|
||||||
|
|
||||||
|
namespace farmlands {
|
||||||
|
namespace components {
|
||||||
|
namespace basic {
|
||||||
|
|
||||||
|
class Collider: public model::Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Collider();
|
||||||
|
virtual ~Collider();
|
||||||
|
|
||||||
|
virtual model::Component* clone() override;
|
||||||
|
virtual void dump(unsigned level) override;
|
||||||
|
virtual void onInitialize() override;
|
||||||
|
virtual void onUpdateLogic() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the collision bounds.
|
||||||
|
*/
|
||||||
|
utils::RectF collisionBoundaries() const { return m_lastBounds; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this object collides with another Collider object.
|
||||||
|
*/
|
||||||
|
Collider* checkCollision();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if this object can move to the given coordinates.
|
||||||
|
* Both objects must be solid to register a collision.
|
||||||
|
*/
|
||||||
|
bool canMove(float newX, float newY);
|
||||||
|
|
||||||
|
// Collision rectangle, relative to sprite bounds (default is [0, 0, 1, 1])
|
||||||
|
// If object doesn't have a sprite, this property is not used
|
||||||
|
utils::RectF collisionRect;
|
||||||
|
|
||||||
|
// If an object is solid, it cannot be passed through.
|
||||||
|
bool solid;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void recomputeBounds();
|
||||||
|
|
||||||
|
Sprite* m_sprite;
|
||||||
|
utils::RectF m_lastBounds;
|
||||||
|
float m_lastX, m_lastY;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace basic */
|
||||||
|
} /* namespace components */
|
||||||
|
} /* namespace farmlands */
|
||||||
|
|
||||||
|
#endif /* COMPONENTS_BASIC_COLLIDER_H_ */
|
163
src/components/npc/MovingNpcController.cpp
Normal file
163
src/components/npc/MovingNpcController.cpp
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* MovingNpcController.cpp
|
||||||
|
*
|
||||||
|
* Created on: Dec 17, 2016
|
||||||
|
* Author: tibi
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <GameState.h>
|
||||||
|
#include <components/items/Pickable.h>
|
||||||
|
#include <components/npc/MovingNpcController.h>
|
||||||
|
#include <input/Input.h>
|
||||||
|
#include <math/GameMath.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace farmlands::components::basic;
|
||||||
|
using namespace farmlands::components::items;
|
||||||
|
using namespace farmlands::graphics;
|
||||||
|
using namespace farmlands::input;
|
||||||
|
using namespace farmlands::model;
|
||||||
|
|
||||||
|
namespace farmlands {
|
||||||
|
namespace components {
|
||||||
|
namespace npc {
|
||||||
|
|
||||||
|
static const float NpcWalkVelocity = 3.0f; // The default velocity of the player when walking (units/sec).
|
||||||
|
|
||||||
|
MovingNpcController::MovingNpcController()
|
||||||
|
: facingDirection(Direction::South),
|
||||||
|
walking(false),
|
||||||
|
m_collider(nullptr),
|
||||||
|
m_currentDestination(0),
|
||||||
|
m_waitTime(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MovingNpcController::~MovingNpcController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Component* MovingNpcController::clone()
|
||||||
|
{
|
||||||
|
MovingNpcController* clone = new MovingNpcController();
|
||||||
|
|
||||||
|
// Movement
|
||||||
|
clone->facingDirection = facingDirection;
|
||||||
|
clone->walking = walking;
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovingNpcController::dump(unsigned level)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < level; i++)
|
||||||
|
std::cout<<" ";
|
||||||
|
|
||||||
|
std::cout << " .Component: MovingNpcController\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovingNpcController::onInitialize()
|
||||||
|
{
|
||||||
|
m_collider = gameObject->component<Collider>();
|
||||||
|
|
||||||
|
static const int offX[] = { 0, 1, 1, 0 };
|
||||||
|
static const int offY[] = { 0, 0, 1, 1 };
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
m_destinationsX[i] = gameObject->transform.x + offX[i] * 10;
|
||||||
|
m_destinationsY[i] = gameObject->transform.y + offY[i] * 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovingNpcController::onUpdateLogic()
|
||||||
|
{
|
||||||
|
updateMovement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovingNpcController::onPreRender()
|
||||||
|
{
|
||||||
|
preRenderMovement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovingNpcController::preRenderMovement()
|
||||||
|
{
|
||||||
|
// Get sprite
|
||||||
|
Sprite* sprite = gameObject->component<Sprite>();
|
||||||
|
|
||||||
|
// Compute current state
|
||||||
|
std::string stateName = (walking) ? "Walking " : "Idle ";
|
||||||
|
|
||||||
|
switch (facingDirection)
|
||||||
|
{
|
||||||
|
case Direction::East:
|
||||||
|
stateName += "right";
|
||||||
|
break;
|
||||||
|
case Direction::West:
|
||||||
|
stateName += "left";
|
||||||
|
break;
|
||||||
|
case Direction::North:
|
||||||
|
stateName += "up";
|
||||||
|
break;
|
||||||
|
case Direction::South:
|
||||||
|
stateName += "down";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprite->setState(stateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovingNpcController::updateMovement()
|
||||||
|
{
|
||||||
|
walking = false;
|
||||||
|
if (m_waitTime > 0)
|
||||||
|
{
|
||||||
|
m_waitTime -= GameState::current().deltaTime;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrived at the destination?
|
||||||
|
bool arrivedX = std::abs(gameObject->transform.x - m_destinationsX[m_currentDestination]) < 0.5f;
|
||||||
|
bool arrivedY = std::abs(gameObject->transform.y - m_destinationsY[m_currentDestination]) < 0.5f;
|
||||||
|
|
||||||
|
if (arrivedX && arrivedY)
|
||||||
|
{
|
||||||
|
m_currentDestination = (m_currentDestination + 1) % 4;
|
||||||
|
m_waitTime += 2.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simply move
|
||||||
|
float speed = NpcWalkVelocity * GameState::current().deltaTime;
|
||||||
|
|
||||||
|
float newX = gameObject->transform.x;
|
||||||
|
float newY = gameObject->transform.y;
|
||||||
|
moveTowards(&newX, &newY, m_destinationsX[m_currentDestination], m_destinationsY[m_currentDestination], speed);
|
||||||
|
|
||||||
|
if (canMove(newX, newY))
|
||||||
|
{
|
||||||
|
facingDirection = getDirection(newX - gameObject->transform.x, newY - gameObject->transform.y);
|
||||||
|
gameObject->transform.x = newX;
|
||||||
|
gameObject->transform.y = newY;
|
||||||
|
walking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction MovingNpcController::getDirection(float vx, float vy)
|
||||||
|
{
|
||||||
|
if (fabsf(vx) >= fabsf(vy))
|
||||||
|
return (vx > 0) ? Direction::East : Direction::West;
|
||||||
|
|
||||||
|
else
|
||||||
|
return (vy > 0) ? Direction::South : Direction::North;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MovingNpcController::canMove(float x, float y)
|
||||||
|
{
|
||||||
|
return m_collider->canMove(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace npc */
|
||||||
|
} /* namespace components */
|
||||||
|
} /* namespace farmlands */
|
55
src/components/npc/MovingNpcController.h
Normal file
55
src/components/npc/MovingNpcController.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* MovingNpcController.h
|
||||||
|
*
|
||||||
|
* Created on: Dec 17, 2016
|
||||||
|
* Author: tibi
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMPONENTS_NPC_MOVINGNPCCONTROLLER_H_
|
||||||
|
#define COMPONENTS_NPC_MOVINGNPCCONTROLLER_H_
|
||||||
|
|
||||||
|
#include <model/Component.h>
|
||||||
|
#include <model/Direction.h>
|
||||||
|
|
||||||
|
namespace farmlands {
|
||||||
|
namespace components {
|
||||||
|
namespace npc {
|
||||||
|
|
||||||
|
class MovingNpcController: public model::Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MovingNpcController();
|
||||||
|
virtual ~MovingNpcController();
|
||||||
|
|
||||||
|
virtual model::Component* clone() override;
|
||||||
|
virtual void dump(unsigned level) override;
|
||||||
|
|
||||||
|
virtual void onInitialize() override;
|
||||||
|
virtual void onUpdateLogic() override;
|
||||||
|
virtual void onPreRender() override;
|
||||||
|
|
||||||
|
// Movement
|
||||||
|
model::Direction facingDirection;
|
||||||
|
bool walking;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Movement
|
||||||
|
void preRenderMovement();
|
||||||
|
void updateMovement();
|
||||||
|
bool canMove(float x, float y);
|
||||||
|
static model::Direction getDirection(float vx, float vy);
|
||||||
|
|
||||||
|
float m_destinationsX[4];
|
||||||
|
float m_destinationsY[4];
|
||||||
|
size_t m_currentDestination;
|
||||||
|
float m_waitTime;
|
||||||
|
|
||||||
|
basic::Collider* m_collider;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace npc */
|
||||||
|
} /* namespace components */
|
||||||
|
} /* namespace farmlands */
|
||||||
|
|
||||||
|
#endif /* COMPONENTS_NPC_MOVINGNPCCONTROLLER_H_ */
|
@ -45,6 +45,7 @@ Player::Player()
|
|||||||
hp(100), maxHp(100),
|
hp(100), maxHp(100),
|
||||||
energy(100), maxEnergy(100),
|
energy(100), maxEnergy(100),
|
||||||
money(0),
|
money(0),
|
||||||
|
m_collider(nullptr),
|
||||||
m_inventory(nullptr),
|
m_inventory(nullptr),
|
||||||
m_grid(nullptr),
|
m_grid(nullptr),
|
||||||
m_pickables(nullptr)
|
m_pickables(nullptr)
|
||||||
@ -92,6 +93,7 @@ void Player::dump(unsigned level)
|
|||||||
|
|
||||||
void Player::onInitialize()
|
void Player::onInitialize()
|
||||||
{
|
{
|
||||||
|
m_collider = gameObject->component<Collider>();
|
||||||
m_inventory = gameObject->component<Inventory>();
|
m_inventory = gameObject->component<Inventory>();
|
||||||
|
|
||||||
auto root = &GameState::current().scene->root;
|
auto root = &GameState::current().scene->root;
|
||||||
@ -310,7 +312,7 @@ void Player::updateMovement()
|
|||||||
|
|
||||||
Direction Player::getDirection(float vx, float vy)
|
Direction Player::getDirection(float vx, float vy)
|
||||||
{
|
{
|
||||||
if (vx != 0)
|
if (fabsf(vx) >= fabsf(vy))
|
||||||
return (vx > 0) ? Direction::East : Direction::West;
|
return (vx > 0) ? Direction::East : Direction::West;
|
||||||
return (vy > 0) ? Direction::South : Direction::North;
|
return (vy > 0) ? Direction::South : Direction::North;
|
||||||
}
|
}
|
||||||
@ -356,6 +358,11 @@ void Player::performAction(model::GameObject* obj)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Player::canMove(float x, float y)
|
||||||
|
{
|
||||||
|
return m_collider->canMove(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
void Player::updatePickables()
|
void Player::updatePickables()
|
||||||
{
|
{
|
||||||
// Don't do anything if inventory is full
|
// Don't do anything if inventory is full
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#ifndef COMPONENTS_PLAYER_PLAYER_H_
|
#ifndef COMPONENTS_PLAYER_PLAYER_H_
|
||||||
#define COMPONENTS_PLAYER_PLAYER_H_
|
#define COMPONENTS_PLAYER_PLAYER_H_
|
||||||
|
|
||||||
|
#include <components/basic/Collider.h>
|
||||||
#include <components/basic/Grid.h>
|
#include <components/basic/Grid.h>
|
||||||
#include <components/basic/Inventory.h>
|
#include <components/basic/Inventory.h>
|
||||||
#include <components/items/Weapon.h>
|
#include <components/items/Weapon.h>
|
||||||
@ -69,7 +70,7 @@ namespace player {
|
|||||||
// Movement
|
// Movement
|
||||||
void preRenderMovement();
|
void preRenderMovement();
|
||||||
void updateMovement();
|
void updateMovement();
|
||||||
bool canMove(float x, float y) { return true; }
|
bool canMove(float x, float y);
|
||||||
static model::Direction getDirection(float vx, float vy);
|
static model::Direction getDirection(float vx, float vy);
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@ -79,6 +80,8 @@ namespace player {
|
|||||||
// Picking up
|
// Picking up
|
||||||
void updatePickables();
|
void updatePickables();
|
||||||
|
|
||||||
|
basic::Collider* m_collider;
|
||||||
|
|
||||||
basic::Inventory* m_inventory;
|
basic::Inventory* m_inventory;
|
||||||
basic::Grid* m_grid;
|
basic::Grid* m_grid;
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ void SpriteRenderer::onRender()
|
|||||||
src.h = spriteH;
|
src.h = spriteH;
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
SdlRenderer::instance().renderTexture(texture, &src, &dest);
|
SdlRenderer::instance().queueRenderTexture(texture, &src, &dest, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,10 @@ void move(float *x, float *y, model::Direction direction, float distance)
|
|||||||
|
|
||||||
void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed)
|
void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed)
|
||||||
{
|
{
|
||||||
float angle = atan2f(towardsX - *x, towardsY - *y);
|
// Y coordinate is opposite of trigonometric coordinates
|
||||||
|
float angle = atan2f(-(towardsY - *y), towardsX - *x);
|
||||||
*x += cosf(angle) * speed;
|
*x += cosf(angle) * speed;
|
||||||
*y += sinf(angle) * speed;
|
*y -= sinf(angle) * speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
float distanceSq(float x0, float y0, float x1, float y1)
|
float distanceSq(float x0, float y0, float x1, float y1)
|
||||||
|
@ -114,6 +114,13 @@ void ResourceManager::loadGame()
|
|||||||
model::GameObject* item = model::GameObject::instantiate(prefab, "inv seed", player);
|
model::GameObject* item = model::GameObject::instantiate(prefab, "inv seed", player);
|
||||||
inventory->add(item);
|
inventory->add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create collision qtree
|
||||||
|
auto mapObjIt = root->findByComponent<components::Map>();
|
||||||
|
Assert (mapObjIt != root->childrenEnd(), "Can't find map!");
|
||||||
|
components::Map* map = (*mapObjIt)->component<components::Map>();
|
||||||
|
utils::RectF colliderArea(0, 0, map->width, map->height);
|
||||||
|
GameState::current().colliderTree = new utils::QTree<components::basic::Collider*>(colliderArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ResourceManager::getPath(ResourceId resourceId)
|
std::string ResourceManager::getPath(ResourceId resourceId)
|
||||||
|
@ -114,6 +114,25 @@ components::basic::Camera* parse<components::basic::Camera> (boost::property_tre
|
|||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
components::basic::Collider* parse<components::basic::Collider> (boost::property_tree::ptree& root)
|
||||||
|
{
|
||||||
|
if (root.size() > 0 && root.front().first == "Collider")
|
||||||
|
root = root.front().second;
|
||||||
|
|
||||||
|
components::basic::Collider* collider = new components::basic::Collider();
|
||||||
|
collider->solid = root.get<bool>("<xmlattr>.solid", false);
|
||||||
|
|
||||||
|
utils::RectF collisionRect;
|
||||||
|
collisionRect.x = root.get<float>("<xmlattr>.x", 0.0f);
|
||||||
|
collisionRect.y = root.get<float>("<xmlattr>.y", 0.0f);
|
||||||
|
collisionRect.w = root.get<float>("<xmlattr>.w", 1.0f);
|
||||||
|
collisionRect.h = root.get<float>("<xmlattr>.h", 1.0f);
|
||||||
|
|
||||||
|
collider->collisionRect = collisionRect;
|
||||||
|
return collider;
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
components::basic::Frame* parse<components::basic::Frame> (boost::property_tree::ptree& root)
|
components::basic::Frame* parse<components::basic::Frame> (boost::property_tree::ptree& root)
|
||||||
{
|
{
|
||||||
@ -461,6 +480,23 @@ model::Direction parseDirection(std::string directionStr)
|
|||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
components::npc::MovingNpcController* parse<components::npc::MovingNpcController> (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 == "MovingNpcController")
|
||||||
|
root = root.front().second;
|
||||||
|
|
||||||
|
components::npc::MovingNpcController* npc = new components::npc::MovingNpcController();
|
||||||
|
|
||||||
|
// Movement
|
||||||
|
std::string direction = root.get<std::string>("<xmlattr>.facingDirection", "South");
|
||||||
|
npc->facingDirection = parseDirection(direction);
|
||||||
|
npc->walking = root.get<bool>("<xmlattr>.walking", false);
|
||||||
|
|
||||||
|
return npc;
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
components::player::Player* parse<components::player::Player> (boost::property_tree::ptree& root)
|
components::player::Player* parse<components::player::Player> (boost::property_tree::ptree& root)
|
||||||
{
|
{
|
||||||
@ -560,6 +596,9 @@ model::GameObject* parse<model::GameObject> (boost::property_tree::ptree& root)
|
|||||||
else if (child.first == "Camera")
|
else if (child.first == "Camera")
|
||||||
gameObj->addComponent(parse<components::basic::Camera>(child.second));
|
gameObj->addComponent(parse<components::basic::Camera>(child.second));
|
||||||
|
|
||||||
|
else if (child.first == "Collider")
|
||||||
|
gameObj->addComponent(parse<components::basic::Collider>(child.second));
|
||||||
|
|
||||||
else if (child.first == "Grid")
|
else if (child.first == "Grid")
|
||||||
gameObj->addComponent(parse<components::basic::Grid>(child.second));
|
gameObj->addComponent(parse<components::basic::Grid>(child.second));
|
||||||
|
|
||||||
@ -607,6 +646,10 @@ model::GameObject* parse<model::GameObject> (boost::property_tree::ptree& root)
|
|||||||
else if (child.first == "Seed")
|
else if (child.first == "Seed")
|
||||||
gameObj->addComponent(parse<components::plants::Seed>(child.second));
|
gameObj->addComponent(parse<components::plants::Seed>(child.second));
|
||||||
|
|
||||||
|
// Components::npc
|
||||||
|
else if (child.first == "MovingNpcController")
|
||||||
|
gameObj->addComponent(parse<components::npc::MovingNpcController>(child.second));
|
||||||
|
|
||||||
// Components::player
|
// Components::player
|
||||||
else if (child.first == "Player")
|
else if (child.first == "Player")
|
||||||
gameObj->addComponent(parse<components::player::Player>(child.second));
|
gameObj->addComponent(parse<components::player::Player>(child.second));
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define STORAGE_PARSERS_PARSERS_H_
|
#define STORAGE_PARSERS_PARSERS_H_
|
||||||
|
|
||||||
#include <components/basic/Camera.h>
|
#include <components/basic/Camera.h>
|
||||||
|
#include <components/basic/Collider.h>
|
||||||
#include <components/basic/Grid.h>
|
#include <components/basic/Grid.h>
|
||||||
#include <components/basic/Inventory.h>
|
#include <components/basic/Inventory.h>
|
||||||
#include <components/basic/InventoryItem.h>
|
#include <components/basic/InventoryItem.h>
|
||||||
@ -28,6 +29,7 @@
|
|||||||
#include <components/Map.h>
|
#include <components/Map.h>
|
||||||
#include <components/plants/Plant.h>
|
#include <components/plants/Plant.h>
|
||||||
#include <components/plants/Seed.h>
|
#include <components/plants/Seed.h>
|
||||||
|
#include <components/npc/MovingNpcController.h>
|
||||||
#include <components/player/Player.h>
|
#include <components/player/Player.h>
|
||||||
#include <graphics/MapRenderer.h>
|
#include <graphics/MapRenderer.h>
|
||||||
#include <graphics/SpriteRenderer.h>
|
#include <graphics/SpriteRenderer.h>
|
||||||
@ -49,6 +51,9 @@ namespace storage {
|
|||||||
template <>
|
template <>
|
||||||
components::basic::Camera* parse<components::basic::Camera> (boost::property_tree::ptree& root);
|
components::basic::Camera* parse<components::basic::Camera> (boost::property_tree::ptree& root);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
components::basic::Collider* parse<components::basic::Collider> (boost::property_tree::ptree& root);
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
components::basic::Frame* parse<components::basic::Frame> (boost::property_tree::ptree& root);
|
components::basic::Frame* parse<components::basic::Frame> (boost::property_tree::ptree& root);
|
||||||
|
|
||||||
@ -109,6 +114,9 @@ namespace storage {
|
|||||||
template <>
|
template <>
|
||||||
components::plants::Seed* parse<components::plants::Seed> (boost::property_tree::ptree& root);
|
components::plants::Seed* parse<components::plants::Seed> (boost::property_tree::ptree& root);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
components::npc::MovingNpcController* parse<components::npc::MovingNpcController> (boost::property_tree::ptree& root);
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
components::player::Player* parse<components::player::Player> (boost::property_tree::ptree& root);
|
components::player::Player* parse<components::player::Player> (boost::property_tree::ptree& root);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define UTILS_QTREE_H_
|
#define UTILS_QTREE_H_
|
||||||
|
|
||||||
#include <utils/Assert.h>
|
#include <utils/Assert.h>
|
||||||
|
#include <utils/Exceptions.h>
|
||||||
#include <utils/INonAssignable.h>
|
#include <utils/INonAssignable.h>
|
||||||
#include <utils/Rect.h>
|
#include <utils/Rect.h>
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ namespace utils {
|
|||||||
/**
|
/**
|
||||||
* Quad tree
|
* Quad tree
|
||||||
*/
|
*/
|
||||||
template <typename T, size_t Capacity>
|
template <typename T, size_t Capacity = 16>
|
||||||
class QTree : public INonAssignable
|
class QTree : public INonAssignable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -74,11 +75,13 @@ namespace utils {
|
|||||||
bool empty() const;
|
bool empty() const;
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
|
|
||||||
void insert(T element, float x, float y);
|
void insert(const T& element, float x, float y);
|
||||||
|
void move(iterator it, float newX, float newY);
|
||||||
void erase(iterator it);
|
void erase(iterator it);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
iterator find(T element);
|
iterator find(const T& element);
|
||||||
|
iterator find(const T& element, float x, float y);
|
||||||
iterator lower_bound(const RectF& area);
|
iterator lower_bound(const RectF& area);
|
||||||
iterator upper_bound(const RectF& area);
|
iterator upper_bound(const RectF& area);
|
||||||
|
|
||||||
@ -312,7 +315,7 @@ namespace utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, size_t Capacity>
|
template<typename T, size_t Capacity>
|
||||||
void QTree<T, Capacity>::insert(T element, float x, float y)
|
void QTree<T, Capacity>::insert(const T& element, float x, float y)
|
||||||
{
|
{
|
||||||
Assert(m_bounds.contains(x, y), "Can't add element outside bounds.");
|
Assert(m_bounds.contains(x, y), "Can't add element outside bounds.");
|
||||||
|
|
||||||
@ -344,6 +347,54 @@ namespace utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Capacity>
|
||||||
|
void QTree<T, Capacity>::move(iterator it, float newX, float newY)
|
||||||
|
{
|
||||||
|
Assert(!it.m_tree->m_isSplit, "Container modified.");
|
||||||
|
Assert(it.m_item < it.m_tree->m_itemsCount, "Container modified.");
|
||||||
|
|
||||||
|
// Find destination tree
|
||||||
|
QTree<T, Capacity>* destTree = this;
|
||||||
|
bool found = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Go to parent
|
||||||
|
if (!destTree->m_bounds.contains(newX, newY))
|
||||||
|
destTree = destTree->m_parent;
|
||||||
|
|
||||||
|
// Go to child
|
||||||
|
else if (destTree->m_isSplit)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
if (destTree->m_children[i]->m_bounds.contains(newX, newY))
|
||||||
|
{
|
||||||
|
destTree = destTree->m_children[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (!found && destTree != nullptr);
|
||||||
|
|
||||||
|
if (destTree == nullptr)
|
||||||
|
THROW(InvalidArgumentException, "Iterator doesn't belong to this tree.");
|
||||||
|
|
||||||
|
// No need to move
|
||||||
|
if (destTree == it.m_tree)
|
||||||
|
{
|
||||||
|
it->x = newX;
|
||||||
|
it->y = newY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to move. Preform an 'erase' and an 'insert'
|
||||||
|
T dataTemp = it->data;
|
||||||
|
it.m_tree->erase(it);
|
||||||
|
destTree->insert(dataTemp, newX, newY);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, size_t Capacity>
|
template<typename T, size_t Capacity>
|
||||||
void QTree<T, Capacity>::erase(iterator it)
|
void QTree<T, Capacity>::erase(iterator it)
|
||||||
{
|
{
|
||||||
@ -385,7 +436,7 @@ namespace utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, size_t Capacity>
|
template<typename T, size_t Capacity>
|
||||||
typename QTree<T, Capacity>::iterator QTree<T, Capacity>::find(T element)
|
typename QTree<T, Capacity>::iterator QTree<T, Capacity>::find(const T& element)
|
||||||
{
|
{
|
||||||
for (auto it = begin(); it != end(); it++)
|
for (auto it = begin(); it != end(); it++)
|
||||||
if (it->data == element)
|
if (it->data == element)
|
||||||
@ -394,6 +445,31 @@ namespace utils {
|
|||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Capacity>
|
||||||
|
typename QTree<T, Capacity>::iterator QTree<T, Capacity>::find(const T& element, float x, float y)
|
||||||
|
{
|
||||||
|
if (!m_bounds.contains(x, y))
|
||||||
|
{
|
||||||
|
if (m_parent)
|
||||||
|
return m_parent->find(element, x, y);
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
if (m_isSplit)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
if (m_children[i]->m_bounds.contains(x, y))
|
||||||
|
return m_children[i]->find(element, x, y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_itemsCount; i++)
|
||||||
|
if (m_items[i].data == element && m_items[i].x == x && m_items[i].y == y)
|
||||||
|
return iterator(this, RectF(x, y, 0, 0), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, size_t Capacity>
|
template<typename T, size_t Capacity>
|
||||||
typename QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(const RectF& area)
|
typename QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(const RectF& area)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user