summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEkaitz Zarraga <ekaitz@elenq.tech>2022-11-29 18:19:25 +0100
committerEkaitz Zarraga <ekaitz@elenq.tech>2022-11-29 18:19:25 +0100
commitbf34d835e4ea49bd4ef2e5de2b1fea2fd3c95adc (patch)
treeb1970daefeb7a6a8f406c83126de49c3b928484e
parentcbf859a37aa6b16db7e53cf75405cda720617392 (diff)
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
-rw-r--r--Makefile11
-rw-r--r--src/app.cpp71
-rw-r--r--src/app.h27
-rw-r--r--src/main.cpp96
-rw-r--r--src/states.cpp89
-rw-r--r--src/states.h93
-rw-r--r--src/states/ids.h19
-rw-r--r--src/states/titleState.cpp25
-rw-r--r--src/states/titleState.h14
-rw-r--r--src/vec.h7
10 files changed, 352 insertions, 100 deletions
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<TitleState>(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 <SFML/Graphics.hpp>
+#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<SFML/Graphics.hpp>
-#include<vector>
-#include<cstdio>
-#include<cmath>
+#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 <SFML/Graphics.hpp>
+#include <functional>
+#include "states/ids.h"
+#include "resourceManager.h"
+#include "resourceIds.h"
+
+class StateStack;
+
+class State {
+ public:
+ using Ptr = std::unique_ptr<State>;
+
+ 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<Op> pendingOps_;
+ std::vector<State::Ptr> states_;
+ std::map<States::Id, std::function<State::Ptr()>> 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<typename T>
+ void registerState(States::Id id);
+
+
+ State::Ptr createState(States::Id id);
+};
+
+
+template<typename T>
+void StateStack::registerState(States::Id id){
+ static_assert(std::is_base_of<State, T>::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 <SFML/Graphics.hpp>
+
+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