Software einrichten und Einführung in C++
In diesem Kapitel richten wir die notwendige Software ein, um unser Projekt mit C++ und OpenGL umzusetzen. Das Ziel ist es, eine plattformübergreifende Software zu entwickeln, die sowohl auf Windows, Mac als auch Linux lauffähig ist. Damit können Sie mit Teammitgliedern zusammenarbeiten, die möglicherweise unterschiedliche Betriebssysteme verwenden. Das nachfolgend beschriebene Setup ist unabhängig von vorhandenen IDEs oder Compilern und kann bei Bedarf am Ende des Kurses restlos entfernt werden.

Organisatorischer Hinweis: Dieses Kapitel bearbeiten wir gemeinsam im ersten Veranstaltungstermin. Bitte lesen Sie vorab das Konzept der Veranstaltung, damit Sie wissen, wie der Kurs aufgebaut ist.
Schritt 1: Projektordner einrichten
Die nachfolgende .zip-Datei enthält den Projektordner mit Ordnerstruktur, Konfigurationsdateien und den benötigten Bibliotheken.
Laden Sie die Datei herunter und entpacken Sie sie an einen Ort Ihrer Wahl. Im Laufe des Semesters werden Sie nach und nach die einzelnen Kapitel in diesem Ordner implementieren.
Den Inhalt dieses Ordners werden wir in der ersten Veranstaltung im Detail besprechen.
Schritt 2: Compiler, Debugger und Entwicklungsbibliotheken installieren
Um C++ Code auszuführen, muss dieser zuerst kompiliert werden — der Compiler übersetzt den gesamten Quellcode einmalig in direkten Maschinencode, den der Prozessor ohne virtuelle Maschine oder Interpreter ausführt. Wir nutzen im Rahmen der Computergrafik-Veranstaltungen den Compiler CLANG.
In Java oder TypeScript kümmert sich die IDE oder ein Paketmanager wie Maven oder npm um Kompilierung und Abhängigkeiten. In C++ sind das traditionell getrennte Werkzeuge, die Sie einzeln installieren und zusammenstecken. Das klingt aufwändig, gibt Ihnen aber volle Kontrolle über Ihren Build-Prozess. Genau diese Kontrolle ist einer der Gründe, warum Spieleengines in C++ geschrieben werden.
CLANG ist ein moderner Compiler und stammt ursprünglich von Apple. Mittlerweile wirken auch Microsoft und Google an der Entwicklung mit. Da er auch aktuelle Plattformen wie Apple Silicon oder Windows on ARM unterstützt, ist er ideal für unseren Einsatz in der Lehre geeignet. Gleiches gilt natürlich auch wenn Sie Ihr Spiel für Mac, PC und Konsole herausbringen möchten und dabei jeweils die letzten 10% Performance herauskitzeln wollen.
Als Debugger nutzen wir LLDB. Dieser stammt vom gleichen Entwicklerteam und harmoniert daher sehr gut mit CLANG. Außerdem verwenden wir clangd als Sprachserver, der uns später Syntax-Highlighting in der IDE liefert.
Zusätzlich nutzen wir Make zur Automatisierung unseres Build-Prozesses. Der entscheidende Vorteil von Make ist dabei die Abhängigkeitsverfolgung: Make vergleicht Zeitstempel und übersetzt nur die Dateien neu, die sich seit dem letzten Build geändert haben — bei größeren Projekten der Unterschied zwischen Sekunden und Minuten. Make ist ebenfalls nützlich, um Aufgaben wie das Verpacken von 3D-Modellen, das Komprimieren von Texturen und das Kopieren von Sounds zu automatisieren, ohne von einem bestimmten Betriebssystem oder einer IDE abhängig zu sein.
Um OpenGL nutzen zu können, sind je nach Betriebssystem einige zusätzliche Besonderheiten zu beachten. Wir nutzen daher die Bibliothek GLFW, die uns die Fensterverwaltung abnimmt und eine einheitliche Schnittstelle zur OpenGL API bereitstellt.
Wir schauen uns nun an, wie wir diese Software auf der jeweiligen Plattform installieren:
Linux
Unter Linux werden die benötigten Pakete einfach über den jeweiligen Paketmanager installiert. Hier ein paar Beispiele:
Ubuntu 24.04 LTS ⋅ Kali Linux 2024.2 ⋅ Debian 13:
sudo apt install clang clangd lldb make git libglfw3 libglfw3-dev
Debian 12 ⋅ Raspberry Pi OS:
sudo apt install clang-19 clangd-19 lldb-19 make git libglfw3 libglfw3-dev
sudo ln -sf /usr/lib/llvm-19/bin/clang++ /usr/bin/clang++
sudo ln -sf /usr/lib/llvm-19/bin/clangd /usr/bin/clangd
sudo ln -sf /usr/lib/llvm-19/bin/lldb /usr/bin/lldb
Fedora 40:
sudo dnf install clang clang-tools-extra lldb make git glfw glfw-devel
Arch Linux:
sudo pacman -S clang clangd lldb make git glfw
Falls Sie eine andere Distribution verwenden, können die Paketnamen abweichen. Sobald die Installation abgeschlossen ist, ist Ihr System startklar.
macOS
Unter macOS sind alle nötigen Tools in einem praktischen Bundle namens Command Line Developer Tools zusammengefasst. Sie müssen dazu nicht das komplette XCode installieren.
Unabhängig davon, ob Sie XCode nutzen oder nicht, öffnen Sie ein Terminal und führen folgenden Befehl aus um die Command Line Developer Tools zu installieren.
sudo xcode-select --install
Entweder Sie erhalten eine Mitteilung, dass die Tools bereits installiert sind oder es öffnet sich ein Dialog um sie zu installieren oder zu aktualisieren. In jedem Fall ist Ihr System im Anschluss an diesen Befehl startklar. Nach jedem Update auf eine neue macOS Version kann es notwendig werden, diesen Befehl erneut auszuführen.
Windows 10 / 11
Unter Windows benötigen wir MSYS2. Dabei handelt es sich um eine Sammlung von Linux-Tools, die für die Windows-Plattform kompiliert wurden. Damit haben wir auf der normalen Windows-Kommandozeile, Zugriff auf den Compiler und die üblichen Kommandozeilenwerkzeuge wie ls, rm, make und so weiter.
Besuchen Sie die MSYS2 Website und laden Sie sich die aktuellste Version herunter und installieren Sie die MSYS2.
ACHTUNG: Der Installationsordner darf keine Leerzeichen oder Sonderzeichen enthalten.
Im Folgenden gehe ich davon aus, dass Sie den Standard Installationsort C:\msys64 beibehalten. Falls Sie davon abweichen, achten Sie darauf, auch in den folgenden Anweisungen entsprechende Anpassungen vorzunehmen.
Wenn die Installation fertig ist, lassen Sie das Häkchen Run MSYS2 now aktiviert. Sie landen dann direkt im MSYS2 Terminal. In diesem Terminal geben Sie folgende Befehle ein. Bestätigen Sie eventuelle Rückfragen mit der Enter-Taste.
pacman -S mingw-w64-clang-x86_64-clang-tools-extra mingw-w64-clang-x86_64-lldb make git
Hinweis: Falls Git bereits auf Ihrem Rechner installiert ist, können Sie es hier weglassen.
Die installierten Pakete landen in dem Ordner, den Sie weiter oben als Installationsordner für MSYS2 ausgewählt haben. Damit andere Programme darauf Zugriff haben, müssen wir die entsprechenden Pfade dem Betriebssystem bekannt geben.
Suchen Sie im Startmenü nach Umgebungsvariablen bzw. Environment variables und öffnen Sie die passende Seite der Systemsteuerung. Im oberen Teil des Fensters unter “Benutzervariablen für …” wählen Sie den Eintrag “Path” aus und klicken auf Bearbeiten. Hier fügen Sie die folgenden Einträge hinzu:
C:\msys64\clang64\bin
C:\msys64\clang64\lib
C:\msys64\clang64\include
C:\msys64\usr\bin
Hinweis: Die Reihenfolge der Einträge ist wichtig!

Das war’s. Nun ist Ihr System startklar.
Schritt 3: Testen ob alles funktioniert
Öffnen Sie unter Linux oder macOS ein neues Terminal Fenster oder unter Windows ein neues Power Shell Fenster. Tippen Sie dann nacheinander die folgenden Befehle ein und prüfen Sie, dass jeder Befehl Ihnen eine Versionsnummer anzeigt (bedeutet ihr Computer hat das Programm gefunden) und keine Fehlermeldung.
clang++ -v
# Erwartete Ausgabe: clang version 18.1.3
clangd --version
# Erwartete Ausgabe: clangd version 18.1.3
lldb -v
# Erwartete Ausgabe: lldb version 18.1.3
make -v
# Erwartete Ausgabe: GNU Make 4.3
git -v
# Erwartete Ausgabe: git version 2.48.1
Falls eine Fehlermeldung wie “command not found” erscheint, prüfen Sie noch einmal gewissenhaft alle vorherigen Schritte. Wir werden auch in Zukunft immer mal wieder das Terminal oder die Power Shell benötigen und es ist daher wichtig, dass dies problemlos klappt.
Schritt 4: Visual Studio Code installieren
Wir verwenden in dieser Veranstaltung Visual Studio Code als IDE. VSCode wird von Microsoft als Open-Source-Projekt entwickelt und ist sowohl für Windows, macOS als auch Linux verfügbar. Falls Sie bereits andere Visual Studio-Editionen installiert haben, ist das kein Problem. Sie können VSCode problemlos zusätzlich installieren. Besuchen Sie die Visual Studio Code Download Seite und laden Sie die passende Installationsdatei für Ihr Betriebssystem herunter. Es gibt unterschiedliche Versionen für Windows x86_64 und Windows on ARM sowie für Intel Macs und Apple Silicon. Wählen Sie die passende Version.
Interessante Randnotiz: Der Chefentwickler von Visual Studio Code ist Erich Gamma. Er ist Mitglied der “Gang of Four” und Mitautor des Buchs “Design Patterns – Elements of Reusable Object-Oriented Software”, einem Standardwerk über Entwurfsmuster, das Ihnen in Ihrem Studium möglicherweise bereits begegnet ist.
Erweiterungen für VSCode
VSCode ist modular aufgebaut. Standardmäßig hat es die Funktionen eines besseren Texteditors. Mit Hilfe von Erweiterungen lässt sich der Support für diverse Programmiersprachen, Debugger und mehr nachinstallieren. Öffnen Sie VSCode, gehen Sie in das Erweiterungen Menü und installieren Sie die folgenden Erweiterungen:

clangd (Herausgeber: LLVM) verbindet VSCode mit dem in Schritt 2 installierten clangd-Sprachserver. Die Extension selbst enthält keinen Sprachserver — sie kommuniziert über das standardisierte Language Server Protocol (LSP) mit dem externen clangd-Binary. Das liefert Syntax-Highlighting, Autovervollständigung, Echtzeit-Fehlerunterstreichung und Sprung-zur-Definition für C/C++. In einem Java-Projekt übernimmt das typischerweise die IDE selbst; hier ist es ein separates, austauschbares Werkzeug.
LLDB DAP (Herausgeber: LLVM) erweitert VSCode um eine Debug-Oberfläche für LLDB: Breakpoints setzen, Variablen inspizieren, Schrittweise ausführen, Call Stack einsehen. Hinter dem Kürzel DAP steckt das Debug Adapter Protocol, ein standardisiertes Protokoll zwischen IDE und Debugger — analog zum LSP für Sprachserver. Das Programm lldb-dap übersetzt zwischen dem DAP, das VSCode spricht, und der nativen LLDB-Schnittstelle. Wir müssen VSCode nach der Installation einmalig den Pfad zu diesem Programm mitteilen (siehe nächster Abschnitt).
Compare Folders (Herausgeber: MoshFeu) vergleicht zwei Verzeichnisse Datei für Datei und zeigt Unterschiede nebeneinander an. Im Kursalltag ist das besonders nützlich: Sie können damit Ihren Kapitelordner gegen die Referenzimplementierung im github/-Submodul abgleichen oder die Änderungen zwischen zwei aufeinanderfolgenden Kapiteln (z.B. cgb_03 → cgb_04) auf einen Blick erfassen.
Debugger Pfad einstellen
Die LLDB-DAP-Extension bringt kein eigenes lldb-dap-Binary mit — sie stellt nur die Verbindung zwischen VSCode und diesem Programm her. Das Programm selbst haben wir in Schritt 2 mit dem LLVM-Paket installiert, aber je nach Betriebssystem und Installationsmethode liegt es an einem anderen Ort. Da die Extension den Pfad nicht immer zuverlässig automatisch finden kann, fragt sie evtl. beim ersten Debugger-Start danach. Das ist keine Fehlfunktion, sondern eine einmalige Konfigurationsaufforderung.

Klicken Sie den Button “Open Settings” und tragen Sie dort den Pfad zur Datei lldb-dap bzw. lldb-dap.exe ein. Um den passenden Pfad auf Ihrem System zu finden, nutzen Sie einen der folgenden Befehle.
Linux
sudo find / -name lldb-dap 2>/dev/null
# Mögliche Ergebnisse:
# Ubuntu: /usr/lib/llvm-18/bin/lldb-dap
# Debian: /usr/lib/llvm-19/bin/lldb-dap
# Fedora: /usr/bin/lldb-dap
# Arch: /usr/bin/lldb-dap
macOS
xcrun -f lldb-dap
# Mögliche Ergebnisse:
# mit XCode: /Applications/Xcode.app/Contents/Developer/usr/bin/lldb-dap
# ohne XCode: /Library/Developer/CommandLineTools/usr/bin/lldb-dap
Windows
cmd
dir C:\ /s /b | findstr /i "lldb-dap.exe"
# Mögliches Ergebnis:
# C:\msys64\clang64\bin\lldb-dap.exe
Den Pfad, der Ihnen angezeigt wird, können Sie dann in das entsprechende Feld in den Einstellungen von VSCode eintragen.

Danach sollte die Meldung verschwinden.
Datenschutz
VSCode sendet standardmäßig Nutzungsdaten an Microsoft. Diese Funktion kann relativ leicht deaktiviert werden: Öffnen Sie die Einstellungen über File → Preferences → Settings (macOS: Code → Settings → Settings) und navigieren Sie zu User → Application → Telemetry. Stellen Sie dort Telemetry Level auf off.
Falls Sie GitHub Copilot nutzen, lohnt sich ein Blick in die Datenschutzeinstellungen: GitHub kann Ihre Interaktionen mit Copilot zum Training seiner KI-Modelle verwenden. Das ist besonders relevant, sobald Sie später an Code arbeiten, der nicht quelloffen ist – andernfalls kann es zu rechtlichen oder vertraglichen Problemen kommen, wenn interner Code in fremde Trainingsdaten fließt. Ob das Training in Ihrem Fall aktiv ist und wie es sich abschalten lässt, sehen Sie unter Profilbild (oben rechts) → Settings → Copilot → Privacy, insbesondere bei der Option Allow GitHub to use my data for AI model training.
Dieser Hinweis gilt natürlich gleichermaßen für alle anderen KI Tools.
Schritt 5: Projektordner öffnen
Starten Sie Visual Studio Code und klicken Sie dort auf Ordner öffnen um den in Schritt 1 erstellten Projektordner zu öffnen. In diesem Ordner können Sie nun mit der Implementierung des ersten Kapitels beginnen.
Hello World in C++
Die Erfinder der Programmiersprache C sind auch die Urväter der Hello-World-Programme. Was liegt also näher, als dass wir unseren ersten Einstieg in die C/C++ Programmierung ebenfalls mit einem Hello World! beginnen.
cgb_01/main.cpp
// Hello World in C++
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
}
Speichern Sie die Datei und kompilieren Sie das Programm, indem Sie im Terminal folgendes eingeben:
make cgb_01
Wenn kein Fehler erscheint, können Sie das Programm anschließend mit folgendem Befehl starten:
bin/cgb_01
Hier eine Erklärung der einzelnen Zeilen:
// Hello World in C++ In C und C++ ist jede Zeile, die mit // beginnt, ein Kommentar. Kommentare erlauben es dem Entwickler, Notizen zu hinterlassen. Der Compiler ignoriert diese Zeile komplett.
#include <iostream> Mit #include werden Funktionen aus anderen Bibliotheken oder Dateien in unser Programm eingebunden — ähnlich wie import in Java oder TypeScript. Der Unterschied: #include ist kein symbolischer Import, sondern ein Textersetzungsverfahren. Der Präprozessor kopiert den gesamten Inhalt der angegebenen Datei wörtlich in Ihre Datei, bevor der Compiler überhaupt anfängt. Hier binden wir iostream ein, die Standardbibliothek für Ein- und Ausgabe über Streams.
int main() Hier definieren wir den Einstiegspunkt unseres Programms. Wenn Sie aus Java kommen, kennen Sie public static void main(String[] args). In C++ ist main() eine freistehende Funktion — sie muss in keiner Klasse leben. Der int vor dem Funktionsnamen gibt an, dass die Funktion einen ganzzahligen Wert zurückgibt. Dieser Wert geht ans Betriebssystem und teilt mit, ob das Programm erfolgreich war.
std::cout << "Hello World!" << std::endl; Dies ist unsere Ausgabe — vergleichbar mit System.out.println() in Java oder console.log() in TypeScript. Hier kommen mehrere neue Konzepte zusammen:
std::ist ein Namespace, ein Namensraum. Namespaces lösen das Problem von Namenskollisionen zwischen Bibliotheken: Zwei Bibliotheken können unabhängig voneinander einen Typvectordefinieren, solange sie in verschiedenen Namespaces leben. Der Namespacestdenthält die gesamte C+±Standardbibliothek — ähnlich wie Java-Packages alles ausjava.langoderjava.utilbündeln. Das Präfixstd::sagt dem Compiler: „Suchecoutim Namespacestd.”coutist ein Ausgabestream, der mit dem Terminal verbunden ist. Stellen Sie sich einen Stream als eine Rohrleitung vor, in die Sie Daten hineinschieben — das Terminal auf der anderen Seite zeigt dann das Ergebnis an.<<ist der Stream-Operator. Er sendet den rechten Wert in den linken Stream. Sie können mehrere<<hintereinanderketten, um verschiedene Werte nacheinander auszugeben.std::endlbeendet die Zeile mit einem Zeilenumbruch. Der Unterschied zu\n:std::endlleert zusätzlich den Ausgabepuffer. Für die meisten Zwecke können Sie beides verwenden.
Da es sich um die main()-Funktion handelt, ergänzt der Compiler automatisch return 0, wenn kein explizites return angegeben wird. Das ist eine Besonderheit der main(): Ein fehlender Rückgabewert am Ende wird stillschweigend als Erfolg gewertet. In allen anderen Funktionen würde der Compiler Sie auf das fehlende return hinweisen.
Eine Vereinfachung, um die Software in nur einem Schritt zu bauen und auszuführen, ist das folgende Make-Kommando:
make run_cgb_01
Ausblick: C++23
C++ wird kontinuierlich weiterentwickelt. Mit C++23 wurde eine noch kompaktere Variante für die Textausgabe eingeführt:
cgb_01/main.cpp
#include <print>
int main()
{
std::println("Hello World!");
}
std::println stammt aus der neuen Bibliothek <print> und gibt Text auf der Konsole aus. Anders als bei printf oder std::cout wird der Zeilenumbruch automatisch hinzugefügt — kein \n oder std::endl nötig.
Probieren Sie aus, ob Ihr Compiler bereits den neuen Standard unterstützt! Dazu müssen Sie in zwei Projektdateien den C+±Standard anpassen:
Makefile (Windows: Zeile 8, macOS: Zeile 15, Linux: Zeile 21):
OPT = -std=c++23
.clangd (Zeile 4):
"-std=c++23"
Da C++23 abwärtskompatibel ist, können Sie Makefile und .clangd auf -std=c++23 belassen — der restliche Code des Moduls lässt sich problemlos damit kompilieren. Falls std::println bei Ihnen nicht funktioniert, stellen Sie beide Dateien wieder auf -std=c++20 zurück. Für den Rest des Moduls verwenden wir in jedem Fall die iostream-Variante.
Exkurs: C-Style in C++
In Ihrem Studium und in der Praxis werden Sie häufig auf eine ältere Schreibweise stoßen, die aus der Programmiersprache C stammt:
cgb_01/main.cpp
// Hello World in C-Style C++
#include <cstdio>
int main()
{
printf("Hello World!\n");
return 0;
}
#include <cstdio> bindet die C-Standardbibliothek für Ein- und Ausgabe ein. Das Präfix c im Namen deutet darauf hin, dass diese Bibliothek ursprünglich aus der Programmiersprache C stammt.
printf("Hello World!\n") ist die C-Funktion zur Textausgabe. Das \n am Ende ist der Zeilenumbruch.
return 0; gibt explizit 0 als Rückgabewert zurück und signalisiert dem Betriebssystem: kein Fehler. Der Steam Launcher prüft ebenfalls diesen Wert, um den Entwickler über Abstürze seines Spiels zu informieren.
Diese Schreibweise ist in der Spieleentwicklung nicht ungewöhnlich. Unsere Grafikbibliothek GLFW ist bewusst in C entwickelt worden — Sie werden dieser Syntax also in der Praxis begegnen. Da wir einen C+±Compiler verwenden, können wir jederzeit zwischen C- und C+±Bibliotheken wählen und das Beste aus beiden Welten nutzen.
Build-Prozess verstehen
Wir haben weiter oben den Befehl make genutzt, um unser Programm zu kompilieren. Make prüft zuerst, ob sich eine Datei geändert hat, und kompiliert diese nur, falls das Änderungsdatum neuer ist. Hinter den Kulissen ruft make den Compiler im Terminal auf. Das können wir auch selbst tun.
Die Befehle, die Make ausführt, lauten wie folgt:
mkdir -p obj
clang++ -c cgb_01/main.cpp -g -o obj/cgb_01/main.o
mkdir -p bin
clang++ obj/cgb_01/main.o -g -o bin/cgb_01
Der Build-Prozess besteht aus drei Stufen — Präprozessor, Kompilierung und Linking. In unseren Aufrufen sind Präprozessor und Kompilierung im ersten Schritt zusammengefasst:
-
Präprozessor + Kompilierung — Der erste
clang++-Aufruf mit dem Flag-cverarbeitet zuerst alle#include-Direktiven (Präprozessor, den Sie oben bereits kennengelernt haben) und übersetzt die expandierte Datei dann in Maschinencode. Das Ergebnis ist eine Objektdatei (.o). Sie enthält den kompilierten Code dieser einen Datei, ist aber noch kein ausführbares Programm. -
Linking — Der zweite
clang++-Aufruf verbindet eine oder mehrere Objektdateien zum fertigen Programm. Ausobj/cgb_01/main.owirdbin/cgb_01. Sobald ein Projekt aus mehreren Quelldateien besteht, werden alle separat kompiliert und am Ende zusammengelinkt. In Java gibt es diesen separaten Link-Schritt nicht —javacerzeugt direkt ausführbaren Bytecode.
Die verwendeten Compiler-Flags haben folgende Bedeutung:
| Flag | Bedeutung |
|---|---|
-c |
Nur kompilieren, nicht linken (erzeugt eine .o-Datei) |
-g |
Debug-Informationen einbetten (werden von LLDB benötigt) |
-o |
Name der Ausgabedatei festlegen |
Führen Sie die Befehle manuell nacheinander aus und beobachten Sie, welche Dateien in Ihrem Projektordner erzeugt oder verändert werden.
Was macht der Befehl make clean? Öffnen Sie die Datei Makefile im Projektordner und suchen Sie dort nach dem Wort clean. Dort sehen Sie, welche Befehle dahinterstecken.
Lernziele
Nach diesem Kapitel sollten Sie die folgenden Fragen beantworten können:
- Was ist ein Compiler und wie unterscheidet sich kompilierter Code von interpretiertem Code?
- Welche Schritte durchläuft ein C+±Programm vom Quellcode bis zur fertigen ausführbaren Datei?
- Was macht der Präprozessor und wie unterscheidet sich
#includevonimportin Java? - Was ist ein Linker und warum ist das Linken ein separater Schritt?
- Was ist ein Namespace in C++ und welches Problem löst er?
- Was sind Streams in C++ und warum nutzt C++
<<statt einer einfachen Funktion wieprintln? - Was ist Make und warum nutzt man ein Build-Tool statt den Compiler direkt aufzurufen?
Bearbeitungshinweise
Dieses Kapitel bearbeiten wir gemeinsam im ersten Veranstaltungstermin. Bitte lesen Sie vorab das Konzept der Veranstaltung, damit Sie wissen, wie der Kurs aufgebaut ist.