C++ && SFML game class [duplicate]

Im making c++ && sfml game for my teacher.

I need to make game class naturaly I make constructor and destructor, but I cant figure out what methods should game class contain.

curently it has these : init() – init all variables and other stuff

(before I moved to c++ I was making games in HTML,JS but I never used OOP there. So I have no exp. doing games like this)

SFML test app feels slow/clunky

Similar to this question https://stackoverflow.com/questions/33904998/sfml-extremly-slow-irregular-framerate, I have created a small program to experiment with SFML

I have a very simple SFML test application on ubuntu 20.04 using SFML 2.5.1. There are three circles. One under user control and one bouncing left to right.

Problems:

  • The one moving left to right is not very fast.
  • Even so the one under user control cannot keep up with it.
  • I am using a delta of 1 (pixel per frame?). If I increase this motion is jumpy rather than smooth.

Put simply it feels clunky. So I am assuming I’ve made a noob error of some kind. However, this seems to be the same basic SFML game loop commonly used. How can I make this fast and slick feeling instead?

#include <SFML/Graphics.hpp>  int main() {     sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");     const float FPS = 120.0f; //The desired FPS. (The number of updates each second).     bool redraw = true;      //Do I redraw everything on the screen?     window.setFramerateLimit(FPS);     sf::CircleShape shape2(100.f);     sf::CircleShape shape(10.f);     sf::Clock clock;     shape2.setFillColor(sf::Color::Green);     shape.setFillColor(sf::Color::Red);      sf::CircleShape alien(10.f);     alien.setFillColor(sf::Color::Yellow);     auto alienSpeed = 1.f;         while (window.isOpen())     {         sf::Event event;         float xDelta = 0;         float yDelta = 0;         bool moved = false;         if (window.pollEvent(event))         {            if (event.type == sf::Event::Closed)            {               window.close();            }            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))             {                xDelta -= 1.f;                moved = true;             }             else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))             {                xDelta += 1.f;                moved = true;             }             if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))             {                yDelta -= 1.f;                moved = true;             }             else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))             {                yDelta += 1.f;                moved = true;             }         }          if (xDelta !=0 || yDelta != 0)         {            shape.move(xDelta,yDelta);         }          alien.move(alienSpeed, 0.f);         if (alien.getPosition().x >= 190)         {            alienSpeed = -1.f;         }         else if (alien.getPosition().x <= 0)         {            alienSpeed = 1.f;         }          if(redraw)         {            window.clear();            window.draw(shape2);            window.draw(shape);            window.draw(alien);            window.display();         }     }      return 0; } 

Note: cross posted to https://en.sfml-dev.org/forums/index.php?topic=27617.0

Beginner’s Snake Game using SFML and C++

Never really got past the beginner stage of programming so I’m trying to get better this Summer. I think games are a fun way to learn how to program so I decided to start with snake. I’d appreciate any feedback or learning resources you guys think might be helpful. Cheers!

How it Works

From the menu screen, when the user presses play, the game initializes (the snake body, food generator, and game clock are dynamically allocated). I keep these game elements in an anonymous namespace but I’m not sure if that’s appropriate. After initialization, the game loop runs. Basically, Check for Collisions -> Check if Eating -> Update Snake Nodes -> Render and Display. I left out the supporting source files for food generation, the snakebody, etc for the sake of brevity, but let me know if you’d like me to share that.

    #include "Game.h"      namespace     {         SnakeBody *snakebody;         FoodGenerator *foodgenerator;         sf::Clock *gameclock;     }      void Game::Start()     {         if (mGameState != UNINITIALIZED)             return;          mMainWindow.create(sf::VideoMode(windowparameters::RESOLUTION_X, windowparameters::RESOLUTION_Y, windowparameters::COLOR_DEPTH), "Snake!");         mGameState = SHOWING_MENU;          while (mGameState != EXITING)             GameLoop();          mMainWindow.close();     }      void Game::ShowMenuScreen()     {         MainMenu menuScreen;         MainMenu::MenuResult result = menuScreen.Show(mMainWindow);          switch (result)         {         case MainMenu::Exit:             mGameState = EXITING;             break;          case MainMenu::Play:             mGameState = RUNNING;             break;         }     }      Game::GameState Game::WaitForEnterOrExit()     {         GameState nextstate = GAMEOVER;         sf::Event currentevent;          while (nextstate != EXITING && nextstate != RUNNING)         {             while (mMainWindow.pollEvent(currentevent))             {                 if (currentevent.type == sf::Event::EventType::KeyPressed &&                      sf::Keyboard::isKeyPressed(sf::Keyboard::Enter))                 {                     nextstate = RUNNING;                 }                 else if (currentevent.type == sf::Event::EventType::Closed)                 {                     nextstate = EXITING;                 }             }         }          return nextstate;     }      void Game::InitializeGameElements()     {         snakebody = new SnakeBody();         foodgenerator = new FoodGenerator(windowparameters::RESOLUTION_X, windowparameters::RESOLUTION_Y, windowparameters::UNIT_SPACING);         gameclock = new sf::Clock();     }      void Game::CleanupGameElements()     {         delete(gameclock);         delete(snakebody);         delete(foodgenerator);     }      void Game::HandleEvents()     {         sf::Event currentevent;          while (mMainWindow.pollEvent(currentevent))         {             if (currentevent.type == sf::Event::EventType::KeyPressed)             {                 if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))                 {                     snakebody->RedirectHead(SnakeBody::LEFT);                 }                 else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))                 {                     snakebody->RedirectHead(SnakeBody::RIGHT);                 }                 else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))                 {                     snakebody->RedirectHead(SnakeBody::UP);                 }                 else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))                 {                     snakebody->RedirectHead(SnakeBody::DOWN);                 }                 break;             }             else if (currentevent.type == sf::Event::EventType::Closed)             {                 mGameState = EXITING;                 mMainWindow.close();             }         }     }      void Game::GameTick()     {         // tick scene         if (gameclock->getElapsedTime().asMilliseconds() >= windowparameters::TIC_RATE_IN_MS)         {             // Check Collision with body             if (snakebody->CheckCollision())                 mGameState = GAMEOVER;              else if (snakebody->CheckEating(foodgenerator->mGraphic))             {                 snakebody->IncrementSegments();                 foodgenerator->mUneaten = false;                  std::cout << "SCORE = " << snakebody->mNumSegments << std::endl;             }              // update snake             snakebody->UpdateSegments(0, windowparameters::RESOLUTION_X, 0, windowparameters::RESOLUTION_Y);              // update food             if (!foodgenerator->mUneaten)                 foodgenerator->Generate(snakebody);              // reset screen, render, display             mMainWindow.clear(sf::Color(230, 230, 230));              mMainWindow.draw(foodgenerator->mGraphic);             snakebody->DrawSegments(mMainWindow);              mMainWindow.display();             gameclock->restart();         }     }      void Game::GameLoop()     {         while (true)         {             switch (mGameState)             {             case SHOWING_MENU:                 ShowMenuScreen();                 break;              case GAMEOVER:                 mGameState = WaitForEnterOrExit();                 break;              case RUNNING:                  InitializeGameElements();                  // run game loop                 while (mMainWindow.isOpen() && mGameState == RUNNING)                 {                     HandleEvents();                     GameTick();                 }                  CleanupGameElements();                 break;              case EXITING:                 mMainWindow.close();                 break;              default:                 mMainWindow.close();                 break;             }         }     }      // Because Game is a static class, the member variables need to be instantiated MANUALLY     Game::GameState Game::mGameState = Game::UNINITIALIZED;     sf::RenderWindow Game::mMainWindow; ``` 

Improved snake game in sfml (C++) #2

So i took even more suggestions and improved the code from this question. Some of the suggestions i wasn’t able to implement simply because of my “newbieness” in c++ programming

main.cpp

#include "app.h"  int main() {      // Window aspect ratio is always 4:3, so it only takes     // a value that is then used as the screen width.     // The screen height is calculated in the Game::app class constructor.     Game::app game(800, L"Test");     game.start(); } 

app.h

#pragma once  #include <SFML/Graphics.hpp>  #include "Board.h"  namespace Game {     class app : public sf::Drawable {     public:          app(int windowWidth, const wchar_t* name);         ~app() = default;                 // Runs the app         void start();         void end();      private:          // MEMBER VARIABLES          const float common_divisor;         bool m_iscrashed;         Board board;         sf::RenderWindow window;         sf::Font arial;          // MEMBER FUNCTIONS          void draw(sf::RenderTarget& target, sf::RenderStates states) const override;         void handleEvents();         void updateWindow();     }; } 

app.cpp

#include "app.h"  #include <iostream> #include <thread> #include <chrono>  Game::app::app(int windowWidth, const wchar_t* name)     : common_divisor{ static_cast<float>(windowWidth / Board::width) }, m_iscrashed{ false } {      const int windowHeight = windowWidth * 3 / 4;     if (windowHeight % 3 != 0)         std::wcout << L"Error! the window aspect ratio isn't 4:3.\n";      const char* font_path{ "res/fonts/arial.ttf" };      // Had to make it a normal string because     // the loadFromFile() func doesn't accept wide strings.     if (!arial.loadFromFile(font_path)) {         std::cout << "[ERROR]: Couldn't load font\nFile Path: " << font_path << '\n\n';     }      window.create(sf::VideoMode(windowWidth, windowHeight), name);     window.setFramerateLimit(5); }  void Game::app::handleEvents() {      sf::Event event;      while (window.pollEvent(event)) {         switch (event.type) {          case sf::Event::Closed:             window.close();             break;          case sf::Event::TextEntered:             board.changeDirection(static_cast<char>(event.text.unicode));         }     } }  void Game::app::draw(sf::RenderTarget& target, sf::RenderStates states) const {      for (size_t i = 0, h = Board::height; i < h; ++i) {         for (size_t j = 0, w = Board::width; j < w; ++j) {              Coord here{ j, i };             sf::RectangleShape rect;             rect.setSize({ common_divisor, common_divisor });             rect.setPosition({ common_divisor * j, common_divisor * i });              switch (board.at(here)) {             case Board::WALL:                 target.draw(rect, states);                 break;              case Board::SNAKE:                 rect.setFillColor(sf::Color::Green);                 target.draw(rect, states);                 break;              case Board::FOOD:                 rect.setFillColor(sf::Color::Red);                 target.draw(rect, states);              }          }     }      // Draws the game score     sf::Text text;     text.setFont(arial);     text.setCharacterSize(static_cast<unsigned int>(common_divisor));     text.setPosition({ 0.0f, 0.0f });     text.setString("Score: " + std::to_string(board.score()));     text.setFillColor(sf::Color::Black);      target.draw(text, states); }  // Updates the render window void Game::app::updateWindow() {     if (m_iscrashed)         window.close();      window.clear(sf::Color::Black);     window.draw(*this);     window.display(); }  // Starts the app void Game::app::start() {      while (window.isOpen()) {         handleEvents();         board.update(&m_iscrashed);         updateWindow();     }      end(); }  void Game::app::end() {      std::wcout << L"Game over!\nScore: " << board.score() << L'\n';     std::this_thread::sleep_for((std::chrono::milliseconds)3000);    } 

Board.h

#pragma once      #include "Snake.h"    class Board { public:      Board();     ~Board() = default;      void update(bool* iscrashed);     void changeDirection(char input);      char operator[](int i)    const;     int score()               const;     int at(Coord coord)       const;      static constexpr int width = 20;     static constexpr int height = 15;      static enum Tile {         OPEN = 1,         WALL = 2,         SNAKE = 3,         FOOD = 4     };  private:       // MEMBER VARIABLES     Snake snake;     std::string map;     int m_score = 0;      // MEMBER FUNCTIONS      Coord openRandom();                 // Finds a random open cell     bool place(Coord coord, Tile item); // Sets a cell a certain value       bool isEmpty(Coord coord) const;  }; 

Board.cpp

#include "Board.h"  #include <random>  Board::Board()     : map(static_cast<size_t>(width * height), static_cast<char>(OPEN)) {      // Sets top and bottom walls     for (size_t i = 0; i < width; ++i) {         place({ i, 0 }, WALL);         place({ i, height - 1 }, WALL);     }      // Sets side walls     for (size_t j = 1; j < height - 1; ++j) {         place({ 0, j }, WALL);         place({ width - 1, j }, WALL);     }      place(snake.headLocation(), SNAKE);     place(snake.add(), SNAKE);      place(openRandom(), FOOD); }  int Board::at(Coord coord) const {     return map[coord.y * width + coord.x]; }  bool Board::isEmpty(Coord coord) const {     return at(coord) == OPEN;    }  // Sets a cell a certain value bool Board::place(Coord coord, Tile item) {     if (item != OPEN && !isEmpty(coord))         return false;      map[coord.y * width + coord.x] = item;     return true; }  Coord Board::openRandom() {      std::random_device rd;     std::mt19937 gen(rd());      std::uniform_int_distribution<unsigned int> disX(1, width - 2);     std::uniform_int_distribution<unsigned int> disY(1, height - 2);      Coord coord = { disX(gen), disY(gen) };      while (!isEmpty(coord)) {         coord = { disX(gen), disY(gen) };     }      return coord; }  void Board::update(bool* iscrashed) {     auto newHead{ snake.moveHead() };     place(snake.follow(), OPEN);      switch (at(snake.headLocation())) {     case WALL:     case SNAKE:         *iscrashed = true;         break;      case FOOD:         place(snake.headLocation(), OPEN);         place(snake.add(), SNAKE);         m_score += 100;          place(openRandom(), FOOD);     }      place(newHead, SNAKE);   }  void Board::changeDirection(char input) {     snake.changeDirection(input); }  char Board::operator[](int i) const { return map[i];  } int Board::score()            const { return m_score; } 

Snake.h

#pragma once  #include <vector>  #include "Coord.h"  class Snake { public:      Snake();     ~Snake() = default;      // Changes the dir value based on the input     void changeDirection(char input);      // Adds a piece to the snake and returns its location     Coord add();     size_t size();        /* Moves all pieces and returns     the previous position of last piece */     Coord follow();     Coord moveHead(); // Moves and returns position of new head     Coord headLocation() const;  private:          // MEMBER VARIABLES      struct Snake_segment     {         Coord current, previous;     };      enum direction {         UP = 0,         RIGHT,         DOWN,         LEFT     };      std::vector<Snake_segment> snakeContainer;     direction dir; }; 

Snake.cpp

#include "Snake.h"  // Initializes a two-piece snake Snake::Snake()     : dir { RIGHT } {      Snake_segment head{ {10, 7}, {9, 7} };     snakeContainer.push_back(head);      --head.current.x;     snakeContainer.push_back(head); }  Coord Snake::add() {     snakeContainer.push_back({         snakeContainer.back().previous,         snakeContainer.back().previous     });      return snakeContainer.back().current; }  size_t Snake::size() {     return snakeContainer.size(); }  // Changes the direction based on input (BUGGED) void Snake::changeDirection(char input) {     switch (input) {     case 'w':         if (dir != DOWN) dir = UP;         break;      case 'd':         if (dir != LEFT) dir = RIGHT;         break;      case 's':         if (dir != UP) dir = DOWN;         break;      case 'a':         if (dir != RIGHT) dir = LEFT;      } }  // All the pieces follow the head Coord Snake::follow() {      auto it = snakeContainer.begin();     for (auto prev = it++; it != snakeContainer.end(); ++it, ++prev) {         it->previous = it->current;         it->current = prev->previous;     }      return snakeContainer.back().previous; }   Coord Snake::moveHead() {      snakeContainer[0].previous = snakeContainer[0].current;      switch (dir) {     case UP:         --snakeContainer.front().current.y;         break;      case RIGHT:         ++snakeContainer.front().current.x;         break;      case DOWN:         ++snakeContainer.front().current.y;         break;      case LEFT:         --snakeContainer.front().current.x;      }      return snakeContainer.front().current; }  Coord Snake::headLocation()                   const { return snakeContainer.front().current; } 

Coord.h

#pragma once  struct Coord {     unsigned int x, y; }; 

Improved snake game in SFML (c++)

This is the improved code of a question i asked some days ago, here’s the link

main.cpp

#include "app.h"  int main() {      Game::app game(800, 600, L"Test");     game.start();     game.end(); } 

app.h

#pragma once  #include <SFML/Graphics.hpp>  #include "Snake.h" #include "Board.h"  namespace Game {     class app {     public:          app(int windowWidth, int windowHeight, const wchar_t* name);         ~app() = default;          // Runs the app         void start();         void end();      private:          // MEMBER VARIABLES          const int winWidth, winHeight;         const float common_divisor;         sf::RenderWindow window;         Board board;         sf::Font calibri;          // MEMBER FUNCTIONS          void drawWindow();         void handleEvents();         void updateWindow();     }; } 

app.cpp

#include "app.h"  #include <iostream> #include <thread> #include <chrono>  Game::app::app(int windowWidth, int windowHeight, const wchar_t* name)     : winWidth{ windowWidth }, winHeight{ windowHeight }, common_divisor{ 40.0f } {       if (!calibri.loadFromFile("res/fonts/arial.ttf")) {         std::wcout << L"[ERROR]: Couldn't load font\n";     }      window.create(sf::VideoMode(winWidth, winHeight), name);     window.setFramerateLimit(5); }  // Handles any game event void Game::app::handleEvents() {      sf::Event event;      while (window.pollEvent(event)) {         switch (event.type) {          case sf::Event::Closed:             window.close();             break;          case sf::Event::TextEntered:             board.changeDirection(static_cast<char>(event.text.unicode));         }     } }  // Draws all game objects void Game::app::drawWindow() {      for (size_t i = 0, h = Board::height; i < h; ++i) {         for (size_t j = 0, w = Board::width; j < w; ++j) {              // Draws walls             if (board[i * w + j] == 2) {                 sf::RectangleShape rect;                 rect.setSize({ common_divisor, common_divisor });                  rect.setPosition({ common_divisor * j, common_divisor * i});                 window.draw(rect);             }              // Draws snake             else if (board[i * w + j] == 3) {                  sf::RectangleShape rect;                 rect.setFillColor(sf::Color::Green);                 rect.setSize({ common_divisor, common_divisor });                  rect.setPosition({ common_divisor * j, common_divisor * i });                 window.draw(rect);             }              // Draws food             else if (board[i * w + j] == 4) {                  sf::RectangleShape rect;                 rect.setFillColor(sf::Color::Red);                 rect.setSize({ common_divisor, common_divisor });                  rect.setPosition({ common_divisor * j, common_divisor * i });                 window.draw(rect);             }          }     }      // Draws the game score     sf::Text text;     text.setFont(calibri);     text.setPosition({ 0.0f, 0.0f });     text.setString("Score: " + std::to_string(board.score()));     text.setFillColor(sf::Color::Black);     window.draw(text); }  // Updates the render window void Game::app::updateWindow() {     window.clear(sf::Color::Black);     drawWindow();     window.display(); }  // Starts the app void Game::app::start() {      while (window.isOpen()) {         handleEvents();         board.update(window);         updateWindow();     } }  void Game::app::end() {      std::wcout << L"Game over!\nScore: " << board.score() << L'\n';     std::this_thread::sleep_for((std::chrono::milliseconds)3000);    } 

Snake.h

#pragma once  #include <SFML/Graphics.hpp> #include <vector>  #include "Coord.h"  class Snake { public:      Snake();     ~Snake() = default;      // Changes the dir value based on the input     void changeDirection(char input);      // Adds a piece to the snake and returns its location     Coord add();     size_t size();        /* Moves all pieces and returns     the previous position of last piece */     Coord follow();     Coord moveHead(); // Moves and returns position of new head     Coord headLocation() const;  private:          // MEMBER VARIABLES      struct Snake_segment     {         Coord current, previous;     };      enum direction {         UP = 0,         RIGHT,         DOWN,         LEFT     };      std::vector<Snake_segment> snakeContainer;     direction dir;  public:      Snake_segment operator[](int i) const;   }; 

Snake.cpp

#include "Snake.h"  // Initializes a two-piece snake Snake::Snake()     : dir { RIGHT } {      Snake_segment head{ {10, 7}, {9, 7} };     snakeContainer.push_back(head);      --head.current.x;     snakeContainer.push_back(head); }  Coord Snake::add() {     snakeContainer.push_back({         snakeContainer.back().previous,         snakeContainer.back().previous     });      return snakeContainer.back().current; }  size_t Snake::size() {     return snakeContainer.size(); }  // Changes the direction based on input (BUGGED) void Snake::changeDirection(char input) {     switch (input) {     case 'w':         if (dir != DOWN) dir = UP;         break;      case 'd':         if (dir != LEFT) dir = RIGHT;         break;      case 's':         if (dir != UP) dir = DOWN;         break;      case 'a':         if (dir != RIGHT) dir = LEFT;      } }  // All the pieces follow the head Coord Snake::follow() {      auto it = snakeContainer.begin();     for (auto prev = it++; it != snakeContainer.end(); ++it, ++prev) {         it->previous = it->current;         it->current = prev->previous;     }      return snakeContainer.back().previous; }   Coord Snake::moveHead() {      snakeContainer[0].previous = snakeContainer[0].current;      switch (dir) {     case UP:         --snakeContainer[0].current.y;         break;      case RIGHT:         ++snakeContainer[0].current.x;         break;      case DOWN:         ++snakeContainer[0].current.y;         break;      case LEFT:         --snakeContainer[0].current.x;      }      return snakeContainer.front().current; }  Snake::Snake_segment Snake::operator[](int i) const { return snakeContainer[i];               } Coord Snake::headLocation()                   const { return snakeContainer.front().current;  } 

Board.h

#pragma once  #include "Snake.h"  class Board { public:      Board();     ~Board() = default;      void update(sf::RenderWindow& win);     void changeDirection(char input);     char operator[](int i) const;     int score() const;      static constexpr int width = 20;     static constexpr int height = 15;         private:      enum Tile {         OPEN = 1,         WALL,         SNAKE,         FOOD     };      // MEMBER VARIABLES     Snake snake;     std::string map;     int m_score;      // MEMBER FUNCTIONS      void genFood();     bool place(Coord coord, int item); // Sets a cell a certain value     bool isEmpty(Coord coord) const;     int at(Coord coord)       const;  }; 

Board.cpp

#include "Board.h"  #include <random>  Board::Board()     : m_score{ 0 } {      // Creates a 20x15 grid     map = {         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2     };      genFood(); }  int Board::at(Coord coord) const {     return map[coord.y * width + coord.x]; }  bool Board::isEmpty(Coord coord) const {     return at(coord) == OPEN;    }  // Sets a cell a certain value bool Board::place(Coord coord, int item) {     if (item != OPEN && !isEmpty(coord))         return false;      map[coord.y * width + coord.x] = item;     return true; }  void Board::genFood() {      int fx, fy;      do {         std::random_device gen;         std::uniform_int_distribution<int> disX(0, width - 1);         std::uniform_int_distribution<int> disY(0, height - 1);          fx = disX(gen);         fy = disY(gen);     } while (map[fy * Board::width + fx] != OPEN);      map[fy * width + fx] = FOOD; }  void Board::update(sf::RenderWindow& win) {     auto newHead{ snake.moveHead() };     place(snake.follow(), OPEN);      switch (at(snake.headLocation())) {     case WALL:     case SNAKE:         win.close();         break;      case FOOD:         place(snake.headLocation(), OPEN);         place(snake.add(), SNAKE);         m_score += 100;         genFood();     }      place(newHead, SNAKE); }  void Board::changeDirection(char input) {     snake.changeDirection(input); }  char Board::operator[](int i) const { return map[i];  } int Board::score()            const { return m_score; } 

Coord.h

#pragma once  struct Coord {     unsigned int x, y; }; 

SFML snake game in C++

I made a snake game in sfml and i’m kind of proud of the code’s structure, but proud doesn’t mean it’s good, so i’m putting it here so that if there is something that could be bettered, you would know.

main.cpp

#ifndef UNICODE #define UNICODE #endif  #include "app.h"  int main() {      app game(800, 600, L"Test");     game.start();     game.end(); } 

app.h

#pragma once  #include <SFML/Graphics.hpp>  #include "Snake.h" #include "Board.h"  class app { public:      app(int windowWidth, int windowHeight, const wchar_t* name);     ~app() = default;      // Runs the app     void start();     void end();  private:      // MEMBER VARIABLES      const int winWidth, winHeight;     int score;     bool play;     sf::RenderWindow window;     Snake snake;     Board board;      // MEMBER FUNCTIONS      // Draws the objects     void drawWindow();      // Handles events     void handleEvents();      // Updates the window     void updateWindow(); }; 

app.cpp

#include "app.h"  #include <iostream> #include <thread>    #include <chrono>  app::app(int windowWidth, int windowHeight, const wchar_t* name)     : winWidth{ windowWidth }, winHeight{ windowHeight }, score{ 0 },     play{ false } {      while (true) {         int choice;          std::wcout << L"Choose: " << std::endl;         std::wcout << L"1: Play " << std::endl;         std::wcout << L"2: Quit " << std::endl;          std::cin >> choice;          if (choice == 1) {             play = true;             break;         }         else break;     }      // Clears screen     for (size_t i = 0; i < 10; ++i)         std::wcout << L"\n\n\n\n\n\n\n\n\n\n\n\n" << std::endl;      if (play) {         window.create(sf::VideoMode(winWidth, winHeight), name);         window.setFramerateLimit(5);     } }  // Handles any game event void app::handleEvents() {      sf::Event event;      while (window.pollEvent(event)) {         switch (event.type) {          case sf::Event::Closed:             window.close();             break;          case sf::Event::TextEntered:             snake.changeDirection(static_cast<char>(event.text.unicode));         }     } }  // Draws all game objects void app::drawWindow() {      for (size_t i = 0, h = board.height(); i < h; ++i) {         for (size_t j = 0, w = board.width(); j < w; ++j) {              // Draws walls             if (board[i * w + j] == 2) {                 sf::RectangleShape rect;                 rect.setSize({ static_cast<float>(board.divisor()), static_cast<float>(board.divisor()) });                  rect.setPosition({ static_cast<float>(board.divisor() * j), static_cast<float>(board.divisor() * i)});                 window.draw(rect);             }              // Draws snake             else if (board[i * w + j] == 3) {                  sf::RectangleShape rect;                 rect.setFillColor(sf::Color::Green);                 rect.setSize({ static_cast<float>(board.divisor()), static_cast<float>(board.divisor()) });                  rect.setPosition({ static_cast<float>(board.divisor() * j), static_cast<float>(board.divisor() * i) });                 window.draw(rect);             }              // Draws food             else if (board[i * w + j] == 4) {                  sf::RectangleShape rect;                 rect.setFillColor(sf::Color::Red);                 rect.setSize({ static_cast<float>(board.divisor()), static_cast<float>(board.divisor()) });                  rect.setPosition({ static_cast<float>(board.divisor() * j), static_cast<float>(board.divisor() * i) });                 window.draw(rect);             }          }     } }  // Updates the render window void app::updateWindow() {     window.clear(sf::Color::Black);      drawWindow();      window.display(); }  // Starts the app void app::start() {      while (window.isOpen()) {          handleEvents();          snake.move();         board.update(window, snake, &score);          updateWindow();     } }  void app::end() {      if (play) {          std::wcout << L"You lose!" << std::endl;         std::wcout << L"Score: " << score << std::endl;          std::this_thread::sleep_for((std::chrono::milliseconds)3000);     } } 

Snake.h

#pragma once  #include <SFML/Graphics.hpp> #include <vector>  class Snake { public:      Snake();     ~Snake() = default;      // Changes the dir value based on the input     void changeDirection(char input);      // Adds a piece to the snake     void add();      // Returns the size of snakeContainer     size_t getSnakeSize();      // Moves the snake     void move();  private:      // MEMBER VARIABLES      struct Snake_segment     {         int xPos, yPos, prevxPos, prevyPos;     };      const enum direction {         UP = 0,         RIGHT,         DOWN,         LEFT     };      std::vector<Snake_segment> snakeContainer;     direction dir;      // MEMBER FUNCTIONS      // Makes the segments follow the head     void follow();      // Moves the snake's head     void moveHead();  public:      // Operator overloading (i wasn't able to declare it in .cpp file)     Snake_segment operator[](int i) { return snakeContainer[i]; }  }; 

Snake.cpp

#include "Snake.h"  // Initializes a two-piece snake Snake::Snake()     : dir { RIGHT } {      Snake_segment head { 10, 7, 9, 7 };     snakeContainer.push_back(head);      --head.xPos;     snakeContainer.push_back(head); }  void Snake::add() {      Snake_segment newSegment;     newSegment.xPos = snakeContainer[snakeContainer.size() - 1].prevxPos;     newSegment.yPos = snakeContainer[snakeContainer.size() - 1].prevyPos;      snakeContainer.push_back(newSegment); }  size_t Snake::getSnakeSize() {     return snakeContainer.size(); }  // Changes the direction based on input void Snake::changeDirection(char input) {     switch (input) {     case 'w':         if (dir != DOWN) dir = UP;         break;      case 'd':         if (dir != LEFT) dir = RIGHT;         break;      case 's':         if (dir != UP) dir = DOWN;         break;      case 'a':         if (dir != RIGHT) dir = LEFT;      } }  // All the pieces follow the head void Snake::follow() {      for (size_t i = 1, n = snakeContainer.size(); i < n; ++i) {         snakeContainer[i].prevxPos = snakeContainer[i].xPos;         snakeContainer[i].prevyPos = snakeContainer[i].yPos;          snakeContainer[i].xPos = snakeContainer[i - 1].prevxPos;         snakeContainer[i].yPos = snakeContainer[i - 1].prevyPos;     }  }  // Moves the snake's head void Snake::moveHead() {      snakeContainer[0].prevxPos = snakeContainer[0].xPos;     snakeContainer[0].prevyPos = snakeContainer[0].yPos;      switch (dir) {     case UP:         --snakeContainer[0].yPos;         break;      case RIGHT:         ++snakeContainer[0].xPos;         break;      case DOWN:         ++snakeContainer[0].yPos;         break;      case LEFT:         --snakeContainer[0].xPos;      } }  // Moves the snake void Snake::move() {     moveHead();     follow(); } 

Board.h

#pragma once  #include <SFML/Graphics.hpp>  #include "Snake.h"  class Board { public:      Board();     ~Board() = default;      void update(sf::RenderWindow& win, Snake& snek, int* scor);      int width()            const;     int height()           const;     int divisor()          const;     char operator[](int i) const;  private:      // MEMBER VARIABLES     std::string map;     const size_t mapWidth = 20;     const size_t mapHeight = 15;      // Is used to divide the screen in a grid     const int common_divisor = 40;      // MEMBER FUNCTIONS      // Checks if snek has collided with something     void genFood();     void checkCollisions(sf::RenderWindow& win, Snake& snek, int* scor); }; 

Board.cpp

#include "Board.h"  #include <random>  Board::Board() {      // Creates a 20x15 grid     map = {         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2     };      /*      REMINDER:     1 = FREE SPACE     2 = WALL     3 = SNAKE     4 = FOOD      */      genFood(); }  void Board::genFood() {      int fx, fy;      do {         std::random_device gen;         std::uniform_int_distribution<int> disX(1, mapWidth - 1);         std::uniform_int_distribution<int> disY(1, mapHeight - 1);          fx = disX(gen);         fy = disY(gen);     } while (map[fy * mapWidth + fx] != 1);      map[fy * mapWidth + fx] = 4; }  void Board::update(sf::RenderWindow& win, Snake& snek, int* scor) {      checkCollisions(win, snek, scor);      // Iterates through the whole map     for (size_t i = 0; i < mapHeight; ++i) {         for (size_t j = 0; j < mapWidth; ++j) {              // Makes walls             if (i == 0 || i == mapHeight - 1) map[i * mapWidth + j] = 2;             else if (j == 0 || j == mapWidth - 1) map[i * mapWidth + j] = 2;              // Sets free space             else if (map[i * mapWidth + j] != 4) map[i * mapWidth + j] = 1;              // Sets snek             for (size_t k = 0, n = snek.getSnakeSize(); k < n; ++k) {                 if (snek[k].yPos == i && snek[k].xPos == j)                     map[i * mapWidth + j] = 3;             }          }     } }  void Board::checkCollisions(sf::RenderWindow& win, Snake& snek, int* scor) {      for (size_t i = 0; i < mapHeight; ++i) {         for (size_t j = 0; j < mapWidth; ++j) {              // Checks snek and wall collisions             if (map[snek[0].yPos * mapWidth + snek[0].xPos] == 2 ||                 map[snek[0].yPos * mapWidth + snek[0].xPos] == 3    ) win.close();              // Checks snek and food collisions             else if (map[snek[0].yPos * mapWidth + snek[0].xPos] == 4) {                  map[snek[0].yPos * mapWidth + snek[0].xPos] = 1;                 snek.add();                              *scor += 100;                  genFood();             }         }     }  }  int  Board::width()             const { return mapWidth;       } int  Board::height()            const { return mapHeight;      } int  Board::divisor()           const { return common_divisor; } char Board::operator[](int i)   const { return map[i];         }