Snake game in terminal, created using C++ and STL in Microsoft Visual Studio 2022.
- Useful links
- Project
2.1. Structure
2.2. App loop
2.2.1. App component
2.3. Game loop
2.4. Menu loop
2.5. Additional project files
2.5.1. Console edit
2.5.2. Console renderer
2.5.3. Macros - Remarks
3.1. Multithreading
3.2. Delta time
3.3. Perfectionism
Before we begin, here are some useful links you can check to expand your knowledge:
- Cpp reference - no comments.
- Clang format - useful C++ formatter.
- Game loop pattern - cool article about game loops.
- Game loop with multithreading - discussion, do you actually need multithreading in your game?
Section with pretty detailed description of the project.
Generally, the project structure looks like this:
📁ConsoleSnakeTests - unit tests for the project
📁ConsoleSnake - root project
📁Source - all .cpp and .h here
📁Math - math functions, structs etc.
📁System - console editions, helpers, macros etc.
📁App - app core
📁Menu - game menu core
📁Game - game core
📁Abstractions - game abstractions
📁Food - food logic
📁Map - game map
📁Snake - player snake
ConsoleSnake\Source\App\App.h(cpp)
Basically, the app have a main (app) loop that validates its states and chooses which app component loop to start.
You may find DeltaTime
field in app or app component classes, but don't await game-engine-like tick system here.
Presudo-code for app loop:
app_loop:
switch app_state:
start app_component according to app_state
ConsoleSnake\Source\App\AppComponent.h
App component is merely an abstract class that can be started and ticked.
In addition, it introduces a method for returninig app state that corresponds to app component state.
ConsoleSnake\Source\App\Game\Game.h(cpp)
App contains a pointer to game object that can tick as well as app.
It encapsulates all game logic and can be run independently (in separate game loop).
Pseudo-code for game loop:
game_loop:
switch game_state:
consume_player_input
tick_actors
check_collision
check_end_game
render_game
Actually, game tick has 2 different tick-functions, that are called depending on game state, but you can check it yourself.
ConsoleSnake\Source\App\Menu\MainMenu.h(cpp)
App contains a pointer to main menu object that can tick as well as app.
It is used to handle pretty trivial menu interaction.
Pseudo-code for game loop:
game_loop:
switch game_state:
consume_player_input
render_menu
So, menu allows the player to change the app state that, on its turn, tells if the player should sit in menu or play the game.
In this section, we will describe some project files that are not fully-concerned with game itself.
ConsoleSnake\Source\System\ConsoleEdit.h(cpp)
This class was introduced to simplify console editing.
Current logic concerned only with manipulation of terminal cursor position and visibility.
ConsoleSnake\Source\System\ConsoleRenderer.h(cpp)
The class allows to print data to console in a fast and convenient manner.
ConsoleSnake\Source\System\Macros.h(cpp)
This header defines several macro-statements to either represent sort of option or simplify writing code.
Here, we would like to add several answears to possible questions.
In fact, the project uses multithreading, but, frankly, only in scientific purposes.
You might find a method in game app component that renders current play time on the console, next to game map.
Basically, project can be freely run on 1 thread, just remove or comment separate thread specific code in game app component.
You can find DeltaTime
field in app components and app itself, but don't let it confuse you.
This field is simply used for thread sleep after game tick to make game run a bit slowly and smoothly.
At least, it helped on our hardware, feel free to modify its value or even rewrite tick logic.
Nothing in this world is perfect, neither this project.
Through frame by frame debugging we faced several issues that need to be fixed. Here is the list:
- Move snake head after moving body (i.e reduce edge case bugs).
- Reconsider game loop execution sequence in order to improve gaming experience.