Table of Contents
You can develop Snake Game by using C++ code. Not only that, you can make 2D and 3D games in C++, for Windows, Mac, Linux, Android, and iOS. It’s one of the knowledge for learning game development.
The C++ language is widely used in 2D and 3D game engines to create games. A number of scripting languages, including C++, are used in Godot and Unreal Engine. If you are interested, here are some C++ game projects for beginners.
Let’s learn some basic information regarding Snake Games in python without graphics. Initially, the snake game was developed in C++ language, and still, in advanced games, c++ game engines are used.
Related:
There are two ways to create a snake game in c++.
- Snake game without graphics.
- Snake game with graphic.
I have given the source code for both, so you can create a snake game with graphics or without graphics.
1. Snake Game in C++ Without Graphics
C++ Snake Games with source code can be developed by using several ways.
One of the ways among these is developing Snake Game without Graphics.
Developing a Snake Game in C++
A basic snake game in C++ without graphics can be developed by following these functionalities:
The functionalities of the game
- A symbol representing the snake is 0 (zero).
- A symbol representing the fruit is * (asterisk).
- (W, A, S, D) keys help the snake to propagate in any direction.
- In case the snake eats a fruit, 10 points will be awarded to the user.
- Automatically, the fruit will be produced within the parameters.
- The game will be ended if the snake touches its tail or the boundary.
creation of Snake game in c++
Four steps are basically needed to create a Snake Game without graphics. Following are those steps:
- There are four user-defined functions.
- To play the game, build a rectangular boundary.
- Random generation of the fruit.
- Increment in the score and growth of the snake on eating the fruit.
Source code
User-defined Functions
Following user-defined functions are used in making a Snake Game.
- Draw( ) function is used for the creation of the boundaries. The code written below is used to build the boundaries.
// boundary using draw()
#include <stdio.h>
#include <stdlib.h>
int i, j, height = 17;
int width = 20, gameover, score;
// Function to draw a boundary
void draw() {
// system("cls");
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
if (i == 0 || i == width - 1 || j == 0 ||
j == height - 1) {
printf("#");
} else {
printf(" ");
}
}
printf("\n");
}
}
// Driver Code
int main() {
// Function Call
draw();
return 0;
}
- Setup( ) function will position the fruit within the boundaries. To generate the fruit within the boundary, the following code of the program will be used.
void setup() {
gameover = 0;
// Stores height and width
x = height / 2;
y = width / 2;
label1:
fruitx = rand() % 20;
if (fruitx == 0)
goto label1;
label2:
fruity = rand() % 20;
if (fruity == 0)
goto label2;
score = 0;
}
- Input( ) function helps to take the input from the keyboard keys that are A, W, S, D. The piece of code written below shows the input.
void input() {
if (kbhit()) {
switch (getch()) {
case 'a':
flag = 1;
break;
case 's':
flag = 2;
break;
case 'd':
flag = 3;
break;
case 'w':
flag = 4;
break;
case 'x':
gameover = 1;
break;
}
}
}
- Logic( ) function is used to set the snake’s movement, score increment, end the game when the snake touches the boundary or its tail, and Radom fruit generation after the snake eats the fruit and exits the game. The function for logic is stated below:
void logic() {
sleep(0.01);
switch (flag) {
case 1:
y--;
break;
case 2:
x++;
break;
case 3:
y++;
break;
case 4:
x--;
break;
default:
break;
}
// If the game is over
if (x < 0 || x > height ||
y < 0 || y > width)
gameover = 1;
// If snake reaches the fruit
// then update the score
if (x == fruitx && y == fruity) {
label3: fruitx = rand() % 20;
if (fruitx == 0)
goto label3;
// After eating the above fruit
// generate new fruit
label4: fruity = rand() % 20;
if (fruity == 0)
goto label4;
score += 10;
}
}
- Main( ) starts the execution of the program as it calls all the functions being used in that program.
void main() {
int m, n;
// Generate boundary
setup();
// Until the game is over
while (!gameover) {
// Function Call
draw();
input();
logic();
}
}
Built-in Functions
The following built-in functions are used to create Snake Game.
- kbhit ():
The function is used in C++ or C language for the key determination if they have been pressed or not. A non-zero value is returned by pressing the key. conio.h
header filer is used as a function in the program.
- rand ():
On using the header file stdlib.h
, the function rand()
is declared. This function helps to return a random integer value every time it is called.
Header Files and Variables
Following header files and variables are used in this program.
Summing up everything:
// C program to build the complete
// snake game
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int i, j, height = 17, width = 20;
int gameover, score;
int x, y, fruitx, fruity, flag;
// Function to generate the fruit
// within the boundary
void setup() {
gameover = 0;
// Stores height and width
x = height / 2;
y = width / 2;
label1:
fruitx = rand() % 20;
if (fruitx == 0)
goto label1;
label2:
fruity = rand() % 20;
if (fruity == 0)
goto label2;
score = 0;
}
// Function to draw the boundaries
void draw() {
system("cls");
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
if (i == 0 || i == width - 1 ||
j == 0 ||
j == height - 1) {
printf("#");
} else {
if (i == x && j == y)
printf("0");
else if (i == fruitx &&
j == fruity)
printf("*");
else
printf(" ");
}
}
printf("\n");
}
// Print the score after the
// game ends
printf("score = %d", score);
printf("\n");
printf("press X to quit the game");
}
// Function to take the input
void input() {
if (kbhit()) {
switch (getch()) {
case 'a':
flag = 1;
break;
case 's':
flag = 2;
break;
case 'd':
flag = 3;
break;
case 'w':
flag = 4;
break;
case 'x':
gameover = 1;
break;
}
}
}
// Function for the logic behind
// each movement
void logic() {
sleep(0.01);
switch (flag) {
case 1:
y--;
break;
case 2:
x++;
break;
case 3:
y++;
break;
case 4:
x--;
break;
default:
break;
}
// If the game is over
if (x < 0 || x > height ||
y < 0 || y > width)
gameover = 1;
// If snake reaches the fruit
// then update the score
if (x == fruitx && y == fruity) {
label3: fruitx = rand() % 20;
if (fruitx == 0)
goto label3;
// After eating the above fruit
// generate new fruit
label4: fruity = rand() % 20;
if (fruity == 0)
goto label4;
score += 10;
}
}
// Driver Code
void main() {
int m, n;
// Generate boundary
setup();
// Until the game is over
while (!gameover) {
// Function Call
draw();
input();
logic();
}
}
Result:
1. Snake Game with Graphics in C++
With the evolution of technology, the snake game has also been advanced as it has been sprinkled with the spices of graphics in it which makes the game more colorful and attractive for the young ones.
Moreover, the use of graphics in the C++ Snake Game with Source Code has fulfilled all the needs that the snake game requires in this modern gaming era.
Basic Functionalities of Snake Game with Graphics
Only some changes are made in the functionalities of Snake Game in C++ with Graphics which are as followed:
- A colorful snake is developed with the help of graphics.
- Randf() function of the graphics is used to create fruit.
- With the help of Up, Down, Left, and Right keyboard keys the snake can move in the direction we want.
- There will be an increment of 14 points when the snake eats a fruit.
Creation of Basic Controls
Some basic controls can be created in the Snake Game in C++ with Graphics which are as followed:
- P Control
By clicking on the P key from the keyboard we can pause our game at any time.
- G Control
G key is used to resume the paused game.
- E Control
E character is used to exit the snake game.
Result:
Snake Games in C++ Using Classes
After writing a lot about the programming and codes of snake games for my dear users, I would verily like to discuss the classes which are used in C++ Snake Game.
Here I will thoroughly talk about the development of Snake Game Using C++ Classes. Several steps of classes lead to a professionally developed Snake Game in C++. Some of them are as follows.
Class Responsible for Starting and Running the Game
So, let’s debate over the class that is mainly responsible for initiating and running the game.
- For the startup of the game OnCreate() function is called.
- Once per frame functions OnUpdate() and OnRender() are called and the game loop should be its part.
When the inner game loop is quitting and the program is about to exit OnDestroy() function call is made.
class SnakeGame: public rge::DXGraphicsEngine {
public: SnakeGame();
bool OnCreate();
bool OnUpdate();
bool OnRender();
void OnDestroy();
protected: Snake snake;
FieldGrid field;
int score;
bool gameOver;
int updateFreq;
int updateCounter;
};
//////////////////////// .cpp
SnakeGame::SnakeGame(): DXGraphicsEngine(), field(10, 10), snake(3, rge::v2di(3, 1), Direction::RIGHT), score(0), gameOver(false), updateFreq(10), updateCounter(0) {
}
bool SnakeGame::OnCreate() {
field.AddSnake(snake.GetBody());
field.GenerateFood();
return true;
}
bool SnakeGame::OnUpdate() {
//check user input
if (GetKey(rge::W).pressed) {
snake.SetDirection(Direction::UP);
}
if (GetKey(rge::S).pressed) {
snake.SetDirection(Direction::DOWN);
}
if (GetKey(rge::A).pressed) {
snake.SetDirection(Direction::LEFT);
}
if (GetKey(rge::D).pressed) {
snake.SetDirection(Direction::RIGHT);
}
updateCounter++;
if (!gameOver && updateCounter >= updateFreq) {
updateCounter = 0;
//clear snake body from field
field.ClearSnake(snake.GetBody());
//move
snake.MoveSnake();
//add snake body to field
field.AddSnake(snake.GetBody());
//testcollision
CollisionMessage cm = field.CheckCollision(snake.GetHead());
gameOver = cm.gameOver;
score += cm.scoreChange ? snake.GetLength() * 10 : 0;
if (cm.tileType == TileType::Food) {
field.GenerateFood();
snake.ExtendSnake();
}
}
return true;
}
bool SnakeGame::OnRender() {
std::cout << score << std::endl;
field.Draw( & m_colorBuffer, 100, 20, 10);
snake.DrawHead( & m_colorBuffer, 100, 20, 10);
return true;
}
Snake and Direction Class
The snake class in this game is responsible for the movement and extension (growth) of the snake. An enum for the snake’s direction in which it is moving is also used. It is preferable to use these classes.
enum class Direction {
UP, DOWN, LEFT, RIGHT
};
class Snake {
public:
Snake();
Snake(int length, rge::v2di position, Direction direction);
rge::v2di GetHead() {
return head;
}
std::vector < rge::v2di > GetBody() {
return body;
}
void MoveSnake();
void ExtendSnake();
Direction GetDirection() {
return direction;
}
void SetDirection(Direction direction);
int GetLength() {
return body.size() + 1;
}
void DrawHead(rge::Buffer * buffer, int x, int y, int size);
protected:
std::vector < rge::v2di > body;
rge::v2di head;
Direction direction;
Direction oldDirection;
};
Snake::Snake(): head(rge::v2di(0, 0)), direction(Direction::UP), oldDirection(Direction::UP), body(std::vector < rge::v2di > ()) {
body.push_back(rge::v2di(head.x, head.y + 1));
}
Snake::Snake(int length, rge::v2di position, Direction direction): head(position), direction(direction), oldDirection(direction), body(std::vector < rge::v2di > ()) {
for (int i = 0; i < length - 1; ++i) {
rge::v2di bodyTile;
switch (direction) {
case Direction::UP: {
bodyTile.x = head.x;
bodyTile.y = head.y + (i + 1);
break;
}
case Direction::DOWN: {
bodyTile.x = head.x;
bodyTile.y = head.y - (i + 1);
break;
}
case Direction::LEFT: {
bodyTile.y = head.y;
bodyTile.x = head.x + (i + 1);
break;
}
case Direction::RIGHT: {
bodyTile.y = head.y;
bodyTile.x = head.x - (i + 1);
break;
}
}
body.push_back(bodyTile);
}
}
void Snake::MoveSnake() {
oldDirection = direction;
for (int i = body.size() - 1; i > 0; --i) {
body[i] = body[i - 1];
}
body[0] = head;
switch (direction) {
case Direction::UP: {
head.y--;
break;
}
case Direction::DOWN: {
head.y++;
break;
}
case Direction::LEFT: {
head.x--;
break;
}
case Direction::RIGHT: {
head.x++;
break;
}
}
}
void Snake::ExtendSnake() {
body.push_back(body[body.size() - 1]);
}
void Snake::SetDirection(Direction direction) {
switch (this -> oldDirection) {
case Direction::UP:
case Direction::DOWN: {
if (direction != Direction::UP && direction != Direction::DOWN) {
this -> direction = direction;
}
break;
}
case Direction::LEFT:
case Direction::RIGHT: {
if (direction != Direction::LEFT && direction != Direction::RIGHT) {
this -> direction = direction;
}
break;
}
}
}
void Snake::DrawHead(rge::Buffer * buffer, int x, int y, int size) {
rge::Color c(100, 100, 200);
buffer -> DrawRegion(x + head.x * size, y + head.y * size, x + head.x * size + size, y + head.y * size + size, c.GetHex());
}
Field Grid Class
Fruit generation, collision detection, and storing the stat of the map are the responsibilities of the field grid class. A collision message is generated when a snake collides with itself or the boundary after which the message “game over” is displayed on the screens.
class FieldGrid {
public:
FieldGrid();
FieldGrid(int width, int height);
~FieldGrid();
void GenerateFood();
CollisionMessage CheckCollision(rge::v2di head);
void ClearSnake(std::vector < rge::v2di > body);
void AddSnake(std::vector < rge::v2di > body);
void Draw(rge::Buffer * buffer, int x, int y, int size);
protected:
std::vector < std::vector < Tile * >> field;
int width;
int height;
};
//////////// .cpp
FieldGrid::FieldGrid(): width(10), height(10), field(std::vector < std::vector < Tile * >> ()) {
for (int i = 0; i < width; ++i) {
field.push_back(std::vector < Tile * > ());
for (int j = 0; j < height; ++j) {
field[i].push_back(new EmptyTile());
}
}
}
FieldGrid::FieldGrid(int width, int height): width(width), height(height), field(std::vector < std::vector < Tile * >> ()) {
for (int i = 0; i < width; ++i) {
field.push_back(std::vector < Tile * > ());
for (int j = 0; j < height; ++j) {
field[i].push_back(new EmptyTile());
}
}
}
FieldGrid::~FieldGrid() {
for (int i = 0; i < field.size(); ++i) {
for (int j = 0; j < field[i].size(); ++j) {
delete field[i][j];
}
field[i].clear();
}
field.clear();
}
void FieldGrid::GenerateFood() {
int x = rand() % width;
int y = rand() % height;
while (!field[x][y] -> IsFree()) {
x = rand() % width;
y = rand() % height;
}
delete field[x][y];
field[x][y] = new FoodTile();
}
CollisionMessage FieldGrid::CheckCollision(rge::v2di head) {
if (head.x < 0 || head.x >= width || head.y < 0 || head.y >= height) {
CollisionMessage cm;
cm.scoreChange = false;
cm.gameOver = true;
return cm;
}
return field[head.x][head.y] -> OnCollide();
}
void FieldGrid::ClearSnake(std::vector < rge::v2di > body) {
for (int i = 0; i < body.size(); ++i) {
delete field[body[i].x][body[i].y];
field[body[i].x][body[i].y] = new EmptyTile();
}
}
void FieldGrid::AddSnake(std::vector < rge::v2di > body) {
for (int i = 0; i < body.size(); ++i) {
delete field[body[i].x][body[i].y];
field[body[i].x][body[i].y] = new SnakeTile();
}
}
void FieldGrid::Draw(rge::Buffer * buffer, int x, int y, int size) {
for (int xi = 0; xi < width; ++xi) {
for (int yi = 0; yi < height; ++yi) {
int xp = x + xi * size;
int yp = y + yi * size;
field[xi][yi] -> Draw(buffer, xp, yp, size);
}
}
}
Tile Type Class
The usage of tile type class is made within the field grid. Generally, there are three types of tile type enum.
- Empty tile
- Food tile
When the snake hits the food tile that is a fruit, a feeding event takes place where the snake eats the fruit and grows in length.
- Snake tile
When the snake hits a snake tile that is its tail, a collision is detected and a collision message is generated. Hence, either the game finishes or the user loses one of its lives.
class Tile {
public:
virtual CollisionMessage OnCollide() = 0;
virtual bool IsFree() = 0;
void Draw(rge::Buffer * buffer, int x, int y, int size) {
buffer -> DrawRegion(x, y, x + size, y + size, color.GetHex());
}
protected:
rge::Color color;
};
class EmptyTile: public Tile {
public: EmptyTile() {
this -> color = rge::Color(50, 50, 50);
}
CollisionMessage OnCollide() {
CollisionMessage cm;
cm.scoreChange = false;
cm.gameOver = false;
cm.tileType = TileType::Empty;
return cm;
}
bool IsFree() {
return true;
}
};
class FoodTile: public Tile {
public: FoodTile() {
this -> color = rge::Color(50, 200, 70);
}
CollisionMessage OnCollide() {
CollisionMessage cm;
cm.scoreChange = true;
cm.gameOver = false;
cm.tileType = TileType::Food;
return cm;
}
bool IsFree() {
return false;
}
};
class SnakeTile: public Tile {
public: SnakeTile() {
this -> color = rge::Color(120, 130, 250);
}
CollisionMessage OnCollide() {
CollisionMessage cm;
cm.scoreChange = false;
cm.gameOver = true;
cm.tileType = TileType::Snake;
return cm;
}
bool IsFree() {
return false;
}
};
Collision Message Class
Whenever the snake’s head collides with any of the tile collision messages are sent to the game. This shows that the collision message class is dependent upon the tile type class and is mainly associated with the snake game.
enum class TileType {
Empty,
Snake,
Food
};
class CollisionMessage {
public:
bool scoreChange;
bool gameOver;
TileType tileType;
};
Your efforts in reading all classic Snake Games in C++ are appreciated as it is really time-consuming. Moreover, all the #includes and main methods are omitted from the pieces of the codes as they would just take up extra space.
If you want to write a wholesome program, then I would ask you to add all the #includes
the main methods, and the codes mentioned above.
Conclusion
I hope you are now well versed in the development of C++ snake games as we also have shared their source codes. So try making your own games using this information and let us know if you need further assistance.