Starting from the fundamentals, become a C++ software engineer, step by step.
Free, Unlimited AccessStarting from the basics, become a C++ software engineer, step by step
View CourseLearn C++ and SDL development by creating hands on, practical projects inspired by classic retro games
View CourseLearn C++ and SDL development by recreating classic retro games
Preview CourseIn the previous lessons, we learned some techniques that allow objects in our C++ program to acopt characteristics defined in some external location. This might be a file created by a different program, a stream of data coming from the internet, or simply a text file that we can edit.
For example, we could let designers on our team or our end users add monsters to our game by editing a JSONÂ file:
The Type Object pattern introduced in the previous lesson provides a flexible way to manage variations among game entities by externalizing their common properties into data. This avoids a proliferation of C++ classes and makes game updates and content creation more accessible.
In this final lesson in the chapter, we put this theory into practice, combining it with the SDL_RWops
techniques covered previously. We will build the foundations of a system to load these type objects and their corresponding entities from external data, process that data, and use it to instantiate our C++Â objects.
We’ll use data serialized as text in this project. In the next chapter, we’ll build a program using binary serialization, so we can establish experience in both approaches.
this
Pointerthis
pointer in C++ programming, focusing on its application in identifying callers, chaining functions, and overloading operators.Within our member functions, we have access to a specific keyword: this
.
The this
keyword stores a pointer to the object that our member function was called on.
For example, if we were to call SomeObject.SomeFunction()
, within the body of SomeFunction()
, the this
keyword would contain a pointer to SomeObject
.
Below, we show this in action. We’re logging out the address of SomeObject
using the address-of operator, &
.
At this point, we’re hopefully familiar with calling functions, including passing arguments into our function’s parameters.
However, we may have noticed, that if we modify a parameter within our function, the original variable is left unaffected:
#include <iostream>
using namespace std;
void Increment(int x){ x++; }
int main(){
int Number{1};
cout << "Number: " << Number;
Increment(Number);
cout << "\nNumber: Still " << Number;
}
Number: 1
Number: Still 1
This is often the desired behavior, but not always.
In this lesson, we’ll understand why code like this doesn’t work in the way we might expect. Then, we’ll learn how to make it work.
Previously, we introduced reference variables. These were a way to, indirectly, have two variables pointing to the same location in memory.
Working with memory addresses is often considered quite dangerous; it can introduce some nasty bugs if not managed correctly.
Because of this, references had two restrictions, designed to ward off the most common cause of those bugs:
However, sometimes our use case requires us to do one or both of these things. For this reason, we have pointers.
In the previous lesson, we created a simple Vector3
struct to hold 3 numbers, which we could use to represent concepts like a position in a 3DÂ world:
struct Vector3 {
float x;
float y;
float z;
}
It would be nice if we were able to use operators like +
and +=
with our new custom type, in much the same way we were able to do with built-in types like int
and float
.
For example, we would like to be able to do things like this:
Vector3 CurrentPosition { 1.0, 2.0, 3.0 };
Vector3 Movement { 4.0, 5.0, 6.0 };
// Create a new object using the + operator
Vector3 NewPosition { CurrentPosition + Movement };
After running the above code, we'd want NewPosition
to be a Vector3
with the values 5.0
, 7.0
, and 9.0
.
For this, we need to overload operators.
Large games tend to be complex projects, developed over many years. Because of this, developers often spend as much time building the tools that help build the game as they do building anything that players will directly see.
In this project, we apply the techniques we learned in the previous few chapters around window and mouse management and serialization and deserialization to create a basic level editor tool
On the right of our tool, we’ll have a menu where users have a list of objects - or "actors" - they can choose from. They can drag and drop actors from this menu onto the level on the left to add instances of them to the level. The footer will also allow them to save levels to their hard drive, and load levels they previously saved
The techniques we cover will be applicable to a huge range of tools but, as the project continues, we’ll direct it more towards creating levels for a breakout game. Later in the course, we’ll add the game that loads and plays these levels
In the previous lesson, we set up the foundational classes for our level editor: the main window, the scene manager, and helper classes for images, text, buttons, and assets. We now have a blank window ready to be populated.
This lesson builds upon that foundation by adding the first major UI component: the Actor Menu. This menu will serve as a palette on the right side of the editor, displaying all the available objects (Actors) that users can place into their levels.
We'll create a dedicated ActorMenu class to manage this area's rendering and logic. We will also define a base Actor
class, which will represent any object that can exist in our level, like blocks, enemies, or power-ups.
Finally, we'll implement our first concrete Actor
type – a simple blue block – and add an instance of it to the ActorMenu
, making it visible and ready for interaction in later steps.
We've built the basic structure and the actor menu. Now, let's implement the ability to interact with the actors in that menu. The goal is to allow users to click and drag an actor type to eventually place it in the level.
This lesson covers the first part of drag-and-drop: initiating the drag and providing visual feedback. We'll modify our Actor
class to recognize when it's been clicked.
Upon detecting a click, the Actor
will dispatch a custom SDL_Event
. We'll use SDL_RegisterEvents()
to define this event type.
Then, we'll introduce a new ActorTooltip
class. This class will manage a second, specialized SDL_Window
. This window will appear when the custom drag event is received, display the image of the actor being dragged, and follow the mouse cursor faithfully using SDL_GetGlobalMouseState()
and SDL_SetWindowPosition()
.
We'll carefully select window flags to ensure it behaves like a proper tooltip.
The drag-and-drop mechanism is halfway there; we can pick up actors, but they vanish when released. Let's build the "drop"Â part.
First, we'll define a Level
class. This class represents the main canvas of our editor, responsible for holding and rendering the actors that make up the game level. It will have its own bounds and background.
Next, we'll connect the dragging action to the level. When the user releases the mouse button while dragging (monitored by ActorTooltip
), we need to check if the drop occurs over the Level
. If it does, we'll create a duplicate of the dragged actor. This requires adding a Clone()
capability to our Actor
class hierarchy using virtual
functions. The newly cloned actor is then positioned and added to the Level
's collection.
We'll also implement feedback mechanisms. If the user drags the actor outside the valid level area, the tooltip will become semi-transparent, and the mouse cursor will change to indicate the drop won't work there.
Starting from the fundamentals, become a C++ software engineer, step by step.
Free, Unlimited Access