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

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