From bf34d835e4ea49bd4ef2e5de2b1fea2fd3c95adc Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Tue, 29 Nov 2022 18:19:25 +0100 Subject: Start to use a normal game structure: - App class that has a StateStack - StateStack - Some State definiton and a minimal TitleState to use as a template - Makefile updated accordingly - Delete old code that was there for testing --- Makefile | 11 +++++- src/app.cpp | 71 +++++++++++++++++++++++++++++++++++ src/app.h | 27 +++++++++++++ src/main.cpp | 96 +++-------------------------------------------- src/states.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++ src/states.h | 93 +++++++++++++++++++++++++++++++++++++++++++++ src/states/ids.h | 19 ++++++++++ src/states/titleState.cpp | 25 ++++++++++++ src/states/titleState.h | 14 +++++++ src/vec.h | 7 ---- 10 files changed, 352 insertions(+), 100 deletions(-) create mode 100644 src/app.cpp create mode 100644 src/app.h create mode 100644 src/states.cpp create mode 100644 src/states.h create mode 100644 src/states/ids.h create mode 100644 src/states/titleState.cpp create mode 100644 src/states/titleState.h delete mode 100644 src/vec.h diff --git a/Makefile b/Makefile index 47549f6..51ab372 100644 --- a/Makefile +++ b/Makefile @@ -14,11 +14,14 @@ OBJS = $(addsuffix .o, $(addprefix $(OBJDIR)/, $(BASENM)) ) GRAPHICS_BASENM = $(notdir $(basename $(wildcard src/graphics/*.cpp))) GRAPHICS_OBJS = $(addsuffix .o, $(addprefix $(OBJDIR)/graphics/, $(GRAPHICS_BASENM)) ) +STATES_BASENM = $(notdir $(basename $(wildcard src/states/*.cpp))) +STATES_OBJS = $(addsuffix .o, $(addprefix $(OBJDIR)/states/, $(STATES_BASENM)) ) + all: $(TARGET) -$(TARGET): $(OBJS) $(GRAPHICS_OBJS) +$(TARGET): $(OBJS) $(GRAPHICS_OBJS) $(STATES_OBJS) echo $(GRAPHICS_OBJS) - $(CC) $(OBJS) $(GRAPHICS_OBJS) -o $(TARGET) $(LIBS) + $(CC) $(OBJS) $(GRAPHICS_OBJS) $(STATES_OBJS) -o $(TARGET) $(LIBS) $(OBJS): $(OBJDIR)/%.o: $(SRCDIR)/%.cpp mkdir -p $(OBJDIR) @@ -28,6 +31,10 @@ $(GRAPHICS_OBJS): $(OBJDIR)/graphics/%.o: $(SRCDIR)/graphics/%.cpp mkdir -p $(OBJDIR)/graphics/ $(CC) $(CFLAGS) -o $@ -c $< +$(STATES_OBJS): $(OBJDIR)/states/%.o: $(SRCDIR)/states/%.cpp + mkdir -p $(OBJDIR)/states/ + $(CC) $(CFLAGS) -o $@ -c $< + clean: rm -r $(OBJDIR) $(TARGET) diff --git a/src/app.cpp b/src/app.cpp new file mode 100644 index 0000000..30bccdf --- /dev/null +++ b/src/app.cpp @@ -0,0 +1,71 @@ +#include "app.h" +#include "states/ids.h" +#include "states/titleState.h" + +const sf::Time App::TIME_PER_FRAME = sf::seconds(1.f/60.f); + + +App::App() + : textures_() + , window_(sf::VideoMode(640, 480), "Demo game" ) + , stateStack_( State::Context{&window_, &textures_} ) + //, player_() +{ + window_.setFramerateLimit(60); + + registerStates(); + stateStack_.push(States::Id::Title); +} + +void App::run(){ + // Make the app render the current state, so it needs a state stack too + sf::Clock clock; + sf::Time timeSinceLastUpdate = sf::Time::Zero; + + while(window_.isOpen()){ + + + // TODO Maybe move to a fixed time game loop (I prefer that) + sf::Time dt = clock.restart(); + timeSinceLastUpdate += dt; + + while (timeSinceLastUpdate > TIME_PER_FRAME) + { + timeSinceLastUpdate -= TIME_PER_FRAME; + + processInput(); + update(TIME_PER_FRAME); + + // Check inside this loop, because stack might be empty before update() call + if (stateStack_.isEmpty()) window_.close(); + } + + render(); + } +} + +void App::render(){ + window_.clear(); + stateStack_.render(); + window_.display(); +} + + +void App::update(sf::Time dt){ + stateStack_.update(dt); +} + +void App::processInput(){ + sf::Event event; + while (window_.pollEvent(event)) + { + stateStack_.handleEvent(event); + + if (event.type == sf::Event::Closed) + window_.close(); + } +} + +void App::registerStates(){ + stateStack_.registerState(States::Id::Title); +} diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..7d667f9 --- /dev/null +++ b/src/app.h @@ -0,0 +1,27 @@ +#ifndef APP_H +#define APP_H + +#include +#include "states.h" +#include "resourceManager.h" +#include "resourceIds.h" + +class App { + static const sf::Time TIME_PER_FRAME; + private: + TextureManager textures_; + sf::RenderWindow window_; + StateStack stateStack_; + + void processInput(); + void render(); + void update(sf::Time dt); + + void registerStates(); + + public: + App(); + void run(); +}; + +#endif // APP_H diff --git a/src/main.cpp b/src/main.cpp index 45c2b3a..65b7d5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,93 +1,7 @@ -#include -#include -#include -#include +#include "app.h" -#include "graphics/animation.h" -#include "entity.h" -#include "resourceManager.h" -#include "resourceIds.h" - -namespace gph = Graphics; - - -class Unit: Entity{ - private: - sf::Texture spritesheet_; - public: - - gph::Animation ne_shooting_animation; - gph::Animation se_shooting_animation; - gph::Animation& current_animation = ne_shooting_animation; - sf::Sprite sprite; - - sf::Vector2f position; - sf::Vector2f size; - sf::Vector2f speed; - - - Unit(TextureManager& textures){ - ne_shooting_animation = gph::Animation(textures.get(Textures::Id::Player_NE_Shooting), 6, sf::milliseconds(100 * 6), false); - se_shooting_animation = gph::Animation(textures.get(Textures::Id::Player_SE_Shooting), 6, sf::milliseconds(100 * 6), false); - current_animation = ne_shooting_animation; - - position = sf::Vector2f{0,0}; - speed = sf::Vector2f{0,0}; - } - void walk(float vx, float vy){ - speed.y = vy; - speed.x = vx; - current_animation.reset(); - // current_animation = &walking_animation; - } - void shoot(){ - current_animation.reset(); - // current_animation = &shooting_animation; - } - ~Unit(){}; - void update(sf::Time dt){ - current_animation.update( dt); - } -}; - - -int main() -{ - sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game"); - - sf::Event event; - sf::Clock clock; - - TextureManager textureManager; - textureManager.load(Textures::Id::Player_NE_Shooting, "assets/img/Player/angle2-shooting.png"); - textureManager.load(Textures::Id::Player_SE_Shooting, "assets/img/Player/angle1-shooting.png"); - - Unit unit = Unit(textureManager); - - renderWindow.setFramerateLimit(60); - - sf::Time elapsed_time = sf::Time::Zero; - bool once = true; - - while (renderWindow.isOpen()){ - sf::Time dt = clock.getElapsedTime(); - elapsed_time += dt; - clock.restart(); - while (renderWindow.pollEvent(event)){ - if (event.type == sf::Event::EventType::Closed) - renderWindow.close(); - } - - if(elapsed_time > sf::milliseconds(4000) && once){ - unit.current_animation.reset(); - unit.current_animation = unit.se_shooting_animation; - once = false; - } - - unit.update(dt); - - renderWindow.clear(); - renderWindow.draw(unit.current_animation); - renderWindow.display(); - } +int main (int argc, char* argv[]){ + App app; + app.run(); + return 0; } diff --git a/src/states.cpp b/src/states.cpp new file mode 100644 index 0000000..d7799b3 --- /dev/null +++ b/src/states.cpp @@ -0,0 +1,89 @@ +#include "states.h" + +// StateStack + +StateStack::StateStack(State::Context context) + : pendingOps_() + , context_(context) +{ +} + +void StateStack::update(sf::Time dt){ + for(State::Ptr &s: states_) { + s->update(dt); + } +} + +void StateStack::render(){ + for(State::Ptr &s: states_) { + s->render(); + } +} + +void StateStack::handleEvent(const sf::Event &event){ + for(State::Ptr &s: states_) { + s->handleEvent(event); + } +} + +void StateStack::requestOp(StateStack::Op op){ + pendingOps_.push_back(op); +} + +void StateStack::push(States::Id state){ + states_.push_back(std::move(createState(state))); +} + +void StateStack::pop(){ + states_.pop_back(); +} + +void StateStack::clear(){ + states_.clear(); +} + +void StateStack::applyPending(){ + for(StateStack::Op &op: pendingOps_) { + switch(op.id){ + case StateStack::OpId::Push: + push(op.state); + break; + case StateStack::OpId::Pop: + pop(); + break; + case StateStack::OpId::Clear: + clear(); + break; + } + } + pendingOps_.clear(); +} + +bool StateStack::isEmpty() const{ + return states_.empty(); +} + +State::Ptr StateStack::createState(States::Id id){ + return stateFactory_[id](); +} + + +// State + +State::State(StateStack &stack, State::Context context) + : context_(context) + , stack_(&stack) +{} + +void State::requestPush(States::Id state_id){ + StateStack::Op op {StateStack::OpId::Push, state_id}; + stack_->requestOp(op); +} +void State::requestPop(){ + StateStack::Op op {StateStack::OpId::Push}; + stack_->requestOp(op); +} +void State::requestClear(){ + StateStack::Op op {StateStack::OpId::Clear}; + stack_->requestOp(op); +} diff --git a/src/states.h b/src/states.h new file mode 100644 index 0000000..57898a6 --- /dev/null +++ b/src/states.h @@ -0,0 +1,93 @@ +#ifndef STATES_H +#define STATES_H + +#include +#include +#include "states/ids.h" +#include "resourceManager.h" +#include "resourceIds.h" + +class StateStack; + +class State { + public: + using Ptr = std::unique_ptr; + + struct Context{ + sf::RenderWindow *window; + TextureManager *textures; + // add IO controller + }; + State(StateStack &stack, State::Context context); + virtual void update(sf::Time dt) =0; + virtual void render() =0; + virtual void handleEvent(const sf::Event &event) =0; + + protected: + void requestPush(States::Id state_id); + void requestPop(); + void requestClear(); + + Context context_; + StateStack *stack_; +}; + +class StateStack{ + public: + enum class OpId{ + Push, + Pop, + Clear + }; + + struct Op{ + Op(OpId op_id, States::Id state_id = States::None) + : id(op_id) + , state(state_id){} + OpId id; + States::Id state; + }; + + private: + std::vector pendingOps_; + std::vector states_; + std::map> stateFactory_; + + State::Context context_; + + public: + StateStack(State::Context context); + + void update(sf::Time dt); + void render(); + void handleEvent(const sf::Event &event); + + void requestOp(Op op); + + void push(States::Id state_id); + void pop(); + void clear(); + + void applyPending(); + + bool isEmpty() const; + + template + void registerState(States::Id id); + + + State::Ptr createState(States::Id id); +}; + + +template +void StateStack::registerState(States::Id id){ + static_assert(std::is_base_of::value, + "StateStack: attempting to registerState something that is not a State"); + stateFactory_[id] = [this] () + { + return State::Ptr(new T(*this, context_)); + }; +} + +#endif // STATES_H diff --git a/src/states/ids.h b/src/states/ids.h new file mode 100644 index 0000000..d8c69da --- /dev/null +++ b/src/states/ids.h @@ -0,0 +1,19 @@ +#ifndef STATE_IDS_H +#define STATE_IDS_H + +namespace States +{ + enum Id + { + None, + Title, + Menu, + Game, + Loading, + Pause, + Settings, + Tutorial + }; +} + +#endif // STATE_IDS_H diff --git a/src/states/titleState.cpp b/src/states/titleState.cpp new file mode 100644 index 0000000..a0d17c8 --- /dev/null +++ b/src/states/titleState.cpp @@ -0,0 +1,25 @@ +#include "titleState.h" +#include + +TitleState::TitleState(StateStack &stack, State::Context context) + : State(stack, context) +{ + +} + +void TitleState::update(sf::Time dt){ + +} + +void TitleState::render(){ + sf::RectangleShape rectangle; + rectangle.setSize(sf::Vector2f(100, 50)); + rectangle.setOutlineColor(sf::Color::Red); + rectangle.setOutlineThickness(5); + rectangle.setPosition(10, 20); + context_.window->draw(rectangle); +} + +void TitleState::handleEvent(const sf::Event &event){ + +} diff --git a/src/states/titleState.h b/src/states/titleState.h new file mode 100644 index 0000000..8b914d8 --- /dev/null +++ b/src/states/titleState.h @@ -0,0 +1,14 @@ +#ifndef STATES_TITLESTATE_H +#define STATES_TITLESTATE_H + +#include "../states.h" + +class TitleState : public State{ + public: + TitleState(StateStack &stack, State::Context context); + virtual void update(sf::Time dt) override; + virtual void render() override; + virtual void handleEvent(const sf::Event &event) override; +}; + +#endif // STATES_TITLESTATE_H diff --git a/src/vec.h b/src/vec.h deleted file mode 100644 index a983256..0000000 --- a/src/vec.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef VEC_H -#define VEC_H -struct Vec2{ - int x; - int y; -}; -#endif // VEC_H -- cgit v1.2.3