Beschreibung des Logikalgorithmus und Analyse eines Arbeitsbeispiels in Form eines Techno-Demo-Spiels
WebGL2-Version dieser Demo https://danilw.itch.io/flat-maze-web Weitere Links finden Sie im Artikel.
Der Artikel ist in zwei Teile unterteilt, den ersten über Logik und den zweiten Teil über die Anwendung im Spiel, den ersten Teil :
- Hauptmerkmale
- Links und eine kurze Beschreibung.
- Der Algorithmus der Logik.
- Die Grenzen der Logik. Bugs / Features und Angle Bugs.
- Zugriff auf Indexdaten.
Weitere Beschreibung der Spieldemo, zweiter Teil :
- Verwendete Funktionen dieser Logik. Und schnelles Rendern von einer Million Pixelpartikeln.
- Implementierung, einige Kommentare zum Code, Beschreibung der Kollision in zwei Richtungen. Und Interaktion mit dem Spieler.
- Links zu verwendeten Grafiken mit opengameart und einem Shader für Schatten. Und der Artikel Link zu cyberleninka.ru
Teil 1
1. Hauptmerkmale
Die Idee ist eine Kollision / Physik von Hunderttausenden von Teilchen untereinander in Echtzeit, wobei jedes Teilchen eine eindeutige Kennung aufweist.
Wenn jedes Partikel indiziert ist, können alle Parameter eines Partikels gesteuert werden , z. B. die Masse, seine Gesundheit (PS) oder der Schaden, die Beschleunigung, die Verzögerung, die Objekte und Reaktionen auf das Ereignis in Abhängigkeit vom Typ / Index des Partikels. Außerdem können für jedes Partikel eindeutige Zeitgeber festgelegt werden und so weiter.
Alle Logikfunktionen von GLSL sind vollständig auf jede Game Engine und jedes Betriebssystem übertragbar, das GLES3 unterstützt.
Die maximale Anzahl von Partikeln entspricht der Größe des Framebuffers (fbo, alle Pixel).
Eine angenehme Anzahl von Partikeln (wenn Platz für die Interaktion von Partikeln vorhanden ist) ist (Resolution.x*Resolution.y/2)/2
ist jedes zweite Pixel in x
und jedes zweite Pixel in y
, weshalb die logische Beschreibung dies sagt.
Der erste Teil des Artikels zeigt die minimale Logik, im zweiten am Beispiel eines Spiels die Logik mit einer großen Anzahl von Interaktionsbedingungen.
2. Links und kurze Beschreibung
Ich habe drei Demos zu dieser Logik gemacht:
1. Lesen Sie im GLSL-Fragment-Shader unter shadertoy https://www.shadertoy.com/view/tstSz7 die gesamte Logik des BufferC-Codes. Mit diesem Code können Sie auch Hunderttausende von Partikeln mit ihrer UV-Strahlung an einer beliebigen Position auf einem Fragment-Shader anzeigen, ohne instanziierte Partikel zu verwenden.

2. Portierungslogik für Instanzpartikel (von Godot als Engine verwendet)
Links Webversion , exe (win) , Quellen Projekt Partikel_2D_self_collision .
Kurzbeschreibung: Dies ist eine schlechte Demonstration für instanziierte Partikel , da ich die maximale Zunahme mache, wenn die gesamte Karte sichtbar ist. 640x360 Partikel (230k) werden immer verarbeitet, das ist viel. Siehe unten in der Beschreibung des Spiels, dort habe ich es richtig gemacht, ohne zusätzliche Partikel. (Es gibt einen Partikelindexfehler im Video, der im Code behoben ist.)
3. Das Spiel, darüber unten in der Beschreibung des Spiels. Links Webversion , exe (win) , Quellen
3. Der Algorithmus der Logik
Kurz gesagt:
Die Logik ist ähnlich wie beim Fallenden Sand. Jedes Pixel behält den Bruchteil der Position (Verschiebung innerhalb seines Pixels) und die aktuelle Beschleunigung bei.
Die Logik prüft die Pixel im Radius 1, dass ihre nächste Position zu diesem Pixel gehen möchte (wegen dieser Einschränkung, siehe die Einschränkungen unten) , sowie die Pixel im Radius 2 zur Abstoßung (Kollision).
Der eindeutige Index wird gespeichert, indem die Logik in int-float übersetzt und die Größe für die angegebene Position pos
und pos
Geschwindigkeit verringert wird.
Daten werden folgendermaßen gespeichert: (aufgrund dieses Fehlers siehe Einschränkungen)
pixel.rgba r=[0xfffff-posx, 0xf-data] g=[0xfffff-posy, 0xf-data] b=[0xffff-velx, 0xff-data] a=[0xffff-vely, 0xff-data]

Im Code Zeilennummern für BufC https://www.shadertoy.com/view/tstSz7 , 115 Transition-Check, 139 Collision-Checks.
Dies sind einfache Schleifen, um benachbarte Werte anzunehmen. Und die Bedingung ist, wenn die Position gleich der Position des aktuellen Pixels ist, dann verschieben wir diese Daten zu diesem Pixel (wegen dieser Einschränkung) und der Wert von vel
ändert sich abhängig von den benachbarten Pixeln, falls vorhanden.
Das ist alles Teilchenlogik.
Es ist am besten, Partikel in einem Abstand von 1 Pixel voneinander zu platzieren, wenn sie näher als 1 Pixel sind, dann kommt es zur Abstoßung, zum Beispiel einer Karte mit einem Labyrinth im Spiel, die Partikel stehen an ihren Stellen, ohne sich aufgrund des Abstandes von 1 Pixel zwischen ihnen zu bewegen.
Als nächstes folgt das Rendern (Rendern). Im Fall eines Fragment-Shaders werden Pixel in einem Radius von 1 aufgenommen, um sich überschneidende Bereiche anzuzeigen. Im Fall von INSTANCE_ID
wird ein Pixel an der Adresse INSTANCE_ID
das aus einer linearen Ansicht in ein zweidimensionales Array übersetzt wurde.
4. Einschränkungen der Logik. Bugs / Features und ANGLE-Bugs
- Die Pixelgröße
BALL_SIZE
im Code muss innerhalb der für die Berechnung BALL_SIZE
Grenzen liegen und größer als sqrt(2)/2
und kleiner als 1
. Je näher an 1, desto weniger Platz zum Gehen innerhalb des Pixels (des Pixels selbst), desto weniger Platz. Eine solche Größe wird benötigt, damit die Pixel nicht ineinander fallen. Wenn Sie kleine Objekte haben, kann weniger als 1 eingestellt werden. Es entsteht eine Illusion von Objekten mit weniger als 1 Pixel (berechnet). - Die Geschwindigkeit darf nicht mehr als
1
Pixel betragen, da sonst die Pixel verschwinden. Sie können jedoch eine Geschwindigkeit von mehr als 1
pro Frame festlegen. Wenn Sie mehrere Framebuffer (FBO / Ansichtsfenster) erstellen und mehrere logische Schritte gleichzeitig ausführen , erhöht sich die Framegeschwindigkeit um die Anzahl der zusätzlichen FBOs. Dies habe ich in der Fruchtdemo getan und den Link zu Shadertoy verwendet (bufC kopiert nach bufD). - Druckbegrenzung (wie Schwerkraft oder andere Kraft-Normal-Karte). Wenn mehrere benachbarte Pixel diese Position einnehmen (siehe Bild oben), wird nur eines gespeichert, die restlichen Pixel verschwinden. Dies ist in der Demo zu Shadertoy leicht zu bemerken. Setzen Sie die Maus auf Force, ändern Sie den Wert von
MOUSE_F
in Common auf 10
und lenken Sie die Partikel in die Ecke des Bildschirms. Sie verschwinden dann ineinander. Oder das gleiche mit dem Schwerkraftwert maxG
in Common . - Fehler im Winkel. Damit diese Logik in den GPU (instanziierten) Partikeln funktioniert, ist es am besten (billiger, schneller), die Position und alle anderen Partikelparameter für die Anzeige im Instanz-Shader zu berechnen. Angle erlaubt jedoch nicht die Verwendung von mehr als einer fbo-Textur für einen Shader. Daher muss die Berechnung eines Teils der Logik an den Vertex-Shader übertragen werden, wo die Indexnummer vom Instanz-Shader übertragen werden soll. Das habe ich in beiden Demos mit GPU-Partikeln gemacht.
- Ein schwerwiegender Fehler in beiden Demos (außer im Spiel). Der Positionswert geht verloren, wenn es sich nicht um ein Vielfaches von
1/0xfffff
ist hier https://www.shadertoy.com/view/WdtSWS
Genauer gesagt ist dies kein Fehler, und der Einfachheit halber sollte ich ihn als Teil dieses Algorithmus als Fehler bezeichnen.
Fehler behoben:
Konvertieren Sie den Positionswert nicht in int-float , da 0xff
verschwindet und 8 Bits für Daten verfügbar sind. Der 0xffff
Wert für Daten bleibt jedoch erhalten, was für viele Dinge ausreichen kann.
Ich habe genau das in der Spieldemo getan. Ich verwende nur 0xffff
für die Daten, in denen Partikeltyp, Animations-Timer und Gesundheit gespeichert sind und noch freier Speicherplatz vorhanden ist.
5. Zugriff auf Indexdaten
instanziertes Teilchen hat seine eigene INSTANCE_ID
, es nimmt ein Pixel aus der Textur des Framebuffers mit Teilchenlogik (bufC, Beispiel für Shader). Wenn wir dort die Teilchen- ID (siehe Datenspeicherung) dieses Teilchens entpacken, lesen wir mit dieser ID die Textur mit Daten für Teilchen (bufB) , ein Beispiel für einen Shader).
Im Shadertoy-Beispiel speichert bufB nur die Farbe für jedes Partikel. Es ist jedoch offensichtlich, dass Daten wie Masse, Beschleunigung, Verzögerung, die zuvor geschrieben wurden, sowie logische Aktionen vorhanden sein können (z. B. können Sie jedes Partikel an eine beliebige Position verschieben (teleportieren), wenn dies erledigt ist entsprechende logische Aktion im Code), können Sie auch die Bewegung eines Partikels oder einer Gruppe über die Tastatur steuern ...
Ich meine, Sie können mit jedem der Partikel alles tun, als wären es normale Partikel in einem Array auf dem Prozessor. Der bidirektionale Zugriff vom GPU-Partikel kann seinen Status ändern, aber auch von der CPU aus können Sie den Partikelstatus nach Index ändern (mithilfe logischer Aktionen und Textur) Datenpuffer).
Teil 2
1. Verwendete Funktionen dieser Logik. Und schnelles Rendern von einer Million Pixelpartikeln
Die Größe des Framebuffers (fbo / viewport) für Partikel beträgt 1280x720, die Teile befinden sich nach 1, dies sind 230.000 aktive Partikel (aktive Elemente im Labyrinth).
Es befinden sich immer nicht mehr als 12.000 Partikel mit GPU-Instanz auf dem Bildschirm.
Logik verwendet:
- Die Player-Logik ist von der Partikellogik getrennt und liest nur Daten aus dem Partikelpuffer.
- Der Spieler wird langsamer, wenn er mit Objekten kollidiert.
- Monsterartige Gegenstände fügen dem Spieler Schaden zu.
- Der Spieler hat 2 Angriffe, einer stößt alles ab, der zweite erzeugt Partikel wie ein Feuerball (das Bild ist so)
- Der Feuerballtyp hat seine eigene Masse, und die bilaterale Verfolgung von Kollisionen mit anderen Partikeln funktioniert.
- Andere Partikel wie Zauber und Zombies (eine Art von Zauber ist unverwundbar) werden bei einer Kollision mit einem Feuerball zerstört
- Feuerball geht nach einer Kollision aus
- Physiklevel - Bäume und Quadrate werden vom Spieler abgestoßen, andere Teilchen interagieren nicht, keine Beschleunigungen wirken auf den Feuerball
- Animations-Timer sind für jedes Partikel einzigartig
Verglichen mit der Obst-Demo, bei der es Overhead gibt, beträgt die Anzahl der Partikel mit GPU-Instanzen in diesem Spiel nur 12.000.
Es sieht so aus:

Ihre Anzahl hängt vom aktuellen Zoom ( Zoom ) der Karte ab. Die Erhöhung ist auf einen bestimmten Wert begrenzt, sodass nur diejenigen berücksichtigt werden, die auf dem Bildschirm sichtbar sind.
Der Bildschirm verschiebt sich mit dem Spieler, die Logik zur Berechnung der Verschiebungen ist etwas komplex und sehr situativ. Ich bezweifle, dass sie in einem anderen Projekt Anwendung finden wird.
2. Implementierung, einige Kommentare zum Code.
Der gesamte Spielcode befindet sich auf der GPU.
Die Logik zum Berechnen der Verschiebung von Partikeln in einem Bildschirm mit einer Erhöhung der Scheitelpunktfunktion in der Datei /shaders/scene2/particle_logic2.shader ist eine Partikelshader-Datei (Scheitelpunkt und Fragment), kein instanzierter Shader, ein instanzierter Shader tut nichts, übergibt nur seinen Index aufgrund von Fehler oben beschrieben.
Partikel nach Typ und der gesamten Logik der Interaktion von Partikeln in einer Datei. Dies ist eine Datei einer Frame- Shader-Shader-Datei shaders / scene2 / Particles_fbo_logic.shader
// 1-2 ghost // 3-zombi // 4-18 blocks // +20 is on fire // 40 is bullet(right) 41 left 42 top 43 down
Datenspeicherpixel [pos.x, pos.y, [0xffff-vel.x, 0xff-data1],[0xffff-vel.y, 0xff-data2]]
data1 ist ein Typ, data2 ist eine HP oder ein Timer.
Der Timer läuft auf Frames in jedem Partikel , der Maximalwert des Timers beträgt 255, ich brauche nicht so viel, ich verwende nur 1-16 Maximum ( 0xf
) und 0xf
bleibt unbenutzt, wo Sie beispielsweise den tatsächlichen HP-Wert speichern können, er wird für mich nicht verwendet. (das heißt, ja, ich verwende 0xff
für den Timer , aber tatsächlich habe ich nur weniger als 16 Frames Animation und 0xf
genug, aber ich brauchte keine zusätzlichen Daten)
Eigentlich wird 0xff
nur für den Timer von brennenden Bäumen verwendet, sie werden nach 255 Bildern zu Zombies. Die Timer-Logik befindet sich teilweise in type_hp_logic
im Partikel-Framebuffer-Shader (Link oben).
Ein Beispiel für eine Zwei-Wege-Kollisionsoperation, bei der ein Feuerball beim ersten Treffer ausgeht und das Objekt, mit dem er getroffen wurde, ebenfalls seine Aktion ausführt.
Datei shaders / scene2 / Particles_fbo_logic.shader Zeile 438:
if (((real_index == 40) || (real_index == 41) || (real_index == 42) || (real_index == 43)) && (type_hp.y > 22)) { int h_id = get_id(fragCoord + vec2(float(x), float(y))); ivec2 htype_hp = unpack_type_hp(h_id); int hreal_index = htype_hp.x; if ((hreal_index != 40) && (hreal_index != 41) && (hreal_index != 42) && (hreal_index != 43)) type_hp.y = 22; } else { if (!need_upd) { int h_id = get_id(fragCoord + vec2(float(x), float(y))); ivec2 htype_hp = unpack_type_hp(h_id); int hreal_index = htype_hp.x; if (((hreal_index == 40) || (hreal_index == 41) || (hreal_index == 42) || (hreal_index == 43)) && (htype_hp.y > 22)) { need_upd = true; } } }
real_index
ist ein Typ, Typen sind oben aufgeführt, 40-43 ist ein Feuerball .
ferner ist type_hp.y > 22
der Wert des Timers. Wenn er größer als 22 ist, ist dem Feuerball nichts begegnet.
h_id = get_id(...
nimm den Wert des Typs und HP (Timer) des angetroffenen Partikels
hreal_index != 40...
ignorierter Typ (anderer Feuerball )
type_hp.y = 22
Ein Timer ist auf 22 eingestellt. Dies ist ein Indikator dafür, dass dieser Feuerball mit einem Objekt kollidierte.
else { if (!need_upd)
Variable need_upd überprüft, ob es keine wiederholten Kollisionen gibt, da sich die Funktion in einer Schleife befindet, stoßen wir auf einen Feuerball .
h_id = get_id(...
wenn es noch keine Kollision gab, nehmen wir ein Objekt vom Typ und Timer.
hreal_index == 40...htype_hp.y > 22
dass das Kollisionsobjekt ein Feuerball ist und nicht hreal_index == 40...htype_hp.y > 22
.
need_upd = true
Flag, das need_upd = true
dass der Typ aktualisiert werden muss, seit er auf einen Feuerball gestoßen ist.
weitere Zeile 481
if((need_upd)&&(real_index<24)){
real_index <24 nach Typ kleiner als 24 gibt es nicht brennende Zombie- und Geisterbäume, und in diesem Zustand aktualisieren wir den Typ abhängig vom aktuellen Typ.
Somit kann fast jede Interaktion von Objekten durchgeführt werden.
Interaktion mit dem Spieler:
Datei shaders / scene2 / logic.shader Zeile 143 Funktion player_collision
Diese Logik liest die Pixel um den Spieler in einem Radius von 4 x 4 Pixeln, nimmt die Position jedes der Pixel und vergleicht sie mit der Position des Spielers. Wenn ein Element gefunden wird, folgt die Typprüfung. Wenn dies ein Monster ist, nehmen wir HP vom Spieler.
Dies funktioniert etwas ungenau und ich wollte es nicht beheben , diese Funktion kann genauer gemacht werden.
Während eines Angriffs schieben sich die Partikel vom Spieler weg und der Abstoßungseffekt:
Ein Framebuffer (Ansichtsfenster) wird verwendet, um die Normalität der aktuellen Aktionen zu schreiben, und Partikel ( Particles_fbo_logic.shader ) nehmen diese (von der normalen) Textur in ihre Position und wenden den Wert auf ihre Geschwindigkeit und Position an. Der gesamte Code dieser Logik ist buchstäblich nur ein paar Zeilen, die Datei force_collision.shader
Mit einem Klick auf die linke Maustaste fliegen Feuerballschalen , ihr Aussehen ist nicht sehr natürlich , sie wurden nicht korrigiert und in dieser Form belassen.
Sie können entweder eine normale Zone (Form) für Spawn-Partikel mit einer Verschiebung relativ zum Spieler erstellen (dies wird nicht durchgeführt).
Oder Sie können den Feuerball als Spieler zu einem separaten Objekt machen und normal in einen Puffer ziehen, um Partikel vom Feuerball wegzudrücken , dh in Analogie zum Spieler ...
Wer muss denken, dass sie es selbst herausfinden werden?
3. Links zu den verwendeten Grafiken mit opengameart und dem Shadow Shader
Ich erhielt einen Link zu einem Artikel auf cyberleninka.ru
In der Beschreibung des von mir verwendeten Algorithmus gibt es vielleicht eine detailliertere und korrektere Beschreibung als in diesem Artikel.
Der Shadow Shader funktioniert sehr einfach, basierend auf diesem Shader https://www.shadertoy.com/view/XsK3RR (ich habe einen geänderten Code)
Shader Erstellt eine 1D Radial Lightmap

und Schattierung im Bodenmal- Code shaders / scene2 / mainImage.shader
Links zu den verwendeten Grafiken , alle Grafiken im Spiel von der Website https://opengameart.org
Feuerball https://opengameart.org/content/animated-traps-and-obstacles
Zeichen https://opengameart.org/content/legend-of-faune
Bäume und Blöcke https://opengameart.org/content/lolly-set-01
(und noch ein paar Bilder mit opengameart)
Die Grafiken im Menü wurden vom 2D_GI-Shader abgerufen, einem Dienstprogramm zum Erstellen solcher Menüs:
Wer hat bis zum Ende gelesen - gut gemacht :)
Wenn Sie Fragen haben, bitte, ich kann die Beschreibung auf Anfrage ergänzen.