3d für Anfänger - Partikel

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • 3d für Anfänger - Partikel

      Weitere 3d-Anfänger Tutorials gibt's hier:



      Vorwort: In diesem Tutorial zeige ich dir, wie du mit ein bisschen Mathematik und räumlicher Vorstellung, 3d Partikel in deine Spiele einbaust.
      Große Kenntnisse sind wie immer nicht erforderlich, du solltest dich jedoch mit Transformationen auseinandergesetzt haben.
      Da mich dieses Thema selbst sehr interessiert hat, ist die Beispieldatei diesmal sehr ausführlich. Dort zeige ich 7 verschiedene Einsatzmöglichkeiten von Partikeln, die sich in die Katerorien Natur, Technik und Fantasy einordnen lassen. Einen kleinen Vorgeschmack gibt's hier:


      Grundlagen:

      Bevor wir anfangen, ein paar grundlegende Dinge.
      Partikel werden unter anderem auch als "Billboard" bezeichnet. Dies sind Flächen, die aus einem oder zwei Polygonen bestehen und sich immer in Richtung der Kamera drehen. Diese Rotation täuscht Räumlichkeit vor, wir bräuchten also z.B. keine Ganze Ellipse zu zeichnen, wenn wir einen "leucht-" Effekt haben wollen, sondern können dies viel einfacher, schöner und performanter mit einem Partikel lösen. Das Anwendungsspektrum für Partikel ist schier unendlich, wie du im Laufe des Tutorials erfahren wirst.

      Vorbereitung:

      Da wir nicht direkt mit dem Zeichnen von Partikeln loslegen können, kommt erst ein bisschen Therorie. Wichtig ist hierbei, dass wir nicht, wie es bei meinen anderen Tutorials der Fall ist, in der Topdown sondern der First Person Perspektive arbeiten.

      Das erste, was es zu realisieren gilt, ist es ein Billboard zu erstellen. Zur Erinnerung: Ein Billboard ist eine Fläche, die sich immer in Richtung Spieler dreht.
      Erstellen wir also ein neues Objekt und erstellen vorerst eine einfache Wall, so z.B.:

      GML-Quellcode

      1. d3d_draw_wall(x,y-2,-2,x,y+2,2,-1,1,1);


      Diese einfache Wand, wollen wir nun auf der Z-Ebene in Richtung des Spielers Drehen. Das sähe vereinfacht so aus (Topdown):



      Der rote Punkt ist der Spieler, der schwarze Strich die Billboard.
      Das Ganze auf Z Ebene zu realisieren ist sehr einfach, wir erstellen zuerst eine Variable "dir" in unserem Partikel Objekt.
      Der zugewiesene Wert ist egal, da wir ihn ohnehin jeden Step ändern.

      GML-Quellcode

      1. dir=0;

      Im Step Event kommt der entscheidene Teil:

      GML-Quellcode

      1. dir= point_direction(x,y,obj_player.x,obj_player.y);


      Wir benutzen die eingebaute Methode "point_direction", um den Winkel zu ermitteln, der sich mit den Koordinaten des Partikels und des Spieler Objekts ergibt.
      Das Ganze sähe vereinfacht so aus.



      Das Ganze muss jetzt auch visuell passieren, dafür benutzen wir Transformationen:

      GML-Quellcode

      1. d3d_transform_set_identity();
      2. d3d_transform_add_rotation_z(dir);
      3. d3d_transform_add_translation(x,y,z); //Z muss vorher deklariert werden
      4. d3d_draw_wall(0,-2,-2,0,+2,2,-1,1,1);
      5. d3d_transform_set_identity();


      Was hier passiert sollte weitgehend klar sein: Die Fläche wird auf der Z Ebene um den Winkel "dir" gedreht.

      Das Ganze muss nun auch auf Y Ebene passieren. Da dieser Winkel jedoch anders berechnet wird, kommt Trigonometrie ins Spiel.
      Dies komplett in Text zu fassen wäre Schwachsinn, daher hier eine Skizze (Sideview, Z/X):



      Dies nun in GML umzuschreiben ist sehr einfach:

      Da der GM normalerweise in Radiant statt Grad rechnet, fängt die Zeile so an.

      GML-Quellcode

      1. radtodeg(


      Das Ergebinis der Berechnung wird also in das Gradmaß umgerechnet.
      Als nächstes müssen wir nun die Formel aus der Skizze (arctan(b/a)) benutzen.
      b ist nach der Skizze der Höhenunterschied zwischen Partikel und Spieler. In GML rechnen wir dies so aus:

      GML-Quellcode

      1. abs(z-obj_player.height)


      "abs()" entfernt das Vorzeichen des Ergebnisses, so dass es immer positiv ist. Die Rechnung wäre später fehlerhaft, wenn wir mit einem negativen Ergebnis rechnen.

      a ist die Distanz vom Spieler zum Partikel. Diese können wir wieder mit einer eingebauten Funktion ermitteln:

      GML-Quellcode

      1. point_distance(x,y,obj_player.x,obj_player.y)


      Setzen wir nun a und b in die Formel ein, ergibt sich folgendes:

      GML-Quellcode

      1. radtodeg(arctan(abs(z-obj_player.height)/point_distance(x,y,obj_player.x,obj_player.y)))

      Wollen wir das Ganze perfektionieren, berücksichtigen wir auch die Neigung der Kamera, indem wir

      GML-Quellcode

      1. -obj_player.z_angle


      anhängen.

      Damit wären alle Vorbereitungen getroffen. Unser Draw Event sollte nun so aussehen:

      GML-Quellcode

      1. d3d_transform_set_identity();
      2. d3d_transform_add_rotation_y(radtodeg(arctan(abs(z-obj_player.height)/point_distance(x,y,obj_player.x,obj_player.y)))-obj_player.z_angle);
      3. d3d_transform_add_rotation_z(dir);
      4. d3d_transform_add_translation(x,y,z);
      5. d3d_draw_wall(0,-2,-2,0,+2,2,-1,1,1);
      6. d3d_transform_set_identity();



      Partikel:

      Nun haben wir ein Billboard, welches sich immer in Richtung Spieler dreht. Im Folgenden werden wir am Beispiel von Feuer richtige Partikel daraus machen.
      Zuerst sollte man sich Gedanken machen, welche Eigenschaften das Partikel haben soll. Hier ein paar Beispiele:

      • Geschwindigkeit (X/Y/Z)
      • Lebensdauer
      • Textur
      • Farbe
      • Schwerkraft
      Am Beispiel von Feuer könnte man die Wert so gestalten:

      Geschwindigkeit: Nur auf Z Ebene, Schnell
      Lebensdauer: Kurz
      Textur: Feuer-ähnliche Textur
      Farbe: Gelb-Rot
      Schwekraft: Keine

      Das Create Event könnte man folglich so gestalten:

      GML-Quellcode

      1. z_speed=random(0.5); //Der Z Geschwindigkeit wird ein zufälliger Wert zugewiesen
      2. tex=background_get_texture(bg_fire);
      3. alp=1; //alp = alpha - Transparenz des Partikels


      (Hier eine Beispiel-Textur)

      Damit haben wir die Textur und Geschwindigkeit zum Partikel zugefügt, weiter gehts im Step-Event:

      GML-Quellcode

      1. z+=z_speed; //z wird um die angegebene Geschwindigkeit erhöht
      2. alp-=0.06; //Die Transparenz verringert sich
      3. if(alp<0) //Ist die Transparenz geringer als 0, wird das Objekt zerstört (Lebensdauer)
      4. instance_destroy();


      Abschließend muss das Draw-Event entsprechend abgeändert werden.
      Wir fügen die Farbe und den Blendmode hinzu. Der Blendmode entfernt dunkele Farbtöne aus der Textur btw macht sie transparent. Dies ergibt einen leuchtenden Effekt.

      GML-Quellcode

      1. draw_set_color(c_yellow);
      2. draw_set_blend_mode(bm_add);
      3. d3d_draw_wall(0,-2,-2,0,+2,2,tex,1,1);
      4. draw_set_blend_mode(bm_normal);


      Würde man das Resultat testen, bäkame man diesen Fehler als Ergebnis.


      Die Partikel überlappen sich, obwohl dies eingentlich nicht der Fall sein dürfte. Dies ist ein Problem mit der depth, welches sich durch bestimmte Berechnungen beheben lässt. Es gibt jedoch auch einen anderen Weg dies zu vermeiden, und zwar mit dem Befehl

      GML-Quellcode

      1. d3d_set_hidden(false);


      Mit diesem Befehl stellt man das "Hidden surface removal" ab. Dies ist eigentlich notwendig, da so Objekte, die sich z.B. hinter einer Wand befinden, nicht auf den Bildschirm projeziert werden. Da das Gegenteil unser Ziel ist, stellen wir es ab, nachdem wir den Partikel gezeichnet haben, sollte es jedoch wieder aktivert werden. Das Ergebnis wäre dies:

      GML-Quellcode

      1. draw_set_color(c_yellow);
      2. draw_set_blend_mode(bm_add);
      3. d3d_set_hidden(false);
      4. d3d_draw_wall(0,-2,-2,0,+2,2,tex,1,1);
      5. d3d_set_hidden(true);
      6. draw_set_blend_mode(bm_normal);
      7. draw_set_color(c_white);


      Ganz problemlos ist die Darstellung nach wie vor nicht. Durch das Abstellen vom "Hidden surface removal", kann man die Partikel nun auch durch Wände sehen. Hier hilft vorerst nur trixen, in meinem Example habe ich es beispielsweise so gemacht, dass Partikel nicht mehr gezeichnet werden, wenn ich den jeweiligen Raum verlassen habe.
      Ohne Verdecken sieht unser Feuer dann so aus:



      Damit man einen Effekt wie auf dem Screenshot bekommt, braucht man natürlich ein Objekt, welches die Partikel erstellt. In diesem Fall also ein Objekt mit folgendem Step-Event:

      GML-Quellcode

      1. instance_create(x,y,obj_particle);


      Dies war eines von unendlich vielen Beispielen für Partikel. Ändert man die Werte entsprechend ab, lassen sich überzeugende Ergebnisse erziehlen.
      Hier 3 von 7 Beispielen aus meinem Example:



      Example:

      Das Example umfasst 4 Räume. In drei von ohnen befinden sich Beispiele für Partikel, der vierte ist zum Selbst-probieren.
      Die Räume müssen erst betreten werden, sonst werden die Objekte/Partikel nicht dargestellt.
      Die Kategorien sind Technik, Natur (dritter Screenshot) und Fantasy (erster und zweiter Screenshot).
      Man läuft mit "W" und schaut sich mit der Maus um, Kollision gibt es keine.
      Da ich alle Texturen und Modelle selbst angefertigt habe, steht es euch absolut frei, diese auch in eueren Spielen zu benutzen.
      Viel Spaß beim Rumexperimentieren!

      Moolt

      Dateien
    • Sehr schönes Tutorial, Moolt! Wie immer gut geschrieben. Das Problem mit der Überlappung kann man auf deine Weise angehen, aber wie du selbst schreibst, bringt es neue Probleme mit sich. Ich persönlich setze hier lieber die "depth" der Objekte relativ zur Kameraposition. Das ist zwar je nach Anwendung ein wenig komplizierter, erfüllt aber seinen Zweck bestens.
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • @Mauge: Könntest du deine Methode mit der depth Berechnung vielleicht auch noch kurz
      erklären? Das würde mich als Alternative sehr interessieren.
      Ansonsten: Klasse Tutorial, gut erklärt und vor allem auch sehr effektiv praktisch anwendbar.
      Ich bereue es fast schon, dass ich 3D mehr oder weniger aufgegeben habe :D
      ___________________________________________________________
      Beware of wild pointers
      ______Hinweis für Allergiker: Kann Spuren von Ironie enthalten_____
    • es gibt doch auch die möglchikeit eine 3d depth zu definieren. mir fällt leider der befehl nicht ein, aber das ist sehr vorteilhaft, weil es unabhängig von der normalen depth funktionierte und man die depth dann anderweitig (z.b. für die collision) nutzen kann.
      Ansonsten ein super tut.
      Battle Command - WeltraumEchtzeitStrategie | Meine GM Spiele auf Box.net
      GCM/FA/O d-(--)@>---xpu s-:- !a C++$@ U- P L+ E W++ N o K-- w++ O? M V PS PE-- Y PGP t 5 X R+++ tv+ b DI D G e+ h? r-- x
    • Der Befehl den du meinst lautet

      GML-Quellcode

      1. d3d_set_depth()

      Ich habe in meinem Tutorial angedeutet, dass es andere geschicktere Wege gibt dies zu lösen. Im Hinterkopf hatte ich, wozu mir Schattenphoenix bereits riet, ein point_distante für 3D zu schreiben und die Depth entsprechend anzupassen. Ich denke die einfach Lösung sollte aber für ein Anfänger Tutorial genügen.
    • Klar, reicht eigentlich auch für ein Anfängertutorial. Wenn man's genau betrachtet, passen Partikel schon gar nicht mehr in den Anfängerbereich. Ich probier mal, ein Script für die 3D Distanz zu machen...
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • Hätte einen Lösungsvorschlag, hab das mit Pythagoras gelöst. Bin mir nicht sicher ob das richtig ist, werde es noch ausführlicher testen:

      GML-Quellcode

      1. //d3d_get_distance
      2. var a,b,c;
      3. //a² + b² = c²
      4. a = point_distance(argument0,argument1,argument3,argument4);
      5. b = max(argument2,argument5)-min(argument2,argument5);
      6. c = sqrt(power(a,2)+power(b,2));
      7. return c;
    • Hab grad ein Script dafür geschrieben und es auch gleich für die Allgemeinheit bereitgestellt: point_distance_3d(x1,y1,z1, x2,y2,z2);

      Es errechnet die Distanz der Punkte anhand der Koordinaten (x,y,z).
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • Hey Moolt, hätte da einen Vorschlag. Da du ja nun eine Reihe von Tutorials gemacht hast, könntest du doch alle miteinander verlinken. Das würde es den Lernenden einfacher machen, da die Threads ja meist weit auseinander liegen und man suchen muss. Deshalb wär's in meinen Augen sinnvoll, wenn gleich als erstes eine Art Inhaltsverzeichnis kommt, wo man direkt auf die anderen Tutorials springen kann.
      █████ ██ █ ████ everything ███ █████ is █████ ████ ████ fine ████ ███ █ ██████ love.
      █████ ███████ ███ your █████ ████ government.
    • Der Vorschlag ist gut, danke dir. Ich weiß jedoch nicht, wann ich dies umsetze, da meine Texte/Liste durcheinander gebracht werden, wenn ich sie abspeicher. Ich werds mir merken und auf jeden Fall nachtragen!

      Edit: So, ich hab' nun ein Verzeichnis am Anfang jedes Tutorials zugefügt, so sollte die Übersicht wiederhergestellt sein.
    • Erstmal danke für die ganzen super 3D Tuts sie haben mir echt geholfen :thumbsup:
      ich stelle diese frage extra hier damit man den zusammenhang versteht
      also ich habe das jetzt mit der depth geändert, damit man die partikel nur sieht wenn nix dazwischen ist
      nur jetzt funktionieren die blendmodes nicht mehr richtig:

      gibt es da irgendeine möglichkeit das zu beheben?

      noch die GMK:
      Klick mich !


      EDIT:
      hab grad gesehen das ich die textur ganz weis gemacht hatte
      so siehts mit so einer für die blendmodes aus:

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Virus27 ()

    • Das ist eben die Problematik, die ich am Ende meines Tutorials angesprochen und mit anderen Usern in diesem Thread diskutiert habe.

      Das einfachste ist es, "d3d_set_hidden(false);" zu benutzen, das Problem das dahinter steckt siehtst du im Tutorial.
      Einen anderen Weg haben wir hier bereits modelliert. Dafü müsste man die depth entsprechend der Entfernung zur Kamera ändern, nur bisher scheint es als hätte sich keiner daran gewagt, ich ebenfalls nicht.
    • das hatte ich ja gemacht in jemdem 3D objekt steht:

      GML-Quellcode

      1. d3d_set_depth(d3d_distance(obj_camera.x,obj_camera.y,obj_camera.z,x,y,0))


      das script d3d_distance:

      GML-Quellcode

      1. // von Moolt
      2. var a,b,c;
      3. a = point_distance(argument0,argument1,argument3,argument4);
      4. b = max(argument2,argument5)-min(argument2,argument5);
      5. c = sqrt(power(a,2)+power(b,2));
      6. return c;


      nur damit das auch wirkt musste ich d3d_set_hidden() wegmachen

    • Jaja, was lange währt, währt endlich gut, an dem Problem haben wir rel. Lange gesessen, damit die dinger auch den Winkel haben, den sie haben sollen *g*
      Aber am ists doch gut geworden, bin mal gespannt, wann das nächste kommt.
      So far, Schattenphoenix~
      _____________________________________________________________________________
      "Who needs a stairway to heaven...
      If there is an elevator to hell... ?
      "
      - Vergessen
      "Auch ein perfektes Chaos ist etwas vollkommenes."
      - Jean Genet
    • Benutzer online 1

      1 Besucher