Here is my little snake game which I made in c++ using the SFML library. The game is very simple. I have also included a screenshot of how the GUI looks. The files which I am loading are 30x30 colored squares. Yellow is the background. Red represents the snake. White is the cherry/fruit/prize to increase the size of the snake
#include<SFML/graphics.hpp>
#include<windows.h>
using namespace sf;
struct position{
int x;
int y;
}snake[200];
char direction = 'r';
int length = 2;
void update(){
for(int i = length-1;i > 0;i--){
snake[i].x = snake[i-1].x;
snake[i].y = snake[i-1].y;
}
if (direction == 'u') snake[0].y-=1;
else if (direction == 'd') snake[0].y+=1;
else if (direction == 'r') snake[0].x+=1;
else if (direction == 'l') snake[0].x-=1;
}
int fruitx = 5;
int fruity = 5;
void genrand(){
int rx = rand() % 20;
int ry = rand() % 20;
fruitx = rx;
fruity = ry;
}
int main(){
snake[0].x = 5;
snake[0].y = 5;
RenderWindow window(VideoMode(620,620),"Snake c++");
Texture t;
Texture t2;
Texture t3;
t3.loadFromFile("images/green.png");
t2.loadFromFile("images/red.png");
t.loadFromFile("images/yellow.png");
Sprite bg(t);
Sprite sn(t2);
Sprite fruit(t3);
const int size = 31;
while(window.isOpen()){
Event e;
while(window.pollEvent(e)){
if(e.type == Event::Closed)
window.close();
if(e.type == Event::KeyPressed){
if(e.key.code == Keyboard::Key::Up && direction != 'd') direction = 'u';
else if(e.key.code == Keyboard::Key::Down && direction != 'u') direction = 'd';
else if(e.key.code == Keyboard::Key::Left && direction != 'r') direction = 'l';
else if(e.key.code == Keyboard::Key::Right && direction != 'l') direction = 'r';
}
}
Sleep(60);
update();
window.clear();
for(int i = 0;i < 20;i++){
for(int j = 0;j < 20;j++){
bg.setPosition(i*size,j*size);
window.draw(bg);
}
}
for(int i = 0;i < length;i++) {
if (snake[i].x < 0) snake[i].x+=20;
if (snake[i].x > 19) snake[i].x-=20;
if (snake[i].y < 0) snake[i].y+=20;
if (snake[i].y > 19) snake[i].y-=20;
sn.setPosition(snake[i].x*size,snake[i].y*size);
if (i != 0){
if(snake[i].x == snake[0].x && snake[i].y == snake[0].y)
length = 2;
}
if (snake[i].x == fruitx && snake[i].y == fruity){
length+=1;
genrand();
fruit.setPosition(fruitx*size,fruity*size);
}
window.draw(sn);
}
window.draw(fruit);
window.display();
}
return 0;
}
The snake is represented by an array of position
objects.
When I detect any collision I just reset the length of the snake.
enter image description here
1 Answer 1
Use correct capitalization of file names
Be careful with upper case and lower case in filenames. While Windows might not treat filenames case sensitively, other operating systems might. The correct #include
is:
#include <SFML/Graphics.hpp>
Avoid operating specific functions
You use Sleep()
, which is not a standard C function, but rather Windows specific. Since you are using SFML, you can use sf::sleep()
instead, like so:
sf::sleep(sf::milliseconds(60));
You can then also remove #include <windows.h>
, and then your code compiles and runs on other platforms without errors.
Add error checking
Your code ignores errors trying to open the texture files. Make sure you report an error and exit if any of the required files cannot be found.
Split off more functionality into their own functions
You already created a function update()
to handle moving the snake one frame. However, it would be nice to move more functionality out of main()
into their own functions, so that the game loop in main()
becomes something simple like:
while (window.isOpen()) {
handle_input();
update();
draw();
sleep(milliseconds(60));
}
Avoid moving the whole body each frame
Instead of moving the position of every element of snake[]
, use a std::deque
to hold the positions, and inside update()
, just pop the tail and add a new head:
#include <deque>
struct position {
int x;
int y;
};
std::deque<position> snake;
void update() {
// Calculate the new head position
position new_head = snake.front();
if (direction == 'u') new_head.y--;
else if ...;
// Handle wraparounds here
if (new_head.x < 0) new_head.x += ...;
if (...) ...;
// Remove the tail and add a new head
snake.pop_back();
snake.push_front(new_head);
}
Using std::deque
, the container knows its own length, and this makes it easy to use range-for loops. For example, to draw the snake:
for (auto position: snake) {
sn.setPosition(position.x * size, position.y * size);
window.draw(sn);
}
Use struct position
for all positions
You can also use struct position
to store the coordinates of the fruit:
position fruit;
This makes the code more consistent, and if position
would have added comparison operators, it would save you from manually comparing the x
and y
coordinates separately. Which brings me to:
Use sf::Vector2<int>
for the positions
SFML has a handy class to store coordinates for you, so you can write:
std::deque<sf::Vector2<int>> snake;
sf::Vector2<int> fruit;
This type also has x
and y
member variables. But, it also overloads things like operator=()
, so instead of comparing x
and y
coordinates manually, you can write:
if (snake.front() == fruit) {
// Handle the snake eating the fruit
}
Code formatting
Your code has inconsistent formatting, sometimes there are spaces between operators and around braces, sometimes not. It doesn't really matter that much which code style you use, as long as you are consistent. Instead of manually fixing style issues, consider using a code formatting tool (either an external one like Artistic Style, or your editor's built-in code formatting functions) to keep your code looking tidy.