3D GAMESTUDIO-Magazin Ausgabe 01 | Oktober 20051
3D GAMESTUDIO-Magazin Ausgabe 01 | Oktober 20051
3D GAMESTUDIO-Magazin Ausgabe 01 | Oktober 20051
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │1
Vorwort<br />
Inhalt<br />
Liebe Leserin, lieber Leser,<br />
mit dieser <strong>Ausgabe</strong> fällt der Startschuss für eine hoffentlich lange Serie eines neuen <strong>Magazin</strong>s.<br />
Einen Monat haben wir uns mit der Planung beschäftigt und an Artikeln gearbeitet, um Ihnen<br />
möglichst viele Informationen rund um die Acknex6 Engine zu vermitteln. In dieser <strong>Ausgabe</strong><br />
erwarten Sie eine Menge Tutorials, Interviews mit Sebastian Leopold und Timo Stark, den<br />
Entwicklern von Sylex 3.0 und dem kommenden Realtime-Editor GameEdit sowie einem kleinem<br />
Preview zu GameGameEdit.<br />
Um die nächsten <strong>Ausgabe</strong>n noch besser und größer zu machen, hoffen wir auf Ihre Unterstützung!<br />
• Normalmaps für Computerspiele erstellen<br />
• Vektoren<br />
• Mit Bleifuß zur Physik Engine – Part I<br />
• Viel Rauch um nichts. Ein Nebeltutorial<br />
• Modelldesign - Teil 1: Ein Tisch mit Blender<br />
• Laser - und Photonentorpedoeffekte<br />
• Bitoperatoren<br />
• Möglichkeiten des Physiksystems in Gamestudio Commercial<br />
• Formationsanordnungen in einem <strong>3D</strong> Spiel<br />
• Interview mit Sebastian Leopold (Xexes) über Sylex 3.0<br />
• Interview mit Martin (SFMAT4) und Timo (TripleX) zu ihren derzeitigen Projekten<br />
• Game-Edit<br />
Der Schwierigkeitsgrad als Anzeige<br />
In eigener Sache:<br />
Wenn Sie der Meinung sind, einen Beitrag leisten zu können, kontaktieren Sie einen der <strong>3D</strong>GS-<strong>Magazin</strong><br />
Autoren. Oder über das Forum welches auch über http://3dgsmag.ma-pre.de/forum zu erreichen ist.<br />
Das <strong>3D</strong>GS-MAGAZIN Team<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │2
Grundlagen<br />
Normalmaps für Computerspiele erstellen<br />
von Frank Geppert<br />
Eine Normalmap ist eine Textur, die zusätzlich zu der farbliefernden Textur (Diffusemap) weitere<br />
Informationen liefert. Diese Informationen geben dem Shaderprogramm der Grafikkarte an, aus<br />
welcher Richtung bestimmte Pixel beleuchtet werden sollen. Damit lassen sich räumliche<br />
Oberflächen simulieren, wo eigentlich keine Geometrie räumliche Informationen liefert.<br />
Sehen Sie dazu folgendes Beispiel. Ich habe einen einfachen Würfel erstellt, der nur mit einer<br />
einzigen Farbe texturiert ist und eine Normalmap erhält. Diese Normalmap soll eine Seite des<br />
Würfels so anzeigen, als würde eine Vertiefung auf dieser existieren und eine andere Seite soll so<br />
beleuchtet werden, als würde eine etwas herausstehende Fläche auf ihr existieren.<br />
Um dieses Ziel zu erreichen, muss man eine Normalmap erstellen, die wie folgt aussehen könnte:<br />
2 von den 6 Seiten des Würfels haben zusätzliche Informationen erhalten. Die anderen blieben in<br />
diesem Beispiel flach. Sie sehen, dass z.B. die etwas helleren Teile der Normalmap dafür sorgen,<br />
dass unser Würfel von unten beleuchtet erscheint und die dunkelblauen Streifen lassen den Würfel<br />
von oben beleuchtet erscheinen. Analog dazu gibt es Informationen für die Beleuchtung von den<br />
jeweiligen Seiten, je nachdem wie die Werte im RGB-Kanal verteilt sind.<br />
Mit dieser Methode lässt sich beliebige Geometrie vortäuschen, wo gar keine Geometrie ist. Ein viel<br />
beeindruckenderes Beispiel dazu sehen Sie weiter unten.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │3
Erstellen von Normalmaps aus Texturen<br />
Normalmaps lassen sich auf verschiedene Weise erstellen. Eine Methode ist es, diese in einem<br />
Grafikprogramm zu zeichnen. Photoshop und GIMP haben zudem jeweils Plugins, mit denen man<br />
Normalmaps aus Grafiken berechnen lassen kann. Diese Methode geht sehr einfach und schnell,<br />
führt aber nicht immer zu den gewünschten Ergebnissen.<br />
1. Ich werde dazu ein kurzes Beispiel zeigen, wie man solche Texturen in dem<br />
Grafikprogramm “The GIMP” erstellen kann. Dazu benötigen Sie ein Normalmap-Plugin für<br />
diese Software.<br />
2. Das Plugin ist schnell mit der Internetsuche und den Begriffen “Normalmap GIMP Plugin”<br />
gefunden und mit Hilfe der Anleitung installiert.<br />
3. Nach der Installation finden Sie unter Filter / Abbilden / Normalmap dieses Plugin Das<br />
Plugin präsentiert sich wie folgt:<br />
4. Wählen Sie ihren Filter und ändern Sie bei Bedarf die Parameter. Ich persönlich mag den<br />
“Sobel” Filter am meisten, da dort die Struktur am besten herausgefiltert wird.<br />
Schauen Sie sich bei Bedarf den <strong>3D</strong> Preview mit dem Schalter unter dem Vorschaufenster an.<br />
Dieses <strong>3D</strong>-Bild gibt einen ersten Eindruck, wie der Effekt im Spiel aussehen könnte. Dabei ist es<br />
sogar möglich, eine weitere Textur, die in GIMP geöffnet sein muss über dieses <strong>3D</strong>-Objekt zu<br />
überlagern. So könnten Sie prüfen, wie der Effekt mit der Diffusemap und der Normalmap<br />
aussehen könnte. Allerdings sieht man die Struktur besser, wenn man zum Testen erst einmal eine<br />
Farbtextur weglässt.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │4
Meine Erfahrungen mit diesem Tool zeigen, dass sich Graustufen- und Bumpmap-Grafiken besser<br />
eignen, als reine Farbgrafiken, um daraus Normalmaps zu generieren. Das Ergebnis zeigt<br />
tatsächlich eine interessante Struktur, die man bereits im <strong>3D</strong>-Preview-Fenster bestaunen kann.<br />
Allerdings sind extreme Tiefen und Höhen sowie eine Verfeinerung der Geometrie damit nicht<br />
realisierbar. Dafür empfehle ich das aufwändigere aber leistungsstärkere Verfahren, dass ich<br />
anschließend erklären werde.<br />
Erstellen von Normalmaps aus Geometrie<br />
Sie haben einen Spiele-Charakter, der in sehr detaillierter Form vorliegt (z.B. mehr als 200.000<br />
Polygone). Allerdings verlangt eine Spiele-Engine ein lowpoly-Modell mit z.B. 3000 Polygonen. Mit<br />
der Normalmapping-Technik können Sie ein Brücke zwischen diesen beiden Welten schlagen. Die<br />
Vorgehensweise ist dabei wie folgt:<br />
Sie erstellen ein Highpoly und ein Lowpoly Modell. Lassen Sie eine Normalmap aus dem Highpoly-<br />
Modell berechnen (Melody von nVidia, Lightwave oder <strong>3D</strong>smax) wenden Sie diese Normalmap auf<br />
das Lowpoly-Modell an.<br />
Hier sehen Sie ein Beispiel. Ich habe eine beliebiges Modell, das mit Lightwave geliefert wurde,<br />
geladen. Dieses Modell bestand aus ca. 4.000 Polygonen, die ein Subpatch-Modell von fast 50.000<br />
Polygonen lieferten. Nachfolgend sehen Sie links das Original Modell (high poly), in der Mitte das<br />
dazugehörige lowpoly Modell und anschließend das gleiche lowpoly Modell mit der Normalmap.<br />
Sie werden vermutlich den Unterschied zwischen der highpoly Figur und der lowpoly Figur mit der<br />
Normalmap nur an den Umrissen festmachen können (z.B. die eckige Wade), denn die Beleuchtung<br />
erfolgt wie bei dem Original, auch wenn die Geometrie viel weniger Details enthält. Diese Technik<br />
ist auf die beliebigsten Modelle anwendbar und ermöglicht viel detailliertere Welten, wie viele von<br />
Ihnen vielleicht schon in den Spielen “Doom3, Riddick oder F.E.A.R” sehen konnten.<br />
Ein weiterer Vorteil von Normalmaps ist, dass diese Technik nicht nur mit weniger Details in der<br />
Geometrie auskommt, sondern teilweise auch mit recht kleinen Texturen noch beeindruckend<br />
aussieht. Das liegt daran, dass viele zusätzliche Informationen durch die Beleuchtung und die<br />
Specularity-Effekte hinzukommen. Damit erhöht sich der Detailgrad allein durch die Funktionen des<br />
Shaderprogrammes mehr, als man vielleicht durch eine detaillierte Textur hinzufügen könnte.<br />
Zusätzlich besitzen einige Shader-Programme die Fähigkeit, über den Alpha-Channel der Diffuse-<br />
Map die Specularity verschiedener Bereiche der Grafik zu steuern.<br />
Ich hoffe, dass ich Ihnen einen ersten Einblick in die Arbeit mit Normalmapping liefern konnte.<br />
Wenn es weitere Fragen zu dem Thema gibt, bin ich ich unter info@geppert-software.de zu<br />
erreichen.<br />
Frank Geppert<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │5
Hallo<br />
Vektoren<br />
von Timo Stark aka Triple-X<br />
In diesem Tutorial werde ich auf Vektoren eingehen. Vektoren werden in der <strong>3D</strong> Grafik sehr häufig<br />
gebraucht, werden aber leider meistens, gerade von Anfängern, nicht verstanden. Aus diesem<br />
Grund werde ich in diesem Tutorial darauf eingehen, was Vektoren überhaupt sind, was Sie für<br />
eine Rolle in der Grafikprogrammierung spielen, wie man mit ihnen rechnet und welche<br />
Vektorbefehle es in <strong>3D</strong> Gamestudio gibt.<br />
Für dieses Tutorial sollten Sie etwas Mathematische Kenntnisse mitbringen. Erfahrungen in C-Script<br />
sind nicht nötig, da in diesem Tutorial, außer im letzten Kapitel, nicht auf C-Script eingegangen<br />
wird. Falls Sie ein Tutorial für C-Script suchen, empfehle ich Ihnen das Handbuch oder mein C-<br />
Script Tutorial für Anfänger erreichbar unter: http://www.hawkgames.de/Tutorial.php<br />
Viel Spaß beim lesen, TripleX<br />
Was sind Vektoren?<br />
Nun was sind Vektoren? Ganz allgemein gesagt, ist ein Vektor eine Ansammlung von Werten. Wie<br />
viele Werte einen Vektor bilden hängt von der Anzahl der Dimensionen ab. In einem ndimensionalen<br />
Raum hat ein Vektor n Werte. Jeden dieser Werte nennt man Vektorkomponente.<br />
Grundsätzlich können Vektoren viele Daten darstellen. Es hängt immer nur von der Interpretation<br />
des Vektors ab.<br />
Als Beispiele:<br />
1) Positionsvektor (Drei Dimensionen: X/Y/Z Koordinate)<br />
2) Richtungsvektor (Drei Dimensionen: Pan/Tilt/Roll Winkel)<br />
3) Farbvektor (Drei Dimensionen: Rot/Grün/Blau)<br />
Positionsvektoren<br />
Ein Positionsvektor beschreibt immer eine Position in einem n-dimensionalen Koordinatensystem.<br />
In <strong>3D</strong> Gamestudio werden sowohl 3-Dimensionale Vektoren zur Beschreibung von Welt-<br />
Koordinaten als auch 2-Dimensionale Vektoren zur Beschreibung von Bildschirm-Koordinaten<br />
genutzt. Nun was ist überhaupt ein Koordinatensystem und eine Koordinate? In der<br />
Grafikprogrammierung wird größtenteils mit den kartesischen Koordinatensystem gearbeitet. Im<br />
folgenden Bild sehen Sie ein Beispiel für das 3-dimensionale kartesische Koordinatensystem:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │6
Diese Grafik zeigt das normale kartesische Koordinatensystem. Zu beachten ist, dass in <strong>3D</strong><br />
Gamestudio die Y und die Z Achse vertauscht sind. Die Y Achse geht also in die Tiefe und die Z<br />
Achse nach oben.<br />
Für dieses Koordinatensystem wird eine Einheit festlegt (in <strong>3D</strong> Gamestudio ist das 1 Quant). Nun<br />
lassen sich bestimmte Positionen mit Hilfe von Positionsvektoren treffend beschreiben. Zum<br />
Beispiel der Punkt (0 | 2 | 5) (Schreibweise eines Vektors: ( X-Komponente, Y-Komponente, Z-<br />
Komponente )) liegt 5 Einheiten an der Z Achse und 2 Einheiten an der Y Achse entlang. Also<br />
diagonal nach oben in die Tiefe.<br />
Zu beachten ist, dass jedes <strong>3D</strong>-Objekt sein eigenes relatives Koordinatensystem bildet.<br />
Beispiel: Ein Haus befindet sich auf der absoluten Position (10 | 20 | 10). Ein Mensch hat die<br />
Position (5 | 20 | 30). Vom Menschen aus gesehen besitzt das Haus also die Position (5 | 0 | 20).<br />
Der Mensch bildet also ein eigenes relatives Koordinatensystem mit den Ursprung (5 | 20 | 30).<br />
Die Achsenrichtung und Ursprungsposition stimmen also nicht mit der Achsenrichtung und der<br />
Ursprungsposition des absoluten Koordinatensystems überein.<br />
Richtungsvektoren<br />
Formal (also von der Schreibweise her) unterscheiden sich Richtungsvektoren nicht von<br />
Positionsvektoren. Sie werden nur anderes interpretiert. Genau wie bei Positionsvektoren, richtet<br />
sich die Anzahl der Vektorkomponenten nach der Anzahl der Dimensionen. Zur besseren<br />
Vorstellung: In einem 2-Dimensionalen Raum brauchen Sie 2 Werte um eine Richtung zu<br />
bestimmen, in einem 3-Dimensionalen 3 Werte und so weiter. Also braucht man n<br />
Vektorkomponenten für n Dimensionen.<br />
Jede Vektorkomponente in einem Richtungsvektor besitzt normalerweise nur Werte zwischen 0 und<br />
1. Das heißt er besitzt maximal die Länge 1.<br />
Beispiel: Ein Hund besitzt die Position (10 | 0 | 0). Vom Nullpunkt ausgesehen besitzt der Hund<br />
also die Richtung (1 | 0 | 0).<br />
Sehr oft werden Richtungsvektoren auch zur Bestimmung einer Bewegungsrichtung genutzt, wobei<br />
zu beachten ist, dass bei Bewegungsvektoren auch noch die Geschwindigkeit des jeweiligen<br />
Objektes mit einbezogen wird.<br />
Beispiel: Ein Mann bewegt sich mit der Geschwindigkeit (7 | 0 | 0). Er bewegt sich also mit 7<br />
Einheiten pro Sekunde an der X Achse entlang. Setzt man nun statt (7 | 0 | 0), (1 | 0 | 0),<br />
verändert sich nicht die Richtung, sondern nur die Geschwindigkeit mit der sich der Mann bewegt<br />
(jetzt nur noch eine Einheit pro Sekunde).<br />
Rechnen von Vektoren<br />
Im folgenden werde ich auf das Rechnen mit Vektoren eingehen. Grundsätzlich kann man mit<br />
Vektoren genauso rechnen wie mit Skalaren (reelle Zahl), bis auf wenige Ausnahmen.<br />
Vektor-Addition und -Subtraktion<br />
Addition und Subtraktion mit Vektoren funktioniert genauso wie Addition und Subtraktion bei<br />
normalen reellen Zahlen.<br />
Beispiel: A und B sind Vektoren.<br />
a + b = (xa + xb | ya + yb | za + zb)<br />
a - b = (xa - xb | ya - yb | za - zb)<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │7
Zur Addition und Subtraktion von Vektoren müssen sie also legendlich alle Vektorkomponenten<br />
miteinander addieren/subtrahieren.<br />
Addition und Subtraktion von Vektoren wird sehr oft zum Bewegen von Objekten benötigt. Ein<br />
weiteres Beispiel: Eine Pflanze soll zu der Position (-10 | 23 | 10) der Vektor (0 | 7 | 2) addiert<br />
werden. Die neu berechnete Position der Pflanze ist: (-10 | 30 | 8).<br />
Der entsprechende Befehl in C-Script ist vec_add(vector1,vector2); und vec_sub(vector1,vector2);<br />
Auf Vektorbefehle wird im weiteren genauer eingegangen.<br />
Vektor Multiplikation und Division<br />
Bei der Multiplikation und der Division von Vektoren kann man zwischen zwei Arten unterscheiden<br />
1) Multiplikation / Division eines Vektors mit einer reellen Zahl (Skalar).<br />
2) Multiplikation / Division eines Vektors mit einem anderen Vektor.<br />
Vektor Multiplikation und Division mit reellen Zahlen<br />
Die Multiplikation/Division eines Vektors mit einer reellen Zahl ist sehr einfach zu verstehen. Bei<br />
einer solchen Rechnung wird einfach jede Vektorkomponente mit dem Skalaren multipliziert.<br />
Beispiel A = Vektor, s = Reelle Zahl<br />
A * s = (xa * s | ya * s | za * s)<br />
A / s = (xa / s | ya / s | za / s)<br />
Durch die Multiplikation/Division eines Vektors mit einem Skalar ist es möglich die Länge eines<br />
Vektors zu stauchen bzw. sie zu erhöhen. Dies ist z.B. sehr nützlich bei Bewegungsvektoren<br />
(erhöhen/verringern der Geschwindigkeit). Bei der Multiplikation eines Richtungsvektors mit einem<br />
Skalar bleibt die eigentliche Richtung bestehen.<br />
Als Veranschaulichung: A = Vektor der Länge 1<br />
Vektor A * 1<br />
--------------------><br />
Vektor A * 2<br />
-----------------------------------------><br />
Vektor A * 0.5<br />
----------><br />
In <strong>3D</strong> Gamestudio übernimmt vec_scale(Vektor,Zahl); diese Funktion.<br />
Vektor Multiplikation und Division mit einem Vektor<br />
Grundsätzlich kann man Vektoren auf mehrere Arten miteinander multiplizieren/teilen:<br />
1. Komponentenweise Multiplikation/Division zweier Vektoren.<br />
2. Das Kreuzprodukt<br />
3. Das Punktprodukt<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │8
Komponentenweise Multiplikation/Division zweier Vektoren<br />
Das Komponentenweise multiplizieren und teilen zweier Vektoren funktioniert, wie Sie sich<br />
wahrscheinlich schon denken genauso wie das addieren und subtrahieren zweier Vektoren.<br />
a * b = (xa * xb | ya * yb | za * zb)<br />
a / b = (xa / xb | ya / yb | za / zb)<br />
Diese Möglichkeit der Multiplikation ist in der Grafikprogrammierung allerdings kaum notwendig.<br />
Trotzdem ein Beispiel:<br />
Die Höhe (Y Achse) eines Menschen (10 | -10 | 5) soll vierfach erhöht werden. Dafür wird die<br />
aktuelle Position des Menschen mit dem Vektor (1 | 4 | 1) multipliziert.<br />
a * b = (xa * xb | ya * yb | za * zb) ---><br />
(10 | -10 | 5) * (1 | 4 | 1) = (10 * 1 | -10 * 4 | 5 * 1) = (10 | -40 | 5)<br />
Vergessen Sie nicht, dass wenn sich die Position bei einer Multiplikation nicht verändern soll, in der<br />
Vektorkomponente eins stehen muss und nicht null (0 * Zahl = 0).<br />
Das Kreuzprodukt<br />
Neben der Komponentenweise Multiplikation eines Vektors gibt es auch noch die Möglichkeit des<br />
Kreuzproduktes. In der Mathematik wird dieses Kreuzprodukt nicht über das "Mal Zeichen" sondern<br />
über das Multiplikationskreuz "x" angegeben.<br />
Im folgenden Beispiel sind A und B wie immer Vektoren (hier Richtungsvektoren).<br />
a x b = ((ya * zb) - (za * yb) | (za * xb) - (xa * zb) | (xa * yb) - (ya * xb))<br />
Diese, zugegebenermaßen, komplizierte Rechnung hat bei Richtungsvektoren den Sinn, dass der<br />
neu errechnete Vektor senkrecht auf den beiden anderen Vektoren steht. Dies funktioniert<br />
natürlich nur bei Richtungsvektoren. Ein Vektor kann unmöglich auf einen Positionsvektor<br />
senkrecht stehen (wo ist denn auf einen Punkt senkrecht).<br />
Folgendes Bild zur Veranschaulichung:<br />
B = Erster Vektor<br />
A = Zweiter Vektor<br />
Blauer / Hellgrüner Vektor = (Umgekehrter) Ergebnisvektor -> Steht Senkrecht auf B und A.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │9
Das Punktprodukt<br />
Nun die letzte Möglichkeit des multiplizieren / teilen zweier Vektoren. Das Punktprodukt. Das<br />
besondere an dem Punktprodukt ist, dass das Ergebnis kein dritter Vektor sondern eine Zahl ist.<br />
Der Sinn des Punktproduktes ist es, den Winkel zwischen zwei Richtungsvektoren zu bestimmen.<br />
a * b = (xa * xb) + (ya * yb) + (za * zb)<br />
Das Punktprodukt funktioniert nur mit normalisierten Richtungsvektoren. Das heißt es funktioniert<br />
nur mit Richtungsvektoren der Länge 0 bis 1. Auf normalisierte Vektoren werde ich später noch<br />
gesondert eingehen.<br />
Das Ergebnis dieses Punktproduktes ist der Kosinus des Winkels der von den beiden<br />
Richtungsvektoren eingeschlossen ist. Beeindruckend oder?<br />
Aus dem Kosinus eines Winkels können sie mit Hilfe der acos(Kosinus); ganz einfach einen<br />
"echten" Winkel berechnen.<br />
In <strong>3D</strong> Gamestudio übernimmt vec_dot(Vektor1,Vektor2); diese Funktion.<br />
Die Länge eines Vektors<br />
Als erstes muss definiert werden was die Länge eines Vektors überhaupt ist. Grundsätzlich ist<br />
wichtig zu sagen, dass die Länge eines Vektors nur bei einem Richtungsvektor Sinn macht. Welche<br />
Länge sollte auch eine Position haben?<br />
Was genau die Länge eines Vektors ist, kann man am besten mit Hilfe eines Beispiels erklären.<br />
Ein Mensch bewegt sich mit der Geschwindigkeit (2 | 4 | 3). Also um 2 Quants/sec an der X Achse,<br />
4 Quants/sec an der Y Achse und 3 Quants/sec an der Z Achse. Um wie viel sich der Mensch jetzt<br />
insgesamt bewegt gibt die Länge des Vektors wieder.<br />
Die Länge des Vektors kann man mit Hilfe des Satzes von Pythagoras berechnen.<br />
Um die Vektorlänge besser zu verstehen, wenden wir den Satz des Pythagoras als Erstes im 2-<br />
Dimensionalen Raum an. Der hier gezeigte Vektor besitzt die Position (C | Schnittpunkt der roten<br />
Linie mit C). Die Vektorlänge ist A (die rote Linie). Die Punkte (0 | 0), (C | Schnittpunkt der<br />
roten Linie mit C) und (C | 0) bilden ein rechtwinkliges Dreieck. Daher ist der Einsatz des Satz des<br />
Pythagoras möglich.<br />
Also ist die Länge eines 2-Dimensionalen Vektors:<br />
L = Wurzel(xa² + ya²)<br />
Wie sie vermutlich schon selber folgern können ist also die Formel für die Berechnung der Länge<br />
eines Vektors im 3-Dimensionalen Raum:<br />
L = Wurzel(xa² + ya² + za²)<br />
Diese Funktion übernimmt vec_length(Vektor); in C-Script für Sie.<br />
Dadurch, dass Sie jetzt wissen wie man die Länge eines Vektors berechnet, können Sie auch die<br />
Distanz zwischen zwei Punkten berechnen. Wie geht das werden Sie sich vielleicht fragen. Nun<br />
ganz einfach. Sie bilden durch die Subtraktion des 1. Vektors von den 2. Vektor einen<br />
"Verbindungsvektor". Jetzt berechnen Sie die Länge des Vektors. Dies ist die Distanz zwischen den<br />
beiden Positionsvektoren.<br />
Ein Beispiel indem Sie die Entfernung auch ohne Berechnung sehen würden, damit Sie es glauben<br />
Eine Frau an der Position (10 | 50 | 50) würde gerne Ihre Entfernung zu den Flugzeug (10 | 1000 |<br />
50) wissen. Wie kann sie diese Entfernung berechnen?<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │10
Zuerst der Verbindungsvektor V:<br />
V = A - B<br />
V = (10 | 50 | 50) - (10 | 1000 | 50)<br />
V = (0 | -950 | 0)<br />
Jetzt wird also noch die Länge dieses neu errechneten Vektors bestimmt:<br />
L = Wurzel(xv² + yv² + zv²)<br />
L = Wurzel(0 * 0 + -950 * - 950 + 0 * 0)<br />
L = Wurzel(902500);<br />
L = 950;<br />
Die Distanz beträgt also 950 Quants, wie Sie zweifellos sofort gesehen haben ;-) Da dieses Beispiel<br />
sehr einfach war rechnen Sie es am besten noch mal mit der Position Frau = (0 | 2 | -30) und<br />
Flugzeug = (620 | 1200 | 2) aus. Hier sehen Sie die Distanz nicht auf den 1. Blick. Sie sollten<br />
ungefähr auf das Ergebnis 1343 kommen. In C-Script übernimmt übrigens die Funktion<br />
vec_dist(vector1,vector2); diese Aufgabe.<br />
Vektoren in <strong>3D</strong>-Gamestudio und C-Script<br />
Das schwierigste haben Sie jetzt hinter sich gebracht. Nun kommt der praktische Teil dieses<br />
Tutorials der sich mit der Frage beschäftigt, wie man Vektoren in C-Script benutzen kann.<br />
Definieren von Vektoren<br />
In <strong>3D</strong>-Gamestudio haben Vektoren immer 3-Vektorkomponenten. Dies liegt einfach daran, dass<br />
wie der Name von <strong>3D</strong>-Gamestudio schon sagt, mit einem Vektor ein 3-Dimensionaler Raum<br />
beschrieben wird. Definieren kann man den Vektor über das var Keyword.<br />
Var Vektorname[3];<br />
Der Name ist beliebig. Ich würde allerdings empfehlen, damit Vektoren auch als solche erkenntlich<br />
sind, den Namen mit einem bestimmten Prä oder Suffix (_vec o.ä.) versehen. Falls der Vektor<br />
einen vordefinierten Wert haben soll geht dies über folgende Definierung:<br />
Var Vektorname[3] = Wert X, Wert Y, Wert Z;<br />
Vektoren können auch in Funktionen, also lokal, definiert werden. Auf eine Vektorkomponente<br />
kann mit folgenden Werten zugegriffen werden:<br />
Vektorname[0] = Wert; //Greift auf den X Wert zu<br />
Vektorname[1] = Wert; //Greift auf den Y Wert zu<br />
Vektorname[2] = Wert; //Greift auf den Z Wert zu<br />
Vektorname.x = Wert; //Greift auf den X Wert zu<br />
Vektorname.y = Wert; //Greift auf den Y Wert zu<br />
Vektorname.z = Wert; //Greift auf den Z Wert zu<br />
Vektorname.pan = Wert; //Greift auf den X Wert zu<br />
Vektorname.tilt = Wert; //Greift auf den Y Wert zu<br />
Vektorname.roll = Wert; //Greift auf den Z Wert zu<br />
Vektorname.blue = Wert; //Greift auf den X Wert zu<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │11
Vektorname.green = Wert; //Greift auf den Y Wert zu<br />
Vektorname.red = Wert; //Greift auf den Z Wert zu<br />
Wie Sie sehen macht es C-Script Ihnen sehr einfach auf Vektoren zuzugreifen. Wichtig ist, dass es<br />
keinen Unterschied macht ob Sie<br />
Oder<br />
Vektorname.blue = 128;<br />
Vektorname[0] = 128;<br />
schreiben. Es ist nur einfacher für den Programmierer (also Sie) zu verstehen wie dieser Vektor<br />
interpretiert wird.<br />
Wichtig ist, dass bei lokal definierten Vektoren nur der 1. Interpretationsweg (also über<br />
[0]/[1]/[2]) als richtig anerkannt wird.<br />
X/Y/Z wird bei Positionsvektoren, Pan/Tilt/Roll bei Richtungsvektoren und Blue/Green/Red bei<br />
Farbvektoren genutzt.<br />
Besonderheiten von Vektoren in <strong>3D</strong>-Gamestudio<br />
Folgende Besonderheiten sind bei Vektoren in <strong>3D</strong>-Gamestudio zu beachten:<br />
1) Die Y und die Z Achse sind im Koordinatensystem vertauscht. Das heißt, dass die Z-Achse in die<br />
Höhe und die Y Achse in die Tiefe geht.<br />
2) Mit Vektoren kann man nicht wie mit normalen reellen Zahlen rechnen. Das heißt eine direkte<br />
Zuweisung („=“), oder das einfache addieren („+“) zweier Vektoren mit einfachen Rechenzeichen<br />
ist nicht möglich. Hierzu gibt es Befehle auf die ich im folgenden eingehen werde. Sehr wohl<br />
können allerdings alle Grundrechenarten mit einzelne Vektorkomponenten ( vec1.x = vec2.x; )<br />
durchgeführt werden. Auf diesen Weg sind alle oben angegeben Vektorrechnungen auch in C-Script<br />
möglich.<br />
Vec_Set(Vektor1,Vektor2);<br />
Mit Hilfe dieses Befehls werden alle 3 Vektorkomponenten des ersten Vektors mit dem 3<br />
Vektorkomponenten des zweiten Vektors überschrieben.<br />
Beispiel:<br />
Var Vec1[3] = 10,20,30 ;<br />
Var Vec2[3] = 2,15,22 ;<br />
Vec_set(Vec1,Vec2) ; /* Die Drei Vektorkomponenten von Vec1 nach dieser Anweisung: (2 | 15 |<br />
22) */<br />
//das gleiche (nur etwas schneller) wie:<br />
Vec1.x = Vec2.x;<br />
Vec1.y = Vec2.y ;<br />
Vec1.z = Vec2.z ;<br />
Vec_Add(Vektor1,Vektor2);<br />
Mit Hilfe dieses Befehls werden alle 3 Vektorkomponenten des ersten Vektors mit dem 3<br />
Vektorkomponenten des zweiten Vektors addiert. Das Ergebnis dieser Anweisung wird in den<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │12
ersten Vektor geschrieben.<br />
Beispiel:<br />
Var Vec1[3] = 10,20,30 ;<br />
Var Vec2[3] = 2,15,22 ;<br />
Vec_add(Vec1,Vec2) ; /* Die Drei Vektorkomponenten von Vec1 nach dieser Anweisung: (12<br />
| 35 | 52) */<br />
//das gleiche (nur etwas schneller) wie:<br />
Vec1.x += Vec2.x;<br />
Vec1.y += Vec2.y ;<br />
Vec1.z += Vec2.z ;<br />
Vec_Sub(Vektor1,Vektor2);<br />
Mit Hilfe dieses Befehls werden alle 3 Vektorkomponenten des ersten Vektors mit dem 3<br />
Vektorkomponenten des zweiten Vektors subtrahiert. Das Ergebnis dieser Anweisung wird in den<br />
ersten Vektor geschrieben.<br />
Beispiel:<br />
Var Vec1[3] = 10,20,30 ;<br />
Var Vec2[3] = 2,15,22 ;<br />
Vec_add(Vec1,Vec2) ; /* Die Drei Vektorkomponenten von Vec1 nach dieser Anweisung: (8<br />
| 5 | 8) */<br />
//das gleiche (nur etwas schneller) wie:<br />
Vec1.x -= Vec2.x;<br />
Vec1.y -= Vec2.y ;<br />
Vec1.z -= Vec2.z ;<br />
Vec_Scale(Vektor1,Zahl);<br />
Mit Hilfe dieses Befehls werden alle 3 Vektorkomponenten des ersten Vektors mit der angegeben<br />
Zahl multipliziert (siehe Oben). Das Ergebnis dieser Anweisung wird in den ersten Vektor<br />
geschrieben.<br />
Beispiel:<br />
Var Vec1[3] = 10,20,30 ;<br />
Var factor = 10;<br />
Vec_add(Vec1,factor) ; /* Die Drei Vektorkomponenten von Vec1 nach dieser Anweisung:<br />
(100 | 200 | 300) */<br />
//das gleiche (nur etwas schneller) wie:<br />
Vec1.x *= factor;<br />
Vec1.y *= factor ;<br />
Vec1.z *= factor ;<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │13
Vec_Length(Vektor);<br />
Mit Hilfe von Vec_Length können sie schnell die Länge (s.o.) eines Vektors bestimmen. Die Länge<br />
eines Vektors ist in <strong>3D</strong> Gamestudio nichts anders als seine Distanz zum Nullpunkt (0 | 0 | 0, oder<br />
der vordefinierte Vektor: nullvector).<br />
Vec_Dist (Vektor1,Vektor2);<br />
Mit Hilfe von Vec_Dist können sie schnell die Entfernung zwischen zwei Vektoren (s.o.) bestimmen.<br />
Die Distanz wird von dieser Funktion zurückgegeben („Return-Value“).<br />
Vec_inverse (Vektor);<br />
Die Funktion vec_inverse kehrt den angegeben Vektor um. Das heißt, dass sie jede<br />
Vektorkomponente mit -1 multipliziert.<br />
Vec_dot(Vektor1,Vektor2);<br />
Diese Funktion liefert das Punktprodukt der beiden Vektoren zurück. Grundsätzlich macht sie<br />
genau das, was wir oben beschrieben haben.<br />
Vec_normalize (Vektor,Zahl);<br />
Diese Funktion normalisiert den angegeben Vektor. Besonders an dieser Funktion ist die<br />
angegebene Zahl. In Gegensatz zu dem mathematisch korrekten normalisieren auf eine Länge<br />
unter 1, kann der Vektor auch auf eine andere Vektorlänge (angegeben mit „Zahl“) normalisiert<br />
werden. Folgender Algorithmus wird hierbei angewandt:<br />
temp = vec_length(Vektor.x);<br />
Vektor.x = Vektor.x / temp * zahl;<br />
my.y = Vektor.y / temp * zahl;<br />
my.z =Vektor.z / temp * zahl;<br />
Vec_To_angle (Vektor Angle, Richtungsvektor);<br />
Diese erste etwas komplexere Vektor-Funktion in C-Script. Diese Funktion berechnet aus dem Wert<br />
des Richtungsvektor, den Pan/Tilt und Roll Wert und schreibt diesen in den ersten Vektor. Mit<br />
dieser Funktion kann zum Beispiel ein Entity in eine bestimmte Richtung gedreht werden.<br />
// hole die Richtung von der Entity MY zur Entity YOU<br />
vec_set(temp,your.x); //setzte Temp auf your.x<br />
vec_sub(temp,my.x); //ziehe von temp.x, my.x ab -> Bewegungsvektor entsteht<br />
vec_to_angle(my.pan,temp);// Rechne dieses Bewegungsvektor (vom Nullpunkt aus gesehen)<br />
in Richtungswinkel um und setzte die Werte in den my.pan Vektor<br />
Vielleicht ist dieser Code nicht sehr schnell zu verstehen, aber machen Sie sich am besten eine<br />
Zeichnung in einem 2-Dimensionalen Koordinatensystem zur besseren Vorstellung. Sie werden<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │14
sehen dass das Ergebnis stimmt.<br />
Vec_Rotate(Richtungsvektor, Vektor Angle);<br />
Die zweite etwas komplexere Vektor-Funktion. Diese Funktion dreht die Richtung des ersten<br />
Vektors um die Pan, Tilt und Roll Werte des zweiten Vektors. Hört sich kompliziert an, ist aber an<br />
einem Beispiel sehr einfach zu verstehen.<br />
Vec_set(temp.x,vector(50,0,0)); //setzt temp Vektor auf (50 | 0 | 0)<br />
vec_rotate(temp.x,camera.pan); //rotiert temp Vektor um Kamera Drehung<br />
vec_add(temp.x,camera.x); //addiert die Kameraposition zu dem Temp Vektor<br />
Nach dieser Anweisung ist der Vektor temp.x genau 50 Quants VOR der Kamera<br />
Vec_Cross(Vektor1,Vektor2,Vektor3);<br />
Diese Funktion ist leider nicht in C-Script vorhanden. Daher müssen wir sie auf wohl oder über<br />
selber schreiben. Folgende Funktion bildet das Kreuzprodukt aus Vektor1 und Vektor2 und schreibt<br />
das Ergebnis in Vektor3.<br />
function vec_cross(vec1,vec2,vec3)<br />
{<br />
vec_set(vec3.x,vector((vec1.y * vec2.z) - (vec1.z * vec2.y),(vec1.z * vec2.x) -<br />
(vec1.x * vec2.z),(vec1.x * vec2.y) - (vec1.y * vec2.x)));<br />
}<br />
Ich hoffe, dass dieses Tutorial Ihnen einen Einblick in die große Welt der Vektor-Mathematik gab.<br />
Falls nicht, oder falls noch Fragen offen sind, senden Sie mir bitte eine Email an<br />
TripleX@hawkgames.de .<br />
Vielen Dank für das lesen<br />
Timo Stark aka Triple-X<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │15
Mit Bleifuß zur Physik Engine – Part I<br />
von Ruben Aster aka Asse<br />
Erstellen Sie im ersten Teil dieses Workshops einen physikgesteuerten Ferrari F360 mit einigen<br />
Hindernissen.<br />
Einleitung<br />
Begrüßung<br />
Willkommen zum ersten Teil meines Workshops über eines der besten Plugins, welches z.Zt.<br />
kostenfrei für <strong>3D</strong> GameStudio erhältlich ist – Newton Game Dynamics.<br />
Viele kennen es und fast genauso viele haben Probleme es richtig einzusetzen. Daher hoffe ich,<br />
dass zum Ende dieses Tutorials einige Verwirrungen geklärt sind und Sie die Funktionsweise von<br />
Newton Game Dynamics (im weiteren NGD) besser verstanden haben. Um dies zu erreichen,<br />
werden Sie einen physikgesteuerten Ferrari F360 erstellen und diesem einige Physikobjekte in den<br />
Weg legen.<br />
Ich habe bereits ein Level und eine dazugehörige Skriptdatei angefertigt, welche nur noch<br />
bearbeitet werden muss, damit ich nicht weiter auf das Drumherum eingehen brauche.<br />
Das wird gebraucht<br />
• die Dateien vom Workshop,<br />
http://www.gfxtreme.de/files/3dgs/workshop_newton<strong>01</strong>/ngd_workshop<strong>01</strong>.zip<br />
• <strong>3D</strong> GameStudio Extra, Standart, Commercial oder Pro<br />
• Newton Game Dynamics 1.51 Plugin für <strong>3D</strong>GS<br />
• Das Plugin finden Sie auf dieser Seite (ganz unten):<br />
http://www.physicsengine.com/downloads.html<br />
Sie können dabei wählen, ob Sie nur das Plugin oder auch noch verschiedene Demos und Tutorials<br />
als Bundle runterladen möchten. Dabei empfehle ich selbst 56k Usern wie mir das 30MB Package,<br />
da wirklich einige sehr interessante Beispiele dabei sind, welche die vielfältigen Möglichkeiten von<br />
NGD demonstrieren.<br />
Kurze Erklärung<br />
Da wir uns hier im ersten Teil - also sozusagen der Vorbereitung - befinden, wird das Skripten mit<br />
NGD nur oberflächlich behandelt. Sie werden die meisten Physik-Werte, ähnliche wie bei den <strong>3D</strong>GS<br />
Templates, direkt in WED setzen. Uninteressant wird es jedoch trotzdem nicht werden, denn das<br />
NGD Plugin ist in erster Linie dafür gedacht, so einfach gehandhabt zu werden.<br />
Dennoch ist es ein Vorteil dann im zweiten Teil zu lernen, wie z.Bsp. physikalische Werte gelesen<br />
werden um etwa einen Tacho für unseren Ferrari zu erstellen, ein Motorengeräusch zu erzeugen,<br />
anhand der Reifenreibung quietschende Reifen zu simulieren oder einfach nur einen<br />
überschlagenen Wagen wieder zu reseten.<br />
Also dann, geben wir Gas!<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │16
Vorbereitung<br />
Um ein komplett eigenständiges Projekt zu erstellen, müssen Sie aus Ihrem <strong>3D</strong><br />
GameStudio\template\ Ordner einige Dateien von Newton kopieren. Der Übersicht halber habe ich<br />
im Workshop-Ordner zwei Unterordner angelegt und diese schon in der newton<strong>01</strong>.wdl definiert.<br />
Kopieren Sie die Dateien wie folgt:<br />
newton.dll, newtonsplash.tga, newton.wdl, newtonScript2.wdl, newtonVehicle.wdl nach Workshop -<br />
Newton Physics P1\<br />
click.wav, door_cl.wav, door_op.wav, hit.wav nach Workshop - Newton Physics P1\sounds\<br />
Als nächstes bereiten wir unser Skript für Newton vor.<br />
Öffnen Sie die newton<strong>01</strong>.wdl und linken Sie als Erstes die wdls von Newton gleich unter dem<br />
Kommentar „// hier newton wdls linken“.<br />
include ;<br />
include ;<br />
include ;<br />
Unter dem Kommentar „// hier splashscreen und cls datei definieren“ geben Sie den folgenden<br />
Code ein, welcher den Splashscreen mit dem Newtonlogo und die Newton Hintergrund-Map<br />
definiert.<br />
bmap splashscreen = <newtonsplash.tga>;<br />
string newtonLevel_cls = <newton<strong>01</strong>.cls>;<br />
Die Newton-Hintergrund-Map (.cls) wird von NGD benötigt um Kollision mit der Levelgeometrie zu<br />
simulieren. Erstellt wird diese Datei automatisch beim Starten eines Levels aus WED. Wichtig ist<br />
nur, dass das Level vorher abgespeichert (nicht mit kompilieren verwechseln) wurde, damit die .cls<br />
aus der aktuellen .wmp generiert werden kann. Sollten Sie sich also mal wundern, warum Sie<br />
durch eine Mauer oder ähnliches durchfahren können, so haben Sie nach einer Änderung der<br />
Geometrie nicht gespeichert.<br />
Zu guter letzt müssen wir noch unter dem Kommentar in der main Funktion „// hier Newton Map<br />
erstellen„ die Hintergrund-Map für Newton erstellen lassen:<br />
wait(1);<br />
dll_handle = newtonHandle;<br />
NewtonAddMap(wmb_Level, splashscreen);<br />
Damit haben Sie alle Vorbereitungen abgeschlossen und können nun mit der Erstellung eines<br />
Newton-Projekts beginnen!<br />
Das Fahrzeug erstellen<br />
Skripten<br />
Ich hoffe Sie sind nicht allzu schreibfaul, denn jetzt sind erstmal ein paar Zeilen Code fällig. Doch<br />
keine Sorge, ich werde alles erklären, sodass Sie nicht im Dunkeln tappen.<br />
Definieren Sie zunächst einen Entity-Pointer, mit welchem wir später auf den Wagen zugreifen<br />
können. Falls Sie nicht wissen wo, dann suchen Sie nach dem Kommentar „// hier entity-pointer<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │17
festlegen“.<br />
entity* p_F360;<br />
Legen wir nun eine Aktion für unseren Ferrari an (die Kommentarzeilen dürfen nicht vergessen<br />
werden, Erklärung folgt später noch):<br />
// uses mass, linear_drag, angular_drag_x, angular_drag_y, angular_drag_z, health,<br />
shape_factor; // uses collision_sph_box, start_active, compound_collision,<br />
destructible, spawn_children; // uses car_topSpeed, car_maxToque; action newton_F360<br />
{<br />
var car;<br />
// weise den pointer p_F360 zu<br />
p_F360 = my;<br />
// erstelle den wagen durch newton<br />
NewtonCreateVehicle (wood_material, VehicleTireEvent, VehicleBeginEvent,<br />
VehicleEndEvent);<br />
dll_handle = newtonHandle;<br />
car = NewtonGetBody (my);<br />
// kontrolliere den wagen per skript<br />
NewtonSetBodyAutoActiveState (car, 0);<br />
NewtonSetBodyActiveState (car, 1);<br />
// setze schwerkraft<br />
NewtonSetBodyForceAndTorque (car, VehicleForceEvent);<br />
// while loop zum steuern des wagens<br />
while (1)<br />
{<br />
werden<br />
// umdrehung und reifenstellung zurücksetzen, damit die werte nicht addiert<br />
my.carEngineTorque = 0.0;<br />
my.carSteerTorque = 0.0;<br />
if(key_a)<br />
{<br />
}<br />
// volle Reifenstellung links<br />
my.carSteerTorque = 1.0;<br />
if(key_d)<br />
{<br />
// volle Reifenstellung rechts<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │18
}<br />
}<br />
}<br />
my.carSteerTorque = -1.0;<br />
if(key_w)<br />
{<br />
}<br />
// vollgas positiv<br />
my.carEngineTorque = my.car_maxToque;<br />
if(key_s)<br />
{<br />
}<br />
// halb vollgas negativ<br />
my.carEngineTorque = -my.car_maxToque * 0.5;<br />
wait(1);<br />
Über dieser Aktion legen Sie zwei Funktionen an, die Newton zum erstellen des Wagens benötigt.<br />
Mit Hilfe dieser Funktionen können z.Bsp. quietschende Reifen simuliert werden, wir werden dafür<br />
aber in Teil 2 unsere eigenen Funktionen schreiben.<br />
function VehicleBeginEvent(vehicleConst)<br />
{<br />
}<br />
var p;<br />
p = 0;<br />
function VehicleEndEvent(vehicleConst)<br />
{<br />
}<br />
var p;<br />
p = 0;<br />
Legen Sie noch eine weitere Funktion unter diesen beiden an und dann haben Sie es für’s Erste<br />
fast geschafft. Diese Funktion definiert Verhalten und Einstellungen der Reifen, sie sollte also nicht<br />
ganz uninteressant für Sie sein.<br />
function VehicleTireEvent(vehicleConst, tyreIndex)<br />
{<br />
var steer;<br />
var omega;<br />
var torque;<br />
var resistance;<br />
steer = NewtonGetTyreSteerParam (vehicleConst, tyreIndex);<br />
steer = steer + (my.carSteerTorque - steer) * 0.25;<br />
NewtonSetTyreSteerParam (vehicleConst, tyreIndex, steer);<br />
// reibung der reifen auf dem boden, je niedriger desto rutschiger<br />
NewtonSetTyreLateralFrictionCoef (vehicleConst, tyreIndex, 1);<br />
NewtonSetTyreLongituFrictionCoef (vehicleConst, tyreIndex, 1);<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │19
torque = my.carEngineTorque;<br />
if (torque != 0)<br />
{<br />
resistance = my.car_maxToque * NewtonGetTyreRadius (vehicleConst, tyreIndex) /<br />
my.car_topSpeed;<br />
}<br />
}<br />
else<br />
{<br />
}<br />
resistance = 0.15 * NewtonGetTyreInertia (vehicleConst, tyreIndex);<br />
omega = NewtonGetTyreOmega (vehicleConst, tyreIndex);<br />
torque = torque - resistance * omega;<br />
NewtonSetTyreTorque (vehicleConst, tyreIndex, torque);<br />
Und nun noch eine letzte Funktion. Mit dieser legen Sie die Schwerkraft Ihres Wagens fest.<br />
Wenn Sie einen Blick in die newtonScript2.wdl werfen und nach der Funktion<br />
ApplyGravityForceEvent(body) suchen, werden Sie schnell merken, dass<br />
NewtonBodyAddForce(body, force_x, force_y, force_z) dafür verantwortlich ist. Durch Verändern<br />
der Werte und etwas Skripten könnten Sie Ihren Ferrari z.Bsp. einfach in einen schwebenden<br />
Raketenwagen umfunktionieren.<br />
function VehicleForceEvent(body)<br />
{<br />
}<br />
ApplyGravityForceEvent(body);<br />
NewtonSetBodyAutoActiveState (body, 0);<br />
Na also, so viel war es doch gar nicht. Überlegen Sie nur wieviel Zeilen Code das NGD-Team<br />
schreiben musste damit Sie es so einfach haben. Und sollten Sie die ein oder andere Rechnung<br />
nicht verstanden haben, ist das auch nicht weiter schlimm. Die entscheidenden Einstellungen des<br />
Fahrzeugs und dessen Reifen werden gleich bequem in WED gesetzt. Speichern!<br />
Fahrzeugteile in WED ausrichten<br />
Hände von der Tastatur und WED geöffnet, jetzt wird mit der Maus gearbeitet!<br />
Laden Sie meinen vorgefertigten Raum (newton<strong>01</strong>.wmp) und fügen Sie die Karosserie des F360 in<br />
WED ein, Object -> Add Model, im Ordner models\ das Modell aut_f360.mdl. Platzieren Sie die<br />
Karosserie an einer gewünschten Stelle innerhalb des Raumes.<br />
Bevor Sie die Reifen hinzuladen noch ein Hinweis. Der Mittelpunkt der Reifen sollte genau im<br />
Zentrum liegen, ansonsten eiert es während der Fahrt. Kontrollieren Sie daher vorerst in MED, ob<br />
die Reifen an der richtigen Stelle sitzen und korrigieren Sie dies, wenn nötig.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │20
Wenn alles sitzt, laden Sie jetzt noch das Reifenmodell rue_360_d.mdl, duplizieren es dreimal und<br />
platzieren die vier Reifen, wie auf dem Screenshot zu sehen, um die Karosserie. Dabei ist es<br />
gewollt, dass die Reifen auf der linken Seite in die falsche Richtung zeigen, dies korrigiert NGD<br />
automatisch.<br />
Kleiner Tipp:<br />
Unter File -> Preferences kann man die Werte verändern um Objekte präziser bewegen (Snap) und<br />
drehen (Rotate Snap) zu können.<br />
Als nächstes gruppieren wir alle fünf Teile des Fahrzeugs. Klicken Sie dazu jeweils auf einen davon<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │21
und drücken dann die Taste G. Haben Sie diesen Vorgang für alle Teile durchgeführt, drücken Sie<br />
abschließend Strg + G. Nun sollten all Ihre Fahrzeugteile als ein Objekt vorliegen.<br />
Wählen Sie das Fahrzeug an (es sollten alle Teile rot dargestellt werden) und wählen Sie Object -><br />
Scope -> Scope down bzw. klicken Sie auf das entsprechende Icon in der Menüleiste. Was Sie jetzt<br />
vor sich haben ist ausschließlich das gruppierte Auto mit allen anwählbaren Einzelteilen. Damit<br />
Newton aber korrekt mit dieser Karosserie-Reifen-Konstruktion umgehen kann, müssen jetzt noch<br />
einmal alle vier Reifen zusammen gruppiert werden. Führen Sie dazu einfach nochmals den obigen<br />
Gruppiervorgang durch, nur diesmal ohne die Karosserie.<br />
Konfigurieren des Fahrzeugs<br />
Nun wird es richtig interessant, denn jetzt konfigurieren wir unseren Wagen, sowie die einzelnen<br />
Räder. Wer also schon immer mal einen Ferrari F360 tunen wollte, hat jetzt die Gelegenheit dazu.<br />
Scopen Sie zu der Ebene, in der man die Karosserie einzeln anwählen kann, rechtsklicken Sie<br />
darauf, wählen Properties und im aufpoppenden Dialog wählen Sie den Tab „behaviour“. Rechts<br />
neben dem Eingabefeld für die Aktion klicken Sie auf den offenen Ordner (Choose Action) und<br />
wählen aus der Liste Ihre vorhin erstellte Aktion „newton_F360“.<br />
Hier können Sie nun, wie versprochen, so gut wie alles einstellen was das Herz begehrt. Noch<br />
interessanter sind gleich die Reifeneinstellungen, wir gehen aber erstmal die wichtigsten Werte für<br />
die Karosserie durch. Meine empfohlenen Werte stehen dabei immer in Klammern gleich hinter der<br />
Bezeichnung.<br />
• Mass (130) – Gibt das Gewicht der Karosserie an. Wichtiger Faktor, wenn es darum geht<br />
mit anderen Physikobjekten zu kollidieren. Zudem liegt das Fahrzeug tiefer und schwerer<br />
auf der Strasse, je höher dieser Wert ist.<br />
• Linear_drag und Angular_drag (0.1) – Mit diesen Werten kann man Luftwiderstände<br />
simulieren. Setzen Sie hier einfach überall 0.1 ein.<br />
• Health (0) – Dieser Wert funktioniert in Verbindung mit Destructible und Spawn_children.<br />
Je höher der Wert, desto mehr Schaden hält das Objekt stand.<br />
• Shape_factor (0) – Schwer zu erklären und ich hoffe ich hab es richtig verstanden (immer<br />
wird einem alles auf englisch erklärt :-/ ). Jedenfalls ist die Kollisionsbox eines Objekts vom<br />
Volumen her natürlich größer als das Objekt selber und das Prinzip des Archimedes besagt,<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │22
dass die Schwerkraft eines Körpers auf Wasser gleich dem Gewicht des vom Körper<br />
verdrängten Wassers ist. Nun da die Kollisionsbox größer ist als das Objekt, wird zu viel<br />
Wasser verdrängt und das Objekt sinkt dementsprechend natürlich tiefer. Abhilfe schafft<br />
hier der Wert shape_factor, welcher das Volumen der Kollisionsbox multipliziert und somit<br />
verkleinert. Idealer Weise natürlich auf die Größe des Objekts. Nicht verstand? Auch nicht<br />
weiter schlimm, Sie werden den Wert kaum brauchen.<br />
• Car_topspeed (1500) – Ganz einfach die Höchstgeschwindigkeit des Wagens.<br />
• Car_maxtorque (50) – Gibt an, wie schnell der Wagen beschleunigt.<br />
• Collision_sph_box (unchecked) – Wenn aktiviert, wird eine Kugel zur Kollisionserkennung<br />
benutzt, ansonsten eine Box.<br />
• Start_active (checked) – Wenn aktiviert, fällt, rollt und bewegt sich dieses Objekt falls es<br />
die Lage erfordert, ansonsten reagiert das Objekt erst auf Physik, wenn es von einem<br />
anderen Physikobjekt getroffen wurde.<br />
• Compound_collision (unchecked) – Bei zusammengesetzten Objekten wird jedes einzelne<br />
Teil als Kollisionsbox verwendet. Achtung, keine Gewähr für die Richtigkeit dieser<br />
Beschreibung!<br />
• Destructible (unchecked) – Ist dieses Flag gesetzt, so ist das Objekt anfällig gegen<br />
Schaden und der oben angegebene Health-Wert verringert sich bei jeder Kollision mit einem<br />
anderen Physikobjekt.<br />
• Spawn_children (unchecked) – Erreicht der Health-Wert 0, so wird das Objekt zerstört<br />
und durch seine Child-Objekte ersetzt (z.Bsp. einem Wrack unseres Wagens).<br />
Haben Sie alle Werte gesetzt, sind nun die Reifen an der Reihe. Scopen Sie bis zur untersten<br />
Ebene, in der Sie jedes Rad einzeln anwählen können und weisen Sie jedem Rad die Aktion<br />
„NewtonVehicleTyre“ zu.<br />
Bevor Sie die Werte für jedes Rat setzen, lesen Sie sich erstmal deren Bedeutung durch, bei Reifen<br />
ist es nämlich ein klein wenig komplizierter.<br />
• Mass (2) – Wie bei der Karosserie gibt dieser Wert das Gewicht an.<br />
• Steer_angle (hinten 0, vorne 20) – Gibt an, um wieviel Grad sich die Räder einlenken<br />
lassen. Bei herkömmlichen Wagen sollten Sie für die Hinterreifen 0 eingeben, da diese sich<br />
normalerweise nicht lenken lassen.<br />
• Suspension_span (10) – Die Länge der Aufhängung.<br />
• Suspension_dashpot (1.2) – Gibt die Stärke des Stoßdämpfers der Aufhängung an. Ist<br />
dieser Wert zu niedrig, wird Ihr Wagen hin und her hopsen.<br />
• Suspension_stiffness (5) – Gibt die Straffheit der Aufhängung an. Je höher der Wert,<br />
desto härter liegt der Wagen auf der Strasse.<br />
• Rolling_dry_friction (0.5) – Gibt an, wie stark der Wagen in der Kurve schlittert – nicht<br />
übertreiben!<br />
• Is_leftwheel – Muss markiert werden, wenn es sich um einen linken Reifen des Wagens<br />
handelt.<br />
• Is_poweredwheel – Gibt an, ob das Rad angetrieben wird oder nicht. Soweit ich weiß,<br />
wird z.Zt. sowieso nur Allrad unterstützt, somit kann man diese Checkbox leer lassen.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │23
Nun, da Sie wissen, dass man linke Reifen definieren muss und die Hinterreifen einen<br />
Einlenkwinkel von 0 Grad haben sollten, können Sie Ihre Werte eintragen.<br />
Sind Sie damit fertig, schauen Sie nochmal kurz ob sich das Modell auch innerhalb des Raumes<br />
befindet…<br />
…, speichern und kompilieren das Level und starten es anschließend.<br />
Wenn Ihnen keine Fehlermeldungen in den Weg kamen, sollten Sie Ihren Wagen von oben auf der<br />
Strecke sehen und Ihn mit W-A-S-D steuern können. Zusätzlich können Sie noch mit dem<br />
mittleren Mausrad zoomen und mit gedrückter rechter Maustaste die Kamera um das Auto<br />
bewegen.<br />
Zusätzliche Physikobjekte plazieren<br />
Kurze Erklärung<br />
NGD hat schon einige vorgefertigte Materialien, welche Gegenständen zugeordnet werden können,<br />
um Physik zu simulieren. Da wir aber noch etwas Zeit (und sicher auch Lust haben ;) und einige<br />
wichtige Materialeigenschaften nur per Skript gesetzt werden können, werden wir unser eigenes<br />
Material erstellen.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │24
Öffnen Sie die newtonScript2.wdl und scrollen Sie zur Funktion NewtonSetupSystem(). In dieser<br />
Funktion werden die verschiedenen Materialien erstellt und die Eigenschaften untereinander<br />
definiert.<br />
Um dies zu erklären, stelle ich kurz die Definition für die Eigenschaften zwischen Ground und Wood<br />
Material vor.<br />
// set material interaction propeteries (ground_material, wood_material)<br />
NewtonMaterialSetFriction (ground_material, wood_material, 1.0, 0.7);<br />
NewtonMaterialSetElasticity (ground_material, wood_material, 0.4);<br />
NewtonMaterialSetDepthRecover (ground_material, wood_material, 0.2);<br />
NewtonMaterialSetSoundEffect (ground_material, wood_material, hit);<br />
Wie man anhand der Kommentarzeile richtig vermuten darf, wird hier festgelegt, wie Gegenstände,<br />
welchen die Aktion NewtonWoodEntity zugewiesen wurde, sich beim Zusammenprall mit der<br />
Levelgeometrie (ground_material) verhalten.<br />
Würden wir hingegen diese Zeilen schreiben…<br />
NewtonMaterialSetFriction (ice_material, wood_material, 1.0, 0.7);<br />
NewtonMaterialSetElasticity (ice_material, wood_material, 0.4);<br />
NewtonMaterialSetDepthRecover (ice_material, wood_material, 0.2);<br />
NewtonMaterialSetSoundEffect (ice_material, wood_material, hit);<br />
…würden wir definieren, wie sich Ice Material beim Zusammenprall mit dem Wood Material<br />
verhalten soll.<br />
Eine Liste von allen zuweisbaren Materialeigenschaften samt kurzer Erklärung und den Parametern,<br />
finden Sie in der newton.wdl unter dem Stichpunkt „Physics material creation and interaction<br />
funstion“.<br />
Das erste eigene Material<br />
Kommen wir zum abschließenden Teil dieses Workshops. Wir werden jetzt unser eigenes Material<br />
erstellen und dies für einen Stapel herumstehender Reifen in unserem Level verwenden. Da wir alle<br />
NGD Dateien in unseren Ordner kopiert haben, können wir diese auch bedenkenlos verändern,<br />
ohne das dies größere Auswirkungen haben könnte.<br />
In der newtonScript2.wdl definieren wir zunächst einen Namen für unser Material, der Ordnung<br />
halber gleich unter den vordefinierten Namen.<br />
// this global variable reprent handles to materials use in the level<br />
var ground_material;<br />
var wood_material;<br />
var ivory_material;<br />
var ice_material;<br />
var water_material;<br />
var tyre_material; // das ist der Name für unser Material<br />
Scrollen Sie wieder runter zu der Funktion NewtonSetupSystem() und erstellen Sie Ihr Material.<br />
// all other material needs to be created<br />
wood_material = NewtonCreateMaterial ();<br />
ivory_material = NewtonCreateMaterial ();<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │25
ice_material = NewtonCreateMaterial ();<br />
water_material = NewtonCreateMaterial ();<br />
tyre_material = NewtonCreateMaterial (); // hiermit erstellt Newton Ihr Material<br />
Da wir bis jetzt kaum verschiedene Physikobjekte in unserem Level haben, bis auf unser Auto<br />
natürlich, reicht es erstmal die Eigenschaften zwischen unserem Material und der Levelgeometrie<br />
festzulegen. Schreiben Sie die folgenden Zeilen in die NewtonSetupSystem() Funktion, ich werde<br />
deren Bedeutung gleich erklären.<br />
NewtonMaterialSetFriction (ground_material, tyre_material, 1.0, 0.9);<br />
NewtonMaterialSetElasticity (ground_material, tyre_material, 0.7);<br />
NewtonMaterialSetDepthRecover (ground_material, tyre_material, 0.2);<br />
NewtonMaterialSetSoundEffect (ice_material, wood_material, hit);<br />
• Friction - Als erstes haben wir die Reibung angegeben (statisch und kinetisch) und da<br />
Reifen ja nicht besonders gut rutschen (so soll es auch sein ;), hab ich hier besonders hohe<br />
Werte genommen.<br />
• Elasticity – Dieser Wert gibt an, wie stark die beiden Körper voneinander abprallen, wobei<br />
er für Reifen sogar noch etwas höher sein könnte. Ein Formel1-Reifen der mitten in der<br />
Fahrt vom Fahrzeug getrennt wird ist quasi nicht zu stoppen – glauben Sie mir!<br />
• DepthRecover - Hier bin ich mir nicht ganz sicher. Scheinbar wird hier angegeben, wie<br />
schnell das gegenseitige Durchdringen zweier Objekte wieder gelöst wird.<br />
• SoundEffect – Definieren Sie eine Sound-Datei, die jedesmal abgespielt wird, wenn beide<br />
Gegenstände aufeinander prallen.<br />
Ihr Material ist bereits voll funktionsfähig, eine Kleinigkeit fehlt aber noch um es in WED auch<br />
zuweisen zu können. Alle die sich etwas auskennen, haben sicherlich bemerkt, dass wir noch eine<br />
Aktion für unser Material brauchen, also legen wir dieses gleich unter den anderen Material-<br />
Aktionen an.<br />
action NewtonIceEntity<br />
{<br />
}<br />
NewtonCreateGravityEntity (ice_material);<br />
// uses mass, linear_drag, angular_drag_x, angular_drag_y, angular_drag_z, health,<br />
shape_factor, collision_groupId, collision_sph_box, start_active, compound_collision,<br />
destructible, spawn_children;<br />
action NewtonTyreEntity<br />
{<br />
}<br />
NewtonCreateGravityEntity (tyre_material);<br />
Das war’s schon. Der Kommentar über unserem Code ist übrigens nicht nur informativ, sondern<br />
auch überaus praktisch, denn die Werte unseres Materials würden ohne diesen Kommentar in WED<br />
statt Mass, Health usw. nur Skill1, Skill2 usw. heißen und das ist sehr unübersichtlich.<br />
Schauen Sie es sich am besten gleich selber an, indem Sie nun endlich den Reifenstapel in unser<br />
Level einbringen. Wechseln Sie zu WED, klicken Object -> Add Model und wählen aus dem Ordner<br />
models\ das Modell rue_360_d.mdl. Fügen Sie noch drei weitere Reifenmodelle ein und platzieren<br />
Sie diese zu einem Stapel, der irgendwo in unserem Raum auf dem Boden steht.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │26
Weisen Sie jedem dieser Reifen die Aktion NewtonTyreEntity zu – das hatten Sie vorher schon bei<br />
der Karosserie und den Autoreifen gemacht – und ändern Sie den Wert Mass auf 20.<br />
Wenn Sie fertig sind, dann speichern und kompilieren Sie Ihr Level und starten es anschließend.<br />
Der Reifenstapel sollte jetzt in Ihrem Raum stehen und auf Kollision mit Ihrem Wagen reagieren.<br />
Falls Ihnen einige Eigenschaften nicht zusagen sollten, dann wissen Sie ja wo diese geändert<br />
werden können.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │27
Glückwunsch, die erste Hürde zum Physik-Flitzer haben Sie gemeistert und allzu schwer war es<br />
auch nicht. Sie sind nun in der Lage, die Eigenschaften Ihres Wagens und einfachen Physikobjekten<br />
Ihren Wünschen anzupassen und haben eine bessere Vorstellung über die Funktionsweise von<br />
NGD.<br />
Schlusswort<br />
Bis zum nächsten Teil dieses Workshops können Sie noch etwas mit den Funktionen und Aktionen<br />
von NGD herum experimentieren. z.Bsp. könnten Sie Ihren Ferrari über hügeliges Terrain steuern<br />
oder völlig unkontrollierbar über Eis schlittern lassen. Und keine Hemmungen, was ich hier<br />
niedergeschrieben habe kam auch nur durch experimentieren und durchstöbern der wdls zustande.<br />
Im nächsten Teil werden Sie, wie schon zu Beginn gesagt, weiter in die Funktionen von NGD<br />
eindringen und mit einigem Skriptaufwand coole Sachen mit Ihrem Wagen anstellen. Es wird also<br />
ganz bestimmt nicht langweilig. Schreiben Sie mir am besten Ihre Wünsche und Anregungen per<br />
Mail und ich werde versuchen diese dann einzubringen, somit haben auch andere etwas von Ihren<br />
Ideen. Je nach Resonanz werde ich dann eventuell noch einen dritten Teil veröffentlichen.<br />
Ich hoffe, es hat Ihnen Spaß gemacht und machen Sie’s gut bis zum nächsten Teil.<br />
Ruben Aster aka Asse.<br />
Asse_<strong>3D</strong>GS@web.de<br />
Vielen Dank noch an Psionic für das ausgezeichnete F360 Modell. Ein Besuch der Seite lohnt sich!<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │28
Inhalt<br />
Ein Nebeltutorial für das <strong>3D</strong> Gamestudio<br />
1.Das Nebelsystem des <strong>3D</strong>GS<br />
2.Eigene Systeme und volumetrischer Nebel<br />
3.Specials: Materials und Dynamik<br />
von Torsten „fogman“ Fock<br />
Ist für Sie das Nebelsystem von <strong>3D</strong> Gamestudio ein Rätsel? Oder haben Sie es verstanden und<br />
wollen noch ganz andere Sachen machen? Dann dürften Sie die folgenden Zeilen interessieren...<br />
Zu allen Kapiteln und Beispielen gibt es Beispiellevels, die das geschriebene Veranschaulichen.<br />
Diese Beispiele können Sie zusammen mit dem Tutorial hier<br />
herunterladen: http://home.arcor.de/braindamaged/Nebeltutorial.rar (Ca. 8.8 MB)<br />
Und nun wünsche ich Ihnen viel Spaß beim experimentieren.<br />
Das Nebelsystem des <strong>3D</strong>GS<br />
Beispiellevel: Beispiel_Teil_1<br />
Wie fast alles im Gamestudio ist das Nebelsystem sehr modular aufgebaut. Es gibt vier<br />
Nebelfarben, die per Skript und im WED verändert werden können. Aus diesen Nebelfarben kann<br />
dann per Skript eine ausgewählt werden.<br />
Schauen Sie sich ein einfaches Skript an, welches von diesen Nebelfarben gebrauch macht:<br />
function fog_fnc()<br />
{<br />
}<br />
d3d_fogcolor1.red = 100; // Nebelfarbe 1, Rotanteil<br />
d3d_fogcolor1.green = 100; // Nebelfarbe 1, Grünanteil<br />
d3d_fogcolor1.blue = 100; // Nebelfarbe 1, Blauanteil<br />
fog_color = 1; // Benutze Nebelfarbe 1<br />
Diese Funktion erzeugt einen grauen Nebel, da alle Farbanteile auf den selben Wert eingestellt<br />
sind.<br />
Weitere Beispiele:<br />
d3d_fogcolor2.red = 255;<br />
d3d_fogcolor2.green = 255;<br />
d3d_fogcolor2.blue = 255;<br />
Erzeugt weißen Nebel mit der Nebelfarbe 2.<br />
d3d_fogcolor4.red = 100;<br />
d3d_fogcolor4.green = 0;<br />
d3d_fogcolor4.blue = 100;<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │29
Erzeugt violetten Nebel mit der Nebelfarbe 4. Mit fog_color können Sie nun zwischen diesen<br />
Nebelfarben umschalten um zu bestimmen, welche angezeigt wird.<br />
fog_color = 4;<br />
Würde dementsprechend den vorher definierten, violetten Nebel darstellen. Es ist nicht unbedingt<br />
nötig, die Nebelfarben per Skript zu setzen.<br />
Im WED können sie diese Farben direkt bestimmen und per Skript jederzeit ändern.<br />
Gehen Sie hierzu in die „Map-Proberties“ im „File-Menü“. Hier können Sie unter „Fog“ die<br />
Nebelfarben direkt setzen. Im Skript wählen Sie dann nur noch per „fog_color“ welche Nebelfarbe<br />
dargestellt werden soll.<br />
Nun müssen Sie noch festlegen, wie dicht der Nebel sein soll. Hierfür gibt es drei Parameter:<br />
„view.fog“, „view.fog_start“ und „view.fog_end“.<br />
„fog_start“ beschreibt die Entfernung in Quants zum View, ab der der Nebel beginnen soll.<br />
„fog_end“ beschreibt die Entfernung zum View, wo der Nebel endet.<br />
Je dichter diese Werte zusammenliegen, desto dichter wird der Nebel sein.<br />
Logisch, oder?<br />
Schreiben Sie also...<br />
function fog_fnc()<br />
{<br />
}<br />
d3d_fogcolor1.red = 100; // Nebelfarbe 1, Rotanteil<br />
d3d_fogcolor1.green = 100; // Nebelfarbe 1, Grünanteil<br />
d3d_fogcolor1.blue = 100; // Nebelfarbe 1, Blauanteil<br />
fog_color = 1; // Benutze Nebelfarbe 1<br />
camera.fog_start = 100; // Der Nebel beginnt 100 Quants entfernt von der Kamera<br />
camera.fog_end = 500; // Der Nebel endet 500 Quants entfernt von der Kamera<br />
Beachten Sie das „camera“ der vordefinierte View ist. Sie können natürlich auch jeden selbst<br />
definierten View benutzen.<br />
Um Texturen direkt vom Nebel zu beeinflussen dient der „albedo“. Ihn können Sie im WED bei<br />
den Texture-Settings einstellen. Werte unter fünf stehen für die Nebelfarbe, fünf selbst für<br />
Dunkelheit. So ist es leicht möglich, Oberflächennebel zu erzeugen.<br />
Um zu bestimmen wie stark sich Nebel auf dynamische Schatten auswirkt, benutzen Sie den<br />
„view.fog“ Parameter.<br />
camera.fog = 0; // Nebel wirkt sich nicht auf dynamische Schatten aus.<br />
camera.fog = 100; // Nebel wirkt sich voll auf dynamische Schatten aus.<br />
Und das war das ganze Geheimnis des Nebelsystems!<br />
Wie, das reicht Ihnen nicht...?<br />
Da muß ich mir ja was einfallen lassen...<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │30
Volumetrischer Nebel und eigene Systeme<br />
Wir kochen unser eigenes Süppchen Bodennebel... Beispiellevel: Beispiel_Teil_2_1<br />
Um volumetrischen Nebel zu erzeugen, welcher die Grundlage für eigene Systeme ist, müssen Sie<br />
tricksen. Das Prinzip werde ich anhand von Bodennebel darlegen.<br />
Dieser wirkt zusammen mit dem standard Nebelsystem äußerst atmosphärisch:<br />
Hierfür benötigen Sie zuerst mal den normalen Nebel. Die Funktion kennen sie ja schon.<br />
function fog_fnc()<br />
{<br />
}<br />
d3d_fogcolor1.red = 100; // Nebelfarbe 1, Rotanteil<br />
d3d_fogcolor1.green = 100; // Nebelfarbe 1, Grünanteil<br />
d3d_fogcolor1.blue = 100; // Nebelfarbe 1, Blauanteil<br />
fog_color = 1; // Benutze Nebelfarbe 1<br />
camera.fog_start = 100; // Der Nebel beginnt 100 Quants entfernt von der Kamera<br />
camera.fog_end = 500; // Der Nebel endet 500 Quants entfernt von der Kamera<br />
Starten Sie nun MED. Erstellen Sie einen Würfel und löschen Sie alle Vertices bis auf die obersten<br />
vier, und geben Sie der verbliebenen Fläche eine hellgraue Skin:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │31
Speichern Sie das Modell in Ihrem Projektordner und laden Sie es zwölfmal in Ihr Level. Skalieren<br />
Sie die Modelle so, das sie in den schon erzeugten Nebel hineinragen werden, und ordnen Sie sie in<br />
geringen Abständen übereinander an:<br />
Eine kleine Action benötigen Ihre Modelle auch noch, um sie transparent zu machen und an die<br />
Kamera anzuhängen.<br />
Wie wäre es damit:<br />
action fog_disc<br />
{<br />
my.transparent = on;<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │32
}<br />
my.alpha = 10;<br />
while(1)<br />
{<br />
}<br />
my.x = camera.x;<br />
my.y = camera.y;<br />
wait(1);<br />
Die Modelle werden also transparent sein, mit einem geringen Alphawert. Sonst wäre der<br />
Bodennebel zu „dicht“. Mit der while-Schleife erzwingen Sie, das die x und y Positionen der<br />
„Nebelplatten“ immer denen der Kamera entsprechen.<br />
Da sich Bodennebel am Boden am wohlsten fühlt, lassen wir die z Position unberührt. Weisen Sie<br />
den Modellen die Action zu, und Sie haben Ihren ersten eigenen, volumetrischen Nebel erzeugt!<br />
Eigene Systeme... Beispiellevel: Beispiel_Teil_2_2<br />
Nun können Sie sich gewiß schon denken, wie Sie ein komplett eigenes System realisieren<br />
könnten. Ich möchte auch nicht weiter auf die Gründe eingehen, Sie werden welche haben. Viele<br />
Wege führen auch hier nach Rom, ich möchte einen davon vorstellen.<br />
Zuerst benötigen Sie wieder den MED oder einen anderen Modeller. Erschaffen Sie dort bitte einen<br />
einfachen Zylinder und löschen alle Vertices bis auf die untersten. Geben Sie auch dieser Fläche die<br />
graue Skin:<br />
Nun ziehen Sie den mittleren Vertex nach oben – etwa so:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │33
Speichern Sie das Modell und öffnen Sie Ihr Level in WED. Fügen Sie das Modell wieder zwölfmal<br />
ein – je öfter umso feiner wird Ihr Nebel aufgelöst sein. Übertreiben Sie es aber nicht!<br />
Hier sehen Sie die eingefügten Modelle. Ich habe jedes folgende etwas größer skaliert:<br />
Kommen wir zur dazugehörigen Action:<br />
action fog_disc_2<br />
{<br />
my.transparent = on;<br />
my.alpha = 10;<br />
while(1)<br />
{<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │34
}<br />
}<br />
my.x = camera.x;<br />
my.y = camera.y;<br />
wait(1);<br />
Nein, Sie haben sich nicht verlesen. Es ist genau die gleiche Action die ich für den Bodennebel<br />
verwendet habe, bis auf ihren Namen. Wenn gewünscht, kann die z – Position natürlich auch noch<br />
an die Kamera gehängt werden.<br />
Es ist nur ein Weg, Modelle zu benutzen. Ebenso geht es mit Map Entities oder Sprites ja selbst<br />
Terrain könnten Sie mißbrauchen. Ich habe mich für Modelle entschieden, da diese schnell<br />
gerendert werden und man viele weitergehende Sachen mit ihnen anstellen kann. Was halten Sie<br />
zum Beispiel von ein paar Materialien und waberndem Nebel?<br />
Sehen Sie – ich auch...<br />
Specials: Material und Dynamik<br />
Wunderliche Waschküchen... Beispiellevel: Beispiel_Teil_3<br />
Anhand eines „Nebelsees“ werde ich Ihnen eine Möglichkeit zeigen, dynamischen Nebel zu<br />
erzeugen.<br />
Zu diesem Zweck benötigen Sie ein einfaches Terrain mit einer Vertiefung. Für eben diese<br />
Vertiefung müssen Sie ein Modell erstellen. Beginnen Sie also wieder mit dem MED:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │35
Ich habe einfach das Terrain in ein Modell gewandelt (im Edit-Menü), und alle Vertices gelöscht bis<br />
auf die Vertiefung. Diese habe ich dann mit dem Scale -Tool zu einer planen Fläche gemacht und<br />
ihr die graue Skin gegeben. In WED müssen Sie dieses Modell nun über der Vertiefung im Terrain<br />
platzieren. Laden Sie das Modell dreimal in den WED und verschieben Sie sie etwas<br />
gegeneinander:<br />
Um eine schönere Gesamtatmosphäre zu erzeugen, laden Sie bitte auch noch das Bodennebel –<br />
Modell zwölfmal in den Leveleditor. Ordnen Sie die Flächen so an, wie in Kapitel 2 beschrieben:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │36
Lassen Sie uns dem Nebel Leben einhauchen. Dazu brauchen wir etwas C-Script. Fügen Sie die<br />
folgenden, schon bekannten Funktionen ein um Bodennebel und normalen Nebel zu erhalten:<br />
function fog_fnc()<br />
{<br />
}<br />
d3d_fogcolor1.red = 100; // Nebelfarbe 1, Rotanteil<br />
d3d_fogcolor1.green = 100; // Nebelfarbe 1, Grünanteil<br />
d3d_fogcolor1.blue = 100; // Nebelfarbe 1, Blauanteil<br />
fog_color = 1; // Benutze Nebelfarbe 1<br />
camera.fog_start = 100; // Der Nebel beginnt 100 Quants entfernt von der Kamera<br />
camera.fog_end = 500; // Der Nebel endet 500 Quants entfernt von der Kamera<br />
action fog_disc<br />
{<br />
}<br />
my.transparent = on;<br />
my.alpha = 10;<br />
while(1)<br />
{<br />
}<br />
my.x = camera.x;<br />
my.y = camera.y;<br />
wait(1);<br />
Weisen Sie bitte den zwölf Bodennebel Modellen die Action „fog_disc“ zu. Und vergessen Sie nicht,<br />
die Funktion „fog_function( )“ zu starten!<br />
Nun wird es ein wenig komplizierter. Sie haben sich bestimmt schon Gedanken gemacht, wie man<br />
die drei Flächen für den Nebelsee wabern läßt. Da gäbe es die Möglichkeit die Flächen im MED zu<br />
animieren. Aus verschiedenen Gründen habe ich mich für eine andere Art der Animation<br />
entschieden.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │37
Animierte Modelle würden auf Dauer langweilig aussehen, brauchen mehr Speicher und sind<br />
langsamer in der Berechnung. Lassen Sie uns also eine Action schreiben, die direkt auf die Vertices<br />
der Modelle wirkt, auch „Meshdeforming“ genannt.<br />
Vor dieser Funktion graut es mir am meisten, da sie sich auf Sinusberechnung stützt.<br />
Frisch ans Werk, ich werde Ihnen jede Zeile erklären:<br />
var counter;<br />
var index;<br />
var vertices = 241;<br />
var vertex_array[241];<br />
Diese vier Zeilen definieren drei Variablen und einen Array. Ein Modell für den Nebelsee hat 241<br />
Vertices. Daher dieser Eintrag.<br />
action fog_dynamic<br />
{<br />
my.transparent = on;<br />
my.passable = on;<br />
my.alpha = 15;<br />
while (counter < vertices) // Solange Counterwert kleiner Verticeswert<br />
{<br />
den Array<br />
Temp<br />
vertex_array[counter] = random(360); // Setze Zufallswerte von 0-360 in<br />
counter += 1; // Zähle den Arrayindex um eins hoch<br />
while (1)<br />
{<br />
den Z - Eintrag<br />
Vektor<br />
}<br />
}<br />
while (index < vertices) // Solange Indexwert kleiner Verticeswert<br />
{<br />
}<br />
vec_for_mesh(temp, my, index); // Aktuelle Vertexposition in<br />
temp.z = sin(counter + vertex_array[index]) * 1; // Ändere<br />
vec_to_mesh(temp, my, index); // Bewege den Vertice mit dem<br />
index += 1; // Zähle eins auf den Index<br />
counter += 0.2 * time; // Zähle den Counter hoch<br />
index = 0; // Setze index auf null<br />
wait(1);<br />
Was für ein Monster! Keine Angst, Sie werden sehen, es ist durchaus einfach zu verstehen.<br />
Die ersten drei Zeilen sollten selbsterklärend sein. Das Modell wird transparent und passabel<br />
gemacht, der Alphawert der Transparenz beträgt 15.<br />
Als nächstes folgt eine Schleife, die solange durchlaufen wird, bis die Variable „counter“ einen Wert<br />
von 241 hat. Jener Wert der in der Variable „vertices“ steht. In dieser Schleife werden nun alle 241<br />
Einträge des Arrays mit Zufallszahlen von 0 – 360 gefüllt. Auf den „Counter“ wird jeweils eins<br />
hinzuaddiert, so das die Schleife nach dem 241. Lauf endet. Aufgrund der Abbruchbedingung<br />
können wir in der Schleife auf ein „wait“ verzichten. Es kann keine Endlosschleife entstehen.<br />
In der nächsten Zeile beginnt eine solche „Endlosschleife“. Und in ihr noch eine Schleife. Man nennt<br />
so etwas „verschachtelt“. Es wird also permanent („endlos“) geprüft, ob sich die Bedingung erfüllt<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │38
und die zweite Schleife aktiv wird. Da der die Variable „index“ null ist und in der Schleife wieder<br />
um eins hochgezählt wird, wird sie 241 mal durchlaufen.<br />
Der Befehl „vec_for_mesh“ liest die Vertexposition des Vertices der durch den „index“ ausgewählt<br />
wurde und kopiert sie in den vordefinierten Vektor „temp“. Die Z-Komponente des Vektors wir nun<br />
in einer Sinusfunktion verändert, abhängig vom Wert des „counters“ und dem aktuellen<br />
„Winkeleintrag“ (0-360) in dem Array.<br />
Per „vec_to_mesh“ wird die Vektorposition dem per index ausgewähltem Vertice wieder<br />
„aufgezwungen“ - er bewegt sich auf der Z-Achse. Natürlich muß der „index“ dann wieder um eins<br />
hochgezählt werden, um den nächsten Vertex zu bedienen. Wenn alle Vertices ein Stückchen<br />
bewegt wurden, wird die Schleife verlassen. Der Counterwert wird erhöht, um die Vertices beim<br />
nächsten Durchlauf weiterzubewegen. Die Variable „index“ wird auf null gesetzt – das Spiel beginnt<br />
von vorne.<br />
Spielen Sie ein wenig mit der Action – sie eignet sich auch als Grundstock für Gardinen, Wasser<br />
und was sonst noch so „wabert oder wellt“<br />
Weisen Sie diese Action den „Nebelseemodellen“ zu. Nun haben Sie ihren ersten dynamischen<br />
Nebel erzeugt. Lassen Sie uns noch rasch ein Material für den Nebel definieren. Sie werden<br />
überrascht von den Effekten sein, die wie Wasser oder Toxien wirken können.<br />
Schauen wir uns seine Definition an:<br />
material fog_mat<br />
{<br />
}<br />
ambient_red = 10;<br />
ambient_green =10;<br />
ambient_blue = 10;<br />
diffuse_red = 0;<br />
diffuse_green = 100;<br />
diffuse_blue = 0;<br />
specular_red = 0;<br />
specular_green = 255;<br />
specular_blue = 0;<br />
emissive_red = 0;<br />
emissive_green = 100;<br />
emissive_blue = 0;<br />
power = 5;<br />
Die Grundhelligkeit (Ambient) beträgt 10.<br />
Diffuse beschreibt, wieviel Licht und welche Farbe das Material empfangen kann.<br />
Specular setzt einen Glanzpunkt<br />
Emissive bestimmt mit welchem Wert sich die Entity illuminiert.<br />
Power steht für die Größe des Glanzpunktes.<br />
Schreiben Sie in die Action „fog_dynamic folgendes, gleich unter „my.passable = on;“ :<br />
my.material = fog_mat;<br />
Nun haben Sie dem Nebelsee ein Material zugewiesen. Was noch fehlt ist ein dynamisches Licht,<br />
um die Auswirkungen des Effekts richtig zu sehen.<br />
Sie können ein einfaches Modell nehmen (z. B. einen Würfel) und diesem die folgende Action<br />
geben:<br />
action light_dyn<br />
{<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │39
}<br />
my.invisible = on;<br />
my.lightrange = 300;<br />
my.red = 255;<br />
my.green = 255;<br />
my.blue = 255;<br />
Platzieren Sie Ihre „Sonne“ dicht über dem Nebelsee, builden Sie und starten Sie Ihren Rundgang<br />
durch ein radioaktives Medium.<br />
Nun sind wir am Ende des Tutorials angelangt. Ich hoffe ich konnte Ihnen einige Inspiration über<br />
den kreativen Umgang mit Dunst verschaffen.<br />
Versuchen Sie, das Material zur Laufzeit zu beeinflussen, indem Sie seine Parameter in einer<br />
Funktion verändern, ändern Sie die Skin, oder, oder, oder...<br />
Lassen Sie es mich wissen, wenn Sie einen neuen Effekt ausgetüftelt haben und verlaufen Sie sich<br />
nicht. Sonst muß ich auch noch ein Nebelhorntutorial schreiben.<br />
In diesem Sinne – Rauchen Sie ab!<br />
Torsten „fogman“ Fock<br />
fogman@gmx.com<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │40
Modelldesign - Teil 1: Mesh eines Tischs mit Blender<br />
Einleitung<br />
von Josef/Jupp<br />
Hallo ich bin Josef Brandl und dürfte Ihnen als Josef oder Jupp aus dem <strong>3D</strong>GS-User Forum bekannt<br />
sein. In dieser Reihe werde ich zeigen, wie man professionelle Modelle mit Blender erstellt. Warum<br />
mit Blender und nicht MED? In MED wird sich einiges mit dem nächsten Update ändern, außerdem<br />
kommen Fortgeschrittene schnell an die Grenzen MEDs. In Blender gibt es weitaus mehr Features.<br />
Es ist fast unmöglich alle zu kennen, allein das Handbuch erfasst 686 Seiten. Ein weiterer guter<br />
Grund ist, dass Blender opensource ist, das heißt nicht nur, dass es laufend Updates gibt sondern<br />
auch, dass sich jeder das Programm kostenlos aus dem Internet runterladen kann. Ich hoffe, dass<br />
ich dem einen oder anderen mit diesem Tutorial einen Gefallen tue.<br />
Viel Spaß beim Lesen<br />
Josef<br />
Der Start: Was wird benötigt?<br />
Wir brauchen für diesen Part nur den Open Source Modeller Blender. Sie bekommen die aktuelle<br />
Version bei www.blender3d.org .<br />
Die Tischplatte<br />
Zu allererst öffnen wir das Programm, wir sehen den Standardwürfel. Die rosa Linie um ihn herum<br />
bedeutet, dass er selektiert wurde. Ich habe ihnen eine Auflistung über die Funktionen der Maus<br />
zusammengestellt. Prägen sie sich diese gut ein, dann können wir loslegen.<br />
Info: Die Funktionen der Maus<br />
Linke Maustaste – Ist in Blender fast nur für das Drücken der Buttons zuständig. Außerdem lassen<br />
sich damit Arbeitsschritte abschließen (wie z.B. eine Verschiebung oder Skalierung) und<br />
Mausgesten zeichnen. Ich habe drei Gesten herausgefunden: 1) Malt man eine gerade Linie, so<br />
aktiviert man das Verschiebewerkzeug 2) Malt man einen Kreis, so aktiviert man das<br />
Rotationswerkzeug 3) Malt man ein "V", so aktiviert man das Skalierungswerkzeug. Nach längerer<br />
Arbeit mir Blender kann ich allerdings sagen, dass es viel einfacher ist, wenn man die Hotkeys auf<br />
der Tastatur her nimmt.<br />
Mittlere Maustaste – Diese Taste wurde bei neuen Mäusen durch ein drückbares Mausrad ersetzt.<br />
Mit dieser Taste lässt sich u. a. die Ansicht des <strong>3D</strong> Ports, auf dem Sie sich gerade mit dem Cursor<br />
befinden, rotieren. Sie werden dabei feststellen, dass es entscheidend ist, wo sie gerade Ihren<br />
Cursor auf dem <strong>3D</strong> Port haben. Denn ist die Position weiter außen, so rollt die Kamera, ist die<br />
Position mehr nahe der Mitte, so schaut man sich um. Probieren sie das gleich mal aus. Ich zeige<br />
im Anschluss, wie sie die Kamera wieder zu Ursprungsposition zurückbringen. Wenn Sie zusätzlich<br />
shift drücken, dann können Sie die Ansicht verschieben .Drücken Sie zusätzlich strg, dann zoomen<br />
Sie, allerdings wenn sie ein Mausrad haben, dann nehmen Sie doch das dafür her.<br />
Rechte Maustaste – Es lassen sich damit einzelne Vertices/Edges/Faces/Objectes selektieren,<br />
drücken Sie shift um eine Auswahl damit zu erweitern. Auch können Sie den aktuellen Schritt<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │41
damit abbrechen, z.B. eine Verschiebung.<br />
Genug der Theorie. Lasst uns weiterarbeiten. Der schon vorhandene Würfel ist für einen Tisch<br />
genau das Richtige, darum können wir direkt anfangen ihn zu editieren. Oder anders: Wir wechseln<br />
in den Editiermodus. Das machen wir entweder mit der Tabulator Taste oder wählen in dem Header<br />
des Views den "Editmode" aus.<br />
Drücken Sie im Anschluss die A-Taste, um alles zu de-selektieren.<br />
Info: Die A-Taste<br />
• Diese Taste macht falls es Selektiertes gibt alles un-selektiert, falls nicht wird einfach alles<br />
selektiert.<br />
• Wechseln Sie in die Oben-Ansicht, per Druck auf die 7 des Nummerblocks.<br />
Info: Der Nummernblock<br />
• 0 – Die Ansicht der Kamera<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │42
• 1 – Die x,z Ansicht (von vorne)<br />
• 1 + strg – Die -x,z Ansicht (von hinten)<br />
• 2 – Die Kamera nach unten drehen<br />
• 3 – Die y,z Ansicht (von rechts)<br />
• 3 + strg – Die -y,z Ansicht (von links)<br />
• 4 – Die Kamera nach links drehen<br />
• 5 – Umschalter zwischen perspektivischer und orthographischer Ansicht. Bei der<br />
perspektivischen Ansicht werden anders wie bei der orthographischen Ansicht die Objekte je<br />
weiter sie weg sind kleiner.<br />
• 6 – Die Kamera nach rechts drehen<br />
• 7 – Die x,y Ansicht (von oben)<br />
• 7 + strg – Die -x,y Ansicht (von unten)<br />
• 8 - Die Kamera nach oben drehen<br />
Anmerkung:<br />
Dies wirkt sich auf die <strong>3D</strong> View aus über der sich die Maus gerade befindet.<br />
Sie sehen nun alles von Oben. Ein Druck B-Taste aktiviert das Werkzeug für ein Auswahlrechteck,<br />
das funktioniert so etwa wie in MED oder in Malprogrammen, wie Gimp und Paint. Wir selektieren<br />
damit die Vertices einer Seite unseres Tisches. Die selektierten Vertices werden nun gelb.<br />
Info: Die Bedeutung der Farben im Edit Mode<br />
selektiert unselektiert<br />
Vertices hellgelb violett<br />
Edges dunkelgelb schwarz<br />
Faces rosa blau<br />
Wenn Sie die Kamera ein wenig bewegen, dann sehen Sie, dass wir nicht erreicht haben, was wir<br />
eigentlich wollten. Wir haben nicht alle Vertices auf der Seite selektieren können. Woran das wohl<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │43
liegt?<br />
Die Antwort ist eigentlich ganz einfach: Die Auswahl ist standardmäßig auf Sichtbares limitiert.<br />
Schalten Sie den hier rot eingezeichneten Button aus und wiederholen sie dann die Prozedur mit<br />
der Box (B-Taste).<br />
Wir sehen, dass jetzt alle 4 Vertices selektiert sind. Nun stellen Sie sicher, dass Sie in der Top<br />
Ansicht sind (7). Drücken Sie die E-Taste für eine Extrusion. Wir wählen Region. Bewegen Sie die<br />
Maus. Sie sehen jetzt die neuen Faces die entstanden sind. (Folgendes ist für später wichtig) Die<br />
eigentliche Extrusion ist abgeschlossen! Sie befinden sich nur noch im Verschiebe-Modus, d.h.,<br />
wenn Sie die rechte Maustaste drücken um die Aktion abzubrechen, sind die neu entstandenen<br />
Faces immer noch vorhanden. Sie müssen zusätzlich noch die Kombination aus strg+z-Taste<br />
drücken um auch die Extrusion rückgängig zu machen. Mit einem Druck von strg bewegen wir<br />
unsere Selektion am Raster. Wir verschieben das Ganze nur um ein Kästchen. Per Druck der linken<br />
Maustaste bestätigen Sie. Machen Sie das auch mit der anderen Seite unseres zukünftigen Tischs.<br />
Das Resultat:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │44
Jetzt kommt wieder das Auswahlviereck dran. Selektieren Sie die Vertices an der linken und<br />
rechten Kante. Nicht vergessen: Dieses Tool fügt die neuen Vertices immer zur bestehenden<br />
Auswahl hinzu, d.h. sie brauchen keine zusätzliche Taste drücken, um die Auswahl zu vergrößern.<br />
Wir wollen nun diese Ecken reinskalieren, sodass der Tisch nachher abgerundete Kanten hat.<br />
Drücken Sie es um die selektierte Auswahl zu skalieren. Drücken Sie danach y um die Vertices<br />
entlang der y-Achse zu skalieren. In welche Richtung welche Achse geht sehen sie unten links im<br />
<strong>3D</strong> Port. Halten Sie dann noch strg gedrückt um am Raster zu skalieren. Per Druck der linken<br />
Maustaste bestätigen sie die Aktion.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │45
Wechseln wir nun in die Frontansicht (1) Drücken Sie die A-Taste um alle Vertices zu selektieren.<br />
Nun drücken sie „S“ um wieder zu skalieren. Wir wollen unseren Tisch nun auf eine normale dicke<br />
bringen. Wählen sie mit einem druck der z Taste die z Achse aus und skalieren die platte nun<br />
ungefähr so dick wie auf folgendem Bild:<br />
In der Top Ansicht machen wir jetzt den Tisch noch etwas länglicher, indem wir jeweils die Vertices<br />
oben und unten ein wenig nach innen verschieben. Sie können dazu dieses Verschiebewerkzeug<br />
mit den Pfeilen hernehmen. Wenn sie es nicht haben, dann stellen sie sicher, dass diese Buttons<br />
des View-Headers, siehe Bild, gedrückt sind: Sie wollen nicht immer dieses Werkzeug hernehmen?<br />
Sie können auch G für Grab drücken. Letztlich ist es egal wie sie ihre Auswahl verschieben.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │46
Das Untergestell<br />
Jeder Tisch hat direkt unter der Platte ein paar Streben. Da wir die grundlegenden Funktionen jetzt<br />
besprochen haben werde ich das jetzt kürzer fassen.<br />
Vergewissern sie sich, dass sie in der Top-Ansicht sind und rufen durch Drücken der Leertaste ein<br />
Menü auf. Wählen sie Add >> Plane. Nun haben wir ein flache Platte erstellt. Drücken sie E >><br />
Region für eine Extrusion. Drücken sie Esc und dann S zum reinskalieren. Ihr Ergebnis dürfte in<br />
etwa so aussehen:<br />
Also was haben wir gemacht. Wir haben aus einer flachen Platte durch Extrusion Faces rundherum<br />
erstellt, da die sich aber in der Höhe nicht unterscheiden sollen brachen wir die Verschiebung ab.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │47
Richtig: Das würde auch mit der rechten Maustaste gehen.<br />
Nun wechseln wir in den Face Select Mode. Sie dachten sich höchstwahrscheinlich schon, dass<br />
Blender, ähnlich zu med oder wings auch verschiedene Selektionsmodi hat. Den Selektionsmodus<br />
können sie bequem in View-Header auswählen:<br />
Lasst uns die großen Faces des Planes auswählen, denn die müssen weg. Klicken sie dazu mit der<br />
rechten Maustaste auf den mittleren Punkt, drücken sie entf >>> faces<br />
Den zweiten mittleren Punkt löschen sie auch noch, so bleibt ein Ring übrig. Bei diesem Ring<br />
verwenden wir jetzt eine Extrusion um ihn wie ein Untergestell aussehen zu lassen.<br />
Jetzt müssen wir diesen Ring nur noch verschieben und vielleicht etwas rumskalieren, damit er so<br />
wie in diesem Bild unter der Tischplatte sitzt.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │48
Die Tischbeine<br />
Ein jeder Tisch hat Beine, aber da gibt es auch unterschiedliche, auf jeden Fall verstehe ich zum<br />
Beispiel unter einem Tischbein keinen viereckigen Klotz.<br />
Wir fangen diesmal wieder mit einem Würfel an. Sie wissen jetzt ja wie man neue Formen<br />
hinzufügt und diese bearbeitet, also erstellen sie so einen Cube und Modellieren daraus ein<br />
Tischbein.<br />
Sie können sich entweder an meiner Form orientieren oder eine ganz neue Kreation schaffen.<br />
Bewegen sie den Cursor über das Tischbein und drücken sie die L-Taste. Blender selektiert es so<br />
automatisch. Was macht Blender da genau? Antwort: Er sucht sich den zum Mauscursor<br />
nächstgelegenen Vertex und selektiert ihn und all seine mit ihm verbundenen Vertices.<br />
Wir duplizieren nun einfach das Bein per shift+d. Drücken sie x um es nur entlang der x-Achse zu<br />
verschieben. Bestätigen sie mit Enter/linke Maustaste.<br />
Jetzt müssen wir das neue Bein noch spiegeln, das ist durch eine Skalierung möglich. Drücken sie<br />
S um das Bein zu skalieren und wieder x für die x-Achse, nun geben sie -1 mit dem Nummernblock<br />
ein. Drücken sie Enter zum bestätigen. Verschieben sie es nun noch zur Richtigen Stelle.<br />
Die gleiche Prozedur jetzt mit den beiden nun erstellten Beinen in der Seitenansicht.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │49
Schluss<br />
Und fertig ist der Tisch… nein nicht ganz, aber das können sie auf diesem Bild jetzt nicht sehen.<br />
Die Normalen der Faces der Beine sind durch das Spiegeln auf der falschen Seite. Sichtbar können<br />
sie das machen, indem sie unten bei den Buttons im mesh-fenster den „double sided“-Button<br />
abwählen. Jetzt sehen sie es auch, dass manche faces viel dunkler sind als andere. Im<br />
Objektmodus sehen sie die dunklen gar nicht. Drücken sie strg+n um dies zu beheben. Blender<br />
kalkuliert nun die Normalen neu.<br />
Fertig ist der Tischmesh. In der nächsten <strong>Ausgabe</strong> werde ich erklären, wie man dieses Mesh<br />
"beskinnt". In einem weiteren Teil wird noch dran kommen, wie man das Modell nach MED<br />
exportiert.<br />
Bei Fragen und Anregungen kontaktiert mich im Gamestudio Forum. Meinen Nickname kennt ihr ja.<br />
Ich wünsche noch viel Spaß mit Blender. Bis zur nächsten <strong>Ausgabe</strong>!<br />
"happy blending!"<br />
Josef/Jupp<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │50
Laser - und Photonentorpedoeffekte<br />
von tuschcarsten<br />
Hallo und herzlich Willkommen zum Waffeneffekt-Artikel des <strong>3D</strong>GS <strong>Magazin</strong>s.<br />
Ich wurde schon oft gefragt, wie man Laser-oder Torpedoeffekte macht. Hier die Lösung:<br />
Laserstrahl<br />
Der Strahl soll so aussehen, wenn er fertig ist:<br />
Als erstes machen wir das Modell für den Laserstrahl.<br />
Dazu öffnen wir MED, fügen einen Würfel ein und löschen alle Seiten bis auf eine:<br />
Dann gehen wir in den Polygon-Modus (links oben) und markieren alles. Dann auf [Edit]->[copy<br />
selected] klicken, dann auf paste. Wir gehen wieder in den Vertex-Modus und markieren wieder<br />
alles. Dann auf den [Merge] Button (oben rechts) klicken. Das bewirkt, dass die Faces jetzt<br />
beidseitig ausgerichtet sind.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │51
Jetzt geben wir dem Modell einen Skin und klicken dazu auf [view]->[Skin].<br />
dann ändern wir die Größe des Skins: [Edit]->[resize skin] und geben die Werte 128x32 ein.<br />
Jetzt stellen wir sicher, dass im normalen Editor (nicht im Skin-Editor) alle Faces markiert sind und<br />
klicken im Skin Editor auf [Edit]->[create md2 mapping] und wählen die Option [TOP] aus.<br />
Das gleiche machen wir noch einmal mit der Option [Bottom].<br />
Dann Ziehen wir die Vertices so zurecht, dass die Faces die Skin-map voll ausfüllen.<br />
Dann exportieren wir das ganze als bmp.<br />
Um die soeben gespeicherte Datei zu bearbeiten, öffnen wir sie in Paint. Als erstes muss alles<br />
schwarz gemalt werden (wenn es das nicht schon ist). Dann speichern wir die Datei mit [Speichern<br />
unter..] als 24-Bit Bitmap ab (ACHTUNG: nicht mit 256 Farben).<br />
Dann malen wir einen dicken farbigen strich von links nach rechts durch die Bitmap. Danach noch<br />
einen kleineren weißen Strich in den ersten Strich hinein:<br />
Jetzt das ganze noch mit einem Bildbearbeitungsprogramm wie Photoshop, Photo Express, Gimp<br />
oder Photo Impression schön unscharf machen und fertig ist die Skin-map.<br />
Jetzt müssen wir sie nur noch im Skin Editor von MED importieren:<br />
[View]->[Skins]<br />
[File]->[import skin image].<br />
Jetzt wird der Skin-Editor geschlossen und nochmal gemodelt:<br />
Wir markieren alle vertices und kopieren sie wie oben schon beschrieben. Dann drehen wir sie um<br />
90°, bis sie so aussehen:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │52
Und dann immer so weiter, bis es so aussieht:<br />
{<br />
}<br />
my.bright = on; //scheint hell<br />
my.ambient = 100; //100% Helligkeit<br />
my.unlit = on; //reagiert nicht auf externes Licht und Schatten<br />
my.overlay = on; //kein Schwarz<br />
my.flare = on; //transparent<br />
my.green = 50; //lichtwerte....<br />
my.blue = 200;<br />
my.lightrange = 50;<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │53
Photonentorpedos<br />
Die Photonentorpedos funktionieren in etwa genauso:<br />
Wir öffnen wieder MED und fügen einen Würfel ein.<br />
Wir löschen alle Flächen, bis auf eine, kopieren diese und kehren die Normals um(wie beim<br />
Laserstrahl). Dann markieren wir alle Faces (im Face_mode) und öffnen den Skin Editor.<br />
Dann ändern wir die Größe des Skins: [Edit]->[resize skin] und geben z.B. die Werte 84x81 ein.<br />
Jetzt klicken wir auf [Edit]->[create md2 mapping] und wählen die Option [TOP] aus. Das gleiche<br />
machen wir noch einmal mit der Option [Bottom].<br />
Dann Ziehen wir die Vertices so zurecht, dass die Faces die Skin-map voll ausfüllen.<br />
Dann exportieren wir das ganze als bmp.<br />
Diese Bmp öffnen wir dann mit Paint und malen ein paar rote und orange Striche von der Mitte aus<br />
nach außen. Dann öffnen wir ein anderes Bildbearbeitungsprogramm wie Photoshop o.a. und<br />
verwischen das Ganze. Die Bmp sollte nun etwa so aussehen:<br />
Jetzt müssen wir sie nur noch im Skin Editor von MED importieren:<br />
[View]->[Skins]<br />
[File]->[import skin image].<br />
Jetzt wird der Skin-Editor geschlossen und nochmal gemodelt:<br />
Wir markieren alle vertices und kopieren sie wie oben schon beschrieben. Dann drehen wir sie um<br />
90°, das selbe wiederholen wir solange, bis es so aussieht:<br />
Dann noch das Modell speichern und diese Aktion zuweisen:<br />
action torpedo<br />
{<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │54
}<br />
my.bright = on; //scheint hell<br />
my.ambient = 100; //100% Helligkeit<br />
my.unlit = on; //reagiert nicht auf externes Licht und Schatten<br />
my.overlay = on; //kein Schwarz<br />
my.flare = on; //transparent<br />
my.green = 50; //lichtwerte....<br />
my.red = 200;<br />
my.lightrange = 50;<br />
mfg<br />
euer tuschcarsten<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │55
Bit-Operatoren<br />
von mk.1<br />
Neben den bekannten Rechenoperatoren wie das bekannte Addieren, Subtrahieren, Multiplizieren<br />
und Dividieren gibt es eine Reihe weiterer Operatoren: Die Bit-Operatoren.<br />
Durch ist es erst möglich, Rechenmaschinen wie die heutigen Home-PCs zu konstruieren. Obwohl<br />
sehr primitiv sind sie sehr häufig von großem Nutzen. Es gibt eine Vielzahl von Bit-Operatoren. Die<br />
bekanntesten sind "und", "oder" und "nicht".<br />
Wie funktionieren Bit-Operatoren?<br />
Um mit Bit-Operatoren zu arbeiten, müssen Sie zuallererst wissen, was ein Bit ist.<br />
Das Wort "Bit" ist eine Abkürzung für "binary digit" (eng.: Binärziffer). Während eine Dezimalziffer<br />
(in unserem Dezimalsystem) zehn Zustände haben kann die Zahlen null bis neun), kann ein Bit<br />
nur den Zustand 1 oder 0 annehmen. Je nach Verwendung spricht man auch von true (wahr) und<br />
false (falsch) oder Signal und kein Signal.<br />
Bit-Operatoren funktionieren wie andere Operatoren auch. Zwei Inputs ergeben ein Output. Hier<br />
ein Beispiel für die Addition:<br />
Ein Bit-Operator funktioniert genauso – zwei Inputs, ein Output.<br />
Bit-Operator "und"<br />
In C-Script (sowie den allen anderen neueren Programmiersprachen) wird das Kaufmanns-Und "&"<br />
für den AND-Operator verwendet. Der Output ist immer dann true (1), wenn beide Inputs true (1)<br />
sind:<br />
Die folgende Tabelle zeigt alle möglichen Kombinationen:<br />
0<br />
1<br />
0<br />
1<br />
&<br />
0<br />
0<br />
1<br />
1<br />
=<br />
0<br />
0<br />
0<br />
1<br />
Bit-Operator "oder"<br />
Dieser Operator hat immer dann den Output true, wenn mindestens einer der Inputs true ist:<br />
0<br />
1<br />
0<br />
1<br />
&<br />
0<br />
0<br />
1<br />
1<br />
=<br />
0<br />
1<br />
1<br />
1<br />
Dieser Operator wird durch einen geraden Strich "|" dargestellt.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │56
Das Binärsystem<br />
Das heutige weltweite System ist das Dezimalsystem, welches die 10 Ziffern 0-9 kennt. Neben<br />
unserem System gibt es unendlich viele andere, wie das Hexadezimale System (16 Ziffern), das<br />
Oktalsystem (8 Ziffern), das Binärsystem (2 Ziffern). Die Babyloner verwendeten sogar ein System<br />
mit 60 verschiedenen Ziffern.<br />
Da ein Computer nur zwei Zustände kennt (Signal/kein Signal), ist das Binärsystem das sinnvollste<br />
System zum Rechnen. Wenn Sie an die erste Grundschule zurückdenken, erinnern Sie sich<br />
vielleicht an die "Einer, Zehner, Hunderter". Sie waren Ausdrücke für eine weitere "Stelle" im<br />
System. Die Zahlen null bis neun benötigen, nur eine Stelle, die Zahlen 10 bis 99 zwei Stellen und<br />
so weiter. Das bedeutet, dass Sie, sobald alle Ziffern verwendet wurden, eine neue Stelle<br />
brauchen. Dieses Problem tritt auch im Binärsystem auf, da es nur zwei Ziffern gibt, die Null und<br />
die Eins. Wenn Sie die Zwei darstellen wollen, benötigen Sie bereits zwei Ziffern.<br />
Dezimal Binär<br />
0 0<br />
1 1<br />
2 10<br />
3 11<br />
4 100<br />
5 1<strong>01</strong><br />
6 110<br />
Um eine Zahl aus dem Binärsystem in das Dezimalsystem umzurechnen, bedarf es einiger<br />
Denkarbeit. Im Binärsytem wird immer dann eine neue Stelle verwendet, wenn alle anderen<br />
Stellen nicht mehr ausreichen, um eine Zahl darzustellen. Mit einer Stelle können Sie nur die<br />
Zahlen Null und Eins darstellen, mit zwei Stellen bereits die Zahlen Null bis Drei. Mit drei Stellen<br />
sind bereits die Zahlen Null bis Sieben darstellbar. Zur Umrechnung geben Sie jeder Stelle einen<br />
Umrechnungswert. Diese zusammengerechnet ergeben den Dezimalwert. Die erste Stelle hat<br />
dabei den Umrechnungswert 1, die zweite den Wert 2, die dritte den Wert 4 und die vierte den<br />
Wert 8 und so weiter. Es ist leicht erkennbar, dass hier immer das doppelte des vorigen Wertes<br />
eine Rolle spielt. Die fünfte Stelle würde also den Wert 16 haben. Anders ausgedrückt sind die<br />
Umrechnungswerte Zweierpotenzen:<br />
2 0 = 1<br />
2 1 = 2<br />
2 2 = 4<br />
2 3 = 8<br />
Angenommen, Sie müssten die folgende Binärzahl 11<strong>01</strong> in das Dezimalsystem umrechnen, dann<br />
sollten Sie wie gefolgt vorgehen:<br />
Die Zahl besteht aus vier Stellen, deren Wert 8,4,2,1 ist. In diesem Fall ist der Wert 2 nicht<br />
vorhanden. Zusammengerechnet wird also 8, 4 und 1. Das Ergebnis wäre also 13.<br />
1 1 0 1 Binär<br />
2 3 2 2<br />
0 2 0<br />
Umrechnungswert<br />
8 4 0 0 Dezimalwert<br />
Ein weiteres Beispiel für die folgende Zahl 1<strong>01</strong>1:<br />
Die Werte 8, 2 und 1 sind in der Binärzahl enthalten. Die Dezimalzahl wäre demnach 11.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │57
Variablen als Flags<br />
Entities haben bekanntlich nur 8 Flags, was häufig nicht ausreicht. In dem Fall verwenden viele<br />
Anfänger Skills als Flags, indem sie diese auf den Wert 0 oder 1 setzen. Für acht Flags würden auf<br />
diese Weise bereits acht Skills benötigt werden. Das ist Verschwendung, denn bereits ein Skill<br />
reicht für acht Flags aus. Möglich wird das durch Bitoperatoren und dem Wissen über das<br />
Binärsystem. Im Grunde stellt jede Stelle im Binärsystem ein Flag dar, da sie auf 0 oder 1 gesetzt<br />
werden kann. Die Engine selbst macht sich dies beim move_ und trace_mode zunutze, denn im<br />
Grunde sind die beiden "modes" einfach nur Variablen.<br />
Das Setzen von Flags funktioniert sehr einfach. Da jedes Flag eine "Stelle" repräsentiert, müssen<br />
Sie einfach nur den Umrechnungswert dieser Stelle in die Variable einfügen.<br />
Flag1 entspricht 1<br />
Flag2 entspricht 2<br />
Flag3 entspricht 4<br />
...<br />
Der Hintergrund ist einfach, dass jeder Kombination dieser Zahlen einmalig ist und eine Zahl<br />
niemals doppelt auftritt. In den mode-Beispielen werden diese Flags/Zahlen einfach addiert.<br />
Das ist aber nicht ungefährlich. Angenommen, Sie hätten Flag1 aktiviert, dann wäre die Variable<br />
(ich nenne sie "var") var == 1. Sie benutzen var später nochmal und wollen das Flag1 setzen,<br />
wissen aber nicht, ob es bereits gesetzt ist. Mit var += 1 wäre nun var == 2. Das Ergebnis wäre,<br />
dass Flag2 gesetzt ist, Flag1 aber nicht (denn Flag2 entspricht bereits dem Wert Zwei). Sie können<br />
dies nachprüfen, indem Sie trace_mode = ignore_me + ignore_me setzen. Das Ergebnis wird sein,<br />
dass me nicht ignoriert wird, dafür aber you.<br />
Die richtige Art, ein Flag zu setzen, ist der "oder"-Operator. Im folgenden Beispiel ist Flag4, Flag3<br />
und Flag1 gesetzt. Flag3 wird nun auf on gesetzt. Das Ergebnis bleibt gleich:<br />
Input1<br />
Input2<br />
or- result<br />
1<br />
0<br />
1<br />
1 0 1<br />
1 0 0<br />
1 0 1<br />
Es ist also sicherer, wenn Sie den trace_mode mittels or-Operator setzen:<br />
trace_mode = ignore_me | ignore_you | ignore_push;<br />
Hier ist ein Codebeispiel, mit dem sie für eine Variable var ein bestimmtes Flag setzen können.<br />
function SetFlag(var, flag_num) {<br />
if(flag_num == 1) { var |= 1; } // setze Flag1<br />
if(flag_num == 2) { var |= 2; } // setze Flag2<br />
if(flag_num == 3) { var |= 4; } // setze Flag3<br />
if(flag_num == 4) { var |= 8; } // setze Flag4<br />
if(flag_num == 5) { var |= 16; } // setze Flag5<br />
if(flag_num == 6) { var |= 32; } // setze Flag6<br />
if(flag_num == 7) { var |= 64; } // setze Flag7<br />
if(flag_num == 8) { var |= 128; } // setze Flag8<br />
}<br />
Nachdem Sie nun wissen, wie Flags gesetzt werden, sollten Sie wissen, wie Sie überprüfen<br />
können, ob ein Flag gesetzt ist. Dazu verwenden Sie den und-Operator. Er liefert wie bereits<br />
erläutert nur eine 1 für eine Stelle zurück, wenn beide Inputs an dieser Stelle eine 1 haben. In<br />
folgendem Beispiel ist wieder Flag4, Flag3 und Flag1 gesetzt und es soll überprüft werden, ob<br />
Flag3 gesetzt ist:<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │58
Input1<br />
Input2<br />
und result<br />
1<br />
0<br />
0<br />
1 0 1<br />
1 0 0<br />
1 0 0<br />
Das Ergebnis ist also genau der Umrechnungswert des zu prüfenden Flags, falls das Flag gesetzt<br />
ist. Hier ist der Code zum Prüfen eines Flags:<br />
function IfFlag(var, flag_num) {<br />
}<br />
if(flag_num == 1) {<br />
}<br />
if((var & 1) == 1) { return(1); } // Flag1 ist gesetzt<br />
if(flag_num == 2) {<br />
}<br />
if((var & 2) == 2) { return(1); } // Flag2 ist gesetzt<br />
if(flag_num == 3) {<br />
}<br />
if((var & 4) == 4) { return(1); } // Flag3 ist gesetzt<br />
if(flag_num == 4) {<br />
}<br />
if((var & 8) == 8) { return(1); } // Flag4 ist gesetzt<br />
if(flag_num == 5) {<br />
}<br />
if((var & 16) == 16) { return(1); } // Flag5 ist gesetzt<br />
if(flag_num == 6) {<br />
}<br />
if((var & 32) == 32) { return(1); } // Flag6 ist gesetzt<br />
if(flag_num == 7) {<br />
}<br />
if((var & 64) == 64) { return(1); } // Flag7 ist gesetzt<br />
if(flag_num == 8) {<br />
}<br />
if((var & 128) == 128) { return(1); } // Flag8 ist gesetzt<br />
return(0); // Flag ist nicht gesetzt<br />
Ist das Flag gesetzt, gibt die Funktion eine 1 (true zurück), andernfalls eine 0 (false). Die<br />
Anwendung ist simpel:<br />
if(IfFlag(trace_mode,1)) {<br />
}<br />
error("Flag1 der Variable trace_mode ist gesetzt");<br />
Was nun noch fehlt, ist das entfernen eines Flags. Dazu benötigen sie einen weiteren Bit-Operator,<br />
den "Inversen-Operator", oder auch COMPLEMENT operator. Er wird durch ein Tilde (~)<br />
dargestellt. Er invertiert die Bits einer Variable, sodass aus 0 eine 1 und aus 1 eine 0 wird.<br />
Für das Beispiel der Dezimalzahl 5:<br />
var 0 1 0 1<br />
~var 1 0 1 0<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │59
Man könnte sagen, dass alle aktivierten Flags deaktiviert werden und alle anderen aktiviert. Was<br />
nützt Ihnen das, um ein bestimmtes Flag zu deaktivieren? Betrachten Sie folgende Formel:<br />
var &= ~1<br />
In Worten ausgedrückt: Benutze den und-Operator auf die Bit-Inverse von 1.<br />
1 0 0 0 1<br />
~1 1 1 1 0<br />
Hier ist eine beliebige Variable und die Bit-Inverse von 1, die mit dem AND-Operator benutzt<br />
werden:<br />
var 1 1 0 1<br />
~1 1 1 1 0<br />
and-result 1 1 0 0<br />
Das Ergebnis ist tatsächlich, dass das Flag verschwindet. Hier ist der Code:<br />
function RemovFlag(var, flag_num) {<br />
}<br />
if(flag_num == 1) { var &= ~1; } // entfernt Flag1<br />
if(flag_num == 2) { var &= ~2; } // entfernt Flag2<br />
if(flag_num == 3) { var &= ~4; } // entfernt Flag3<br />
if(flag_num == 4) { var &= ~8; } // entfernt Flag4<br />
if(flag_num == 5) { var &= ~16; } // entfernt Flag5<br />
if(flag_num == 6) { var &= ~32; } // entfernt Flag6<br />
if(flag_num == 7) { var &= ~64; } // entfernt Flag7<br />
if(flag_num == 8) { var &= ~128; } // entfernt Flag8<br />
Zu guter letzt...<br />
Es gibt natürlich noch andere Bit-Operatoren wie z.B. XOR – exclusive or – und Kombinationen<br />
von Bitoperatoren. Die genannten reichen aber erstmal aus. Auch die Funktionen sind sehr<br />
unschön geschrieben und könnten ein Stück gekürzt werden. Zum Verständnis sind sie meiner<br />
Meinung nach aber ausreichend bzw. besser geeignet. Wenn Sie ein wenig mit Bitoperatoren<br />
gearbeitet haben, können Sie Zeilen auch direkt eingeben ohne eine große Funktion zu benutzen.<br />
Hier nochmal einige Kurzbeispiele:<br />
var |= 4; // setzt Flag3<br />
var &= ~8; // entfernt Flag4<br />
if(var & 8) { // Flag4 ist gesetzt<br />
}<br />
return(1);<br />
Noch einfacher ist es, wenn Sie die Umrechnungswerte durch Definitionen ersetzen:<br />
define flag_1, 1;<br />
define flag_2, 2;<br />
define flag_3, 4;<br />
define flag_4, 8;<br />
define flag_5, 16;<br />
var |= flag_2; // setzt Flag2<br />
var &= ~flag_3; // entfernt Flag3<br />
if(var & flag_1) { // Flag1 ist gesetzt<br />
return(1);<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │60
}<br />
Ich hoffe, diese Einführung in Bit-Operatoren war verständlich. Kritik und Lob bitte an das <strong>3D</strong>GS-<br />
<strong>Magazin</strong> oder direkt an mk.1@gmx.net<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │61
Möglichkeiten des Physiksystems in Gamestudio<br />
Commercial<br />
Dies ist kein Tutorial.<br />
Vielmehr soll dieser Artikel dazu anregen sich mit dem „beschränkten“ Physiksystem der<br />
Commercial Version zu beschäftigen, was alles andere als langweilig ist.<br />
Viele „träumen“ wohl von der Professional Version, da wäre es doch gut zu wissen, was man von<br />
der Gamestudiophysik so zu erwarten hat.<br />
Vielen ist auch nicht bewußt was man mit einem Physikobjekt alles anstellen kann. Abgesehen<br />
von diversen Spielereien mit Kugeln eignet sich die limitierte Physik für Gleiter, Schiffe, Schlitten<br />
und Bob´s, kurz, alles was gleitet. Man denke an Wintersportarten. Aber auch fiese Fallen und<br />
Kanonenschüsse, Interaktion mit Objekten und Constraints sind möglich. Wegwerfbare<br />
Gegenstände und eine Gravitygun sind ebenso machbar. Geduldsspiele in der Art eines<br />
Kugellabyrinthes, Ballspiele, von Fußball über Golf bis Tennis. In einem Physikobjekt steckt mehr<br />
als man meinen könnte.<br />
Zuerst sollte man betrachten was man mit einem Objekt alles bekommt und welche Eigenschaften<br />
ein Physikobjekt auszeichnen:<br />
• Gravitation<br />
• Ein Schwerpunkt<br />
• Polygongenaue Kollisionserkennung wenn gewünscht<br />
• Events werden nach wie vor ausgelöst<br />
• Es können Kräfte verschiedenster Arten auf das Objekt angewendet werden<br />
• Mehrere Physikobjekte können simuliert werden, allerdings auf Kosten der Performance<br />
• Es kann Beschränkungen (Constraints) mit der „Welt“ geben.<br />
Was kann man nun damit anfangen? Auf ein Physikobjekt können Kräfte ausgeübt werden,<br />
sogenannte Beschleunigungen. Wenn man diese an Events koppelt, können nette<br />
Interaktionsmöglichkeiten herauskommen. Ein Trampolin oder ähnliches müsste z.B. bei Kontakt<br />
eine Kraft auf das Physikobjekt anwenden. Schauen Sie sich dazu eine Skizze an:<br />
Die Kugel ist das Physikobjekt. Sie kommt von links oben im Bogen<br />
angeflogen und trifft auf die „Trampolin“ Entity. Diese erkennt aufgrund eines<br />
Events die Kollision und wendet per „phent_addcentralforce“ eine Kraft in Z-<br />
Richtung (nach oben) auf das Physikobjekt an. Das Resultat ist eine<br />
physikalisch korrekte Bewegung der Kugel, da sie ihre „Eigenrichtung“ von<br />
rechts nach links beibehält. Die Kraft in Z-Richtung wird nur addiert.<br />
Es ist etwas gewöhnungsbedürftig Physikobjekte über Kräfte und nicht über „ent_move“ oder<br />
„c_move“ zu bewegen. Wenn Sie jedoch einmal ein wenig herum experimentiert haben, werden Sie<br />
das System schnell verstehen. Was sie zuerst verstehen müssen ist der Unterschied zwischen<br />
lokalen und globalen Kräften. Globale Kräfte beziehen sich immer auf das Level und seine<br />
Koordinaten. Hiermit können Sie zum Beispiel eine Drift erzeugen, die als Gegenwind wirkt. Lokale<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │62
Kräfte beziehen sich auf die Koordinaten der Entity und sind z.B. für Flugsimulationen recht<br />
hilfreich. So muß man sich nie Gedanken über die Ausrichtung der Entity zu den Levelkoordinaten<br />
machen.<br />
Was bieten uns nun Constraints in einer solch limitierten Umgebung? Zur Anregung wieder eine<br />
Skizze:<br />
Die mittlere „Platte“ ist mit einer Beschränkung zur Welt verbunden. Hier<br />
wäre es „ph_hinge“. Wenn nun der Player auf die Plattform geht, sorgt ein<br />
Event dafür das die Plattform sich bewegt, z.B. durch „phcon_setmotor“. Es<br />
sind auch noch ganz andere Gemeinheiten möglich - denken Sie an Indiana<br />
Jones und die dicke Steinkugel die ihm nachstellt, Pendel und Messer die von<br />
der Decke hängen oder noch labilere Plattformen. Die Arten der möglichen<br />
Beschränkungen sind vielfältig.<br />
Ich selbst habe noch nicht viel Erfahrung mit der Physikengine gesammelt, kann jedoch sagen<br />
dass sie sehr intuitiv ist. Sie werden sehen, es macht Spaß Kräfte auf Objekte auszuüben und die<br />
Auswirkungen zu erfahren. Danach können Sie versuchen mit Constraints zu arbeiten.<br />
Ich hoffe ich konnte Ihnen ein wenig den Mund wässrig machen. Physik in Spielen bietet großartige<br />
Möglichkeiten, und wenn es nur ein Objekt zur selben Zeit ist. Ein Tutorial zum Thema wird noch<br />
folgen.<br />
Grüße,<br />
Quellen zum Thema:<br />
• Aum 25 - Physik Demo<br />
• Aum 37 - Mehrere Physikobjekte mit Commercial<br />
• Aum 47 – Rollem, ein Marble Klon<br />
• Link: http://www.conitec.net/english/gstudio/aum.htm<br />
- Physikengine Forumthread –<br />
Glossar:<br />
B<br />
Torsten „fogman“ Fock fogman@gmx.com<br />
Link: http://www.coniserver.net/ubbthreads/postlist.php?Cat=0&Board=UBB32<br />
Im folgenden werden die im Text hervorgehobenen Begriffe genauer erläutert.<br />
Beschleunigung --> Kräfte<br />
Beschränkungen --> Constrains<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │63
C<br />
E<br />
G<br />
K<br />
P<br />
Constraints<br />
Einfach gesagt sind Constraints Gelenke, welche die Bewegungen der Objekte einschränken. Es<br />
gibt Scharniere (ph_hinge), Kugelgelenke (ph_ball), Schubverbindungen (ph_slider) und<br />
Radachsen (ph_wheel).<br />
Events<br />
Wenn eine Kollision stattfindet können sogenannte Events ausgelöst werden. Die solcherart<br />
gestartete Funktion kann nun z.B. mit Kräften auf das Objekt wirken.<br />
Gravitation<br />
Die Schwerkraft / Gewichtskraft. Hier bietet sich ein Zitat aus dem Gamestudiomanual an: „Die<br />
Erdanziehungskraft ist normalerweise 9.81 m/s², 1 Quant ist normalerweise als 1 Zoll = 2.54cm<br />
definiert. Um die Erdanziehungskraft zu simulieren, muss vecGravity.z bei Standardscalierung auf<br />
(-9.81 * 100 / 2.54) = -386 gesetzt werden.“<br />
Gravitygun<br />
Eine immer wieder gern gestellte Frage ist, ob dies denn mit GS möglich sei. Natürlich muß ich da<br />
sagen, es wird etwas Arbeit sein, aber warum sollte es nicht funktionieren Eine Gravitygun bewegt<br />
Objekte (Möbel, Autos, Gegner), also wirkt sie mit Kräften auf andere Objekte. Genau das, was<br />
eine Physikengine kann ;)<br />
Kollision<br />
Aneinanderstoßen einer Entities mit anderen Objekten.<br />
Kollisionserkennung<br />
Die Gamestudiophysik hat den großen Vorteil der polygongenauen Kollisionserkennung.<br />
Da diese sehr rechenaufwändig ist, gibt es noch andere Kollisionshüllen. Kugel-,<br />
Quader- und Zylinderform sind möglich.<br />
Kollisionshülle<br />
Die Hülle um das Physikobjekt welche bestimmt ab wann eine Berührung als Kollision zählt.<br />
Kräfte lokal / global<br />
Kräfte sind z.B. lineare Beschleunigung und Drehmomente. Lokale Kräfte beziehen sich auf die<br />
Koordinaten des Objekts, globale Kräfte beziehen sich auf die Levelkoordinaten.<br />
Polygongenaue Kollisionserkennung --> Kollisionserkennung<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │64
S<br />
W<br />
Schwerpunkt<br />
Der Schwerpunkt beschreibt den „Nullpunkt“ eines Objektes. Wenn Sie das Objekt an ihm<br />
aufhängen würden, könnten Sie es in jede Richtung drehen ohne das es sich in seine alte Position<br />
zurückbewegt. Man bezeichnet diesen Zustand auch als „indifferent“, im Gegensatz zu „labil“<br />
(kopflastig, z. B. wenn man einen Stock auf einem Finger balanciert) und „stabil“ (wenn der Stock<br />
wie ein Pendel hängt). Ein Stehaufmännchen hat einen sehr niedrigen Schwerpunkt, es ist unten<br />
schwerer als oben. Ein Fernsehturm hat einen hohen Schwerpunkt, er ist oben schwerer als unten.<br />
In der Gamestudiophysik ist immer der Mittelpunkt einer Entity der Schwerpunkt. (Nicht sein Origin<br />
/ Nullpunkt in MED/WED !)<br />
Welt<br />
Etwas unglücklich gewählt, beschreibt dieser Begriff die Umgebung, also das Level. Wenn Objekte<br />
also eine Beschränkung mit der „Welt“ eingehen, so ist das als ob sie ein Scharnier in eine Straße<br />
betonieren – die Straße wird sich nicht bewegen, wohl aber das Teil welches Sie am anderen Ende<br />
des Scharniers befestigen.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │65
Inhalt:<br />
Formationsanordnungen in einem <strong>3D</strong> Spiel<br />
von Dieter Meier aka ARAS14<br />
• Vorbereitung des Levels und des Mainskriptes<br />
• Änderung des Mainskriptes<br />
• Kamera in der "camera.wdl"<br />
• Funktion "move_view()"<br />
• Bewegung der Führungsfigur in der "Bewegungs.wdl"<br />
• Funktion "get_target()"<br />
• Funktion "gravity_entity()"<br />
• Funktion "move_entity()"<br />
• Funktion "chef_player_move()"<br />
• Aktion "haupt_player"<br />
• Formation der Entity´s in der "Formation.wdl"<br />
• Aktion "entitys"<br />
• Funktion "entity_formation"<br />
• Funktion "pos1_ent"<br />
Für die unter Ihnen die zu wenig Zeit haben selbst ein Level für das Tutorial zu bauen, habe ich das<br />
Ganze schon vorbereitet. Sie finden unter der unten genannten URL.Adr. ein Beispiellevel mit den<br />
dazugehörigen Skripten. Ich wünsche Ihnen viel Spaß dabei.<br />
http://space<strong>01</strong>.annih.de/DemoFormation.zip<br />
Einführung:<br />
Ich möchte in diesem ersten Tutorial auf die Grundzüge einer Formationsanordung und deren<br />
Bewegung eingehen. Desweiteren werden wir eine isometrische Kamera programmieren und das<br />
Skript für eine Playerbewegung mit Hilfe des Cursors realisieren.<br />
Solche Formations Anordnungen werden hauptsächlich in Strategiespielen Anwendung finden. Es<br />
gibt bestimmt noch anderen Möglichkeiten so etwas zu realisieren. Ich möchte Ihnen eine davon<br />
vorstellen.<br />
Vorbereitung des Levels und des Mainskriptes<br />
Bauen Sie ein Level nach Ihren Wünschen oder verwenden Sie das mitgelieferte, das ich für Sie<br />
schon vorbereitet habe. Generieren Sie von Ihrem Level ein Mainskript. Wenn Sie das Demolevel<br />
verwenden brauchen Sie sich diese Arbeit nicht mehr machen, da ich für Sie das Skript schon<br />
vorbereitet habe.<br />
Änderung des Mainscriptes<br />
Öffnen Sie das Mainskript Ihres Levels und entfernen Sie die Include Dateien nach der<br />
"particle.wdl" Datei. Oder klammern Sie diese wie im unteren Beispiel einfach aus. Diese Dateien<br />
benötigen wir für unsere Formationsanordungen nicht. Die ganzen Änderungen im Mainskript<br />
beziehen sich noch auf die alten Templates. Bei Verwendung der Neuen müssen Sie das Skript<br />
dementsprechend anpassen.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │66
// The INCLUDE keyword can be used to include further WDL files,<br />
// like those in the TEMPLATE subdirectory, with prefabricated actions<br />
include ;<br />
include ;<br />
include ; // must be inserted before doors and weapons<br />
include ; // remove when you need no particles<br />
/*<br />
include ; // remove when you need no doors<br />
include ; // remove when you need no actors<br />
include ; // remove when you need no weapons<br />
include ; // remove when you need no fighting<br />
//include ; // include when doing an adventure<br />
include ; // remove when you need no lens flares<br />
*/<br />
Was Sie noch ausklammern müssen wenn Sie ein neues Skript von Ihrem Level generiert haben<br />
sind die Anweisung "lensflare_start();" und "move_view_cap = 1;". Suchen Sie nach folgenden<br />
Zeilen und klammern Sie diese aus.<br />
// initialize lens flares when edition supports flares<br />
ifdef CAPS_FLARE;<br />
endif;<br />
//lensflare_start();<br />
// use the new 3rd person camera<br />
//move_view_cap = 1;<br />
Schreiben Sie ans Ende der Funktion "main()" noch den Befehl "mouse_toggle();", damit der<br />
Cursor sofort nach Laden des Levels sichtbar wird.<br />
// client_move(); // for a possible multiplayer game<br />
// call further functions here...<br />
mouse_toggle();<br />
}<br />
Als nächstes includieren Sie bitte die zwei Dateien "Bewegung.wdl" und "Formation.wdl". Diese<br />
zwei Dateien werden später unsere Skripte, wie der Name schon sagt, für die Formation und die<br />
Bewegung enthalten.<br />
Sollten Sie die beigestellten Skripte verwenden, müssen Sie im Skript "DemoFormation.wdl"<br />
unbedingt den Verweis auf den Template-Ordner hier:<br />
path "D:\\PROGRAMME\\GSTUDIO6\\template"; auf den Path Ihres Template Ordners<br />
umschreiben.<br />
Kamera in der "camera.wdl"<br />
Die Kamera sollte uns sowohl einen Einblick über das ganze Szenario, als auch eine detailierte<br />
Ansicht bieten, was bedeutet sie sollte auf jeden Fall zoombar sein. Die Veränderung des<br />
Drehwinkels (Pan-Winkel) und des Schwenkwinkels (Tilt-Winkel) sollten wir auch nicht vergessen<br />
um später unsere Einheiten nicht aus den Augen zu verlieren. Die Zoom-, Dreh- und<br />
Schwenkbewegungen der Kamera werden wir im nächsten Tutorial programmieren und ausführlich<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │67
esprechen. In diesem hier werden wir uns mit einer einfachen festen Kamera begnügen. Legen<br />
Sie nun eine WDL-Datei an und nennen sie"camera.wdl". Sie beinhaltet die Steuerung der Kamera.<br />
Beginnen wir mit den Variablen für die Kamera die man benötigt.<br />
var camera_pan = 0; // Winkel der Kamera<br />
var camera_dist = 250; // Abstand zum Player<br />
var camera_z_pos = 800; // Höhe der Kamera<br />
Damit hätten wir schon alle Variablen die wir für die "camera.wdl" benötigen. Was auch noch sehr<br />
hilfreich ist, wäre die Definition aller Funktionen die im dazugehörigen Skript vorkommen. Ich<br />
bevorzuge dies immer, um zu vermeiden das eine Funktion aufgerufen wird, die vorher noch nicht<br />
deklariert worden ist und gehe so einer Engine Fehlermeldung, die daraufhin bestimmt kommen<br />
wird aus dem Weg. Demzufolge schreiben wir nach den Variablen die Definition der Funktionen. In<br />
diesem Fall haben wir nur eine Funktion zum Definieren.<br />
function move_view();<br />
Funktion "move_view()"<br />
Beginnen wir nun mit der Funktion "move_view()". Es umfaßt die eigentlich Bewegung der Kamera.<br />
function move_view()<br />
{<br />
zugehörigen<br />
}<br />
camera.genius = player; // Entity wird unsichtbar wenn die Kamera die<br />
// Grenzen berührt<br />
// Berechnung X-Koord. Kamera<br />
camera.x = player.x + (camera_dist * sin(camera_pan));<br />
// Berechnung Y-Kood. Kamera<br />
camera.y = player.y + (camera_dist * cos(camera_pan));<br />
camera.z = player.z + camera_z_pos; // Berechnung Höhe der Kamera<br />
vec_diff(temp,player.x, camera.x);<br />
vec_to_angle (temp,temp);<br />
camera.pan = temp.pan; // Kamera Pan-Wert<br />
camera.tilt = temp.tilt; // Kamera Tilt-Wert<br />
Die erste Zeile der Funktion bewirkt das der Player unsichtbar wird sobald die Kamera die Grenzen<br />
der Entity berührt. Was in unserem Fall aber nicht auftreten wird. Zeile Zwei und Drei geben die<br />
Berechnung der x- und y-Koordinaten der Kamera bezogen auf die Weltkoordinaten an. Ich will<br />
hier nicht weiter auf diese Winkelberechnungen eingehen, da es hier den Rahmen wohl sprengen<br />
würde. Hierzu gibt es auf der Website von Grimber ein sehr ausführliches Tutorial von "Evelyn<br />
Boo". Nur soviel sei gesagt das wir mit den Angaben in der Klammer "(camera_dist *<br />
sin(camera_pan))" die x-Koordinate unserer Kameradistanz berechnen und mit der Anderen die y-<br />
Koordinate. Die dritte Zeile beinhaltet die Berechnung der Kamerahöhe. In den darauffolgenden<br />
Zeilen wird einmal der Vektor von der Kamera zum Player berechnet und mit "vec_to_angle" der<br />
Winkel dieses Vektors bestimmt. Danach werden die Pan- und Tilt-Winkelder Kamera danach<br />
ausgerichtet. So das erste Skript wäre nun vollendet. Wenden wir uns nun schwierigeren Dingen<br />
zu.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │68
Bewegung der Führungsfigur in der "Bewegungs.wdl"<br />
Öffnen Sie eine weitere WDL-Datei und geben Sie ihr den Namen "Bewegung.wdl". Diese Datei<br />
beinhaltet alle Funktionen die sich mit der Bewegung der Führungsfigur, in diesem Fall der Player-<br />
Entity, befassen.Sollten Ihnen die Funktionen "chef_player_move" bekannt vorkommen ist das<br />
durchaus möglich. Sie bezieht sich im Großen und Ganzen auf das sehr gute diablo-Script aus<br />
einem Aum-Heft. Sie wurde von mir überarbeitet und angepaßt. Beginnen wir wieder mit den<br />
benötigten Variablen.<br />
//// Variablen<br />
var vecfrom;<br />
var vecto;<br />
var my_target;<br />
var ziel_pos;<br />
var force;<br />
var friction;<br />
var dist[3];<br />
var abs_dist[3];<br />
//// Definitionen der Skill´s<br />
define _force, skill36;<br />
define _vz_entity,skill45; // Geschwindigkeit vertikal der Entity<br />
define _v_entity,skill47; // Geschwindigkeit horizontal der Entity<br />
define ziel,skill48; // Freigabe Zielposition 0 = Aus, 1 = An<br />
Als nächstes rufen wir wieder alle Funktionen auf um eine etwaige Fehlermeldung zu umgehen.<br />
function get_target();<br />
function gravity_entity();<br />
function move_entity();<br />
function chef_player_move();<br />
Funktion "get_target()"<br />
Jetzt beginnen wir mit dem ersten Skript. Es wird die Funktion "get_target()" sein. Mit deren Hilfe<br />
wir dem Player durch Anklicken im Level ein Ziel zuweisen.<br />
function get_target() // Zielposition<br />
{<br />
vecfrom.x = mouse_pos.x; // X-Position des Cursors<br />
vecfrom.y = mouse_pos.y; // Y-Position des Cursors<br />
vecfrom.z = 10;<br />
vec_set(vecto,vecfrom);<br />
vec_for_screen(vecfrom,camera); // Vektor bekommt Bildschirmkoordinaten<br />
vecto.z = 5000;<br />
vec_for_screen(vecto,camera); // Vektor bekommt Bildschirmkoordinaten<br />
trace(vecfrom,vecto); // trace von der Kamera auf angeklickten Punkt<br />
vec_set(my_target,target);<br />
vec_set(ziel_pos,target);<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │69
vec_sub(ziel_pos,player.x); // Bestimmung des Richtungsvektors<br />
vec_to_angle(player.pan,ziel_pos); // Drehung der Playerentity in Richtung der<br />
Zielpos.<br />
}<br />
player.tilt = 0;<br />
player.ziel = 1; // Spieler hat Zielposition zugewiesen<br />
Zeile 1-7 beziehen sich auf die Berechnung der x- und y-Position des Cursors und Umrechnung<br />
dieser Koordinaten mit Hilfe des "vec_for_screen" Befehls in einen Vektor. Diese Vektoren<br />
benötigen wir um mit dem "trace" Befehl von einem gedachten Punkt, direkt hinter dem Bildschirm<br />
hinein ins Level zutracen und zwar 5000 Quants hinter den Bildschirm. Der Schnittpunkt mit der<br />
Levelgeometrie wird im "target" Vektor gespeichert und gibt uns somit unseren Zielpunkt an.<br />
Danach bestimmen wir den Richtungsvektor von der Playerposition zur Zielposition mit "vec_sub"<br />
und anschließend drehen wir noch den Player in Richtung der Zielposition mit Hilfe des Befehls<br />
"vec_to_angle".<br />
Funktion "gravity_entity()"<br />
Folgt nun eine Funktion die verhindert das eine Entity aus irgendwelchen unvorhersehbaren<br />
Gründen in der Luft schwebt ohne herunter zufallen. Die "gravity_entity" Funktion.<br />
function gravity_entity()<br />
{<br />
vec_set (temp.x,nullvector);<br />
vec_set(temp,my.x);<br />
temp.z -=4000;<br />
trace_mode = ignore_me + ignore_sprites + use_box + ignore_models;<br />
result = trace(my.x,temp); // Trace auf den Boden<br />
if (result > 5)<br />
{<br />
}<br />
else<br />
{<br />
}<br />
force.z = -5; // Schwerkraft<br />
friction = 0.1;//Reibung<br />
force.z = -0.5 * result; // Boden-Elastizität<br />
friction = 0.5; // Bodenreibung<br />
my._vz_entity = time * force.z + max(1-time * friction,0) * my._vz_entity; //<br />
Geschw. vert.<br />
}<br />
abs_dist.z = time * my._vz_entity; // Distanz vertikal<br />
Die erste Zeile verwenden wir dazu um den Vektor "temp" zu löschen, also alle Werte auf Null zu<br />
setzen. Danach überschreiben wir den Vektor mit den Koordinaten der Entity. Nachdem wir der<br />
"temp.z" Variablen einen Wert 4000 Quants unter der Entity zugewiesen haben, führen wir einen<br />
"trace" von der EntityPosition zu einem Punkt 4000 Quants darunter durch. Stößt der Trace auf ein<br />
Hindernis wird diese Entfernung in der Variablen "result" gespeichert und dient uns somit zur<br />
weiteren Berechnung. Ist das Ergebnis der Result-Variablen größer als 5 Quants setzen wir die<br />
Schwerkraft auf den Wert -5 und die Reibung, da wir uns ja folglich in der Luft befinden, auf einen<br />
sehr niedrigen Wert hier 0.1. Sollte das Gegenteil der Fall sein und unser "result" ist kleiner als 5<br />
oder hat gar einen negativen Wert, was bedeuten würde, daß die Entity schon in den Boden<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │70
eingedrungen ist, kommt unsere Formel "force.z = -0.5 * result" zum Einsatz. Sollte das Ergebnis<br />
unserer Messung negativ sein wird mit dieser Formel der "force.z" Wert, also die Bewegung<br />
vertikal, wieder auf einen positiven Wert gebracht, da ja "Minus * Minus" bekanntlich "Plus" ist.<br />
Das bedeutet unsere Entity macht wieder eine Bewegung noch Oben aus dem Boden heraus . Die<br />
letzten Zeilen bewirken einen stetigen Anstieg dieser Geschwindigkeit und deren Berechnung der<br />
vertikalen Distanz der Entity.<br />
Funktion "move_entity()"<br />
Bewegungsfunktion um Entity mit Hilfe des relativen- und absoluten Vectors zu bewegen.<br />
function move_entity()<br />
{<br />
my._v_entity = time * force.x + max(1-time*0.7,0) * my._v_entity;<br />
//Geschwindigkeit vor.<br />
}<br />
dist.x = time * my._v_entity; //Distanz vorwärts<br />
dist.y = 0;<br />
dist.z = 0;<br />
move_mode = ignore_passable + glide;<br />
result = ent_move(dist,abs_dist); // Bewege den Player<br />
Die ersten beiden Zeilen beziehen sich auf die vorwärts Bewegung der Entity, bezogen auf ihren<br />
"force.x"Wert. Hierfür benötigen wir nur die Variable "dist.x" da wir mit der Entity nur eine relative<br />
Bewegung ausführen, bezogen auf die Richtungswinkel der Entity. Somit kann man die Werte<br />
"dist.y" und "dist.z"auf Null setzen. Danach geben wir noch den "move_mode" an und bewegen<br />
dann die Entity mit "ent_move".<br />
Funktion "chef_player_move()"<br />
In dieser Funktion finden wir die "vec_dist" Berechnung zum Zielpunkt und die Aktivierung der<br />
Funktionen "gravity_entity" und "move_entity".<br />
function chef_player_move()<br />
{<br />
while(1)<br />
{<br />
}<br />
if(player.ziel == 1) // Player hat eine Zielposition<br />
{<br />
// Nur Bewegung wenn Distanz zum Ziel größer 35 Quants ist<br />
while(vec_dist(player.x,my_target.x) > 35)<br />
{<br />
force = my._force * 12; // Geschwindigkeitsberechnung des Player´s<br />
// Vorzeitiger Abbruch der Bewegung beim drücken der rechten Maustaste<br />
}<br />
if(mouse_right == 1){wait(1);player.ziel = 0;break;}<br />
gravity_entity(); // Sprung zur Gravitationsfunktion der Entity<br />
move_entity(); // Sprung zur Bewegungsfunktion der Entity<br />
wait(1);<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │71
}<br />
wait(1);<br />
}<br />
Diese Funktion wird stetig ausgeführt. Danach wird mit "if(player == 1)" überprüft ob der Player<br />
eine Zielposition hat. In diesem Fall wird eine zweite Schleife gestartet, die testet ob der Player<br />
mehr als 35 Quants vom Zielpunkt entfernt ist. Trifft dies zu wird die Geschwindigkeit des Players<br />
gesetzt und die Funktionen "gravity_entity" und "move_entity" ausgeführt. Mit Hilfe der Zeile<br />
"if(mouse_right == 1)" wird der Zielbefehl durch drücken der rechten Maustaste wieder<br />
abgebrochen. Sollte die Entfernung weniger als 35 Quants betragen wird die Schleife beendet und<br />
erst wieder gestartet wenn der Abstand zwischen Ziel und Player größer geworden ist.<br />
Aktion "haupt_player"<br />
Diese Aktion teilen wir in WED unserer Entity zu. Sie wird unseren Player darstellen. Alle anderen<br />
Entities der Formation werden sich um diese Entity sammeln und ihr folgen.<br />
action haupt_player<br />
{<br />
}<br />
player = me;<br />
my._force = 1; // Grundgeschwindigkeit des Players<br />
chef_player_move();<br />
while(1)<br />
{<br />
}<br />
move_view(); // Aktivierung der Kamerafunktion<br />
wait(1);<br />
Als Erstes weisen wir dem "me"-Pointer den "player"-Pointer zu. Jetzt können wir diesen Pointer in<br />
allen anderen Funktionen verwenden, ohne eine "Empty Pointer" Fehlermeldung zu bekommen.<br />
Danach geben wir der Entity eine Grundgeschwindigkeit mit "my._force = 1" und starten<br />
anschließend unsere "chef_player_move()" Funktion. Es wird noch eine Schleife gestartet in der wir<br />
die Kamerafunktion aufrufen. Damit stellen wir sicher das uns die Kamera auch folgt, da diese<br />
Funktion immer durchlaufen wird. Wie Sie bestimmt schon bemerkt haben wird unsere<br />
"get_target" Funktion noch nirgends aufgerufen. Sie soll ja nur beim Drücken der linken Maustaste<br />
aktiviert werden, darum realisieren wir das Ganze mit einem einfachen Befehl.<br />
// Linke Maustaste aktiviert die Zielposition für den Player<br />
On_mouse_left get_target;<br />
So damit hätten wir schon die ersten Teile unseres Tutorials geschafft, lassen Sie uns jetzt zum<br />
eigentlichen Hauptteil kommen. Die Skripte für die Entities die unserem Player in einer Formation<br />
folgen sollen.<br />
Formation der Entities in der "Formation.wdl"<br />
Was brauchen wir dazu? Einmal natürlich Entities die sich immer in der gleichen Formation hinter<br />
dem Player sammeln. Wir benötigen also bestimmte Fix-Punkte die wir am Anfang festlegen. Das<br />
Ganze realisieren wir über ein Array aus dem wir diese Parameter auslesen. Diese Parameter<br />
werden immer unsere Zielposition sein. Um diese Position festzulegen benötigen wir noch eine<br />
Hilfsentity,in der wir diese Parameter speichern und die sich immer mit dem Player bewegt. Auf<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │72
diese Weise können wir später der Hilfsentity diverse Formationen vorgeben. Nun brauchen wir nur<br />
noch unsere Folgeentity, ich nenne sie einmal unseren Soldaten. Unser Soldat richtet sich nach der<br />
Hilfsentity aus und läuft auf sie zu. Nun wollen wir das Ganze einmal in ein hoffentlich lauffähiges<br />
Skript umwandeln.Beginnen wir wieder mit unseren Variablen und Definitionen.<br />
//// Target MDL<br />
string mace_mdl = ; // Hilfsentity<br />
//// Variablen<br />
var i; // Array Index<br />
var position[8] = -100,-100,-100,100,-200,-200,-200,200; // Array<br />
//// Definitionen der Skill´s<br />
define x_pos,skill30; // x_pos der Hilfsentity<br />
define y_pos,skill31; // y_pos der Hilfsentity<br />
define z_pos,skill32; // z_pos der Hilfsentity<br />
define pos_target,skill36; // Zielposition für Soldaten<br />
//// Funktionen<br />
Aktion "entitys"<br />
function entity_formation(); // Steuerung des Soldaten<br />
function pos1_ent(); // Steuerung der Hilfsentity<br />
Als Erstes beginnen wir mit der Aktion "entitys", hier wird die Funktion der Soldaten aufgerufen. Es<br />
bedarf wohl nicht mehr Erklärung.<br />
action entitys<br />
{<br />
}<br />
my.entity_force = 15; // Festlegung der Geschwindigkeit<br />
entity_formation(); // Aufruf der Funktion<br />
Funktion "entity_formation"<br />
Nun das Skript für die Steuerung des Soldaten. Ich werde Ihnen zuerst das Skript vorstellen und<br />
Ihnen anschließend die Erklärung für einzelne Befehle des Skriptes liefern.<br />
function entity_formation()<br />
{<br />
while(player == null){wait(1);} // Erst weiter wenn Player erstellt ist<br />
my.x_pos = player.x + position[i]; // x_pos der Hilfsentity<br />
i += 1;<br />
my.y_pos = player.y + position[i]; // y_pos der Hilfsentity<br />
i += 1;<br />
you = ent_create(mace_mdl,my.x_pos,pos1_ent); // Hilfsenetity<br />
my.skill40 = handle (you); // you-Pointer abspeichern<br />
while (1)<br />
{<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │73
}<br />
you = ptr_for_handle(my.skill40); // you-Pointer zurück holen<br />
vec_set(temp,you.x);<br />
trace_mode = ignore_models + ignore_sprites + ignore_passents + use_box;<br />
trace(my.x,temp); // Trace zur Hilfsentity<br />
vec_set(my.pos_target,target);<br />
vec_diff(temp,target,my.x); // Zielvector ermitteln<br />
vec_to_angle(my.pan,temp); // Soldat zum Ziel drehen<br />
my.tilt = 0;<br />
if(vec_dist(my.x,my.pos_target) > 35)<br />
}<br />
wait(1);<br />
}<br />
{<br />
force.x = 5; // Geschwindigkeit des Soldaten<br />
vec_diff(temp,my.pos_target,my.x);<br />
vec_to_angle(my.pan,temp); // Zur Zielposition drehen<br />
my.tilt = 0;<br />
gravity_entity(); // Gravitation<br />
move_entity(); // Bewegen des Soldaten<br />
Beginnen wir mit der ersten Zeile "while(player == null){wait(1);}", wir warten so lange bis der<br />
Player erstellt ist, um eine "Empty Pointer" Fehlermeldung zu umgehen. In den nächsten vier<br />
Zeilen werden die Parameter aus dem Array ausgelesen und in den Variablen "my.x_pos" und<br />
"my.y_pos" gespeichert. Als nächstes erstellen wir die Hilfsentity an den vorher ausgelesenen<br />
Positionen und weisen ihr den "you" Pointer zu. Das Ganze speichern wir mit Hilfe eines "handle" in<br />
dem "skill.40" unseres Soldaten. Den "you" Pointer benötigen wir später noch, um immer zu<br />
wissen welche Hilfsentity zu welchem Soldaten gehört. Nun befinden wir uns wieder in einer<br />
Schleife, dort wird der "you" Pointer ausgelesen und mit seiner Hilfe ein "trace" vom Soldaten zur<br />
Hilfsentity ausgeführt. Das Ziel wird im "target" Vektor gespeichert und anschließend der Soldat in<br />
Richtung des Ziels gedreht. Nun springen wir wieder in eine "if" Anweisung, in der wird der<br />
Abstand zu unseren Ziel berechnet. Ist die Entfernung größer als 35 Quants wird die<br />
Geschwindigkeit des Soldaten auf "5" gesetzt, nochmals der Richtungsvektor berechnet und der<br />
Soldat gedreht. Dann wird die Gravitation und die Bewegung des Soldaten aufgerufen.<br />
Funktion "pos1_ent"<br />
In dieser Funktion befinden sich alle Bewegungsbefehle der Hilfsentity.<br />
function pos1_ent()<br />
{<br />
my.passable = on; // Mache mich passierbar<br />
my.invisible = on; // Mache mich unsichtbar<br />
vec_diff(temp,my.x,player.x); // Abstandsvektor zum Player<br />
vec_set(my.x_pos,temp);<br />
while(1)<br />
{<br />
vec_set(temp,my.x_pos);<br />
vec_add(temp,player.x); // Abstandsvektor und Playervektor addieren<br />
vec_set(my.x,temp); // Hilfsentity zum Player ausrichten<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │74
}<br />
}<br />
wait(1);<br />
Als Erstes wird die Hilfsentity passierbar und unsichtbar geschalten, daß sie für den Soldaten kein<br />
Hindernis darstellt. Danach wird der Abstandsvektor zum Player berechnet, daß die Entity immer in<br />
der gleichen Entfernung zum Player steht. In der "while" Schleife wird stetig dieser Abstand zu den<br />
Koordinaten des Players addiert und die Hilfsentity dementsprechend bewegt. Rufen Sie nun Ihr<br />
Level auf und setzen Sie mehrere Entities in Ihr Level. Der Führungsentity weisen Sie die Aktion<br />
"haupt_player" zu und den Entities die ihm folgen sollen die Aktion "entitys". Sollten Sie alles<br />
richtig gemacht haben müßte es so aussehen wie auf diesem Screenshot. Die Soldaten laufen zum<br />
Player und stellen sich in Keilformation hinter ihm auf.<br />
So, wir sind nun am Ende des Tutorial angekommen. Ich hoffe es hat Ihnen etwas geholfen. Wenn<br />
Sie jetzt sagen,"aber da fehlt ja noch Einiges.", dann haben Sie recht. In einem der nächsten<br />
Tutorials werden wir die Kamera ausbauen. Sie wird zoom-, dreh- und schwenkbar werden,<br />
desweiteren werden sich die Soldaten dann auch nach dem Pan-Winkel des Player bewegen und<br />
nicht wie hier nur geradlinig. Wir werden auch versuchen mehrere Formationen anzulegen und<br />
abzuspeichern und wieder aufzurufen. Auch die Animation und Kollisionsüberwachung fehlt noch,<br />
genauso werden wir auch noch ein Sprite für die Zielpositionen einbinden. Aber wie schon gesagt<br />
das wird noch in späteren Tutorials kommen.<br />
Für Fragen oder Kommentare stehe ich gerne zur Verfügung.<br />
Unter<br />
aras_game@chefmail.de<br />
können Sie alles in Bezug auf dieses Tutorial loswerden.<br />
Dann bis zum nächsten Tutorial Ihr<br />
Dieter "ARAS14" Meier<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │75
Interview... mit Sebastian Leopold (Xexes)<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Im Rahmen eines der Projekte, an denen Du beteiligt warst, hast Du einige Grafik-Funktionen<br />
entwickelt. Was war der Hauptgrund für diese Arbeit? Was fehlte euch an A6?<br />
Sebastian Leopold:<br />
Durch die Einschränkung auf die Commercial Edition von A6 konnten wir Features wie „render-totexture“,<br />
welches für unser Projekt unerlässlich war, nicht nutzen. Die Grafikfunktionen wurden<br />
also aus der Not geboren. Als der Grundstein dafür dann einmal gelegt war, wollten wir auch noch<br />
andere Features verwenden, die wir selbst einbauen mussten, da <strong>3D</strong>GS keine Möglichkeiten dafür<br />
bereitstellte.<br />
Dazu zählen beispielsweise Spiegel und Post-Processing.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Kannst Du uns eine kleine Übersicht von Funktionen geben, die Du in diesem Rahmen eingebaut<br />
hast?<br />
Sebastian Leopold:<br />
Natürlich:<br />
- Spiegel<br />
- Wasser-Renderer<br />
- Sky-Renderer<br />
- A6 Mesh-Renderer<br />
- Texturverwaltung mit HDR-Unterstützung<br />
- PostProcessing Effekte<br />
- Softshadows<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Das klingt sehr interessant. Kannst Du einige Worte zum Sky- und Mesh-Renderer schreiben?<br />
Sebastian Leopold:<br />
Der Sky-Renderer ist recht einfach aufgebaut. Es ist eigentlich nur eine Klasse für eine Skybox, die<br />
sich in der Render-Liste befindet. Man braucht jedoch nicht für jeden Sky ein Modell und auch<br />
keine unnötigen Definitionen eines Skies in C-Script. Es reicht aus, einfach eine Textur zu laden<br />
und sie zuzuweisen. Das ganze geht in 6 Zeilen Code.<br />
Der Mesh-Renderer verwendet das Sylex 3.0 Effektsystem und zeichnet <strong>3D</strong>GS Modelle bzw.<br />
Entities. Dadurch wird es möglich Render-Targets sehr schnell auf Modelle zu transferieren<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Du hast das Sylex 3.0 in der aktuellen Version mit Quellcode zur Verfügung gestellt und gut<br />
dokumentiert. Die User waren sehr beeindruckt. Willst Du irgendwann an dem System weiter<br />
arbeiten oder wirst Du nur hin und wieder mal schauen, was die Nutzer damit anstellen?<br />
Sebastian Leopold:<br />
Ich habe es mit Sourcecode veröffentlicht damit die User es selbst weiter entwickeln können. Ich<br />
selbst werde nichts mehr daran machen.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Gibt es schon weitere Pläne in Sachen Spiele-Entwicklung bei Dir?<br />
Sebastian Leopold:<br />
Naja - aufgeben werde ich nicht. Es gibt aber auch noch keine konkreten Pläne. Ich habe aber<br />
mehrere Angebote von Entwicklerteams, dort als Programmierer zu arbeiten. Vielleicht entwickle<br />
ich aber auch eine eigene <strong>3D</strong>-Engine.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │76
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Du warst mit Sylex sehr erfolgreich. Warum setzt Du dort nicht an und erstellst ein kommerzielles<br />
Plugin? Im Forum gab es einige Stimmen, die bereit wären, dafür zu zahlen.<br />
Sebastian Leopold:<br />
Ich möchte eigentlich nicht mehr mit A6 arbeiten, da mir die Engine zu viele Einschnitte macht.<br />
Außerdem ist es einfacher, eine Engine von Grund auf zu entwickeln, als ein Plugin für eine bereits<br />
Bestehende zu erstellen. Wenn ich eine neue Engine programmiere, soll sie auf RTS-Spiele<br />
optimiert werden.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Ja, das können sicher einige Leute verstehen. Dennoch wird die Community leider ein sehr<br />
talentiertes Mitglied weniger haben.<br />
Gibt es Tipps, die Du den Lesern geben kannst, bezüglich deren Projekte und Team-Management,<br />
sodass sie aus euren Erfahrungen und Fehlern lernen können?<br />
Sebastian Leopold:<br />
Es sollte immer festgelegt sein, welches Aufgabengebiet welches Teammitglied hat. Des Weiteren<br />
halte ich es für sehr sehr wichtig, sich zuerst eine funktionierende Testumgebung aufzubauen. In<br />
meinen Projekten war ich meistens der einzige Programmierer. Oft kam es vor, dass Leveldesigner<br />
versucht haben, die Skripte nach Ihren Wünschen selbst zu verändern. Das bringt aber nichts. Ein<br />
zentrales Portal für alle Mitglieder, wie ein Forum, ist also Pflicht.<br />
Davon abgesehen kann ich eigentlich nur raten, dass man ein Quellenverzeichnis führt, in welchem<br />
man auflistet, wo man welche Textur her hat. Einheitliche Namensgebungen erleichtern die Arbeit<br />
und zum Thema Team-Management wird noch etwas kommen, da dürft ihr euch mal überraschen<br />
lassen.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Wirst Du der Community noch eine Weile erhalten bleiben?<br />
Sebastian Leopold:<br />
Na sicher, Unkraut vergeht nicht!<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Vielen Dank für das Interview.<br />
Sebastian Leopold:<br />
Ich Danke !<br />
Das Interview wurde von Frank Geppert im Auftrag des <strong>3D</strong>GS-<strong>Magazin</strong>s durchgeführt.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │77
Interview... Martin (SFMAT4) und Timo (TripleX)<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
An welchen Projekten arbeitet Ihr zur Zeit?<br />
zu ihren derzeitigen Projekten<br />
Martin:<br />
Defender Competition Demo Level und am Material Editor. Ich helfe Harry Potter noch bei seinem<br />
Mittelalter-Projekt<br />
Defender Competition wird ein Frist Person Ego Shooter ähnlich der UT-Serie. Mit dem Unterschied,<br />
das hier nicht Mann gegen Mann antritt, sondern der Spieler ein Level gegen anströmende<br />
Gegnerhorden verteidigen muss. Dabei kann er Geschütze aufstellen (und bedienen) oder Söldner<br />
anheuern.<br />
Der Material Editor (oder wahrscheinlich GameEdit) wird ein Tool mit dessen Hilfe der User in der<br />
Engine weit reichende Änderungen vornehmen kann.<br />
Bei beiden arbeite ich eng mit TripleX zusammen, wobei Trip fast alle Skript-technischen Aufgaben<br />
übernimmt. Ich erstelle sämtliche Grafiken und Effekte.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Wenn ich das richtig verstehe, dann ist doch der Material-Editor schon weit mehr als nur ein<br />
Material-Editor. Wie weit wollt ihr mit dem Tool gehen?<br />
Martin:<br />
Ich denke wir werden ihn soweit ausbauen, wie es für interne Änderungen im Spiel sinnvoll ist,<br />
und ergänzen das, was von den 3dgs Usern gewünscht wird.<br />
Timo:<br />
Da ich die letzten 2 Monate sehr intensiv am Map Material-Editor programmiert habe, ist nun<br />
tatsächlich schon sehr viel mehr möglich, als ursprünglich geplant war. Allerdings müssen wir auch<br />
Grenzen setzen, da man theoretisch fast unbegrenzt viele Features einbauen könnte.<br />
Man wird z.B. Materialien, Actions und Positionen von Entities direkt im Spiel ändern können.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Kehren wir wieder zu Eurem Spiel-Projekt zurück. Wie weit seid Ihr damit fortgeschritten?<br />
Martin:<br />
Das ist schwer zu sagen, weil mir immer wieder was neues einfällt. Ich denke, dass ich mit dem<br />
Level und den Grafiken zu 90% fertig bin und skript-technisch hinkt Trip leider noch hinterher.<br />
Daher würde ich das auf maximal 40% einstufen.<br />
Timo:<br />
Tatsächlich hinke ich skript-technisch noch etwas hinterher, was größtenteils daran liegt, dass ich<br />
parallel am Material Editor arbeite, um Martin eine möglichst mächtiges Tool zum Map optimieren<br />
geben zu können. Allerdings werde ich mich nun mehr auf das Spiel Konzentrieren.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Habt Ihr schon Charakter-Modelle für die Spieler und Gegner?<br />
Martin:<br />
Nein, leider noch nicht.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Was könnt Ihr aus Eurer Erfahrung zur A6-Engine sagen? Wie gut passt sie zu Eurem Projekt?<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │78
Martin:<br />
Die A6 Engine ist ausreichend für das Projekt. Allerdings hatte ich vor einiger Zeit massive<br />
Probleme, was die Lightmap-Grenze anbelangte. Leider ist die Shader-Unterstützung nicht so, wie<br />
man es sich wünscht. Insgesamt gesehen bewege ich mich am Limit der Engine. Mit verschiedenen<br />
Tricks lassen sich aber immer noch irgendwo einige FPS heraus kitzeln.<br />
Timo:<br />
Wie Martin schon gesagt hat, gehen wir mit unserem Projekt sehr an die Grenzen der A6-Engine.<br />
Aber das ist ja auch unser Ziel: Ein <strong>3D</strong> Gamestudio Projekt erschaffen, das das volle Potenzial der<br />
Engine zeigt.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Wie lange arbeitet Ihr schon an dem Projekt?<br />
Martin:<br />
Seit bestimmt 3 Jahren. Allerdings habe ich lange Pausen dazwischen eingelegt und schon fertige<br />
Dinge durch neue bessere ersetzt.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Woher bekommt Ihr Eure guten Texturen?<br />
Martin:<br />
Die erstelle ich alle selbst mit Photoshop und meiner Digicam.<br />
Einige Grundstrukturen habe ich von Noctua-Graphics.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Martin, Welche Software-Tools nutzt Du, welche Tipps kannst Du den Lesern geben?<br />
Martin:<br />
Ich benutze 3dsmax und Photoshop.<br />
Bei 3dsmax hat es sich bewährt die dort erstellten Modelle über 3ds zu exportieren und im MED zu<br />
importieren. Damit kann man sogar komplex texturierte Objekte verlustfrei in die A6 Engine<br />
bekommen.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Martin, ich habe gehört, Du arbeitest an einer für die Leser vielleicht sehr interessanten Webseite.<br />
Kannst Du uns dazu einiges sagen?<br />
Martin:<br />
Ich versuche eine Shader-Bibliothek aufzubauen.<br />
Möglichst viele A6 Shader sollen mit umfangreichen Erläuterungen und Beispielen dort verfügbar<br />
sein.<br />
<strong>3D</strong>GS-<strong>Magazin</strong>:<br />
Vielen Dank für das nette Gespräch.<br />
Das Interview wurde im Auftrag des <strong>3D</strong>GS-<strong>Magazin</strong>s von Frank Geppert durchgeführt.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │79
Game-Edit<br />
Ein Preview des Map-Editors<br />
Hallo,<br />
Im folgendem werde Ich Ihnen, den sich im Moment in Entwicklung befindenden, Map-Editor<br />
Game-Edit vorstellen.<br />
Die Idee hinter Game-Edit<br />
Die grundsätzliche Idee hinter dem Tool Game-Edit ist einfach: Der User implementiert das Tool<br />
mit 2-3 simplen Befehle in sein Projekt und kann ab nun über einen einfachen Knopfdruck den<br />
Map-Editor öffnen, in dem das Level und vorhandene Materials bearbeitet werden können. Die<br />
Änderungen sind alle Ingame sichtbar (z.B. das verschieben eines Entities um eine bestimmte<br />
Quantanzahl).<br />
Das alleine wäre noch nicht sonderlich spannend wenn Game-Edit nicht alle veränderten Daten<br />
direkt in der Map-/Wdl Datei speichern würde. Falls nötig wird sogar am Ende des Spiels gebuildet.<br />
Auf Deutsch heißt das, dass alle Änderungen die Sie in Game-Edit vornehmen auch über einen<br />
Neustart hinweg gespeichert bleiben.<br />
Anwendungsbeispiele<br />
„Das ist ja schön und gut nur wozu brauche ich das, ich habe doch WED“ werden Sie sich vielleicht<br />
fragen. Daher werde ich Ihnen nun ein Anwendungsbeispiel zeigen.<br />
Feature List<br />
• Sie starten Ihr Projekt (im folgenden Beispiel einen Ego-Shooter) und spielen es ganz<br />
normal. Beim kämpfen durch Ihr Level fällt Ihnen auf, dass der Spieler an einer bestimmten<br />
Stelle dringend noch eine Waffe/Ammopack/Lebenspack bräuchte. Deshalb öffnen Sie<br />
innerhalb des Spiels über einen Tastendruck den Map-Editor und platzieren ein neues<br />
Ammopack (u.ä.) im Level. Nun verlassen Sie Game-Edit gleich wieder und können ganz<br />
normal – mit dem neuen Ammopack weiterspielen. Dadurch ist es gut möglich, ein gutes<br />
Fine-Balancing zu erschaffen.<br />
• Grundsätzlich ähneln sich alle Anwendungsbeispiele sehr (bestimmte Einstellungen werden<br />
eben verändert), daher werde ich hier auf weitere verzichten.<br />
Schon eingebaut:<br />
• Echtzeit-Bearbeiten der Einstellungen von Modellen / Sprites und Terrains<br />
• Echtzeit-Bearbeiten der Einstellung von Materials. Unter anderem ist es auch möglich<br />
Shader Code usw. zu bearbeiten.<br />
• Ein automatischer Material Loader, der Ihren Code nach Material Definitionen durchsucht<br />
und falls nötig überschreibt.<br />
• Erschaffen, Entfernen und Morphen von Materials und Entities<br />
• Verändern der Map-Properties (Nebel u. Ambientfarben, Clipping Einstellungen)<br />
• Eine Objektliste, die alle Objekte die in der Map vorhanden sind anzeigt. Dadurch ist es<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │80
möglich mehrere Entities auf einmal zu verändern.<br />
• Eine einfach gehaltenes GUI.<br />
Geplant:<br />
Screenshots<br />
• Echtzeit-Deformierung von Terrains und Modellen<br />
• Echtzeit-Zeichnen auf Skins von Modellen und Terrains.<br />
• Ein umfangreiches Statistikfenster mit Graphen u.v.m.<br />
• Mehrfach-Entitie Erschaffung, zum erstellen von mehreren Entities auf einmal (Wälder usw.)<br />
In diesem Kapitel gibt es noch einige Screenshots des Editors. Zu beachten ist, dass dies alles work<br />
in progress (wip) Features sind. D.h. das der Editor noch in einer frühen Phase der Entwicklung ist<br />
und sich daher noch einiges ändern und vermutlich auch verbessern wird.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │81
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │82
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │83
Daten und Fakten<br />
Programmierer: Timo Stark aka TripleX<br />
2D Designer: Martin Teichmann aka SFMAT4<br />
633 KB und 16422 Zeilen Code<br />
2 Monate Entwicklungszeit<br />
Geschätzt, weitere 2 Monate Entwicklungszeit nötig.<br />
Einbindung in Ihr Projekt<br />
Das einbinden von Game-Edit in Ihr entsprechendes Projekt wird sehr einfach vonstatten gehen. Es<br />
ist auch für absolute Nicht-programmierer schaffbar.<br />
Ein Beispiel der Einbindung:<br />
material_init("Maps"); //Alle Map Daten liegen in dem Unterordner "Maps"<br />
Material_Bind_WDL("Data\\Scripts\\materials.wdl"); //Teilt der Engine mit, dass in der<br />
Materials.wdl Materialien liegen<br />
Material_Bind_WDL("main_op.wdl"); //Teilt der Engine mit, dass in der Main_op.wdl Materialen<br />
liegen<br />
//bis zu 40 WDL Dateien sind so einbindbar!<br />
Ich hoffe dass diese Preview Ihnen einen Einblick in Game-Edit gewährt hat.<br />
<strong>3D</strong> <strong>GAMESTUDIO</strong>-<strong>Magazin</strong> ■ <strong>Ausgabe</strong> <strong>01</strong> | <strong>Oktober</strong> 2005 │84