Wir setzen das Gespräch über 3D-Shooter über das Wochenende fort. Wenn überhaupt, dann erinnere ich Sie daran, dass dies die zweite Hälfte ist:
Wie gesagt, ich gebe mein Bestes, um den Wunsch der Schüler zu unterstützen, etwas mit ihren eigenen Händen zu tun. Insbesondere wenn ich Vorlesungen über die Einführung in die Programmierung halte, lasse ich ihnen als praktische Übungen fast völlige Freiheit. Es gibt nur zwei Einschränkungen: Die Programmiersprache (C ++) und das Thema des Projekts, dies sollte ein Videospiel sein. Hier ist ein Beispiel für eines der Hunderte von Spielen, die meine Erstsemester gemacht haben:
Leider wählen die meisten Schüler einfache Spiele wie 2D-Plattformer. Ich schreibe diesen Artikel, um zu zeigen, dass es nicht schwieriger ist, die Illusion einer dreidimensionalen Welt zu erschaffen, als Mario Broz zu klonen.
Ich erinnere Sie daran, dass wir an einem Punkt angehalten haben, an dem Sie die Wände strukturieren können:

Stufe 13: Zeichne Monster auf die Karte
Was ist ein Monster in unserem Spiel? Dies sind seine Koordinaten und Texturnummer:
struct Sprite { float x, y; size_t tex_id; }; [..] std::vector<Sprite> sprites{ {1.834, 8.765, 0}, {5.323, 5.365, 1}, {4.123, 10.265, 1} };
Nachdem wir mehrere Monster definiert haben, zeichnen wir sie zunächst einfach auf die Karte:

Die Änderungen
können Sie hier sehen .

Stufe 14: Schwarze Quadrate anstelle von Monstern in 3D
Jetzt werden wir Sprites im 3D-Fenster zeichnen. Dazu müssen wir zwei Dinge bestimmen: die Position des Sprites auf dem Bildschirm und seine Größe. Hier ist die Funktion, die anstelle jedes Sprites ein schwarzes Quadrat zeichnet:
void draw_sprite(Sprite &sprite, FrameBuffer &fb, Player &player, Texture &tex_sprites) {
Lassen Sie uns herausfinden, wie es funktioniert. Hier ist das Diagramm:

In der ersten Zeile betrachten wir den absoluten Winkel sprite_dir (den Winkel zwischen der Richtung vom Spieler zum Sprite und der Abszisse). Der relative Winkel zwischen dem Sprite und der Blickrichtung wird offensichtlich durch einfaches Subtrahieren von zwei absoluten Winkeln erhalten: sprite_dir - player.a. Die Entfernung vom Spieler zum Sprite ist trivial zu berechnen, und die Größe des Sprites ist eine einfache Division der Bildschirmgröße durch die Entfernung. Nun, nur für den Fall, ich schneide zweitausend von oben ab, um keine riesigen Quadrate zu erhalten (dieser Code kann übrigens leicht durch Null geteilt werden). h_offset und v_offset geben die Koordinaten der oberen linken Ecke des Sprites auf dem Bildschirm an. dann füllt eine einfache Doppelschleife unser Quadrat mit Schwarz. Überprüfen Sie mit einem Stift und einem Blatt Papier, ob h_offset und v_offset korrekt berechnet wurden. Bei meinem Commit liegt ein (unkritischer) Fehler vor. Glauben Sie dem Code im Artikel :) Nun, der neuere Code im Repository wurde ebenfalls behoben.

Die Änderungen
können Sie hier sehen .

Schritt 15: Tiefenkarte
Unsere Quadrate sind auf wundersame Weise gut, aber es gibt nur ein Problem: Das entfernte Monster späht um die Ecke und das Quadrat ist vollständig gezeichnet. Wie man ist Sehr einfach. Wir zeichnen Sprites,
nachdem die Wände gezeichnet wurden. Daher kennen wir für jede Spalte unseres Bildschirms den Abstand zur nächsten Wand. Wir speichern diese Abstände in einem Array von 512 Werten und übergeben das Array an die Sprite-Rendering-Funktion. Die Sprites werden auch spaltenweise gezeichnet, sodass wir für jede Spalte des Sprites den Abstand dazu mit dem Wert aus unserem Tiefenarray vergleichen.

Die Änderungen
können Sie hier sehen .

Stufe 16: Problem mit Sprites
Sie sind großartige Monster geworden, nicht wahr? Aber zu diesem Zeitpunkt werde ich keine Funktionalität hinzufügen, im Gegenteil, ich werde alles durch Hinzufügen eines weiteren Monsters zerstören:

Die Änderungen
können Sie hier sehen .

Stufe 17: Sprites sortieren
Was war das Problem? Das Problem ist, dass ich eine beliebige Reihenfolge für das Zeichnen von Sprites haben kann und für jeden von ihnen die Entfernung mit den Wänden vergleiche, aber nicht mit anderen Sprites, sodass die entfernte Kreatur über die nächste kroch. Ist es möglich, eine Lösung mit einer Tiefenkarte zum Zeichnen von Sprites anzupassen?
Versteckter TextDie richtige Antwort lautet "Sie können". Aber wie? Schreiben Sie in die Kommentare.
Ich werde den anderen Weg gehen und das Problem dumm in der Stirn lösen. Ich werde einfach alle Sprites vom entferntesten zum am weitesten entfernten zeichnen. Das heißt, ich werde die Sprites in absteigender Reihenfolge der Entfernung sortieren und sie in dieser Reihenfolge zeichnen.

Die Änderungen
können Sie hier sehen .

Schritt 18: SDL-Zeit
Es ist Zeit für SDL. Es gibt viele verschiedene plattformübergreifende Fensterbibliotheken, und ich verstehe sie überhaupt nicht. Persönlich mag ich
imgui , aber aus irgendeinem Grund bevorzugen meine Schüler SDL, also verbinde ich mich damit. Die Aufgabe für diese Phase ist sehr einfach: Erstellen Sie ein Fenster und zeigen Sie das Bild aus der vorherigen Phase an:

Die Änderungen
können Sie hier sehen . Ich gebe keinen Link mehr zum Gitpod, weil SDL im Browser hat noch nicht gelernt zu starten :(
Update: ERLERNT! Sie können den Code mit einem Klick im Browser ausführen!
Schritt 19: Ereignisverarbeitung und -bereinigung
Eine Reaktion auf Tastenanschläge hinzuzufügen ist nicht einmal lustig, ich werde es nicht beschreiben. Beim Hinzufügen von SDL habe ich die Abhängigkeit von stb_image.h entfernt. Es ist schön, aber das Kompilieren dauert zu lange.
Für diejenigen, die nicht verstehen, sind die Quellen der neunzehnten Stufe
hier . Nun, hier ist eine typische Leistung:
Fazit
Mein Code enthält im Moment nur 486 Zeilen, und gleichzeitig habe ich sie überhaupt nicht gespeichert:
haqreu@daffodil:~/tinyraycaster$ cat *.cpp *.h | wc -l 486
Ich habe meinen Code nicht geleckt und absichtlich schmutzige Wäsche entsorgt. Ja, ich schreibe so (und nicht nur ich). An einem Samstagmorgen habe ich mich einfach hingesetzt und das geschrieben :)
Ich habe das fertige Spiel nicht geschafft, meine Aufgabe ist es nur, einen ersten Anstoß für den Flug Ihrer Fantasie zu geben. Schreiben Sie Ihren eigenen Code, er wird wahrscheinlich besser sein als meiner. Teilen Sie Ihren Code, teilen Sie Ihre Ideen, senden Sie Pull-Anfragen.