In this lesson, we'll build the foundational components of our Snake game. We'll implement the essential components for window management, asset handling, and user interface. This framework will serve as the backbone for our game's implementation.
In this lesson, we'll build the foundation of our Snake game by implementing a grid system. We'll create Cell
and Grid
classes to manage the game state, handle rendering, and prepare for the snake's movement.
By the end, you'll have a fully functional grid that displays the initial snake position and first apple.
In this lesson, we'll implement the core mechanics of our snake game. We'll create the game state management system, handle user input for controlling the snake's movement, and implement the snake's movement logic. By the end, you'll have a fully functional snake that responds to player controls.
We'll start by defining the fundamental data structures needed to track our snake's state, including its position, length, and direction. Then we'll expand our state system to advance the game through time.
Finally, we’ll allow our game state to respond to user input, allowing the player to control the direction their snake moves.
Now that we have our basic movement system in place, it's time to make our Snake game truly playable. We'll implement apple consumption mechanics, handle snake growth, and create a dynamic apple spawning system.
By the end of this section, our snake will be able to move around the grid eating apples, and getting longer for every apple it consumes.
Now that we have our core Snake game mechanics working, it's time to enhance the user experience by adding interactive UI elements. In this lesson, we'll implement a restart button that allows players to reset the game without having to close and reopen the application.
We’ll also update our GameState
class so the game initially starts in a paused state, and doesn’t advance until the user starts moving their snake.
Now that we have a working Snake game with basic movement and apple collection, it's time to add win-and-loss conditions to create a complete gaming experience.
In this lesson, we'll implement collision detection for game over states, add a winning condition when the snake reaches maximum length, and provide visual feedback for both outcomes.
In this lesson, we'll implement a score counter for our Snake game to help players track their progress.
We'll build a UI component that displays the current score alongside the maximum possible score, complete with a custom background and an apple icon.
Right now, our snake moves instantly between cells. We'll enhance this by implementing a sliding animation, giving the illusion of the snake smoothly traversing the grid. This involves dynamically adjusting the portion of each cell occupied by the snake.
To do this, we’ll be using our Tick()
mechanism to provide frame-by-frame adjustments to our snake’s visuals.
This is the final part of our project, so we’ll also finish things off with a list of suggestions on how we can further develop the game and put our skills to the test!
In this lesson, we’ll introduce how to write data from our program to external sources. We’ll focus on writing to files for now, but the techniques we cover lay the foundations for working with other data streams, such as network traffic.
As we might expect, SDL’s mechanism for writing data shares much in common with their API for reading data. We’ll rely on SDL_RWops
objects, and the functions we use will be familiar to what we learned in the previous lesson.
Like before, we’ll start with a basic main
function that initializes SDL, and calls Write()
and Read()
functions within an included File
 namespace.
// main.cpp
#include <SDL.h>
#include "File.h"
int main(int argc, char** argv) {
SDL_Init(0);
File::Write("output.txt");
File::Read("output.txt");
return 0;
}
Our Read()
function logs out the file’s contents, using techniques we covered in the previous lesson. In this lesson, we’ll work on the Write()
function, which is currently empty:
// File.h
#pragma once
#include <iostream>
#include <SDL.h>
namespace File {
void Read(const std::string& Path) {
char* Content{static_cast<char*>(
SDL_LoadFile(Path.c_str(), nullptr)
)};
if (Content) {
std::cout << "Content: " << Content;
} else {
std::cout << "Error loading file: "
<< SDL_GetError();
}
SDL_free(Content);
}
void Write(const std::string& Path) {
// ...
}
}
Running our program, we should see an error output from our Read()
function, as it’s trying to read a file that we haven’t created yet:
Error loading file: Parameter 'src' is invalid
When we’re working on a mouse-based game, creating an engaging user interface typically requires us to change the visual appearance of the cursor.
For example, we might customize the cursor to fit the theme of our game. We may also want to apply further modifications to the cursor based on what the user is pointing at, like changing the cursor to a sword if the pointer is hovering over an enemy. This helps players quickly understand what effect clicking will have.
This lesson will guide you through the various cursor customization options available in SDL2. We’ll cover: