Large refactoring, using the Entity-Component model.

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

23
src/GameState.cpp Normal file
View File

@ -0,0 +1,23 @@
/*
* GameState.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <GameState.h>
namespace farmlands {
GameState GameState::s_current;
GameState& farmlands::GameState::current()
{
return s_current;
}
void farmlands::GameState::setCurrent(GameState& state)
{
s_current = state;
}
}

View File

@ -8,11 +8,10 @@
#ifndef MODEL_GAMESTATE_H_
#define MODEL_GAMESTATE_H_
#include <base/GameObject.h>
#include <base/RenderContext.h>
#include <model/Level.h>
#include <model/Player.h>
#include <graphics/SdlRenderer.h>
#include <graphics/GameRenderer.h>
#include <graphics/GuiRenderer.h>
#include <model/Configuration.h>
#include <resources/ResourceManager.h>
@ -20,32 +19,18 @@
namespace farmlands {
struct ViewportState
{
bool initialized;
int width, height;
};
struct Camera
{
float posX, posY;
float scale;
};
struct GuiState
{
};
struct GameState
class GameState
{
// Resource layer
resources::ResourceManager resManager;
public:
static GameState& current();
static void setCurrent(GameState& state);
// Graphics layer
graphics::SdlRenderer sdlRenderer;
graphics::GuiRenderer guiRenderer;
graphics::GameRenderer gameRenderer;
// Render context
base::RenderContext renderContext;
// Gui
GuiState gui;
@ -54,13 +39,14 @@ namespace farmlands {
model::Configuration config;
// Current game
ViewportState viewport;
Camera camera;
model::Player player;
model::Level* currentLevel;
base::GameObject root;
float elapsedTime;
private:
static GameState s_current;
};
}

44
src/assets/Ground.h Normal file
View File

@ -0,0 +1,44 @@
/*
* Ground.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef ASSETS_GROUND_H_
#define ASSETS_GROUND_H_
namespace farmlands {
namespace assets {
/**
* Maps tiles to ground name
*/
enum class Ground
{
Dirt = 0,
DirtVariation0 = 1,
DirtVariation1 = 2,
DirtVariation2 = 3,
DirtVariation3 = 4,
DirtVariation4 = 5,
DirtVariation5 = 6,
DirtVariation6 = 7,
DirtVariation7 = 8,
DirtVariation8 = 9,
SoilCenter = 30,
SoilWet = 36
};
inline bool groundIsDirt(int cell) { return cell >= Ground::Dirt && cell <= Ground::DirtVariation8; }
inline bool groundIsDrySoil(int cell) { return cell == Ground::SoilCenter; }
inline bool groundIsWetSoil(int cell) { return cell == Ground::SoilWet; }
}
}
#endif /* ASSETS_GROUND_H_ */

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_ */

View File

@ -5,16 +5,17 @@
* Author: tibi
*/
#include <model/Sprite.h>
#include <base/Sprite.h>
#include <utils/Assert.h>
#include <algorithm>
namespace farmlands {
namespace model {
namespace base {
Sprite::Sprite()
: anchorX(0), anchorY(0),
name(),
m_states(),
m_currentState(0),
m_currentFrame(0),

View File

@ -14,7 +14,7 @@
#include <unordered_map>
namespace farmlands {
namespace model {
namespace base {
/**
* Defines an animation frame
@ -72,6 +72,7 @@ namespace model {
// Public fields
float anchorX, anchorY;
std::string name;
private:
std::vector<SpriteState> m_states;

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_ */

View File

@ -0,0 +1,53 @@
/*
* DebugController.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <GameState.h>
#include <base/Camera.h>
#include <controller/DebugController.h>
#include <input/Input.h>
using namespace farmlands::input;
namespace farmlands {
namespace controller {
static const float ScaleVelocity = 0.5f;
static const float ScaleShiftVelocity = 2.0f;
DebugController::DebugController()
{
}
DebugController::~DebugController()
{
}
void DebugController::onInitialize()
{
m_camera = GameState::current().renderContext.camera();
}
void DebugController::onUpdateLogic()
{
// Compute velocity
float vel = ScaleVelocity;
if (Input::instance().pressed(GameKey::Run))
vel = ScaleShiftVelocity;
// Time independent
vel *= GameState::current().elapsedTime;
if (Input::instance().pressed(GameKey::Debug_ZoomIn))
m_camera->scale *= 1 + vel;
if (Input::instance().pressed(GameKey::Debug_ZoomOut))
m_camera->scale *= 1 - vel;
}
} /* namespace controller */
} /* namespace farmlands */

View File

@ -0,0 +1,33 @@
/*
* DebugController.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef CONTROLLER_DEBUGCONTROLLER_H_
#define CONTROLLER_DEBUGCONTROLLER_H_
#include <base/Component.h>
#include <base/Camera.h>
namespace farmlands {
namespace controller {
class DebugController: public base::Component
{
public:
DebugController();
virtual ~DebugController();
virtual void onInitialize() override;
virtual void onUpdateLogic() override;
private:
base::Camera* m_camera;
};
} /* namespace controller */
} /* namespace farmlands */
#endif /* CONTROLLER_DEBUGCONTROLLER_H_ */

View File

@ -5,7 +5,13 @@
* Author: tibi
*/
#include "FarmlandsGame.h"
#include <base/GameObject.h>
#include <base/Camera.h>
#include <controller/DebugController.h>
#include <controller/FarmlandsGame.h>
#include <graphics/backend/SdlRenderer.h>
#include <graphics/BackgroundRenderer.h>
#include <graphics/SpriteRenderer.h>
#include <resources/Resources.h>
#include <SDL2/SDL.h>
@ -14,65 +20,123 @@
#include <unistd.h>
#include <iostream>
using namespace farmlands::base;
using namespace farmlands::graphics;
using namespace farmlands::graphics::backend;
using namespace farmlands::resources;
namespace farmlands {
namespace controller {
FarmlandsGame::FarmlandsGame() :
m_running(true),
m_gameState(),
m_time(0),
m_guiController(),
m_playerController()
m_time(0)
{
}
void FarmlandsGame::createScene()
{
// Create camera
Transform* cameraTransform = new Transform();
cameraTransform->x = 120;
cameraTransform->y = 100 - 1;
Camera* camera = new Camera();
camera->scale = 4.0f;
GameObject* cameraObj = new GameObject();
cameraObj->addComponent(cameraTransform);
cameraObj->addComponent(camera);
GameState::current().root.addChild(cameraObj);
GameState::current().renderContext.setCamera(cameraObj);
cameraObj->onCreate();
// Create background
BackgroundRenderer* backRenderer = new BackgroundRenderer();
backRenderer->level = GameState::current().currentLevel;
GameObject* backObj = new GameObject();
backObj->addComponent(backRenderer);
GameState::current().root.addChild(backObj);
backObj->onCreate();
// Create player
Transform* playerTransform = new Transform();
playerTransform->x = 120;
playerTransform->y = 100;
SpriteRenderer* playerRender = new SpriteRenderer();
playerRender->sprite = ResourceManager::instance().sprite(R::Sprites::Player);
PlayerController* playerCtrl = new PlayerController();
GameObject* playerObj = new GameObject();
playerObj->addComponent(playerTransform);
playerObj->addComponent(playerRender);
playerObj->addComponent(playerCtrl);
GameState::current().root.addChild(playerObj);
playerObj->onCreate();
// Create debug object
DebugController* dbgController = new DebugController();
GameObject* dbgObj = new GameObject();
dbgObj->addComponent(dbgController);
GameState::current().root.addChild(dbgObj);
dbgObj->onCreate();
}
bool FarmlandsGame::initialize()
{
// Initialize game state
m_gameState.viewport.initialized = false;
m_gameState.camera.scale = 4;
bool ok = true;
// Initialize render system
if (!m_gameState.sdlRenderer.initialize(&m_gameState))
ok &= SdlRenderer::instance().initialize(&GameState::current().renderContext);
if (!ok)
return false;
m_gameState.gameRenderer.initialize(&m_gameState);
m_gameState.guiRenderer.initialize(&m_gameState);
// Initialize controllers
m_guiController.initialize(&m_gameState);
m_playerController.initialize(&m_gameState);
// Initialize & load resources
m_gameState.resManager.initialize(&m_gameState);
m_gameState.resManager.loadGameAssets();
m_gameState.resManager.loadLevel(resources::R::Levels::Farm);
m_gameState.currentLevel = m_gameState.resManager.level(resources::R::Levels::Farm);
ResourceManager::instance().initialize();
ResourceManager::instance().loadGameAssets();
ResourceManager::instance().loadLevel(resources::R::Levels::Farm);
GameState::current().currentLevel = ResourceManager::instance().level(resources::R::Levels::Farm);
GameState::current().renderContext.viewport.pixelsPerUnitX = GameState::current().currentLevel->m_cellWidth;
GameState::current().renderContext.viewport.pixelsPerUnitY = GameState::current().currentLevel->m_cellHeight;
// Set up scene
createScene();
// Finish initialization
GameState::current().root.onInitialize();
return true;
}
void FarmlandsGame::onUpdateLogic()
{
m_playerController.updateLogic();
GameState::current().root.onUpdateLogic();
}
void FarmlandsGame::onPreRender()
{
GameState::current().root.onPreRender();
SdlRenderer::instance().renderBegin();
}
void FarmlandsGame::onRender()
{
m_gameState.sdlRenderer.renderBegin();
m_gameState.gameRenderer.render();
// m_gameState.guiRenderer.render();
m_guiController.render();
m_gameState.sdlRenderer.renderEnd();
GameState::current().root.onRender();
}
void FarmlandsGame::onEvent(SDL_Event& event)
{
// Let controllers handle event
if (m_guiController.processEvent(event))
return;
if (m_playerController.processEvent(event))
if (GameState::current().root.onEvent(event))
return;
// Nobody? Handle global events
@ -92,9 +156,16 @@ void FarmlandsGame::onEvent(SDL_Event& event)
}
}
void FarmlandsGame::onPostRender()
{
SdlRenderer::instance().renderEnd();
GameState::current().root.onPostRender();
}
void FarmlandsGame::stop()
{
m_running = false;
GameState::current().root.onDestroy();
}
int FarmlandsGame::run()
@ -108,7 +179,7 @@ int FarmlandsGame::run()
{
// Update elapsed time
Uint32 now = SDL_GetTicks();
m_gameState.elapsedTime = (now - m_time) * 0.001f;
GameState::current().elapsedTime = (now - m_time) * 0.001f;
m_time = now;
SDL_Event event;
@ -116,7 +187,9 @@ int FarmlandsGame::run()
onEvent(event);
onUpdateLogic();
onPreRender();
onRender();
onPostRender();
}
// Cleanup

View File

@ -11,8 +11,6 @@
#include <GameState.h>
#include <controller/GuiController.h>
#include <controller/PlayerController.h>
#include <graphics/GameRenderer.h>
#include <graphics/SdlRenderer.h>
#include <resources/ResourceManager.h>
#include <SDL2/SDL.h>
@ -29,19 +27,19 @@ namespace controller {
protected:
bool initialize();
void onUpdateLogic();
void onRender();
void onEvent(SDL_Event& event);
void onUpdateLogic();
void onPreRender();
void onRender();
void onPostRender();
void stop();
private:
bool m_running;
GameState m_gameState;
Uint32 m_time;
void createScene();
GuiController m_guiController;
PlayerController m_playerController;
bool m_running;
Uint32 m_time;
};
}

View File

@ -7,18 +7,15 @@
#include <GameState.h>
#include <controller/GuiController.h>
#include <gui/RenderContext.h>
#include <gui/widgets/TextArea.h>
#include <cassert>
namespace farmlands
{
namespace controller
{
GuiController::GuiController()
: m_gameState(nullptr)
: m_canvas()
{
}
@ -26,15 +23,12 @@ GuiController::~GuiController()
{
}
void GuiController::initialize(GameState* gameState)
void GuiController::onInitialize()
{
assert(gameState != nullptr);
assert(gameState->viewport.initialized);
m_gameState = gameState;
m_context = &GameState::current().renderContext;
// Set up canvas
m_canvas.setSize(m_gameState->viewport.width, m_gameState->viewport.height);
m_canvas.setSize(m_context->viewport.width, m_context->viewport.height);
// Add a text element
auto text = new gui::widgets::TextArea();
@ -50,7 +44,7 @@ void GuiController::initialize(GameState* gameState)
m_canvas.addChild(text);
}
bool GuiController::processEvent(SDL_Event& event)
bool GuiController::onEvent(SDL_Event& event)
{
bool handled = m_canvas.handleEvent(event);
@ -69,18 +63,11 @@ bool GuiController::processEvent(SDL_Event& event)
return handled;
}
void GuiController::render()
void GuiController::onRender()
{
// Compute render context
gui::RenderContext renderContext =
{
.sdlRenderer = &m_gameState->sdlRenderer,
.resManager = &m_gameState->resManager,
.uiScale = 1,
};
// Render
m_canvas.render(renderContext);
m_canvas.render(m_context);
}
} /* namespace controller */

View File

@ -8,18 +8,16 @@
#ifndef CONTROLLER_GUICONTROLLER_H_
#define CONTROLLER_GUICONTROLLER_H_
#include <base/Component.h>
#include <base/RenderContext.h>
#include <gui/layout/Canvas.h>
#include <SDL2/SDL.h>
namespace farmlands {
// Forward declarations
struct GameState;
namespace controller {
class GuiController
class GuiController : public base::Component
{
public:
GuiController();
@ -28,21 +26,13 @@ namespace controller {
/**
* Initializes game renderer
*/
void initialize(GameState* gameState);
/**
* Processes an UI event
*/
bool processEvent(SDL_Event& event);
/**
* Renders the GUI
*/
void render();
virtual void onInitialize() override;
virtual bool onEvent(SDL_Event& event) override;
virtual void onRender() override;
private:
GameState* m_gameState;
gui::layout::Canvas m_canvas;
base::RenderContext* m_context;
};
} /* namespace controller */

View File

@ -7,13 +7,48 @@
#include <GameState.h>
#include <controller/PlayerController.h>
#include <graphics/SpriteRenderer.h>
#include <input/Input.h>
#include <utils/Assert.h>
using namespace farmlands::input;
using namespace farmlands::graphics;
using namespace farmlands::model;
namespace farmlands {
namespace controller {
/**
* The default velocity of the player when walking (units/sec).
*/
static const float PlayerWalkVelocity = 2.0f;
/**
* The default velocity of the player when running (units/sec).
*/
static const float PlayerRunVelocity = 4.0f;
/*
* Movement speed when attacking.
*/
static const float PlayerAttackVelocity = 0.1f;
/**
* Direction enum based on sign of velocity on x and y
*/
static const Direction VelocitySignDirections[3][3] =
{
{ Direction::NorthWest, Direction::West, Direction::SouthWest },
{ Direction::North, Direction::None, Direction::South },
{ Direction::NorthEast, Direction::East, Direction::SouthEast },
};
PlayerController::PlayerController()
: m_gameState(nullptr)
: m_transform(nullptr),
m_attackTimeLeft(0),
m_vx(0), m_vy(0),
m_facingDirection(Direction::South)
{
}
@ -21,61 +56,88 @@ PlayerController::~PlayerController()
{
}
void PlayerController::initialize(GameState* gameState)
void PlayerController::onInitialize()
{
Assert(gameState != nullptr, "Game state must not be NULL!");
m_gameState = gameState;
m_transform = gameObject->component<base::Transform>();
}
bool PlayerController::processEvent(SDL_Event& event)
bool PlayerController::onEvent(SDL_Event& event)
{
bool attack1 = (Input::instance().down(GameKey::Action, event));
bool attack2 = (Input::instance().down(GameKey::Action2, event));
if (m_attackTimeLeft == 0 && (attack1 || attack2))
{
attack();
m_attackTimeLeft = 20 + attack2 * 20;
}
return false;
}
void PlayerController::updateLogic()
void PlayerController::onUpdateLogic()
{
// Get keyboard status
const Uint8* keys = SDL_GetKeyboardState(NULL);
// Compute movement velocity
float velMultiplier = PlayerWalkVelocity;
float deltaX = 0;
float deltaY = 0;
if (Input::instance().pressed(GameKey::Run))
velMultiplier = PlayerRunVelocity;
// Compute movement delta multiplier
float deltaMultiplier = 5.0f * m_gameState->elapsedTime; // Equivalent to units per second
if (keys[m_gameState->config.keys.Run])
deltaMultiplier *= 5;
// Handle scale changes (debugging only)
if (keys[m_gameState->config.keys.Debug_ZoomOut])
m_gameState->camera.scale *= 1.0f - 0.05f * deltaMultiplier;
if (keys[m_gameState->config.keys.Debug_ZoomIn])
m_gameState->camera.scale *= 1.0f + 0.05f * deltaMultiplier;
// Handle movement
if (keys[m_gameState->config.keys.Right] || keys[m_gameState->config.keys.AltRight])
deltaX += 1;
if (keys[m_gameState->config.keys.Up] || keys[m_gameState->config.keys.AltUp])
deltaY -= 1;
if (keys[m_gameState->config.keys.Left] || keys[m_gameState->config.keys.AltLeft])
deltaX -= 1;
if (keys[m_gameState->config.keys.Down] || keys[m_gameState->config.keys.AltDown])
deltaY += 1;
float newX = m_gameState->player.posX + deltaX * deltaMultiplier;
float newY = m_gameState->player.posY + deltaY * deltaMultiplier;
if (canMove(newX, newY))
if (m_attackTimeLeft)
{
m_gameState->player.posX = newX;
m_gameState->player.posY = newY;
m_gameState->player.lastDeltaX = deltaX * deltaMultiplier;
m_gameState->player.lastDeltaY = deltaY * deltaMultiplier;
setDirection(deltaX, deltaY);
m_gameState->camera.posX = m_gameState->player.posX;
m_gameState->camera.posY = m_gameState->player.posY - 1;
velMultiplier = PlayerAttackVelocity;
--m_attackTimeLeft;
}
// Make movement time independent
velMultiplier *= GameState::current().elapsedTime;
// Get velocity of axes
float vx = Input::instance().getX() * velMultiplier;
float vy = Input::instance().getY() * velMultiplier;
// Check if we can move to the new position
float newX = m_transform->x + m_vx;
float newY = m_transform->y + m_vy;
if ((vx || vy) && canMove(newX, newY))
{
m_vx = vx;
m_vy = vy;
m_transform->x = newX;
m_transform->y = newY;
m_facingDirection = getDirection(vx, vy);
base::Transform* cam = GameState::current().renderContext.cameraTransform();
cam->x = newX;
cam->y = newY - 1;
}
else
{
m_vx = 0;
m_vy = 0;
}
}
void PlayerController::onPreRender()
{
// Get sprite
SpriteRenderer* spriteRenderer = gameObject->component<SpriteRenderer>();
// Compute current state
bool idle = (m_vx == 0 && m_vy == 0);
std::string stateName = (idle) ? "Idle " : "Walking ";
if (m_facingDirection & Direction::East)
stateName += "right";
else if (m_facingDirection & Direction::West)
stateName += "left";
else if (m_facingDirection & Direction::North)
stateName += "up";
else
stateName += "down";
spriteRenderer->sprite->setState(stateName);
}
bool PlayerController::canMove(float x, float y)
@ -84,20 +146,17 @@ bool PlayerController::canMove(float x, float y)
return true;
}
static const model::Direction directions[3][3] =
Direction PlayerController::getDirection(float vx, float vy)
{
{ model::Direction::NorthWest, model::Direction::West, model::Direction::SouthWest },
{ model::Direction::North, model::Direction::South, model::Direction::South },
{ model::Direction::NorthEast, model::Direction::East, model::Direction::SouthEast },
};
int xx = (0 < vx) - (vx < 0);
int yy = (0 < vy) - (vy < 0);
void PlayerController::setDirection(float dx, float dy)
return VelocitySignDirections[xx + 1][yy + 1];
}
void PlayerController::attack()
{
int xx = (0 < dx) - (dx < 0);
int yy = (0 < dy) - (dy < 0);
if (xx != 0 || yy != 0)
m_gameState->player.direction = directions[xx + 1][yy + 1];
// For now - nothing
}
} /* namespace controller */

View File

@ -8,6 +8,8 @@
#ifndef CONTROLLER_PLAYERCONTROLLER_H_
#define CONTROLLER_PLAYERCONTROLLER_H_
#include <base/Component.h>
#include <base/Transform.h>
#include <SDL2/SDL.h>
namespace farmlands {
@ -17,34 +19,27 @@ struct GameState;
namespace controller {
class PlayerController
class PlayerController : public base::Component
{
public:
PlayerController();
virtual ~PlayerController();
/**
* Initializes game renderer
*/
void initialize(GameState* gameState);
/**
* Processes an event.
*
* @returns true if the event was handled, or false otherwise.
*/
bool processEvent(SDL_Event& event);
/**
* Called at the update logic step for every frame
*/
void updateLogic();
virtual void onInitialize() override;
virtual bool onEvent(SDL_Event& event) override;
virtual void onUpdateLogic() override;
virtual void onPreRender() override;
private:
bool canMove(float x, float y);
void setDirection(float dx, float dy);
static model::Direction getDirection(float vx, float vy);
GameState* m_gameState;
bool canMove(float x, float y);
void attack();
base::Transform* m_transform;
uint32_t m_attackTimeLeft;
float m_vx, m_vy;
model::Direction m_facingDirection;
};
} /* namespace controller */

View File

@ -0,0 +1,108 @@
/*
* BackgroundRenderer.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <GameState.h>
#include <base/GameObject.h>
#include <graphics/backend/SdlRenderer.h>
#include <graphics/BackgroundRenderer.h>
#include <resources/ResourceManager.h>
#include <math/GameMath.h>
#include <utils/Assert.h>
#include <cmath>
using namespace farmlands::graphics::backend;
using namespace farmlands::resources;
namespace farmlands {
namespace graphics {
BackgroundRenderer::BackgroundRenderer()
: level(nullptr),
m_context(nullptr)
{
}
BackgroundRenderer::~BackgroundRenderer()
{
}
void BackgroundRenderer::onInitialize()
{
Assert(gameObject != nullptr, "Component not properly initialized!");
m_context = &GameState::current().renderContext;
}
void BackgroundRenderer::onRender()
{
float cellW = m_context->viewport.pixelsPerUnitX * m_context->camera()->scale;
float cellH = m_context->viewport.pixelsPerUnitY * m_context->camera()->scale;
// Compute how many cells fit on the screen
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);
// Clamp cell positions
minCellX = clamp(minCellX, 0, (int)level->columnCount() - 1);
maxCellX = clamp(maxCellX, 0, (int)level->columnCount() - 1);
minCellY = clamp(minCellY, 0, (int)level->rowCount() - 1);
maxCellY = clamp(maxCellY, 0, (int)level->rowCount() - 1);
// Draw each layer
for (size_t i = 0; i < level->layerCount(); i++)
{
int textureId = level->texture(i);
// Render only visible tiles
for (int y = minCellY; y <= maxCellY; y++)
for (int x = minCellX; x <= maxCellX; x++)
{
int cellId = level->cell(i, y, x);
// Obtain texture
SDL_Texture* texture = ResourceManager::instance().texture(textureId);
// Calculate source rect
SDL_Rect src;
getCell(texture, cellId, &src.x, &src.y);
src.w = m_context->viewport.pixelsPerUnitX;
src.h = m_context->viewport.pixelsPerUnitY;
// Compute destination rect
SDL_Rect dest;
dest.x = floorf(m_context->xToScreen(x));
dest.y = floorf(m_context->yToScreen(y));
dest.w = ceilf(cellW);
dest.h = ceilf(cellH);
// Blit
SdlRenderer::instance().renderTexture(texture, &src, &dest);
}
}
}
void BackgroundRenderer::getCell(SDL_Texture* texture, uint32_t cell, int* outX, int* outY)
{
int texWidth, texHeight;
SdlRenderer::instance().getTextureSize(texture, &texWidth, &texHeight);
int ppuX = m_context->viewport.pixelsPerUnitX;
int ppuY = m_context->viewport.pixelsPerUnitY;
// Compute texture coordinates
*outX = (cell * ppuX) % texWidth;
*outY = ((cell * ppuX) / texWidth) * ppuY;
}
} /* namespace graphics */
} /* namespace farmlands */

View File

@ -0,0 +1,41 @@
/*
* BackgroundRenderer.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef GRAPHICS_BACKGROUNDRENDERER_H_
#define GRAPHICS_BACKGROUNDRENDERER_H_
#include <base/Component.h>
#include <base/Camera.h>
#include <base/Transform.h>
#include <model/Level.h>
namespace farmlands {
namespace graphics {
class BackgroundRenderer: public base::Component
{
public:
BackgroundRenderer();
virtual ~BackgroundRenderer();
virtual void onInitialize() override;
virtual void onRender() override;
// Public fields
model::Level* level;
private:
void getCell(SDL_Texture* texture, uint32_t cell, int* outX, int* outY);
// Private fields
base::RenderContext* m_context;
};
} /* namespace graphics */
} /* namespace farmlands */
#endif /* GRAPHICS_BACKGROUNDRENDERER_H_ */

View File

@ -1,199 +0,0 @@
/*
* GameRenderer.cpp
*
* Created on: Nov 13, 2016
* Author: tibi
*/
#include <GameState.h>
#include <graphics/GameRenderer.h>
#include <math/GameMath.h>
#include <resources/Resources.h>
namespace farmlands {
namespace graphics {
GameRenderer::GameRenderer()
: m_gameState(nullptr)
{
}
GameRenderer::~GameRenderer()
{
}
void GameRenderer::initialize(GameState* gameState)
{
m_gameState = gameState;
}
void GameRenderer::render()
{
prepareRender();
renderTileLayers();
renderPlayer();
}
void GameRenderer::prepareRender()
{
m_cellW = m_gameState->currentLevel->m_cellWidth * m_gameState->camera.scale;
m_cellH = m_gameState->currentLevel->m_cellHeight * m_gameState->camera.scale;
}
void GameRenderer::renderTileLayers()
{
// Compute how many cells fit on the screen
float cellsOnScreenX = m_gameState->viewport.width / m_cellW;
float cellsOnScreenY = m_gameState->viewport.height / m_cellH;
int minCellX = floorf(m_gameState->camera.posX - cellsOnScreenX / 2);
int minCellY = floorf(m_gameState->camera.posY - cellsOnScreenY / 2);
int maxCellX = ceilf(m_gameState->camera.posX + cellsOnScreenX / 2);
int maxCellY = ceilf(m_gameState->camera.posY + cellsOnScreenY / 2);
// Clamp cell positions
minCellX = clamp(minCellX, 0, (int)m_gameState->currentLevel->columnCount() - 1);
minCellY = clamp(minCellY, 0, (int)m_gameState->currentLevel->rowCount() - 1);
maxCellX = clamp(maxCellX, 0, (int)m_gameState->currentLevel->columnCount() - 1);
maxCellY = clamp(maxCellY, 0, (int)m_gameState->currentLevel->rowCount() - 1);
// Draw each layer
for (size_t i = 0; i < m_gameState->currentLevel->layerCount(); i++)
{
int textureId = m_gameState->currentLevel->texture(i);
// Render only visible tiles
for (int y = minCellY; y <= maxCellY; y++)
for (int x = minCellX; x <= maxCellX; x++)
{
int cellId = m_gameState->currentLevel->cell(i, y, x);
// Obtain texture
int texWidth, texHeight;
SDL_Texture* texture = m_gameState->resManager.texture(textureId);
SDL_QueryTexture(texture, NULL, NULL, &texWidth, &texHeight);
// Compute texture coordinates
int cellCol = (cellId * m_gameState->currentLevel->m_cellWidth) % texWidth;
int cellRow = ((cellId * m_gameState->currentLevel->m_cellWidth) / texWidth) * m_gameState->currentLevel->m_cellHeight;
// Calculate source position
SDL_Rect src =
{
cellCol,
cellRow,
(int) m_gameState->currentLevel->m_cellWidth,
(int) m_gameState->currentLevel->m_cellHeight
};
// Calculate destination position
SDL_Rect dest =
{
(int) xToScreen(x),
(int) yToScreen(y),
(int) ceilf(m_cellW),
(int) ceilf(m_cellH)
};
// Blit
SDL_RenderCopy(m_gameState->sdlRenderer.internalRenderer(), texture, &src, &dest);
}
}
}
void GameRenderer::renderSprite(model::Sprite* sprite, float destX, float destY)
{
float posX = xToScreen(destX);
float posY = yToScreen(destY);
// Obtain texture
int texId = sprite->currentFrame().tileSetId;
SDL_Texture* texture = m_gameState->resManager.texture(texId);
// Compute src rectangle
SDL_Rect src;
getCell(texture, sprite->currentFrame().tileSetCell, &src.x, &src.y);
src.w = sprite->currentFrame().width * m_gameState->currentLevel->m_cellWidth;
src.h = sprite->currentFrame().height * m_gameState->currentLevel->m_cellHeight;
// Compute destination rectangle
SDL_Rect dest;
dest.x = posX - sprite->anchorX * src.w * m_gameState->camera.scale;
dest.y = posY - sprite->anchorY * src.h * m_gameState->camera.scale;
dest.w = src.w * m_gameState->camera.scale;
dest.h = src.h * m_gameState->camera.scale;
// Draw
SDL_RenderCopy(m_gameState->sdlRenderer.internalRenderer(), texture, &src, &dest);
// Advance animation frame
sprite->advanceTime(1);
}
void GameRenderer::renderPlayer()
{
// Compute current state
model::Sprite* sprite = m_gameState->resManager.sprite(resources::R::Sprites::Player);
bool walking = (m_gameState->player.lastDeltaX != 0 || m_gameState->player.lastDeltaY != 0);
std::string stateName = (walking) ? "Walking " : "Idle ";
switch(m_gameState->player.direction)
{
case model::Direction::SouthEast:
case model::Direction::East:
case model::Direction::NorthEast:
stateName += "right";
break;
case model::Direction::North:
stateName += "up";
break;
case model::Direction::NorthWest:
case model::Direction::West:
case model::Direction::SouthWest:
stateName += "left";
break;
case model::Direction::South:
stateName += "down";
break;
}
sprite->setState(stateName);
// Draw
renderSprite(sprite, m_gameState->player.posX, m_gameState->player.posY);
}
void GameRenderer::getCell(SDL_Texture* texture, int cell, int* outX, int* outY)
{
int texWidth, texHeight;
SDL_QueryTexture(texture, NULL, NULL, &texWidth, &texHeight);
// Compute texture coordinates
*outX = (cell * m_gameState->currentLevel->m_cellWidth) % texWidth;
*outY = ((cell * m_gameState->currentLevel->m_cellWidth) / texWidth) * m_gameState->currentLevel->m_cellHeight;
}
float GameRenderer::xToWorld(float x)
{
return (x - m_gameState->viewport.width / 2) / m_cellW + m_gameState->camera.posX;
}
float GameRenderer::yToWorld(float y)
{
return (y - m_gameState->viewport.height / 2) / m_cellH + m_gameState->camera.posY;
}
float GameRenderer::xToScreen(float x)
{
return (x - m_gameState->camera.posX) * m_cellW + m_gameState->viewport.width / 2;
}
float GameRenderer::yToScreen(float y)
{
return (y - m_gameState->camera.posY) * m_cellH + m_gameState->viewport.height / 2;
}
} /* namespace graphics */
} /* namespace farmlands */

View File

@ -1,64 +0,0 @@
/*
* GameRenderer.h
*
* Created on: Nov 13, 2016
* Author: tibi
*/
#ifndef GRAPHICS_GAMERENDERER_H_
#define GRAPHICS_GAMERENDERER_H_
#include <model/Sprite.h>
#include <graphics/SdlRenderer.h>
#include <resources/ResourceManager.h>
#include <memory>
#include <SDL2/SDL.h>
namespace farmlands {
// Forward declarations
struct GameState;
namespace graphics {
class GameRenderer
{
public:
GameRenderer();
virtual ~GameRenderer();
/**
* Initializes game renderer
*/
void initialize(GameState* gameState);
/**
* Renders everything
*/
void render();
private:
void prepareRender();
void renderTileLayers();
void renderPlayer();
void renderSprite(model::Sprite* sprite, float destX, float destY);
float xToWorld(float x);
float yToWorld(float y);
float xToScreen(float x);
float yToScreen(float y);
void getCell(SDL_Texture* texture, int cell, int* outX, int* outY);
GameState* m_gameState;
// Size of a cell (scaled)
float m_cellW, m_cellH;
};
} /* namespace graphics */
} /* namespace farmlands */
#endif /* GRAPHICS_GAMERENDERER_H_ */

View File

@ -1,72 +0,0 @@
/*
* GuiRenderer.cpp
*
* Created on: Nov 13, 2016
* Author: tibi
*/
#include <GameState.h>
#include <graphics/GuiRenderer.h>
#include <resources/Resources.h>
#include <sstream>
#include <SDL2/SDL.h>
namespace farmlands
{
namespace graphics
{
GuiRenderer::GuiRenderer()
: m_gameState(nullptr),
m_fps(0),
m_fpsIndex(0)
{
}
GuiRenderer::~GuiRenderer()
{
}
void GuiRenderer::initialize(GameState* gameState)
{
m_gameState = gameState;
}
void GuiRenderer::render()
{
computeFps();
// Prepare stuff
TTF_Font* font = m_gameState->resManager.font(resources::R::Fonts::DejaVuSans, 10);
SDL_Color white = { 255, 255, 255, 255 };
SDL_Rect dest = { 10, 10, 0, 0 };
// Figure out what text to print
std::stringstream textStream;
textStream << "FPS: " << m_fps;
// Render text
SDL_Texture* fpsText = m_gameState->sdlRenderer.renderText(textStream.str(), font, white);
SDL_QueryTexture(fpsText, NULL, NULL, &dest.w, &dest.h);
SDL_RenderCopy(m_gameState->sdlRenderer.internalRenderer(), fpsText, NULL, &dest);
}
void GuiRenderer::computeFps()
{
// Compute FPS
m_fpsVals[m_fpsIndex++] = 1.0f / m_gameState->elapsedTime;
if (m_fpsIndex >= GUIRENDERER_FPS_VALS)
{
m_fpsIndex = 0;
m_fps = 0;
for (int i = 0; i < GUIRENDERER_FPS_VALS; i++)
m_fps += m_fpsVals[i];
m_fps /= GUIRENDERER_FPS_VALS;
}
}
} /* namespace graphics */
} /* namespace farmlands */

View File

@ -1,48 +0,0 @@
/*
* GuiRenderer.h
*
* Created on: Nov 13, 2016
* Author: tibi
*/
#ifndef GRAPHICS_GUIRENDERER_H_
#define GRAPHICS_GUIRENDERER_H_
#define GUIRENDERER_FPS_VALS 30
namespace farmlands {
// Forward declarations
struct GameState;
namespace graphics {
class GuiRenderer
{
public:
GuiRenderer();
virtual ~GuiRenderer();
/**
* Initializes game renderer
*/
void initialize(GameState* gameState);
/**
* Renders the GUI
*/
void render();
private:
void computeFps();
GameState* m_gameState;
float m_fpsVals[GUIRENDERER_FPS_VALS];
float m_fps;
int m_fpsIndex;
};
} /* namespace graphics */
} /* namespace farmlands */
#endif /* GRAPHICS_GUIRENDERER_H_ */

View File

@ -0,0 +1,86 @@
/*
* SpriteRenderer.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <GameState.h>
#include <base/GameObject.h>
#include <base/Camera.h>
#include <graphics/backend/SdlRenderer.h>
#include <graphics/SpriteRenderer.h>
#include <utils/Assert.h>
using namespace farmlands::graphics::backend;
namespace farmlands {
namespace graphics {
SpriteRenderer::SpriteRenderer()
: sprite(nullptr),
m_transform(nullptr),
m_context(nullptr)
{
}
SpriteRenderer::~SpriteRenderer()
{
}
void SpriteRenderer::onInitialize()
{
Assert(gameObject != nullptr, "Component not properly initialized!");
m_transform = gameObject->component<base::Transform>();
m_context = &GameState::current().renderContext;
}
void SpriteRenderer::onRender()
{
float posX = m_context->xToScreen(m_transform->x);
float posY = m_context->yToScreen(m_transform->y);
// Obtain texture
int texId = sprite->currentFrame().tileSetId;
SDL_Texture* texture = resources::ResourceManager::instance().texture(texId);
// Compute src rectangle
SDL_Rect src;
getCell(texture, sprite->currentFrame().tileSetCell, &src.x, &src.y);
src.w = sprite->currentFrame().width * m_context->viewport.pixelsPerUnitX;
src.h = sprite->currentFrame().height * m_context->viewport.pixelsPerUnitY;
// Compute destination rectangle
float scale = m_context->camera()->scale;
SDL_Rect dest;
dest.x = posX - sprite->anchorX * src.w * scale;
dest.y = posY - sprite->anchorY * src.h * scale;
dest.w = src.w * scale;
dest.h = src.h * scale;
// Draw
SdlRenderer::instance().renderTexture(texture, &src, &dest);
}
void SpriteRenderer::onPostRender()
{
sprite->advanceTime(1);
}
void SpriteRenderer::getCell(SDL_Texture* texture, uint32_t cell, int* outX, int* outY)
{
int texWidth, texHeight;
SdlRenderer::instance().getTextureSize(texture, &texWidth, &texHeight);
int ppuX = m_context->viewport.pixelsPerUnitX;
int ppuY = m_context->viewport.pixelsPerUnitY;
// Compute texture coordinates
*outX = (cell * ppuX) % texWidth;
*outY = ((cell * ppuX) / texWidth) * ppuY;
}
} /* namespace graphics */
} /* namespace farmlands */

View File

@ -0,0 +1,45 @@
/*
* SpriteRenderer.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef GRAPHICS_SPRITERENDERER_H_
#define GRAPHICS_SPRITERENDERER_H_
#include <base/Component.h>
#include <base/RenderContext.h>
#include <base/Sprite.h>
#include <base/Transform.h>
#include <SDL2/SDL.h>
namespace farmlands {
namespace graphics {
class SpriteRenderer: public base::Component
{
public:
SpriteRenderer();
virtual ~SpriteRenderer();
virtual void onInitialize() override;
virtual void onRender() override;
virtual void onPostRender() override;
// Public fields
base::Sprite* sprite;
private:
void getCell(SDL_Texture* texture, uint32_t cell, int* outX, int* outY);
// Private fields
base::Transform* m_transform;
base::RenderContext* m_context;
};
} /* namespace graphics */
} /* namespace farmlands */
#endif /* GRAPHICS_SPRITERENDERER_H_ */

View File

@ -6,7 +6,7 @@
*/
#include <GameState.h>
#include <graphics/SdlRenderer.h>
#include <graphics/backend/SdlRenderer.h>
#include <iostream>
#include <cassert>
@ -15,15 +15,21 @@
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
namespace farmlands
{
namespace graphics
namespace farmlands {
namespace graphics {
namespace backend {
// Singleton instance
SdlRenderer SdlRenderer::s_instance;
SdlRenderer& SdlRenderer::instance()
{
return s_instance;
}
SdlRenderer::SdlRenderer()
: m_sdlWindow(nullptr),
m_sdlRenderer(nullptr),
m_gameState(nullptr)
m_sdlRenderer(nullptr)
{
}
@ -38,10 +44,8 @@ SdlRenderer::~SdlRenderer()
SDL_Quit();
}
bool SdlRenderer::initialize(GameState* gameState)
bool SdlRenderer::initialize(base::RenderContext* renderContext)
{
m_gameState = gameState;
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cerr << "Failed to initialize SDL!\n";
return false;
@ -58,10 +62,10 @@ bool SdlRenderer::initialize(GameState* gameState)
return false;
}
m_gameState->viewport.initialized = true;
m_gameState->viewport.width = 1024;
m_gameState->viewport.height = 768;
m_sdlWindow = SDL_CreateWindow("Farmlands", 0, 0, m_gameState->viewport.width, m_gameState->viewport.height, SDL_WINDOW_SHOWN);
renderContext->viewport.initialized = true;
renderContext->viewport.width = 1024;
renderContext->viewport.height = 768;
m_sdlWindow = SDL_CreateWindow("Farmlands", 0, 0, renderContext->viewport.width, renderContext->viewport.height, SDL_WINDOW_SHOWN);
if (!m_sdlWindow) {
std::cerr << "Failed to create main window!\n";
return false;
@ -88,6 +92,11 @@ void SdlRenderer::renderEnd()
SDL_RenderPresent(m_sdlRenderer);
}
void SdlRenderer::renderTexture(SDL_Texture* texture, SDL_Rect* src, SDL_Rect* dest)
{
SDL_RenderCopy(m_sdlRenderer, texture, src, dest);
}
SDL_Texture* SdlRenderer::renderText(const std::string& text, TTF_Font* font, SDL_Color color)
{
assert(font != nullptr);
@ -102,7 +111,12 @@ SDL_Texture* SdlRenderer::renderText(const std::string& text, TTF_Font* font, SD
return texture;
}
void SdlRenderer::getTextureSize(SDL_Texture* texture, int* width, int* height)
{
SDL_QueryTexture(texture, NULL, NULL, width, height);
}
}
} /* namespace graphics */
} /* namespace farmlands */

View File

@ -8,30 +8,30 @@
#ifndef GRAPHICS_SDLRENDERER_H_
#define GRAPHICS_SDLRENDERER_H_
#include <base/RenderContext.h>
#include <string>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
namespace farmlands {
// Forward declarations
struct GameState;
namespace graphics {
namespace backend {
class SdlRenderer
{
public:
SdlRenderer();
virtual ~SdlRenderer();
static SdlRenderer& instance();
~SdlRenderer();
/**
* Initializes the game renderer.
*
* @returns true if successful, false otherwise.
*/
bool initialize(GameState* gameState);
bool initialize(base::RenderContext* renderContext);
/**
* Performs steps for beginning to render.
@ -48,6 +48,16 @@ namespace graphics {
*/
SDL_Texture* renderText(const std::string& text, TTF_Font* font, SDL_Color color);
/**
* Renders a textue
*/
void renderTexture(SDL_Texture* texture, SDL_Rect* src, SDL_Rect* dest);
/**
* Returns the size of a texture
*/
void getTextureSize(SDL_Texture* texture, int* width, int* height);
// Getters
/**
* Gets the internal SDL renderer object;
@ -56,12 +66,15 @@ namespace graphics {
private:
SdlRenderer();
SDL_Window* m_sdlWindow;
SDL_Renderer* m_sdlRenderer;
GameState* m_gameState;
static SdlRenderer s_instance;
};
}
} /* namespace graphics */
} /* namespace farmlands */

View File

@ -1,27 +0,0 @@
/*
* RenderContext.h
*
* Created on: Nov 27, 2016
* Author: tibi
*/
#ifndef GUI_PRIMITIVES_RENDERCONTEXT_H_
#define GUI_PRIMITIVES_RENDERCONTEXT_H_
#include <graphics/SdlRenderer.h>
#include <resources/ResourceManager.h>
namespace farmlands {
namespace gui {
struct RenderContext
{
graphics::SdlRenderer* sdlRenderer;
resources::ResourceManager* resManager;
float uiScale;
};
}
}
#endif /* GUI_PRIMITIVES_RENDERCONTEXT_H_ */

View File

@ -19,7 +19,7 @@ Canvas::~Canvas()
{
}
void Canvas::render(RenderContext& context)
void Canvas::render(base::RenderContext* context)
{
for (auto child : m_children)
child->render(context);

View File

@ -9,7 +9,6 @@
#define GUI_PRIMITIVES_CANVAS_H_
#include <gui/primitives/UILayout.h>
#include <gui/RenderContext.h>
namespace farmlands {
namespace gui {
@ -21,7 +20,7 @@ namespace layout {
Canvas();
virtual ~Canvas();
virtual void render(RenderContext& context) override;
virtual void render(base::RenderContext* context) override;
};
}

View File

@ -5,8 +5,11 @@
* Author: tibi
*/
#include <graphics/backend/SdlRenderer.h>
#include <gui/primitives/UIElement.h>
using namespace farmlands::graphics::backend;
namespace farmlands
{
namespace gui
@ -24,7 +27,7 @@ UIElement::~UIElement()
{
}
void UIElement::render(RenderContext& context)
void UIElement::render(base::RenderContext* context)
{
if (m_backColor.a > 0)
{
@ -36,8 +39,8 @@ void UIElement::render(RenderContext& context)
(int) m_h
};
SDL_SetRenderDrawColor(context.sdlRenderer->internalRenderer(), m_backColor.r, m_backColor.g, m_backColor.b, m_backColor.a);
SDL_RenderFillRect(context.sdlRenderer->internalRenderer(), &rect);
SDL_SetRenderDrawColor(SdlRenderer::instance().internalRenderer(), m_backColor.r, m_backColor.g, m_backColor.b, m_backColor.a);
SDL_RenderFillRect(SdlRenderer::instance().internalRenderer(), &rect);
}
}

View File

@ -10,7 +10,7 @@
#include <SDL2/SDL.h>
#include <gui/RenderContext.h>
#include <base/RenderContext.h>
namespace farmlands {
namespace gui {
@ -22,7 +22,7 @@ namespace primitives {
UIElement();
virtual ~UIElement();
virtual void render(RenderContext& context);
virtual void render(base::RenderContext* context);
virtual bool handleEvent(SDL_Event& event);
// Getters & setters

View File

@ -5,8 +5,10 @@
* Author: tibi
*/
#include <graphics/backend/SdlRenderer.h>
#include <gui/widgets/TextArea.h>
#include <resources/Resources.h>
#include <resources/ResourceManager.h>
#include <cassert>
#include <vector>
@ -14,12 +16,12 @@
#include <SDL2/SDL_ttf.h>
namespace farmlands
{
namespace gui
{
namespace widgets
{
using namespace farmlands::graphics::backend;
using namespace farmlands::resources;
namespace farmlands {
namespace gui {
namespace widgets {
TextArea::TextArea()
: m_text(),
@ -39,12 +41,12 @@ TextArea::~TextArea()
{
}
void TextArea::render(RenderContext& context)
void TextArea::render(base::RenderContext* context)
{
UIElement::render(context);
// Obtain font
TTF_Font* font = context.resManager->font(m_fontId, m_fontSize * context.uiScale);
TTF_Font* font = ResourceManager::instance().font(m_fontId, m_fontSize * context->uiScale);
if (font == nullptr)
return; // TODO: handle error (maybe log it)
@ -94,7 +96,7 @@ void TextArea::render(RenderContext& context)
dest.y = y() + totalH;
// Draw
SDL_RenderCopy(context.sdlRenderer->internalRenderer(), lineTexture, NULL, &dest);
SdlRenderer::instance().renderTexture(lineTexture, NULL, &dest);
totalH += lineH;
}
@ -256,7 +258,7 @@ void TextArea::wrapText(TTF_Font* font)
m_wrappedText.erase(m_wrappedText.size() - 1);
}
void TextArea::renderLines(RenderContext& context, TTF_Font* font)
void TextArea::renderLines(base::RenderContext* context, TTF_Font* font)
{
// Clean up old textures
for (SDL_Texture* tex : m_renderedLines)
@ -270,7 +272,7 @@ void TextArea::renderLines(RenderContext& context, TTF_Font* font)
for (std::string line : lines)
{
// Render line
SDL_Texture* tex = context.sdlRenderer->renderText(line, font, m_color);
SDL_Texture* tex = SdlRenderer::instance().renderText(line, font, m_color);
m_renderedLines.push_back(tex);
}
}

View File

@ -49,7 +49,7 @@ namespace widgets {
TextArea();
virtual ~TextArea();
virtual void render(RenderContext& context) override;
virtual void render(base::RenderContext* context) override;
// Getters and setters
inline std::string text() const { return m_text; }
@ -71,7 +71,7 @@ namespace widgets {
private:
void wrapText(TTF_Font* font);
void renderLines(RenderContext& context, TTF_Font* font);
void renderLines(base::RenderContext* context, TTF_Font* font);
std::string m_text;
std::string m_wrappedText;

40
src/input/GameKey.h Normal file
View File

@ -0,0 +1,40 @@
/*
* GameKeys.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef INPUT_GAMEKEY_H_
#define INPUT_GAMEKEY_H_
namespace farmlands {
namespace input {
enum GameKey
{
Right,
Up,
Left,
Down,
Action,
Action2,
Run,
AltRight,
AltUp,
AltLeft,
AltDown,
Debug_ZoomIn,
Debug_ZoomOut,
_Count
};
}
}
#endif /* INPUT_GAMEKEY_H_ */

View File

@ -0,0 +1,40 @@
/*
* GameKeyConfiguration.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <input/GameKeyConfiguration.h>
namespace farmlands {
namespace input {
/**
* Default keyboard configuration
*/
const GameKeyConfiguration GameKeyConfiguration::defaultConfig =
{
.keys =
{
SDL_SCANCODE_RIGHT, // Right
SDL_SCANCODE_UP, // Up
SDL_SCANCODE_LEFT, // Left
SDL_SCANCODE_DOWN, // Down
SDL_SCANCODE_SPACE, // Action
SDL_SCANCODE_LCTRL, // Action2
SDL_SCANCODE_LSHIFT, // Run
SDL_SCANCODE_D, // AltRight
SDL_SCANCODE_W, // AltUp
SDL_SCANCODE_A, // AltLeft
SDL_SCANCODE_S, // AltRight
SDL_SCANCODE_KP_PLUS, // Debug_ZoomIn,
SDL_SCANCODE_KP_MINUS, // Debug_ZoomOut,
}
};
}
}

View File

@ -0,0 +1,30 @@
/*
* GameKeyConfiguration.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef INPUT_GAMEKEYCONFIGURATION_H_
#define INPUT_GAMEKEYCONFIGURATION_H_
#include <input/GameKey.h>
#include <SDL2/SDL_scancode.h>
namespace farmlands {
namespace input {
/**
* Defines a keyboard configuration
*/
struct GameKeyConfiguration
{
SDL_Scancode keys[GameKey::_Count];
static const GameKeyConfiguration defaultConfig;
};
} /* namespace input */
} /* namespace farmlands */
#endif /* INPUT_GAMEKEYCONFIGURATION_H_ */

81
src/input/Input.cpp Normal file
View File

@ -0,0 +1,81 @@
/*
* Input.cpp
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#include <input/Input.h>
#include <SDL2/SDL.h>
namespace farmlands {
namespace input {
Input Input::s_instance;
Input& Input::instance()
{
return s_instance;
}
Input::Input()
: m_config(GameKeyConfiguration::defaultConfig)
{
}
Input::~Input()
{
}
void Input::initialize()
{
}
bool Input::pressed(GameKey key)
{
const Uint8* keys = SDL_GetKeyboardState(NULL);
SDL_Scancode code = m_config.keys[key];
return keys[code];
}
bool Input::down(GameKey key, SDL_Event& event)
{
if (event.type == SDL_KEYDOWN)
{
SDL_Scancode code = m_config.keys[key];
return event.key.keysym.scancode == code;
}
return false;
}
float Input::getX()
{
// Keyboard
if (pressed(GameKey::Right) || pressed(GameKey::AltRight))
return 1.0f;
else if (pressed(GameKey::Left) || pressed(GameKey::AltLeft))
return -1.0f;
// TODO: controller
return 0.0f;
}
float Input::getY()
{
// Keyboard
if (pressed(GameKey::Down) || pressed(GameKey::AltDown))
return 1.0f;
else if (pressed(GameKey::Up) || pressed(GameKey::AltUp))
return -1.0f;
// TODO: controller
return 0.0f;
}
} /* namespace input */
} /* namespace farmlands */

42
src/input/Input.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Input.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef INPUT_INPUT_H_
#define INPUT_INPUT_H_
#include <input/GameKey.h>
#include <input/GameKeyConfiguration.h>
#include <SDL2/SDL.h>
namespace farmlands {
namespace input {
class Input
{
public:
static Input& instance();
virtual ~Input();
void initialize();
bool pressed(GameKey key);
bool down(GameKey key, SDL_Event& event);
float getX();
float getY();
private:
Input();
static Input s_instance;
GameKeyConfiguration m_config;
};
} /* namespace input */
} /* namespace farmlands */
#endif /* INPUT_INPUT_H_ */

View File

@ -13,14 +13,15 @@ namespace model {
enum Direction
{
East = 0,
NorthEast = 1,
None = 0,
East = 1,
North = 2,
NorthWest = 3,
West = 4,
SouthWest = 5,
South = 6,
SouthEast = 7
South = 8,
NorthEast = North | East,
NorthWest = North | West,
SouthWest = South | West,
SouthEast = South | East
};
}

View File

@ -21,6 +21,8 @@ namespace model {
float lastDeltaX, lastDeltaY;
Direction direction;
bool attacking;
uint32_t attackTimeLeft;
int inventorySelection = -1;
int inventory[PLAYER_INVENTORY_SIZE];

28
src/model/TileMask.h Normal file
View File

@ -0,0 +1,28 @@
/*
* TileMask.h
*
* Created on: Nov 30, 2016
* Author: tibi
*/
#ifndef MODEL_TILEMASK_H_
#define MODEL_TILEMASK_H_
namespace farmlands {
namespace model {
/**
* Tile mask used to determine what tile to use depending on neighboring tiles.
*/
enum class TileMask
{
Right = 1,
Top = 2,
Left = 4,
Bottom = 8
};
}
}
#endif /* MODEL_TILEMASK_H_ */

View File

@ -8,8 +8,8 @@
#ifndef STORAGE_RESOURCEMANAGER_H_
#define STORAGE_RESOURCEMANAGER_H_
#include <base/Sprite.h>
#include <model/Level.h>
#include <model/Sprite.h>
#include <vector>
#include <map>
@ -40,32 +40,37 @@ namespace resources {
} texture;
model::Level* level;
model::Sprite* sprite;
base::Sprite* sprite;
};
};
typedef uint32_t ResourceId;
class ResourceManager
{
public:
ResourceManager();
virtual ~ResourceManager();
void initialize(GameState* gameState);
static ResourceManager& instance();
~ResourceManager();
void initialize();
// Loading routines
void loadMainMenu();
void loadGameAssets();
void loadLevel(int levelId);
void loadLevel(ResourceId levelId);
TTF_Font* font(int id, int pointSize);
SDL_Texture* texture(int id);
model::Level* level(int id);
model::Sprite* sprite(int id);
TTF_Font* font(ResourceId id, int pointSize);
SDL_Texture* texture(ResourceId id);
model::Level* level(ResourceId id);
base::Sprite* sprite(ResourceId id);
private:
ResourceManager();
/**
* Obtains the path of the resource in native format.
*/
std::string getPath(int resourceId);
std::string getPath(ResourceId resourceId);
/**
* Obtains the id of a resource based on its path.
@ -73,13 +78,12 @@ namespace resources {
*/
int getId(std::string resourcePath);
void loadFont(int fontId, int pointSize);
void loadTexture(int textureId);
void loadSprite(int spriteId);
void loadLevelLayer(model::Level* level, size_t layerNumber, int resourceId);
void loadFont(ResourceId fontId, int pointSize);
void loadTexture(ResourceId textureId);
void loadSprite(ResourceId spriteId);
void loadLevelLayer(model::Level* level, size_t layerNumber, ResourceId resourceId);
// State
GameState* m_gameState;
static ResourceManager s_instance;
// Loaded resources
LoadedResource* m_loadedResources;

View File

@ -14,9 +14,15 @@
namespace farmlands {
namespace resources {
ResourceManager ResourceManager::s_instance;
ResourceManager& ResourceManager::instance()
{
return s_instance;
}
ResourceManager::ResourceManager()
: m_gameState(nullptr),
m_loadedResources(new LoadedResource[sizeof(RInfo) / sizeof(RInfo[0])])
: m_loadedResources(new LoadedResource[sizeof(RInfo) / sizeof(RInfo[0])])
{
for(size_t i = 0; i < sizeof(RInfo) / sizeof(RInfo[0]); i++)
m_loadedResources[i].loaded = false;
@ -53,9 +59,8 @@ ResourceManager::~ResourceManager()
delete[] m_loadedResources;
}
void ResourceManager::initialize(GameState* gameState)
void ResourceManager::initialize()
{
m_gameState = gameState;
}
void ResourceManager::loadMainMenu()
@ -67,7 +72,7 @@ void ResourceManager::loadGameAssets()
loadSprite(R::Sprites::Player);
}
std::string ResourceManager::getPath(int resourceId)
std::string ResourceManager::getPath(ResourceId resourceId)
{
Assert(resourceId >= 0 && resourceId < sizeof(RInfo) / sizeof(RInfo[0]), "Resource id out of bounds.");

View File

@ -14,7 +14,7 @@
namespace farmlands {
namespace resources {
TTF_Font* ResourceManager::font(int id, int pointSize)
TTF_Font* ResourceManager::font(ResourceId id, int pointSize)
{
// Open from cache
auto it = m_fontCache.find(FONTID(id, pointSize));

View File

@ -19,7 +19,7 @@ using namespace nlohmann;
namespace farmlands {
namespace resources {
void ResourceManager::loadLevelLayer(model::Level* level, size_t layer, int resourceId)
void ResourceManager::loadLevelLayer(model::Level* level, size_t layer, ResourceId resourceId)
{
Assert(RInfo[resourceId].type == ResourceType::LevelLayer, "Resource must be a level layer.");
@ -54,7 +54,7 @@ void ResourceManager::loadLevelLayer(model::Level* level, size_t layer, int reso
}
void ResourceManager::loadLevel(int levelId)
void ResourceManager::loadLevel(ResourceId levelId)
{
// Sanity checks
Assert(RInfo[levelId].type == ResourceType::Level, "Resource must be a level!");
@ -100,7 +100,7 @@ void ResourceManager::loadLevel(int levelId)
m_loadedResources[levelId].level = level;
}
model::Level* ResourceManager::level(int id)
model::Level* ResourceManager::level(ResourceId id)
{
Assert(RInfo[id].type == ResourceType::Level, "Resource must be a level!");

View File

@ -4,7 +4,7 @@
* Created on: Nov 29, 2016
* Author: tibi
*/
#include <model/Sprite.h>
#include <base/Sprite.h>
#include <resources/ResourceManager.h>
#include <resources/Resources.h>
#include <utils/Assert.h>
@ -15,12 +15,13 @@
#include <json.hpp>
using namespace nlohmann;
using namespace farmlands::base;
namespace farmlands {
namespace resources {
void ResourceManager::loadSprite(int spriteId)
void ResourceManager::loadSprite(ResourceId spriteId)
{
Assert(RInfo[spriteId].type == ResourceType::Sprite, "Resource must be a sprite!");
if (m_loadedResources[spriteId].loaded)
@ -36,7 +37,7 @@ void ResourceManager::loadSprite(int spriteId)
json spriteJs;
spriteIn >> spriteJs;
model::Sprite* sprite = new model::Sprite();
Sprite* sprite = new Sprite();
sprite->anchorX = spriteJs.value("anchorX", 0.0f);
sprite->anchorY = spriteJs.value("anchorY", 0.0f);
@ -44,14 +45,14 @@ void ResourceManager::loadSprite(int spriteId)
for (auto state : statesJs)
{
model::SpriteState spriteState;
SpriteState spriteState;
spriteState.name = state.value("name", std::string());
// Obtain frames
json framesJs = state.at("frames");
for (auto frame : framesJs)
{
model::Frame spriteFrame;
Frame spriteFrame;
spriteFrame.tileSetCell = frame.value("cell", 0u);
spriteFrame.width = frame.value("width", 0u);
spriteFrame.height = frame.value("height", 0u);
@ -74,7 +75,7 @@ void ResourceManager::loadSprite(int spriteId)
m_loadedResources[spriteId].sprite = sprite;
}
model::Sprite* ResourceManager::sprite(int id)
Sprite* ResourceManager::sprite(ResourceId id)
{
Assert(RInfo[id].type == ResourceType::Sprite, "Resource must be a sprite!");

View File

@ -4,15 +4,18 @@
* Created on: Nov 12, 2016
* Author: tibi
*/
#include <GameState.h>
#include <graphics/backend/SdlRenderer.h>
#include <resources/ResourceManager.h>
#include <resources/Resources.h>
#include <utils/Assert.h>
using namespace farmlands::graphics::backend;
namespace farmlands {
namespace resources {
void ResourceManager::loadTexture(int resourceId)
void ResourceManager::loadTexture(ResourceId resourceId)
{
Assert(RInfo[resourceId].type == ResourceType::Texture, "Resource must be a texture!");
if (m_loadedResources[resourceId].loaded)
@ -24,7 +27,7 @@ void ResourceManager::loadTexture(int resourceId)
if (surface == NULL)
THROW(utils::ResourceLoadException, "Failed to load texture " + texturePath);
SDL_Texture* texture = SDL_CreateTextureFromSurface(m_gameState->sdlRenderer.internalRenderer(), surface);
SDL_Texture* texture = SDL_CreateTextureFromSurface(SdlRenderer::instance().internalRenderer(), surface);
if (texture == NULL)
THROW(utils::ResourceLoadException, "Failed to create texture " + texturePath);
@ -34,7 +37,7 @@ void ResourceManager::loadTexture(int resourceId)
m_loadedResources[resourceId].texture.texture = texture;
}
SDL_Texture* ResourceManager::texture(int id)
SDL_Texture* ResourceManager::texture(ResourceId id)
{
Assert(RInfo[id].type == ResourceType::Texture, "Resource must be a texture!");