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; ```