Large refactoring, using the Entity-Component model.

This commit is contained in:
2016-11-30 19:50:01 +02:00
parent bcd0a359fc
commit 9c8cbf8518
53 changed files with 1616 additions and 675 deletions

24
src/base/Camera.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Camera.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef MODEL_CAMERA_H_
#define MODEL_CAMERA_H_
#include <base/Component.h>
namespace farmlands {
namespace base {
struct Camera : public Component
{
float scale;
};
}
}
#endif /* MODEL_CAMERA_H_ */

59
src/base/Component.cpp Normal file
View File

@ -0,0 +1,59 @@
/*
* Component.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <base/Component.h>
namespace farmlands {
namespace base {
Component::Component()
: gameObject(nullptr)
{
}
Component::~Component()
{
}
void Component::onCreate()
{
}
void Component::onInitialize()
{
}
bool Component::onEvent(SDL_Event& event)
{
return false;
}
void Component::onUpdateLogic()
{
}
void Component::onPreRender()
{
}
void Component::onRender()
{
}
void Component::onPostRender()
{
}
void Component::onDestroy()
{
}
}
/* namespace base */
} /* namespace farmlands */

41
src/base/Component.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Component.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef COMPONENT_H_
#define COMPONENT_H_
#include <SDL2/SDL.h>
namespace farmlands {
namespace base {
class GameObject;
class Component
{
public:
Component();
virtual ~Component();
// Game object methods
virtual void onCreate();
virtual void onInitialize();
virtual bool onEvent(SDL_Event& event);
virtual void onUpdateLogic();
virtual void onPreRender();
virtual void onRender();
virtual void onPostRender();
virtual void onDestroy();
GameObject* gameObject;
};
}
/* namespace base */
} /* namespace farmlands */
#endif /* COMPONENT_H_ */

155
src/base/GameObject.cpp Normal file
View File

@ -0,0 +1,155 @@
/*
* GameObject.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <base/GameObject.h>
#include <base/Component.h>
namespace farmlands {
namespace base {
GameObject::GameObject()
: m_components(),
m_children(),
m_parent(nullptr)
{
}
GameObject::~GameObject()
{
// Delete children
for (auto child : m_children)
{
child->onDestroy();
delete child;
}
// Delete components
for (auto pair : m_components)
{
pair.second->onDestroy();
delete pair.second;
}
}
void GameObject::addChild(GameObject* obj)
{
m_children.push_back(obj);
obj->m_parent = this;
}
GameObject* GameObject::removeChild(size_t index)
{
GameObject* child = m_children.at(index);
child->m_parent = nullptr;
m_children.erase(m_children.begin() + index);
return child;
}
size_t GameObject::childrenCount() const
{
return m_children.size();
}
GameObject* GameObject::child(size_t index)
{
return m_children.at(index);
}
GameObject* GameObject::parent()
{
return m_parent;
}
void GameObject::onCreate()
{
// Call components
for (auto pair : m_components)
pair.second->onCreate();
// Call children
for (auto child : m_children)
child->onCreate();
}
void GameObject::onInitialize()
{
// Call components
for (auto pair : m_components)
pair.second->onInitialize();
// Call children
for (auto child : m_children)
child->onInitialize();
}
bool GameObject::onEvent(SDL_Event& event)
{
bool handled = false;
// Call components
for (auto it = m_components.begin(); it != m_components.end() && !handled; it++)
handled = it->second->onEvent(event);
// Call children
for (auto it = m_children.begin(); it != m_children.end() && !handled; it++)
handled = (*it)->onEvent(event);
return handled;
}
void GameObject::onUpdateLogic()
{
// Call components
for (auto pair : m_components)
pair.second->onUpdateLogic();
// Call children
for (auto child : m_children)
child->onUpdateLogic();
}
void GameObject::onPreRender()
{
// Call components
for (auto pair : m_components)
pair.second->onPreRender();
// Call children
for (auto child : m_children)
child->onPreRender();
}
void GameObject::onRender()
{
// Call components
for (auto pair : m_components)
pair.second->onRender();
// Call children
for (auto child : m_children)
child->onRender();
}
void GameObject::onPostRender()
{
// Call components
for (auto pair : m_components)
pair.second->onPostRender();
// Call children
for (auto child : m_children)
child->onPostRender();
}
void GameObject::onDestroy()
{
}
} /* namespace base */
} /* namespace farmlands */

97
src/base/GameObject.h Normal file
View File

@ -0,0 +1,97 @@
/*
* GameObject.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef GAMEOBJECT_H_
#define GAMEOBJECT_H_
#include <utils/Assert.h>
#include <SDL2/SDL.h>
#include <typeindex>
#include <vector>
#include <unordered_map>
namespace farmlands {
namespace base {
class Component;
class RenderContext;
class GameObject
{
public:
// Constructors
GameObject();
virtual ~GameObject();
// Components API
template <typename T> void addComponent(T* component);
template <typename T> T* component();
template <typename T> void removeComponent();
// Tree methods
void addChild(GameObject* obj);
GameObject* removeChild(size_t index);
void destroyChild(size_t index);
GameObject* child(size_t index);
size_t childrenCount() const;
GameObject* parent();
// Game object methods
void onCreate();
void onInitialize();
bool onEvent(SDL_Event& event);
void onUpdateLogic();
void onPreRender();
void onRender();
void onPostRender();
void onDestroy();
private:
std::unordered_map<std::type_index, Component*> m_components;
std::vector<GameObject*> m_children;
GameObject* m_parent;
};
template <typename T>
void GameObject::addComponent(T* component)
{
std::type_index typeIndex(typeid(T));
Assert(m_components.count(typeIndex) == 0, "A component of the same type already exists!");
m_components.emplace(typeIndex, component);
component->gameObject = this;
}
template <typename T>
T* GameObject::component()
{
// Compute type index
std::type_index typeIndex(typeid(T));
Assert(m_components.count(typeIndex) != 0, "Component doesn't exist!");
// Get component
T* comp = dynamic_cast<T*> (m_components.at(typeIndex));
Assert(comp != nullptr, "This is bad!!! Type of component is really messed up!!!");
return comp;
}
template <typename T>
void GameObject::removeComponent()
{
std::type_index typeIndex(typeid(T));
return m_components.erase(typeIndex);
}
} /* namespace base */
} /* namespace farmlands */
#endif /* GAMEOBJECT_H_ */

View File

@ -0,0 +1,47 @@
/*
* RenderContext.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <base/Camera.h>
#include <base/GameObject.h>
#include <base/RenderContext.h>
#include <base/Transform.h>
namespace farmlands {
namespace base {
float RenderContext::xToWorld(float x)
{
float cellW = viewport.pixelsPerUnitX * m_camera->scale;
return (x - viewport.width / 2) / cellW + m_cameraTransform->x;
}
float RenderContext::yToWorld(float y)
{
float cellH = viewport.pixelsPerUnitY * m_camera->scale;
return (y - viewport.height / 2) / cellH + m_cameraTransform->y;
}
float RenderContext::xToScreen(float x)
{
float cellW = viewport.pixelsPerUnitX * m_camera->scale;
return (x - m_cameraTransform->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;
}
void RenderContext::setCamera(GameObject* camera)
{
m_cameraObj = camera;
m_cameraTransform = camera->component<Transform>();
m_camera = camera->component<Camera>();
}
}
}

49
src/base/RenderContext.h Normal file
View File

@ -0,0 +1,49 @@
/*
* RenderContext.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef GRAPHICS_RENDERCONTEXT_H_
#define GRAPHICS_RENDERCONTEXT_H_
#include <base/Viewport.h>
namespace farmlands {
namespace base {
class GameObject;
struct Transform;
struct Camera;
class RenderContext
{
public:
float xToWorld(float x);
float yToWorld(float y);
float xToScreen(float x);
float yToScreen(float y);
inline GameObject* cameraObj() { return m_cameraObj; }
inline Camera* camera() { return m_camera; }
inline Transform* cameraTransform() { return m_cameraTransform; }
void setCamera(GameObject* camera);
/**
* Screen properties
*/
Viewport viewport;
float uiScale = 1.0f;
private:
Transform* m_cameraTransform;
Camera* m_camera;
GameObject* m_cameraObj;
};
}
}
#endif /* GRAPHICS_RENDERCONTEXT_H_ */

103
src/base/Sprite.cpp Normal file
View File

@ -0,0 +1,103 @@
/*
* Sprite.cpp
*
* Created on: Nov 29, 2016
* Author: tibi
*/
#include <base/Sprite.h>
#include <utils/Assert.h>
#include <algorithm>
namespace farmlands {
namespace base {
Sprite::Sprite()
: anchorX(0), anchorY(0),
name(),
m_states(),
m_currentState(0),
m_currentFrame(0),
m_currentFrameTimeLeft(0)
{
}
Sprite::~Sprite()
{
}
void Sprite::addState(const SpriteState& state)
{
Assert(state.frames.size() > 0, "State must have at least one frame!");
#ifdef BUILD_DEBUG
uint32_t totalDuration = 0;
for (auto frame : state.frames)
totalDuration += frame.duration;
Assert(totalDuration > 0, "State must have a frame which last at least one tick.");
#endif
Assert(m_stateNames.count(state.name) == 0, "A state with the same name already added!");
m_states.push_back(state);
m_stateNames.emplace(state.name, m_states.size() - 1);
}
void Sprite::setState(size_t stateId)
{
Assert(stateId < m_states.size(), "Inexistent state.");
// Avoid resetting state
if (stateId == m_currentState)
return;
m_currentState = stateId;
m_currentFrame = 0;
m_currentFrameTimeLeft = currentFrame().duration * 1;
}
void Sprite::setState(const std::string& name)
{
Assert(m_stateNames.count(name) > 0, "Inexistent state.");
setState(m_stateNames.at(name));
}
void Sprite::advanceTime(uint32_t steps)
{
Assert(m_states.size() > 0, "Sprite must have at least one state!");
while (steps > 0)
{
// There is time left in the current frame?
if (m_currentFrameTimeLeft > 0)
{
uint32_t sub = std::min(steps, m_currentFrameTimeLeft);
m_currentFrameTimeLeft -= sub;
steps -= sub;
}
if (m_currentFrameTimeLeft == 0)
{
// Move to the next frame
if (++m_currentFrame >= currentState().frames.size())
m_currentFrame = 0;
m_currentFrameTimeLeft = currentFrame().duration * 1;
}
}
}
SpriteState& Sprite::currentState()
{
Assert(m_states.size() > 0, "Sprite must have at least one state!");
return m_states.at(m_currentState);
}
Frame& Sprite::currentFrame()
{
Assert(currentState().frames.size() > 0, "State must have at least one frame!");
return currentState().frames.at(m_currentFrame);
}
} /* namespace model */
} /* namespace farmlands */

89
src/base/Sprite.h Normal file
View File

@ -0,0 +1,89 @@
/*
* Sprite.h
*
* Created on: Nov 29, 2016
* Author: tibi
*/
#ifndef MODEL_SPRITE_H_
#define MODEL_SPRITE_H_
#include <utils/Exceptions.h>
#include <vector>
#include <unordered_map>
namespace farmlands {
namespace base {
/**
* Defines an animation frame
*/
struct Frame
{
uint32_t tileSetId;
uint32_t tileSetCell;
uint32_t width, height;
uint32_t duration;
};
/**
* Defines a sprite state (aka an animation).
*/
struct SpriteState
{
std::string name;
std::vector<Frame> frames;
};
/**
* Defines a sprite
*/
class Sprite
{
public:
Sprite();
virtual ~Sprite();
/**
* Adds a state to the sprite.
*/
void addState(const SpriteState& state);
/**
* Sets the current state.
*/
void setState(size_t stateId);
/**
* Sets the current state.
*/
void setState(const std::string& name);
/**
* Advances the current frame
*/
void advanceTime(uint32_t steps);
// Getters
SpriteState& currentState();
Frame& currentFrame();
// Public fields
float anchorX, anchorY;
std::string name;
private:
std::vector<SpriteState> m_states;
std::unordered_map<std::string, size_t> m_stateNames;
size_t m_currentState;
size_t m_currentFrame;
uint32_t m_currentFrameTimeLeft;
};
} /* namespace model */
} /* namespace farmlands */
#endif /* MODEL_SPRITE_H_ */

24
src/base/Transform.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Transform.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef BASE_TRANSFORM_H_
#define BASE_TRANSFORM_H_
#include <base/Component.h>
namespace farmlands {
namespace base {
struct Transform: public Component
{
float x, y;
};
} /* namespace base */
} /* namespace farmlands */
#endif /* BASE_TRANSFORM_H_ */

24
src/base/Viewport.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Viewport.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef MODEL_VIEWPORT_H_
#define MODEL_VIEWPORT_H_
namespace farmlands {
namespace base {
struct Viewport
{
bool initialized = false;
int width, height;
float pixelsPerUnitX, pixelsPerUnitY;
};
}
}
#endif /* MODEL_VIEWPORT_H_ */