Get Started Now

Intro to C++ Programming

Starting from the fundamentals, become a C++ software engineer, step by step.

LATEST UPDATES

Screenshot from Cyberpunk 2077
Module One

Intro to C++ Programming

Starting from the basics, become a C++ software engineer, step by step

Screenshot from Cyberpunk 2077
Screenshot from The Witcher 3: Wild Hunt
Screenshot from Warhammer: Total War
Screenshot from Cyberpunk 2077
Capstone Project

Building Minesweeper with C++ and SDL2

Apply what we learned to build an interactive, portfolio-ready capstone project using C++ and the SDL2 library

Screenshot from Cyberpunk 2077
Screenshot from The Witcher 3: Wild Hunt
Screenshot from Warhammer: Total War
Screenshot from Cyberpunk 2077
Module Two

Professional C++

Learn C++ and SDL development by recreating classic retro games

Screenshot from Cyberpunk 2077
Screenshot from The Witcher 3: Wild Hunt
Screenshot from Warhammer: Total War
MOST POPULAR

Tick Rate and Time Deltas

Learn how to create smooth, time-aware game loops that behave consistently across different hardware configurations
Abstract art representing computer programming
Ryan McCombe
Published

In the previous lesson, we introduced the concept of ticking and tick functions, which allow our game objects to update on every iteration of our application loop.

However, we should note that when we’re implementing logic in a Tick() function, we don’t know how quickly our objects will tick. That is, we do not know how much time has passed since the previous invocation of that object’s Tick() function.

// Goblin.h
#pragma once
#include "GameObject.h"

class Goblin : public GameObject {
 public:
  int xPosition;

  void Tick() override {
    // This object moves by one pixel per
    // invocation of Tick(), but how frequently
    // is Tick() invoked?
    xPosition += 1;
  }
};

As we add more complexity to our tick functions or more ticking objects, our game will need to perform more work on each iteration of our application loop. As such, it will iterate less frequently, meaning Tick() is invoked less frequently, meaning our object will move slower.

On the other hand, if we perform optimizations or our user has a more powerful computer, our loop can iterate faster, causing our objects to move faster.

To address this inconsistency, we typically want to define properties like movement speed in terms of real-world units of time, such as seconds or milliseconds. Let’s learn how to do this.

High-Resolution Timers

Learn to measure time intervals with high accuracy in your games
Abstract art representing computer programming
Ryan McCombe
Published

Previously, we introduced the SDL_GetTicks64() function, which returns the number of milliseconds that have passed since SDL was initialized.

However, in some situations, milliseconds are not granular enough. We need to use timers that use smaller units, such as microseconds and nanoseconds.

Timers that use these smaller units are typically called high-resolution timers. How they work and how we access them is something that varies from platform to platform, but SDL provides some utilities that can help us.

Callbacks and Function Pointers

Learn to create flexible and modular code with function pointers
Abstract art representing computer programming
Ryan McCombe
Published

At this point, we’re familiar with the many ways we store and transfer objects around our program. We can pass them as arguments to functions, return them from functions, and store them as variables.

// Passing an int to a function
SetNumber(42);

// Returning an int from a function
int GetNumber() {
  return 42;
}

// Storing an int to a member variable
SomeObject.SomeMember = 42;

What might be less familiar is that we can do all of these things with functions, too:

void SomeFunction() {
  // ...
];

// Passing a function to a function
SetFunction(MyFunction);

// Returning a function from a function
auto GetFunction() {
  return MyFunction;
}

// Storing a function as a member variable
SomeObject.SomeFunction = MyFunction;

This capability gives us a huge amount of flexibility in how we design our program and when used well, it helps us keep our code simple even as our behaviors get more and more complex.

A language that allows functions to be treated like any other data type is said to support first-class functions. C++ supports this concept in a few different ways. In the rest of this chapter, we’ll focus on one of them - function pointers.

Member Function Pointers and Binding

Explore advanced techniques for working with class member functions
Abstract art representing computer programming
Ryan McCombe
Published

Previously, we’ve seen how we can create pointers to free functions:

#include <functional>

void FreeFunction() {/*...*/}

int main() {
  std::function FunctionPtr{FreeFunction};
}

In large projects, it’s more common that we’ll be working with member functions - that is, functions that are defined as part of a class or struct:

class SomeClass {
  void MemberFunction() {/*...*/}
};

In this lesson, we’ll learn how to work with pointers to these types of functions, and manage the additional complexity that is involved.

Ticking

Using Tick() functions to update game objects independently of events
Abstract art representing computer programming
Ryan McCombe
Published

In our previous projects, we’ve updated the state of our objects based on events detected in our application loop:

// Application Loop
while (shouldContinue) {
  // Handle Events
  while (SDL_PollEvent(&Event)) {
    if (Event.type == SDL_QUIT) {
      shouldContinue = false;
    }
    GameObject.HandleEvent(Event);
  }
    
  // Render Objects
  GameWindow.Render();
  GameObject.Render(GameWindow.GetSurface());
    
  // Update Frame
  GameWindow.Update();
}

However, in more complex projects, most objects may need to update their state consistently, even when they’re not receiving any events. For example, characters not controlled by our player may need to continue to act and move, and animation, and visual effects should continue to update.

Writing Data to Files

Learn to write and append data to files using SDL2's I/O functions.
Abstract art representing computer programming
Ryan McCombe
Published

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

Managing Memory Manually

Learn the techniques and pitfalls of manual memory management in C++
Abstract art representing computer programming
Ryan McCombe
Published

Throughout this chapter, we’ve started to take more proactive control over the memory used in our programs, and controlling the lifecycle of objects (when they’re created, and when they’re destroyed)

We’ve used standard library utilities like std::unique_ptr and std::vector to help with this. In this lesson, we’ll learn how to take full control by manually managing our object lifecycles.

We’ll also see how this can be difficult, dangerous, and something to avoid where possible.

However, avoiding it is not always an option. In larger projects, we’ll interact with libraries or other systems that implement manual memory management, and understanding the techniques involved and problems to avoid will be extremely useful.

Copy Constructors and Operators

Explore advanced techniques for managing object copying and resource allocation
Abstract art representing computer programming
Ryan McCombe
Updated
Published

In this lesson, we’ll explore how objects are copied in more depth. There are two scenarios where our objects get copied. The first is when a new object is created by passing an existing object of the same type to the constructor:

struct Weapon{/*...*/};

int main() {
  Weapon SwordA;
  
  // Create SwordB by copying SwordA
  Weapon SwordB{SwordA};
}

This copying process also happens when we pass an argument by value to a function. The function parameter is created by copying the object provided as the corresponding argument:

struct Weapon{/*...*/};

void SomeFunction(Weapon W) {/*...*/}

int main() {
  Weapon Sword;
  
  // Create the W parameter by copying Sword
  SomeFunction(Sword);
}

The second scenario is when an existing object is provided as the right operand to the = operator. In the following example, we’re expecting an existing PlayerTwo to be updated by copying values from PlayerOne:

struct Weapon{/*...*/};

int main() {
  Weapon SwordA;
  Weapon SwordB;
  
  // Update SwordA by copying values from SwordB
  SwordA = SwordB;
}

As we’ve likely noticed, C++ supports these behaviors by default, even when the objects we’re copying use our custom types. In this lesson, we’ll explore what that default behavior does, and learn how to override it when our classes and structs have more complex requirements.

Dynamic Arrays using std::vector

Explore the fundamentals of dynamic arrays with an introduction to std::vector
Abstract art representing computer programming
Ryan McCombe
Updated
Published

Inevitably, we will want to store a group of related objects. That might be a collection of characters in a party, a collection of buttons on a UI, or countless other use cases.

Let's imagine we have these objects, which we want to store and manage as a single collection:

class Character {};
Character Frodo;
Character Gandalf;
Character Gimli;
Character Legolas;
Character Aragorn;

Programmers have invented a huge range of options for storing collections of objects. These containers are broadly called data structures, and which data structure we should use depends on our specific requirements. We’ll cover a much wider range of data structures in the next course. Here, we’ll introduce the most common choice - the dynamic array.

Under the hood, arrays are contiguous blocks of memory, big enough to store multiple objects in sequence.

Reading Data from Files

Learn how to read and parse game data stored in external files using SDL_RWops
Abstract art representing computer programming
Ryan McCombe
Published

In this chapter, we’ll introduce read/write operations, specifically using SDL_RWops. This is the API that SDL provides for reading from and writing to files on our players’ hard drives.

Many of the topics covered here additionally lay the foundation for more advanced capabilities. Streaming data to and from files has much in common with streaming data over a network, for example, which is fundamental for creating multiplayer games.

In this lesson, we’ll create a basic SDL application that calls Write() and then Read() within a File namespace we’ll create. The file I/O system is initialized by default, so we can simply pass 0 as the initialization flag to SDL_Init():

#include <SDL.h>
#include "File.h"

int main(int argc, char** argv) {
  SDL_Init(0);
  File::Write("data.txt");
  File::Read("data.txt");
  return 0;
}

The following code example shows the content of our File namespace.

We’ll focus on the Read() function in this lesson and will cover writing in the next lesson. For now, we can just note that Write() creates a file on our hard drive containing the text "Hello World", so we have something to test our Read() function with:

// File.h
#pragma once
#include <iostream>
#include <SDL.h>

namespace File{
  void Read(const std::string& Path) {}
  
  void Write(const std::string& Path) {
    SDL_RWops* Context{
      SDL_RWFromFile(Path.c_str(), "wb")};
    const char* Content{"Hello World"};
    SDL_RWwrite(Context, Content, sizeof(char),
                strlen(Content));
    SDL_RWclose(Context);
  }
}

Placing Flags

Implement flag placement and tracking to complete your Minesweeper project.
Abstract art representing computer programming
Ryan McCombe
Updated
Published

In this final part of our Minesweeper series, we'll add the ability for players to place flags on suspected mine locations.

We'll update our game logic to handle flag placement and removal, create a visual representation for flags, and implement a counter to track the number of flags placed.

By the end of this lesson, you'll have a fully functional Minesweeper game with all the classic features players expect!

Engine Overview

An introduction to the generic engine classes we'll use to create the game
Abstract art representing computer programming
Ryan McCombe
Updated
Published

In this series, we'll build a fully functional Minesweeper game from scratch, giving you hands-on experience with game development concepts and C++ programming.

We'll separate our project into two main parts:

  1. An "Engine" module containing components that are generally useful across a wide range of projects, not specific to Minesweeper.
  2. A Minesweeper-specific module that builds upon our engine to create the actual game.

For example, we'll create a general Button class in our engine that can be used across various projects. The cells of our Minesweeper grid will then inherit from this Button class, expanding it with Minesweeper-specific logic such as whether the cell contains a bomb and counting the number of bombs in adjacent cells.

Building a Versatile Image Class

Designing a flexible component for handling images in SDL-based applications
Abstract art representing computer programming
Ryan McCombe
Published

In this lesson, we’ll combine the techniques we’ve covered so far in this chapter into a cohesive Image class. We’ll focus on creating a friendly API so external code can easily control how images are rendered onto their surfaces We’ll cover:

  • Creating a flexible API for image handling
  • Implementing file loading and error checking
  • Setting up source and destination rectangles
  • Adding different scaling modes for rendering
  • Optimizing performance for game loops

As a starting point, we’ll be building upon a basic Window and application loop, built using topics we covered earlier in the course:

Module One
3D art showing a progammer setting up a development environment

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 59 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Capstone Project
3D art representing computer programming

Building Minesweeper with C++ and SDL2

Apply what we learned to build an interactive, portfolio-ready capstone project using C++ and the SDL2 library

Free, unlimited access

This course includes:

  • 37 Lessons
  • 100+ Code Samples
  • 92% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Module Two
A computer programmer

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 124 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Get Started Now

Intro to C++ Programming

Starting from the fundamentals, become a C++ software engineer, step by step.

Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved