Welcome. If you've never written a line of code in your life, you're in exactly the right place. If you've written plenty of C++ but want to start building games, you're also in the right place. This chapter is the on-ramp for both — and for everyone in between. By the time you finish it, you'll have a working C++ project, a window on screen with a colored square you can move around with the keyboard, and a first taste of the tools you'll be using for the rest of the book.
This is also the longest setup section in the book. Installing tools and linking a third-party library is the sort of thing that's fiddly the first time and forgettable thereafter. Take it slowly here so the rest of the book can move quickly.
Project folder:
SDL3 Projects/HelloSquare— the complete source for this chapter's "Controllable Square" lives here, ready to build once you've finished the setup steps below.
In this chapter, we will:
- Get a brief overview of the book and the games we're going to build
- Install Visual Studio 2026 and the C++ workload we need
- Take a short tour of the IDE
- Create a new C++ project and link it to the SDL 3 library
- Learn what code comments are and why we use them
- Build the "Controllable Square" — a real, running SDL program
- Walk through every line of the code
- Take a first look at debugging in Visual Studio
Let's get to it.
An Overview of the Book
This book teaches C++ by building games, in that order. The C++ comes first because it has to — you can't build games without knowing how to write the code that makes them run. But the games come fast, and they keep coming. Every other chapter is a project. The theory chapters teach you a piece of C++, and then immediately the next chapter puts it to use on screen.
Across the book we'll build a series of small games and graphical apps using SDL 3, a free, open-source library that gives us a window, a 2D renderer, and keyboard, mouse, and game controller input. SDL is the same library used by full commercial games and emulators, but it's small enough that we can understand what's happening at every step. We will not use a fancy engine. We will build everything ourselves, and you will see exactly how each piece works.
Toward the end of the book we'll change gears and use AI as our coding partner. By that point you'll know enough C++ to read what the AI writes, push back when it makes a bad call, and steer it toward a finished game. AI is a tool, not a replacement for knowing things. We'll use it like one.
For now, our goal is simpler. We're going to get the tools installed, the library linked, and a square moving on screen. That's all of Chapter 1. Let's start with Visual Studio.
Setting Up Visual Studio
Visual Studio 2026 is Microsoft's flagship Integrated Development Environment, or IDE. An IDE is one big program that bundles together everything you need to write, compile, and debug code: the text editor, the compiler, the debugger, the project files, and a host of conveniences. There are other good C++ IDEs, but Visual Studio is the one most game developers on Windows use, and the one this book is built around.
The Community edition is free for individual use, open-source projects, and small teams. That's the one we want.
Downloading Visual Studio
Open your web browser and go to https://visualstudio.microsoft.com/downloads/. Find the Community edition and click the download button. You'll get a small installer file — a few megabytes — that downloads the rest of Visual Studio for you once you launch it.
Run the installer. The first thing it does is ask you which workloads you want to install. A workload is a bundle of components for a particular kind of development: web, mobile, desktop, games, and so on. Visual Studio is huge, and installing the lot would eat tens of gigabytes for no good reason.
We need exactly one workload: Desktop development with C++. Tick the box next to it.
On the right-hand side of the same screen you'll see a list of optional components for the workload. The defaults are fine. Leave everything as it is, click Install, and let it run. Depending on your internet speed this can take anything from ten minutes to an hour. Go and make a cup of tea.
When the installer finishes you'll be prompted to sign in. You can sign in with a free Microsoft account, or skip the sign-in for now. Either is fine.
Setting Up the Project to Use SDL
Now we'll create a new project and wire it up to SDL 3. This is the fiddly part. Take it slowly, do each step carefully, and the result will be a project you can copy and reuse for every other chapter in the book.
Downloading SDL 3
Open your browser and go to libsdl.org. Find the Downloads section and click on latest SDL releases. We want the pre-built Windows binaries — a file with a name like SDL3-devel-3.x.x-VC.zip. The VC part stands for Visual C++ and means the package is ready to drop into a Visual Studio project.
Download that zip file and extract it somewhere sensible. I keep mine at C:\SDL3. You can put it wherever you like, but you'll need to remember the path in a moment.
Inside the extracted folder you'll see two folders we care about: include and lib. The include folder contains the header files we need to write SDL code, and the lib folder contains the compiled library files we need to link against. Hang on to those names — they'll matter shortly.
Creating the Project
Open Visual Studio, click Create a New Project. In the dialog that appears, search for Empty Project and pick the C++ version. Click Next.
Give the project a name. For this chapter we'll call it ControllableSquare. Choose a location for it — somewhere you'll be able to find it again, like C:\Projects or a folder on your desktop. Leave Place solution and project in the same directory unchecked. Click Create.
Visual Studio thinks for a moment and then drops you into a brand new, completely empty project.
A Quick Tour of the IDE
As it is the first time you have opened Visual Studio there will likely be a page of announcements from Microsoft. You can click the close cross to clear this page.
You're now looking at the main Visual Studio window. There's a lot here, but most of it will only matter when there's a project loaded. For now, just locate the four panels you'll be using constantly throughout this book:
- The editor sits in the middle. This is where your code lives. When a project is open, each file you've opened gets its own tab along the top.
- The Solution Explorer is on the right. This shows the structure of your project — its files and folders. Think of it as the file browser for your codebase. You'll see folders for Header Files, Source Files, and Resource Files, all empty for now.
- The Output window is at the bottom. The compiler talks to you here. So does the debugger. When something goes wrong, this is the first place you look.
- The build and debug controls sit along the top toolbar — a green "Play" arrow, a stop button, dropdowns for the build configuration, and so on.
If you can't see one of those panels, you can always find it under the View menu at the top of the window.
That's the tour. Visual Studio has hundreds of features, but the four panels above are what you'll use day in and day out.
Adding a Source File
We need a .cpp file to write our code in. Right-click on the Source Files folder in the Solution Explorer and choose Add > New Item. Pick C++ File (.cpp), name it main.cpp, and click Add.
A new, empty file opens in the editor. Leave it for now — we'll fill it in once the project is configured.
Telling the Project Where SDL Is
This is the step that catches people out. Visual Studio doesn't magically know where you put SDL — you have to tell it. We need to tell it three things: where to find the SDL header files, where to find the SDL library files, and which library files to actually link against.
Right-click on the project name (ControllableSquare) in the Solution Explorer and choose Properties at the very bottom. A big window opens with a tree of categories down the left side. Don't be intimidated. We'll touch four settings and that's it.
Before you change anything, look at the dropdowns at the top of the window. There's one labeled Configuration (Debug/Release) and one labeled Platform (Win32/x64). Set Configuration to All Configurations and Platform to All Platforms so the changes we make apply to both Debug and Release builds.
Now make these four changes:
Include directories. Navigate to C/C++ > General in the tree on the left. Find the Additional Include Directories field on the right. Click in it, then click the small arrow that appears, then choose Edit. Add a new line containing the path to SDL's
includefolder — for me that'sC:\SDL3\include. Click OK.Library directories. Navigate to Linker > General. Find the Additional Library Directories field. Edit it and add the path to SDL's
lib\x64folder — for me that'sC:\SDL3\lib\x64. Click OK.Library file. Navigate to Linker > Input. Find the Additional Dependencies field. Edit it and add
SDL3.libon its own line at the top. Click OK.Subsystem. Still in the Linker category, click System. Find SubSystem and set it to Windows (/SUBSYSTEM:WINDOWS) if it isn't already. SDL provides its own entry point on Windows, and this setting tells the linker to use it.
Click OK to close the properties window.
The DLL
There's one last piece. SDL 3 is a dynamic library, which means part of it lives in a .dll file that needs to sit next to your finished .exe when you run it. Otherwise, Windows won't be able to find it and your program will fail to start with a complaint about a missing DLL.
Open Windows Explorer and navigate into your SDL folder, then into lib\x64. Find SDL3.dll and copy it. Now navigate to your project folder, then into the x64 folder, then into Debug. (If Debug doesn't exist yet, build the project once — we'll do that in a minute — and it will appear.) Paste SDL3.dll in there.
You'll need to do this once for the Debug folder and once for the Release folder, but Release can wait until you actually build a Release version. For now, Debug is enough.
The setup is done. Take a breath. Every project chapter from here on will skim through this section in a sentence or two, because you'll have done it before. The first time is the slow time.
Code Comments
Before we write any real code, let's talk about comments. A comment is a piece of text in your source file that the compiler ignores entirely. It exists for human readers — for you, for your collaborators, and for the future version of yourself who comes back to this code in six months and has forgotten what it does.
C++ has two kinds of comments. A single-line comment starts with two forward slashes and runs to the end of the line:
// This is a single-line comment.
int score = 0; // Comments can also sit after code on the same line.
A multi-line comment starts with /* and ends with */, and can stretch across as many lines as you need:
/*
This is a multi-line comment.
It can span as many lines as you want.
Useful for longer explanations.
*/
In this book you'll see plenty of comments inside the code blocks. They aren't there to repeat what the code says — they're there to explain why the code does what it does, or to flag something a beginner might not pick up on at a glance. Get into the habit of writing comments as you code. Your future self will thank you.
With that, we're ready to build something.
Coding the "Controllable Square" Project
Here's what we're going to make. A black window, 800 pixels wide and 600 pixels tall. A bright square in the middle of the window. Press W, A, S, or D on the keyboard and the square slides in that direction. Press Escape (or click the X on the window) to close it.
That's it. It sounds modest, but it's a real game-shaped program: a window, a render loop, an event system, keyboard input, and timed movement. Everything we build for the rest of the book is an elaboration of these same pieces.
Open main.cpp in the editor. We'll build the program up in pieces and explain every one. At the end I'll show you the whole file in one piece so you can copy and paste it if you'd rather check it against what you typed.
The Includes
Every C++ source file starts with a list of #include lines. These tell the compiler to bring in code from other files. We need two:
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
In the preceding code, the first line brings in the main SDL header — everything we need to create a window, draw shapes, and read input. The second line brings in a small SDL helper that lets us use a normal main function on Windows even though Windows programs technically use a different entry point. SDL hides that complication from us.
Some Constants
Underneath the includes, we'll define a few constants — values that don't change while the program runs. Constants make code easier to read and easier to tweak later. We met const briefly in Chapter 2; here are a few more for our window:
const int WINDOW_W = 800; // window width in pixels
const int WINDOW_H = 600; // window height in pixels
const float SQUARE_SZ = 80.0f; // side length of the square
const float SPEED = 300.0f; // movement speed in pixels per second
In the preceding code, four constants describe the window and the square. If you decide later that you want a bigger window or a faster square, you change one number here and the rest of the program updates automatically. That's far better than scattering the value 800 across twenty different places in the code. We will be covering constants and variables in full in the next chapter.
The main Function
Every C++ program starts at a function called main. This is the entry point — when you run the program, this is the first thing that happens. We'll cover functions properly in Chapter 8, but for now just think of main as the box that all our code goes inside:
int main(int argc, char* argv[])
{
// All our code will go here.
return 0;
}
In the preceding code, main returns an int (a whole number) to whatever launched the program. By convention, returning 0 means "everything went fine". The argc and argv parameters let a program receive arguments from the command line. We won't use them in this book — they're there because the standard says so. Ignore them.
Everything we add from here on goes between the opening { and the return 0; line.
Starting SDL Up
Before we can do anything visual, we have to start SDL. This is a single function call:
if (!SDL_Init(SDL_INIT_VIDEO))
{
SDL_Log("SDL_Init failed: %s", SDL_GetError());
return 1;
}
In the preceding code, SDL_Init starts up the parts of SDL we want — in this case, the video subsystem. It returns true on success and false on failure. The ! flips that around, so the body of the if only runs if initialization failed. In that case we print an error message and return 1 from main — by convention, any non-zero return value means "something went wrong".
Don't worry about the if and ! — we'll cover those properly in Chapter 4. For now, just read it as "if SDL didn't start, complain and exit."
Creating a Window and a Renderer
With SDL running we can ask it to create a window for us:
SDL_Window* window = SDL_CreateWindow(
"Controllable Square", // title shown in the title bar
WINDOW_W, WINDOW_H, // size of the window in pixels
0 // flags: 0 means a plain default window
);
if (!window)
{
SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
SDL_Quit();
return 1;
}
In the preceding code, SDL_CreateWindow creates a window and hands us back a pointer to it. The SDL_Window* type is a pointer — a value that refers to something stored elsewhere in memory. Pointers are a big topic, properly covered in Chapter 10. For now, all you need to know is that window holds a reference to the window SDL just made, and we'll pass it around whenever we want to do something to that window.
Once again we check whether the call succeeded. If it didn't, we shut SDL down with SDL_Quit and exit.
Now we create a renderer:
SDL_Renderer* renderer = SDL_CreateRenderer(window, nullptr);
if (!renderer)
{
SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
The renderer is the object that actually draws things into the window. We pass it the window we just created, and nullptr for the second argument to let SDL pick the best drawing backend (usually hardware-accelerated via DirectX). The nullptr keyword represents the "null pointer" — a pointer that points to nothing. We'll meet it properly in Chapter 10.
Notice that if the renderer fails to create, we tidy up by destroying the window before quitting. Cleanup matters, even when things go wrong.
The Square's State
We need to remember where the square is. Two float variables are enough — one for its X position and one for its Y position. We'll start it in the middle of the window:
float x = (WINDOW_W - SQUARE_SZ) / 2.0f;
float y = (WINDOW_H - SQUARE_SZ) / 2.0f;
In the preceding code, (WINDOW_W - SQUARE_SZ) / 2.0f calculates the X position that centers the square horizontally. We take the window width, subtract the square's width, and divide by two. The same idea works for Y.
We also need to keep track of when the last frame happened, so we can measure how much time has passed between frames. SDL gives us a function for that:
Uint64 lastTime = SDL_GetTicks();
SDL_GetTicks returns the number of milliseconds since SDL was started, as a 64-bit unsigned integer (Uint64). We stash the current value in lastTime so we can compare against it on the next frame.
The Game Loop
Now for the heart of the program — the game loop. A game is fundamentally a loop that runs roughly 60 times per second, and each pass through the loop does three things: handle input, update the game state, and draw the new frame. Our loop will look like this:
bool running = true;
SDL_Event event;
while (running)
{
// 1. Handle events
// 2. Update
// 3. Render
}
In the preceding code, running is a bool flag that controls whether the loop keeps going. As long as it's true, the while loop runs again. The moment we set it to false, the loop ends and the program exits. The SDL_Event variable will hold each event SDL has queued up for us — a key press, a mouse click, a window close, and so on.
We'll cover while loops properly in Chapter 6. For now, read this as "keep looping until running becomes false." Let's fill in the three steps.
Handling Events
Inside the loop, the first thing we do is drain SDL's event queue:
while (SDL_PollEvent(&event))
{
if (event.type == SDL_EVENT_QUIT)
running = false;
if (event.type == SDL_EVENT_KEY_DOWN &&
event.key.key == SDLK_ESCAPE)
running = false;
}
In the preceding code, SDL_PollEvent pulls one event from the queue and writes it into our event variable. It returns true if it got an event and false if the queue is empty. We loop until the queue is empty.
For each event, we ask what kind it is. If it's SDL_EVENT_QUIT (the user clicked the X on the window), we set running to false. If it's a key press and the key was Escape, we do the same. Everything else, we ignore.
The &event syntax there passes the address of our event variable to the function so SDL can write into it. The & is another pointer-related thing — Chapter 10 again.
Measuring Time
Before we move the square, we need to know how much time has passed since the last frame. This matters because frames don't take the exact same amount of time — sometimes a frame takes 16 milliseconds, sometimes 20, sometimes 14. If we just added a fixed amount to the square's position every frame, it would move at different speeds on different machines, and that would be terrible. Instead, we calculate the elapsed time and use that:
Uint64 now = SDL_GetTicks();
float delta = (now - lastTime) / 1000.0f; // seconds
lastTime = now;
In the preceding code, we ask SDL for the current time, subtract the time at the last frame, and divide by 1000 to convert from milliseconds to seconds. We store the result in delta, then update lastTime for the next frame.
If a frame takes 16 ms, delta will be 0.016. If our speed is 300 pixels per second, then SPEED * delta is 4.8 pixels — the distance the square should move this frame. This pattern, multiplying by delta, is called delta time and it's how every real-time game keeps movement consistent.
Moving the Square
Now for the keyboard input that moves the square:
const bool* keys = SDL_GetKeyboardState(nullptr);
if (keys[SDL_SCANCODE_W]) y -= SPEED * delta;
if (keys[SDL_SCANCODE_S]) y += SPEED * delta;
if (keys[SDL_SCANCODE_A]) x -= SPEED * delta;
if (keys[SDL_SCANCODE_D]) x += SPEED * delta;
In the preceding code, SDL_GetKeyboardState gives us a pointer to a big table of true/false values, one per key on the keyboard. We then check four specific keys — W, A, S, and D — and adjust the square's position accordingly.
Notice the Y direction. In screen coordinates, Y increases downward. So pressing W (move up) means subtracting from Y, not adding. This trips everyone up the first time.
Don't worry about the const bool* syntax — that's another pointer thing for Chapter 10. Treat keys like an array of true/false values that you can ask "is this key down right now?"
Keeping the Square On Screen
If we let the player keep pressing D forever, the square would slide right off the right edge of the window and disappear. We can prevent that with one of SDL's little helper functions:
x = SDL_clamp(x, 0.0f, WINDOW_W - SQUARE_SZ);
y = SDL_clamp(y, 0.0f, WINDOW_H - SQUARE_SZ);
In the preceding code, SDL_clamp takes a value and two bounds, and returns the value forced into that range. If x is below zero, it becomes zero. If it's above the right edge, it gets pinned to the right edge. Otherwise, it stays as it is.
Drawing the Frame
Finally, we draw. Every frame, we do the same three things: clear the window, draw what we want to see, and present the finished frame to the screen.
// Clear the window to dark grey
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
SDL_RenderClear(renderer);
// Draw the square in orange
SDL_SetRenderDrawColor(renderer, 255, 140, 0, 255);
SDL_FRect square = { x, y, SQUARE_SZ, SQUARE_SZ };
SDL_RenderFillRect(renderer, &square);
// Show the finished frame
SDL_RenderPresent(renderer);
In the preceding code, SDL_SetRenderDrawColor sets the color SDL will use for the next drawing operation. The four numbers are red, green, blue, and alpha (opacity), each from 0 to 255. We set it to dark grey, then call SDL_RenderClear to fill the entire window with that color.
Then we switch the draw color to orange and create an SDL_FRect — an SDL floating-point rectangle — with our square's position and size. SDL_RenderFillRect draws that rectangle filled in.
Finally, SDL_RenderPresent swaps the finished frame onto the screen. Until this is called, all our drawing is happening on a hidden "back buffer" that the player never sees. Presenting at the end avoids flickering — the player only ever sees complete frames.
Cleaning Up
When the loop ends, we tidy up our SDL resources before the program exits:
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
In the preceding code, we destroy the renderer and the window in reverse order of creation, then shut SDL down. The operating system would eventually do this for us anyway, but it's a good habit to clean up after yourself explicitly.
The Complete Program
Here is the whole file in one piece. If you've typed everything as you went, this is what you should have. If not, you can copy and paste this in:
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
const int WINDOW_W = 800;
const int WINDOW_H = 600;
const float SQUARE_SZ = 80.0f;
const float SPEED = 300.0f;
int main(int argc, char* argv[])
{
if (!SDL_Init(SDL_INIT_VIDEO))
{
SDL_Log("SDL_Init failed: %s", SDL_GetError());
return 1;
}
SDL_Window* window = SDL_CreateWindow(
"Controllable Square",
WINDOW_W, WINDOW_H,
0
);
if (!window)
{
SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
SDL_Quit();
return 1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, nullptr);
if (!renderer)
{
SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
float x = (WINDOW_W - SQUARE_SZ) / 2.0f;
float y = (WINDOW_H - SQUARE_SZ) / 2.0f;
Uint64 lastTime = SDL_GetTicks();
bool running = true;
SDL_Event event;
while (running)
{
// -- Events --
while (SDL_PollEvent(&event))
{
if (event.type == SDL_EVENT_QUIT)
running = false;
if (event.type == SDL_EVENT_KEY_DOWN &&
event.key.key == SDLK_ESCAPE)
running = false;
}
// -- Delta time --
Uint64 now = SDL_GetTicks();
float delta = (now - lastTime) / 1000.0f;
lastTime = now;
// -- Input --
const bool* keys = SDL_GetKeyboardState(nullptr);
if (keys[SDL_SCANCODE_W]) y -= SPEED * delta;
if (keys[SDL_SCANCODE_S]) y += SPEED * delta;
if (keys[SDL_SCANCODE_A]) x -= SPEED * delta;
if (keys[SDL_SCANCODE_D]) x += SPEED * delta;
// -- Keep on screen --
x = SDL_clamp(x, 0.0f, WINDOW_W - SQUARE_SZ);
y = SDL_clamp(y, 0.0f, WINDOW_H - SQUARE_SZ);
// -- Render --
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 255, 140, 0, 255);
SDL_FRect square = { x, y, SQUARE_SZ, SQUARE_SZ };
SDL_RenderFillRect(renderer, &square);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Running the Program
Save the file with File > Save or Ctrl+S. Now hit the big green Play arrow at the top of the IDE, or press F5. Visual Studio will compile your code, link it against SDL, and launch the program.
If everything went well, an 800-by-600 window opens with an orange square in the middle. Press W, A, S, or D to move it. Press Escape or click the X on the window to close it.
That's your first SDL program. Congratulations. The square doesn't look like much, but every game in this book is built on top of these same pieces — a window, a loop, input, and drawing.
Common Errors and How to Fix Them
If the program didn't compile, didn't link, or didn't run, here are the usual suspects.
"Cannot open include file: 'SDL3/SDL.h'" — The compiler can't find SDL's headers. Re-check the Additional Include Directories setting in C/C++ > General. It should point to your SDL include folder.
"Unresolved external symbol" errors at the linking stage — The compiler found the headers, but the linker can't find the library. Re-check Additional Library Directories in Linker > General (should point to lib\x64) and Additional Dependencies in Linker > Input (should list SDL3.lib).
The program builds but crashes with "SDL3.dll was not found" — You forgot to copy SDL3.dll next to your .exe. Copy it from SDL3\lib\x64 into your project's x64\Debug folder.
A black window appears for half a second and disappears — SDL_Init is failing. The error message should be in the Output window at the bottom. Most often, this means SDL didn't install correctly. Re-download the package and re-extract it.
The square doesn't move — Make sure the window has focus (click on it). Also double-check you used SDL_SCANCODE_W (and friends), not SDLK_W. Scancodes refer to the physical key position; key codes refer to the character produced. Both have their uses, but they aren't interchangeable.
If you get stuck on something not listed here, the error message in the Output window is your friend. Read it carefully. Search for it online if you have to. Debugging — which we're about to meet — is the next skill we'll start building.
Introduction to Debugging
If you've done any programming before, you've already met bugs. If you haven't, you're about to. A bug is a flaw in your code that makes it do something other than what you intended. Bugs are normal. Every programmer alive writes bugs, every day, and the job is mostly finding and fixing them. The skill of finding bugs is called debugging, and Visual Studio is genuinely excellent at it.
There are two things every beginner should learn early about debugging.
The first is that the Output window is your friend. When the program builds, the Output window at the bottom of Visual Studio shows the build steps and any warnings or errors. When the program runs, anything you log with SDL_Log shows up here, and any error messages SDL produces appear here too. Get into the habit of glancing at it constantly.
The second is the breakpoint. A breakpoint is a marker you put on a line of code that tells the debugger "pause the program just before this line runs, and let me look around." Once paused, you can inspect the value of any variable, step through the code one line at a time, and watch what changes.
Let's try it on the Controllable Square. Find this line in your main.cpp:
if (keys[SDL_SCANCODE_W]) y -= SPEED * delta;
Click in the left margin of the editor, right next to that line. A red dot should appear. That's a breakpoint.
Now run the program with F5 as before. The window opens. Click on it so it has focus, then press and hold the W key. Visual Studio pauses. The line with the breakpoint is highlighted in yellow — that line hasn't run yet, it's about to.
Look at the bottom of the IDE. There's a panel called Locals that shows the value of every local variable. You can see x, y, delta, and the rest. Try pressing F10 to step over the line. Watch y decrease. Press it again. Watch it decrease again. Press F5 to resume normal running.
That's all there is to it. Right-click the red dot to delete the breakpoint when you're done.
We'll come back to the debugger throughout the book, picking up new tricks as we need them: the call stack, the Watch window, stepping into functions, conditional breakpoints. For now, just knowing that you can pause your program and look around is enough. The next time something doesn't work, your first move shouldn't be to add print statements everywhere — it should be to set a breakpoint and look.
Summary
You've set up a complete C++ development environment, linked it to a real game library, and built a small but genuinely working SDL program. The square moves. You can control it. It quits cleanly. That's more than a lot of beginner C++ tutorials manage in a whole chapter, never mind the first one.
It is true that we don't fully understand the code yet, but we will begin putting that right in the next chapter.
In the next chapter we'll step away from SDL for a moment and dig into variables — the basic building blocks of everything else we'll do. After that, in Chapter 3, we'll come straight back to SDL and put those variables to work in a more visual project. The pattern of theory chapter followed by project chapter is going to be our rhythm all the way through Act 1. Welcome aboard.