Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo an alle,
um zu zeigen, dass es im Moment ganz gut voran kommt, wollt ich mal kurz wieder mal ein kleines Update präsentieren. Da ich gerade gesehen habe, dass es bisher extrem wenig Screenshots im Thread gibt, gibt es diesmal auch mal wieder welche.
Wie letztes mal bereits geschrieben, hab ich zwischendurch immer mal wieder per Stift und Papier die erste Map erstellt. Zwar ist das alles relativ grob gewesen, jedoch hab ich nicht all zu viel Freiraum offen gelassen was der Produktivität hoffentlich positiv entgegenkommt.
Die erste Map wird in einem Industriegebiet in einer Lagerhalle spielen. Im Erdgeschoss des Gebäudes werden sich die Security-Büros, die Server sowie die Alarmanlage, der Eingangsbereich und zwei große Lagerhallen befinden. Im ersten Obergeschoss werden sich normale Büros und weitere Zugänge zu den Lagerhallen vorhanden sein. Im dritten und letzten Stock befindet sich das Büro des Chefs sowie das Archiv.
Von der Aufgabe her wird es so sein, dass der Dieb unter anderem Firmenunterlagen oder Bargeld stehlen muss. Wo genau sich die Einstiegsbereiche für den Dieb befinden, hab ich noch nicht genau festgelegt - jedoch hab ich dafür genügend Möglichkeiten. Die Ziele werden am Anfang per Zufall verteilt, so dass nicht immer alles gleich ist. Vom Design her habe ich Versucht, immer mehrere Wege für jedes Ziel zur Verfügung zu stellen.
In der letzten Woche hab ich jedenfalls angefangen, die gezeichnete Map zu realisieren. Angefangen habe ich die erste von zwei Lagerhallen, die zu ca. 70 bis 80% fertig ist. Um euch ein etwas besseres Bild zu ermöglichen, hab ich unten die aktuellen Screenshots aus dem Editor angehängt. Bei den Screenshots hab ich HDR, Blooming und Depth-of-Field aktiviert. Bisher besteht der Raum aus ca. 35.000 Dreiecken und 8 Texturen
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo erstmal,
Script-Anbindung Ich habe mal wieder die Script-Anbindung etwas erweitert. Nun kann ich in der Script-Engine eigene Trigger-Klassen erstellen. Jede Trigger-Klasse hat als Basis eine sehr Basis-Trigger-Klasse - hier mal der Auszug aus der Script-Engine-Klasse
Code:
type
TGETrigger =class(TGEScriptObject)
private
FActive:boolean;
public
procedure Trigger;virtual;
property Active:booleanread FActive;
end;
implementation
procedure TGETrigger.Trigger;
begin
Self.FActive:=notSelf.FActive;
end;
Im Programm selbst erstelle ich dann die im Map-Script alle abgeleitete Klassen von TGETrigger, die ich dann einfach durch eine Funktion aufrufen kann. Natürlich kann ich die Trigger-Klassen auch aus der Script-Engine heraus aufrufen. Dies wird dann auch später so sein. Es wird dann z.B. eine globale Alarm-Trigger-Klasse geben, die dann einzelne Untertrigger aufrufen wird. Ein Untertrigger wird dann visuelle Alarm-Trigger auslösen (z.B. blinkendes Licht), eine andere Klasse wird einen akustischen Sound abspielen, usw.
Weiter bin ich zwar noch nicht gekommen, aber soll es ja nicht überstürzen
Deferred Shading diesmal hab ich wieder etwas technologisches zu Berichten: ich habe den Render-Pfad der Engine erweitert - nun ist auch Deferred Shading kein Problem mehr *freu*. Bisher hab ich den Renderer noch nicht optimiert, da ich mit dem Deferred-Renderer den gleichen Grafikoutput wie mit dem Forward Renderer erreichen wollte - was ich gerade erst geschafft habe
Der Deferred Renderer arbeitet mit 4 Texture-Units mit jeweils 32 Bit Pixelgröße. Dabei werden in den Texturen die Diffuse-Farbe, die Flächennormale, eine Material-ID, Specular und Shinines-Werte sowie Glow-Werte gespeichert. Dank der Trennung von Geometrie und Beleuchtung konnte ich meine Shader übersichtlicher und optimierter schreiben. Da ich weiterhin den Forward-Renderer unterstütze, habe ich die Shader natürlich nicht erstetzt sondern in einem anderen Ordner gespeichert. In der Engine selbst musste ich für den Deferred Renderer nicht viel hinzufügen: ich musste zwei RenderPass-Typen (Geometry und Lighting) hinzufügen, den einzelnen Lichtern noch Shadern verpassen (war bisher material-gesteuert) sowie einen Renderer für die Lichter schreiben. Die meiste Zeit ging für die Shader drauf, an der Engine selbst musste ich nur 3 Tage basteln.
Nun mal zu den Performance-Tests: getestet habe ich mit dem bereits bekannten Model aus meinem ersten Model-Importer-Test. Da dieses Model sehr viele Dreiecke hat, war es für den Test ideal. Getestet hab ich das ganze mit 5 Spotlights und einem Point-Light - jeweils mit dynamischen Schatten und einer Spot-Textur. Alle Post-Scene-Effekts waren abgeschaltet und die Auflösung war auf 800x500 gesetzt. Als Grafikkarte kam wieder meine Laptop-ATI-Radeon 3470 zum Einsatz.
Mit dem Forward-Renderer hab ich beim komplett sichtbaren Model 4 bis 5 Frames pro Sekunde. Mit dem (noch nicht optimierten !!!) Deferred-Renderer hab ich 20 bis 24 Frames pro Sekunde. Durch den Deferred-Renderer habe ich weniger Artefakte, jedoch sind die Soft-Shadows noch nicht so schön - aber das versuch ich noch zu verbessern.
Insgesamt bin mit dem Deferred-Ansatz sehr zufrieden - allein schon der extreme Performance-Schub ist enorm. Alpha-Blending wollte ich dann als Forward-Pass hinzufügen, jedoch hab ich das noch nicht ausprobiert. Jedoch ergeben sich durch den G-Buffer extrem viele neue Post-Effekt-Möglichkeiten: Edge-Antialiasing, Screen-Motion-Blur, ... und natürlich die bisher bereits eingebauten Effekte wie Depth of Field.
Screenshots gibts diesmal leider keine, aber es war ja auch eher eine technologische Änderung und keine Grafikverbesserung.
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo
Ich meld mich mal wieder mit einem Status-Update. Diesmal gibt es wieder einiges zu berichten.
Netzwerk Ich habe mich vor kurzen hingesetzt und mir nochmal die bisher implementierte Netzwerk-Logik angeschaut. Jedoch habe ich da ein paar gravierende Mängel entdeckt und habe mich hingesetzt und die Netzwerk-Anbindung überarbeitet. Die komplette Netzwerk-Logik ist jetzt einfacher und stabiler aufgebaut. Zudem unterstützt sie TCP, UDP sowie Verschlüsselung. Die Netzwerk-Anbindung ist von TStream abgeleitet und kann somit sehr einfach und überall verwendet werden. Als Basis gibt es den Typ TGENetStream. Dieser wird dann abgeleitet in TGENetTCPStream und TGENetUDPStream. Als weiteres gibt es Netzwerk-Adapter, die ebenfalls von TGENetStream abgeleitet sind. Über diese Netzwerk-Adapter lassen sich beliebig viele Schichten in das Basis-Model implementieren. So gibt es z.B. einen BufferedNetStream, der die Daten beim Auslesen in einen Zwischenspeicher läd um den Socket wieder frei zu machen und die Daten beim Schreiben ebenfalls zwischenspeichert, die dann an einem Stück gesendet werden. Zum anderen gibt es einen Verschlüsselungs-Stream, der die Daten vor dem weiterreichen an die nächste Schlicht verschlüsselt und ankommende Daten entschlüsselt. Die bisher implementierte Verschlüsselung ist symmetrisch, wobei der Ver/Ent-Schlüsselungs-Key per asymmetrische Verschlüsselung verschlüsselt und ausgetauscht wird. Beim Start tauschen dann Client und Server die jeweiligen Public-Keys (zur Verschlüsselung aus) und verschlüsseln dann solange, bis ein symmetrischer Schlüssel ausgetauscht wurde.
Um diese Netzwerk-Logik zu testen habe ich zwei einfache Tests erstellt: zum einen habe ich einen HTTP-Client in der Script-Sprache und einen HTTP-Server in der Script-Sprache erstellt. Der HTTP-Server versendet einfach nur die angeforderten Daten (ohne auf Sicherheit zu setzen) und der Client gibt die empfangenen Daten einfach an der Konsole aus. Der zweite Test war ein kleiner UDP-Chat - wobei dieser Test nicht wirklich sinnvoll war: der Client hat einen String an den Server geschickt und der Server hat ihn einfach wieder zurück geschickt.
Jedoch habe ich jetzt schon mal eine funktionierende Netzwerk-Schicht auf die ich weiter aufbauen kann.
Laden von Spielen Ein wichtiges Ziel für mich ist es, die komplette Spiellogik in die Script-Sprache auszulagern. Somit soll es möglich sein, schnell und einfach komplett neue Spiele zu erstellen.
Dies habe ich jetzt so gelöst: Ich habe ein bestimmtes Basis-Verzeichnis, das immer vorhanden sein muss. In diesem Verzeichnis befindet sich dann eine .run-Datei. Diese Datei enthält den compilierten ByteCode der Script-Sprache. Jedoch steht in dieser Datei keine Spiellogik drinnen sondern nur alle verfügbaren Spiele.
Um ein Spiel hinzuzufügen, muss man in der Script-Sprache eine bestimmte Klasse implementieren und die Properties setzen. Dabei gibt man z.B. den Name des Spiels und - ganz wichtig - den Start-Pfad sowie die Start-Datei des Spiels an. Diese Daten werden dann in die erste .run-Datei kompiliert.
Wenn man nun das Programm startet, erscheint erstmal ein Auswahl-Fenster. In diesem Fenster werden dann alle vorhanden Spiele angezeigt, wobei der Benutzer dann ein Spiel auswählt und es dann startet. Wenn er das Spiel dann startet, wird der angegebene Pfad des Spiels als Basispfad gesetzt und die Start-Datei geladen und schließlich ausgeführt. Die Start-Datei enthält ebenfalls kompilierten ByteCode der Script-Sprache - nur jetzt halt die komplette Spiellogik.
Das Programm erstellt dann den Render-Context und erstellt alle nötigen Script-Klassen. Dann führt das Programm die Initialisierungs-Routine der Haupt-Script-Klasse aus. Nun führt das Hauptprogramm die MainLoop-Methode so lange aus, bis die Script-Sprache als Ergebnis "False" zurück liefert oder bis der Benutzer das Fenster schließt. Dann werden alle Script-Klassen zerstört und das Programm wird beendet. Im Endeffekt besteht das Hauptprogramm aus ziemlich genau 150 Zeilen. Jedoch ist die Performance dadurch nicht viel geringer, da die Script-Sprache ja nicht die kritischen Methoden wie z.B. die Collision-Detection und das Zeichnen ausführt - im Endeffekt steuert die Script-Sprache nur die Engine.
Benutzer-Events in der Script-Sprache In der Script-Sprache soll ja die komplette Spiellogik niedergeschrieben sein. Darunter zählt natürlich auch die Verarbeitung von Benutzer-Events wie z.B. Tastendrücke oder Mausbewegungen. Um auf diese in der Script-Sprache reagieren zu können, kann ein Script einen Event-Manager erstellen und diesen dann im Haupt-Event-Manager der Anwendung registrieren. Dieser Event-Manager bekommt dann alle Benutzer-Events vom Haupt-Manager weitergereicht und können so in der Script-Sprache verarbeitet werden.
Wie kommt die Spiellogik ins Spiel Im Endeffekt wird am Anfang nur eine definierte Klasse der Script-Sprache geladen. Diese Klasse enthällt dann drei Methoden: "Initialize", "Finalize" und "MainLoop". Über diese drei Methoden muss die Script-Sprache die komplette Logik aufbauen. Aber das hört sich schwerer an als es ist, da man das in einer nativ-Code-Anwendung genauso gemacht hätte.
Bisherige Einschränkungen Es gibt bisher noch einige Einschränkungen, die ich noch beheben muss. Zum einen ist nur ein Bruchteil der Engine in der Script-Sprache verfügbar, jedoch lässt sich das noch relativ einfach ausbügeln. Was aber noch etwas Probleme machen wird: es gibt noch keine mögliche Verbindung zwischen einer Spielkarte und der Spiellogik - will heißen: ich kann von der Spiellogik heraus nicht auf die Script-Klassen der Karte zugreifen. Das liegt im Moment noch daran, dass die Karte eine eigene Script-Application hat, die ich jedoch nicht einfach in die Logik-Application integrieren kann. Was jedoch sehr einfach ginge ist eine einfache Schnittstelle im Nativ-Code, der die beiden Applications verbindet. Jedoch weiß ich noch nicht, wie ich das Interface genau aufbauen will.
... und es geht noch weiter Für alle, die sich bis hierhin durchgelesen haben (oder für alle, die einfach ein paar Teile übersprungen haben oder von unten nach oben gelesen haben oder sonstige Abkürzungen in Anspruch genommen haben ) habe ich nun noch ein kleines Schmankerl:
Es gibt mal wieder eine Tech-Demo zum DOWNLOAD. In dieser TechDemo könnt ihr schon mal den vorhin beschriebenen Loader sehen und auch schon die erste Test-Application starten. Wenn ihr denn Test dann startet, dauert es schon mal 30 bis 60 Sekunden, da die Karte geladen wird. Sie ist zwar sehr klein und bereits aus Screenshots bekannt, jedoch ist das Level-Format noch das Format aus dem Editor - also mit Zusatz-Informationen, die am Schluss nicht mehr gebraucht werden. Jedoch werden diese ebenfalls geladen.
Wenn ihr dann die Karte sehen solltet (was ich natürlich hoffe), könnt ich euch mit WSAD fortbewegen und mit gedrückter linken Maustaste euch umschauen. In der Karte geht alle 7 Sekunden zwei rotierende Lichter an und aus. Zudem wird die Deckenbeleuchtung alle 23 Sekunden an- bzw. ausgeschaltet. Die Zeiten sind von mir einfach willkürlich gesetzt. Beim An- und Ausschalten werden einfach bestimmte Trigger in der Karte gesetzt, die dann wieder die entsprechenden Aktionen ausführt. Im Endeffekt ist jede Bewegung von der Script-Sprache umgesetzt. Falls es euch zu dunkel wird, könnt ihr mit der Leertaste die Taschenlampe an- oder abschalten. Ihr könnt sie auch an der aktuellen Position festsetzen indem ihr Enter drückt.
WAS JEDOCH GANZ WICHTIG:
BENUTZUNG AUF EIGENE GEFAHR!!! - ICH HAFTE FÜR KEINE SCHÄDEN
Bisher nur mit meiner ATI-KARTE getestet
OpenGL 2.0 ist Grundvoraussetzung
Grafikkarte muss mindestens 4 Render-Targets unterstützen (Deferred Shading)
Entpackt ist das Archiv 40 MB groß
Bekanntes Problem: bei manchen Sichtwinkeln stimmen die Projektionen nicht mehr
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hi
Es gibt mal wieder was neues zu Berichten:
Tech-Demo Da ja die Tech-Demo bei manchen im Deferred-Renderer ein paar Probleme gemacht hat, habe ich nun versucht, die Probleme zu beheben. Zum einen gab es das "Schwarze-Streifen-Problem" das bei der GeForce 7600 aufgetreten ist. Ich weiß (leider) noch nicht, ob das Problem mit dem letzten Update behoben wurde. Aber ich habe vorsorglich noch eine weitere Sache eingebaut, um das Problem zu beheben. Es wäre super, wenn das einer von den betroffenen noch verifizieren könnte .
Zudem hat igel457 um alle Lichtquellen einen roten Rand gehabt. Nach längeren hin und her hab ich noch keine Lösung dafür gefunden, jedoch probiere ich es mit der neuen Version noch einmal. Es ist wieder mal ein Shader-Update, der nun hoffentlich funktioniert *hoff*.
Neuerung im Level-Format Da ich ja bisher die Level-Editor-Version der Karte in den TechDemos drinnen hatte und diese auch noch sehr groß ist, habe ich nun eine Final-Map-Version implementiert. Dadurch hat sich die Größe von 38 MB auf nur noch 8 MB verringert. Dies hat mehrere positive Konsequenzen: zum einen wurde dadurch die Ladezeit verringert (ungefähr 2/3 weniger Zeitverbrauch). Außerdem wird zusätzlich der Arbeitsspeicherverbrauch um die Hälfte verringert - unter anderem weil ich noch ein paar weitere Sachen aus dem Arbeitsspeicher verbannt habe.
Collsion-Detection Ich habe nun angefangen, mich um die Collision-Detection zu kümmern und habe nun die ersten Resultate vorzuweisen .
Im ersten Schritt wird die Karte durch einen Collision-Tree in Einzelbereiche unterteilt. Dieser Collision-Tree ist ein einfacher Octree mit einer relativ großen Baumtiefe. In den untersten Nodes werden dann alle Dreiecke gespeichert, die sich in dem Node befinden. Bei der Collision-Dectection passiert dann folgendes: Ich schau, in welchen Nodes sich das bewegte Objekt aktuell und später befindet. Durch die Baumstruktur passiert das relativ schnell. Wenn dann alle Nodes in einer Liste gesammelt sind, werden alle Dreiecke der Nodes auf eine mögliche Kollision mit dem bewegten Objekt überprüft. Wenn dies der Fall ist, wird die Bewegung des Objektes angepasst. Um nicht zu viel Rechenpower zu verbrauchen, teste ich die Dreiecke der Map nur mit der Bounding-Box des bewegten Objektes. Bisher habe ich nur eine Kugel als Boundingbox drinnen, jedoch werde ich noch AABB, OOBB und Zylinder einbauen.
Um ein Objekt mit der Kollision zu verbinden, muss ich nur dem Objekt eine Collision-Bounding-Box zuweisen. Diese kümmert sich dann um die korrekte Collision-Detection und um die -Reaction.
Um das zu Testen, habe ich in der Script-Test-Anwendung einfach ein paar Kugeln erstellt und diesen dann jeweils ein Collision-Objekt zugewiesen. Von der Performance her bin ich schon ganz zufrieden. Es ist auf meinem 2GHz-Laptop kein Problem, 200 Kugeln durch den Raum fliegen zu lassen - dafür dass ich komplett mit der Map-Geometrie arbeite ist das schon sehr gut. Jedoch wird es im finalen Spiel nur sehr wenige solcher Objekte geben - ich geh im Moment von ca. 20 aus.
Die Kollisions-Verarbeitung ist bei weitem nicht real, jedoch schaut das Ergebnis schon mal nicht schlecht aus - auch wenn manche Reaktionen eher suboptimal sind .
Script-Eingbindungen der Engine Für den Test musste ich einige neue Sachen in der Script-Engine zur Verfügung stellen. Dadurch habe ich gleich mal alle Importe etwas überarbeitet. Nun ist schon relativ viel in der Script-Engine verfügbar. Jedoch muss ich noch an der Script-Engine selber etwas arbeiten, da diese noch keine Records von außen verarbeiten kann und ich somit noch nicht mit Vektoren arbeiten kann. Aber das sollte nicht so das Problem werden.
Tech-Demo-Download Um euch die Kollision zu zeigen (und um nochmal nachzufragen, ob die Probleme jetzt behoben sind), habe ich die aktuelle Version der Tech-Demo online gestellt. Diese enthällt wie oben erwähnt, alle Versuche zur Problembehebung sowie das neue Levelformat. Hier ist nochmal die aktuelle Key-Map:
WSAD: Bewegen
1: Nightvision aus
2: Nightvision an (Post-Scene-Effects müssen an sein)
3: Deckenbeleuchtung an/aus machen
4: Bürobeleuchtung an/aus machen
F: Taschenlampe an/aus
Enter: Taschenlampe fixieren/wieder in die Hand nehmen
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
So, es gibt mal wieder ein kleines Update!
[Parallax] Bump-Mapping Wie ihr vielleicht aus den Screenshots aus einem der ersten Posts sehen konntet, unterstützt die Engine schon lange [Parallax] Bump-Mapping. Jedoch hab ich das in der Map der Tech-Demo nicht benutzt, da ich bisher dazu keine Zeit hatte. Jedoch habe ich mir jetzt mal die Zeit genommen und die Shader in die Map eingebaut - wobei eingebaut etwas falsch ist. Ich habe eigentlich nur die Normal- und Heightmaps der entsprechenden Texturen in die Textur-Library hinzugefügt und die passenden Shader den Materials zugewiesen. Zusätzlich habe ich nun Specular-Reflections auch im Deferred-Shading-Modus drinnen.
Natürlich will ich euch das Ergebnis nicht vorenthalten (zum Vergrößern: drauf klicken) Links seht ihr die alte Variante ohne Bump-Mapping. Rechts wird die gleiche Diffuse-Texture benutzt wie links - jedoch wird durch das Bump-Mapping die Detailstufe extrem gesteigert.
Hier nochmal ein etwas stärkerer Lichteinfallswinkel. Links wieder die alte Version (so wie aus der TechDemo bereits bekannt) und links mit Bump-Mapping.
Dieses Beispiel finde ich persönlich besonders gut. Ohne Bump-Mapping (links) ist die Textur nicht zu erkennen und die Mauer wirkt wie eine graue Fläche. Mit Bump-Mapping denkt man, dass die Mauer rau ist und extrem viele Details hat.
Nun noch ein Beispiel mit Specular-Reflections. Links ist der alte Modus aktiv. Rechts wirkt das Fass sehr zerbeult, jedoch glänzt das Metal leicht.
Glow-Effects Als weiteren Effekt habe ich einen Glow-Effekt eingebaut. Dieser lässt Oberflächen ein wenig leuchten. Für die Screenshots unten habe ich die bekannte Karte bereits erweitert. Nun gibt es einen Server-Raum, in denen die einzelnen Server natürlich Kontrolllämpchen haben und somit ganz leicht leuchten. Beim linken Bild ist kein Licht im Raum, beim rechten Bild habe ich eben die Nightvision aktiviert, um den Raum etwas zu zeigen. Die Glows bestehen aus einer einfachen Glow-Textur, mit der die Diffuse-Textur multipliziert wird. Durch einfaches austauschen der Glow-Textur kann ich sogar einfache Animationen erstellen. So wechseln die Leuchten ständig, was etwas dynamik in den Raum bringt.
Probleme bei der Tech-Demo Reloaded Leider habe ich es noch nicht geschafft, die Probleme, die mit dem Deferred Renderer zusammenhängen nochmal genau zu analysieren. Bei den roten Linien habe ich einen weiteren Verdachtsfall, den ich mir bald anschauen werde. Den GeForce-Problemen werde ich nächsten Sonntag auf den Leib rücken, da ich dann das aktuelle Built auf meiner GeForce 7600 ausprobieren kann und hoffentlich das Problem reproduzieren und beheben kann.
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
So, jetzt mal das letzte Update für 2009
GUI Ich habe nun angefangen einige GUI-Controls zu erstellen. Ein Grundgerüst hatte ich am Anfang bereits erstellt, jedoch musste ich das fast komplett umschreiben. Als ich damals das Event-Grundgerüst geschrieben habe, konnte ich noch keine Script-Funktionen nativ aufrufen. Daher hatte ich zuerst ein Interface zum Aufrufen von Script-Funktionen erstellt. Da dieses nun komplett obsolet ist, habe ich das komplett gelöscht und somit ein neues Event-System eingebaut. Um das zu testen, habe ich gleich 3 Controls geschrieben - einmal ein Panel, ein Label und ein Button. Eine GUI wird komplett per XML konfiguriert. Die Events werden dann dynamisch im Script auf Script-Funktionen geleitet.
Script-Engine Ich habe mich mal wieder an die Script-Engine selbst gesetzt. Ich habe die RunTime etwas optimiert und habe zudem den Linker erweitert. Dieser hat nun eine weitere Optimierungsstufe drinnen. Dadurch konnte ich die Ausführungsgeschwindigkeit um ca. 20% verbessert. Außerdem kann nun Records aus dem Programm heraus an die Script-Engine senden und zurück natürlich auch. Somit habe ich nun Vektoren in die Script-Engine eingebaut. Dadurch habe ich das Interface der Engine erweitert. Denn jetzt kann ich nun auch endlich die Vektoren dafür verwenden. Insgesamt ist jetzt ca. 85% der aktuellen Engine, die von der Script-Engine heraus benutzt werden können soll, eingebaut.
Datenbank-Anbindung Da die Script-Engine Packages unterstützt, habe ich ein weiteres Package für eine SQLite-Datenbank erstellt. Ich kann nun in der Script-Engine auf eine SQLite-Datenbank zugreifen, SQL-Anweisungen ausführen usw. Somit kann man im Spiel jetzt einfach Daten speichern und laden.
Shader Da ich nun seit 2 Wochen einen neuen Laptop samt einer neuen Grafikkarte habe, konnte ich meine Shader nun nochmal überprüfen. Somit konnte ich nun weitere Probleme beheben. Zudem habe ich im Deferred-Renderer einen gescheiten Shadow-Filter drinnen. Der Filter wird abhängig von der Shader-Qualität ausgewählt. Somit sind die Schatten bei niedriger Shader-Qualität schlechter als bei der maximalen Qualität. Jedoch kann man das durch eine hohe Shadow-Quality das wieder wett machen, da man dann hoch aufgelöste Shadow-Maps hat.
Wann kommt ein neuer Download Das wird noch eine weile dauern. Zwar könnte ich eine aktuelle Version hochladen, jedoch bin ich damit noch nicht zufrieden. Jedoch gibt es eine weitere, nicht so gute, Nachricht. Da ich im Januar umziehe, werde ich in diesem Zeitraum nicht weiterarbeiten können. Zudem werde ich noch seltener auf Nachrichten antworten können - aber ab ca. Februar sollte es dann wieder normal weiter gehen.
In diesem Sinne - einen guten Rutsch ins neue Jahr
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo,
da ich gerade kurz Zeit hatte und ich gesehen habe, dass mein letztes Video schon etwas länger her ist, hab ich mal wieder eins erstellt.
In dem Video kann man den aktuellen HUD-Test sehen. Dieser Test besteht aus einem einfachen Hintergrund und drei Labels. Der Inhalt des HUDs wird im Script geändert, die einzelnen Komponenten werden per XML geladen sowie zusätzlich noch per Script positioniert. Die Schriftart ist eine frei LCD-Schriftart von dafont.com. Für die Textausgabe benutze ich die wirklich hervorragende TextSuite von Lossy eX. Die gebogenen dezenten Balken im Hintergrund von mir. Aktuell (noch nicht im Video) befinden sich über dem unteren Bogen zwei weitere Bögen, die zur Mitte hin zeigen. Der rechte Bogen repräsentiert dann die Lebensenergie und der linke Bogen wahrscheinlich die Restenergie der Taschenlampe. Diese wird dann mit der Zeit weniger, läd sich dann aber wieder auf sobald die Taschenlampe ausgeschaltet ist. Vor dem oberen Bogen wird sich dann wahrscheinlich die Restzeit der aktuellen Runde befinden.
Da ich jetzt schon länger Vektoren in der Script-Engine habe, habe ich nun etwas mehr Bewegung in die Taschenlampe gebracht. Diese Bewegung soll die Bewegung der Spielfigur etwas nachahmen. Zum einen schwenkt die Taschenlampe hin und her und zum anderen verschiebt sich der Fokus der Taschenlampe leicht hin und her. Wenn die Spielfigur sprintet, werden die Bewegungen schneller. Wahrscheinlich werde ich den Ausschlag der Taschenlampe noch etwas stärker machen, wenn der Spieler rennt.
Zudem zeige ich in dem Video ein paar neue Räume, die zu der bereits bekannten Lagerhalle gehören. Die Räume sind noch sehr leer (bis auf den Server-Raum) und werden natürlich noch weiter mit Details ausgeschmückt. Im Server-Raum kann man nun auch übrigens den Glow-Effekt sehen, den ich vorletztes mal angesprochen habe.
Am Ende des Videos sollte man vor allem auf die Schatten achten. Ich habe den Shadow-Filter ja schon verbessert - dies sind die ersten Bilder mit dem neuen Filter.
Das Video gibt es entweder auf der Projektseite oder direkt auf YouTube (YouTube ist wahrscheinlich besser, da dort der Player größer ist).
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo an alle
nach einer etwas längeren Online-Pause gibt es mal wieder ein etwas ( ) größeres Status-Update
Render-Engine-Optimierungen Ich habe mich mal wieder an die Render-Engine gesetzt und ein paar Optimierungen vorgenommen. Zuerst habe ich mich an die Shader gesetzt. Diese habe ich nun von der Performance her zum Teil sehr verbessert. Durch mehrere Discards im Shader konnte ich z.B. die Licht-Shader sehr viel schneller machen.
Als weiteres habe ich mir den schon eingebauten Octree vorgenommen - dieser wird nun endlich auch benutzt. Dadurch kann ich jetzt nicht sichtbare Lichter komplett sparen sowie die Render-Geometry einschränken. Um die Sichtbarkeit eines Lichtes zu überprüfen, schaue ich einfach nach, ob das jeweilige Licht einen für den Spieler sichtbaren Octree-Node beeinflusst. Wenn die Anzahl der beeinflussten Nodes größer 0 ist, wird das Licht im aktuellen Frame gezeichnet.
Da es in einer Map auch dynamische Elemente gibt, musste ich die Octree-Generierung etwas erweitert. In den Octree kommen jetzt nur noch statische Elemente, die sich während der Laufzeit nicht ändern. Dynamische Elemente werden im Moment immer gezeichnet. Jedoch werde ich es später so machen, dass sich dynamische Elemente automatisch bei jeder Veränderung in die Octree-Nodes einsortieren, in denen sich das Element befindet
Danach habe ich die State-Changes im Renderer minimiert. Zuerst werden dabei alle VBOs nach Material sortiert. Dann wird das Material gebunden und alle VBOs, die sichtbar sind und das aktuelle Material haben, gezeichnet. Dann wird das Material wieder deaktiviert und das nächste Material ist an der Reihe.
Script-Engine-Optimierungen Als weiteres habe ich die Script-Engine mal wieder etwas optmiert. Eigendlich hatte ich das im Moment noch nicht vor, da es jedoch ein paar böse Bugs gab, habe ich mich nach dem Bugfix weiter mit der Script-Engine beschäftigt.
Die ScriptEngine hat einen eigenen MemoryManager, der über einen Cache verfügt. Da ich in der Script-Engine relativ oft Speicher anfordere und wieder freigebe, bringt dieser Cache einiges an Performance-Zuwachs. Zudem schont er den Speicher, da er Fragmentierungen dadurch verringert. Bisher war der Cache ganze 32 KB groß und war somit relativ schnell voll. Nun habe ich den Cache auf 1.5 MB erweitert was schon etwas an Geschwindigkeit brachte. Jedoch hat das noch nicht ausgereicht. Denn alle Klassen und Records in der Script-Sprache haben diesen MemoryManager nicht benutzt, wodurch dieser nicht wirklich ausgereizt wurde. Dies habe ich nun geändert.
Zudem habe ich im Linker um eine Optimierungsstufe erweitert. Mit dieser Stufe werden Vergleiche von Variablen sehr viel schneller, da diese nun nicht mehr doppelt in den Stack gepushed werden müssen. Das gleiche Prinzip werde ich dann später noch für Operationen einbauen, aber das kommt noch.
Map-Erweiterungen und Verbesserungen Wie im Kommentarthread bereits ausführlich beschrieben, habe ich die bisher vorhandene Map erweitert. Zum einen sind sehr viele Details in den unteren Gang dazugekommen, zum anderen habe ich nun auch endlich die zweite Lagerhalle erstellt. Zudem ist jetzt schon der Rohbau des Ganges im 1. Obergeschoss fertig und wartet nur noch darauf, mit Details gefüllt zu werden. Wenn jemand einen Teil davon mal sehen will, gibt es hier ein Video.
Collision-Detected für Spieler Was wäre ein 3D-Shooter, wenn man durch die Map fliegen könnte - richtig: irgendwie komisch. Daher habe ich mich nun endlich hingesetzt und einen weiteren Bounding-Volume-Typ hinzugefügt: eine AABB. Diese Bounding-Box wird dann im Script einfach über den Spieler gelegt und schon kann es losgehen . Für die Spieler-Collision ist es dann aber nicht mehr sinnvoll, die komplette Level-Geometry für die Collision-Detection zu benutzen. Daher habe ich einen weiteren Collision-Tree eingebaut. Im Prinzip ist dieser zweite Collision-Tree fast identich mit dem bisherigen - aber: Es gibt nun zwei Typen von Kollisionen: eine detailierte und eine einfache Kollision. Im Detailed-COllision-Tree werden alle Dreiecke der Map gespeichert. Dieser Baum wird dann z.B. für Schuss-Kollisionen benutzt, da dort eine BoundingBox zum Teil überhaupt nicht funktionieren würde (man stelle sich vor, man schießt auf einen sichtbaren Spieler, jedoch wird dieser nicht verletzt, da eine Bounding-Box im Weg ist ).
Jetzt zum Simple-Collision-Tree: Dort werden an sich auch alle Dreiecke gespeichert - jedoch etwas anders. Dafür muss ich jetzt etwas weiter ausholen. Jedes Objekt in der Map hat erstmal keine speziellen Informationen - also Bounding-Volume oder so. Nun ist es so: der Level-Designer (also im Moment ich ) kann jetzt folgendes machen: Zuerst erstellt man ein sehr detailiertes Mesh mit sehr vielen Dreiecken. Diesem Mesh sagt der Designer nun, dass die Dreiecke nur für die detailierte Kollision benutzt werden sollen. Um das Mesh legt er dann eine beliebige Form - z.B. einen oder mehrere Würfel. Diesen Würfeln sagt der Designer nun, dass diese nur für die einfache Kollisionen benutzt werden sollen.
Am Ende ist es dann so, dass nur die Dreiecke im Detail-Collision-Tree landen, die auch explizit dafür genutzt werden sollen. Im Simple-COllision-Tree sind dagegen nur die Dreiecke, die explizit für die einfache Kollision benutzt werden sollen. Natürlich kann ein Mesh sich auch in beiden (für eine einfache Wand brauche ich keine Bounding-Volume) oder in keinem Baum befinden.
Zonen für Spieler-Interaktionen Als weitere Neuerungen in dem Maps habe ich nun Zonen eingeführt. Als Basis für eine Zone gibt es eine abstrakte Klasse, die Funktionen zur Kollisions-Abfrage einführt. Danach werden die einzelnen Zonen-Typen eingefügt, die dann die Kollisions-Methoden mit Leben füllen. Bisher habe ich zwei Zonen-Typen eingefügt: Sphere-Zones (also Kugel-Zonen) und Cube-Zones (also Würfel). Nun kann man von der Map einfach Abfragen, welche Zonen ein Ray schneidet oder in welchen Zonen sich ein Spieler befindet.
Jede Zone an sich hat keine Funktion - diese wird erst durch eine Script-Klasse eingeführt. Dafür gibt es als Basis einfache einfache Zonen-Klasse in der Script-Engine mit folgenden Eingeschaften:
Nun erstelle ich eine weitere Klasse, die ich von TGELevelZone ableite und weise diese Script-Klasse dem Zonen-Objekt im Level zu. Die abgeleitete Klasse übernimmt dann die komplette Steuerung der Logik.
Also Konvention habe ich mir dabei folgendes überlegt: eine Zone kann mehrere Funktionen haben. Z.B. kann eine Zone dafür verwendet werden, um bestimmte Aktionen auszulösen, wenn sich ein Spieler hineinbewegt. Eine ID ist dabei ein Aktionstyp. In der Spiellogik kann man z.B. folgendes definieren: Wenn eine Zone beim Spielereintritt getriggert werden soll, dann muss sie die ID 100 unterstützten. Wenn eine Zone diese ID unterstützt, dann wird der Status der ID 100 auf eins gesetzt, wenn der Spieler sich in die Zone bewegt hat und auf null gesetzt, wenn der Spieler die Zone verlassen hat.
Um das mal etwas zu verdeutlichen, mach ich mal schnell ein Beispiel: angenommen, ich möchte, dass sich ein Licht einschaltet, wenn der Spieler in eine Zone geht. Zuerst erstelle ich im Editor (oder im Script) eine Zone an der gewünschten Stelle. Danach erstelle ich eine Script-Klasse, die ich von TGELevelZone ableite. In dieser Klasse überschreibe ich dann die Methoden "GetHasId" und "SetState". In "GetHasId" gebe ich für die ID 100 "true" als Ergebnis zurück. In der Methode "SetState" mache ich folgendes:
Code:
if id =100then
begin
lightToSwitch :=Self.Map.FindLight('The name of the light, I want to switch');
if value =1then// Player walked in
lightToSwitch.Enabled:=True
else
lightToSwitch.Enabled:=False;// Player walked out
end;
Nun weise ich der erstellten Zone die gerade erstellte Script-Klasse zu und schon bin ich (fast) fertig.
Was noch fehlt: die Zone wird noch nicht getriggert. Also mach ich jetzt folgendes:
Code:
// zuerst hole ich mir alle Zonen, in der sich der Spieler gerade befindet
Zones :=Self.Map.ZonesFromAABB(Self.Player.BoundingBox);
for i:=0to Zones.Count-1do
begin
// Abfrage, ob die Zone die "Spieler hat sich hineinbewegt" unterstützt.
// Diese Methode ruft dann intern die Funktion "GetHasId" der Script-Klasse auf,
// die ich ja vorher überschrieben habe
if Zones.Items[i].HasID[100]then
begin
// Spieler befindet sich in der Zone - ruft intern "SetState" der Script-Klasse auf
Zones.Items[i].State[100]:=1;
// Zone zwischen speichern
Self.PlayerZones.Add(Zones.Items[i]);
end;
end;
// Nun schaue ich, ob sich eine zwischengespeicherte Zone nicht
// mehr in Reichweite des Spielers befindet
for i:=Self.PlayerZones.Count-1downto0do
if Zones.IndexOf(Self.PlayerZones.Items[i]) < 0then
begin
// Der Zone sagen, dass der Spieler sich nicht mehr darin befindet
Self.PlayerZones.Items[i].State[100]:=0;
Self.PlayerZones.Delete(i);
end;
Puhh!!! Das war jetzt viel. Das ganze ist jedoch sehr flexibel und lässt extrem viele Interaktionsmöglichkeiten zu, denn die Logik für jede Zone ist nicht fest in der Engine drinnen, sondern wird erst über die ScriptEngine eingeführt. Da ich dort so zielmlich alles mit der Engine machen kann, sind dadurch sehr viele Sachen möglich.
Spieler-Steuerung Als weiteres habe ich die Spieler-Steuerung erweitert. Nun muss man nicht mehr die linke Maustaste gedrückt halten, wenn man sich umsehen will. Jetzt kann man einfach eine vorgefertige Funktion nehmen, mit der die Maus im Fenster zentriert wird. Mit einer anderen Funktion kann man den Unterschied zwischen der Bildschirmmitte und der aktuellen Maus-Position abfragen. Durch diesen Delta-Wert wird nun das Sichtfeld gedreht. Damit kann sich jetzt ganz normal wie in jedem Shooter umsehen. Das ganze wird von einem Engine-Controller gesteuert, der die Zentrierung automatisch verhindet, wenn das Fenster minimiert wird bzw. den Fokus verliert. Dadurch ist vom Framework selber her gesichert, dass sich die Maus nach einem Alt+Tab nicht weiterhin immer wieder in der Bildschirmmitte springt.
Aufräumen der Script-Header Der Zugriff aus der Script-Sprache zur Engine wird ja über mehrere Header-Files definiert. Diese Header-Files habe ich von der Struktur her wieder mal etwas aufgeräumt. Da die Script-Engine ja partielle Units unterstützt (also eine Unit, die sich aus mehreren einzelnen Dateien zusammensetzt), musste ich nach dem Aufräumen keine Scripte ändern.
... und noch ein paar Zahlen Ich habe mir den Projekt-Ordner mal angeschaut und ein paar Zahlen zusammengetragen. Zwar sind diese Zahlen nicht wirklich aussagekräftig, aber vielleicht interessiert es ja den ein oder anderen.
Anzahl der Dateien Die Engine an sich besteht bisher aus 198 .pas-Dateien und hat 133.665 Zeilen. Mein altes Projekt hatte insgesamt ca. 119.000 Zeilen - mit kompletter Logik, Sound, Menüs, Bots, WayPoints, ... Die Hauptanwending (Game.exe) hat bisher 1.206 Zeilen Quelltext (22 Zeilen .dpr, 884 Zeilen das Startfenster und ganze 300 Zeilen das komplette Hauptfenster. Die Spiellogik (also ein wenig rumlaufen) besteht im Moment aus 1126 Zeilen Script-Quelltext-Zeilen. Dazu kommen noch 1010 Script-Quelltext-Zeilen Logik für die Karte.
Speicherplatz Der komplette Ordner des Projektes inklusive Dokumentation, Testprojekte, Texturen (zum Teil mehrfach) hat insgesamt 2,1 GB. Es befinden sich Backups auf insgesamt drei PCs und auf einem USB-Stick. Ein komplettes Backup (.rar, beste Komprimierung, Solid Archive) hat im Moment 800 MB.
Soooo, das war es auch für den Moment schon wieder
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Da ich in den letzten Tagen ganz gut voran gekommen bin, wollte ich mal den aktuellen Stand präsentieren.
Zonen in Aktion Wie im letzten Post bereits beschrieben, sind jetzt in den Maps Zonen möglich. Nun habe ich die ersten Test-Scripte für die Zonen fertig. Somit kann ich die aktuelle Implementation testen und bin bisher wirklich sehr zufrieden.
Als Test habe ich zwei Zonentypen eingebaut. Einmal einen Zonentyp, der beim Eintreten in die Zone eine Tür öffnet und diese wieder zeitverzögert beim Verlassen schließt. Das Zeitverzögert wird über einen neu eingebauten Timer ausgelöst, der mit den normalen Frametime-Timer verknüpft ist. Der zweite Zonentype ist ein Schuss-Zonen-Typ, der beim schießen auf die Zone ausgelöst wird. Dabei wird zuerst eine Ray-Intersection mit dem Level vorgenommen, um die Schusslänge zu minimieren. Dann werden alle Schuss-Zonen, durch die der Schuss geht, getriggert. Somit kann man jetzt schon mal Lichter ausschießen.
Zeitversetzte Aktionen Wie gerade erwähnt habe ich eine neue Hilfsklasse für die Engine eingebaut. Es gibt jetzt eingebaute Timer, die nach einem angegebenen Intervall immer ein Event auslösen. Dieses Event wird in der Script-Engine auf eine Script-Methode gesetzt und kann dann somit verarbeitet werden. Durch diesen Timer habe ich jetzt ein paar Lichter erweitert. Somit schalten sich jetzt Lichter nicht mehr alle gleichzeitig an, sondern zeitversetzt. Das soll das typische Verhalten von Leuchtstoffröhren nachahmen. Zwar müsste ich noch eine Licht-Intensitäts-Anpassung (also ein leichtes Ansteigen der Lichtstärke) einbauen um es perfekt zu machen, jedoch habe ich das bisher aus Zeitgründen noch nicht umgesetzt.
Funken, Flammen und Rauch ... ... sind jetzt auch möglich. Ich habe nun eine eigene Partikel-Engine eingebaut, die schon relativ flexibel ist. Fireblade ist für mich leider auf Grund der (L)GPL nicht in Frage gekommen - zwar schade, aber leider ist das jetzt so.
Die Partikel-Engine ist in verschiedene Klassen aufgeteilt. Als Basis ist natürlich erstmal der einzelne Partikel. Darin werden nur die aktuellen Zustände sowie ein paar physikalische Daten gespeichert. Danach kommt der Partikel-Container, der sich um die Verwaltung von mehreren Partikeln kümmert und diese z.B. auch der Tiefe nach sortiert. Zudem hat dieser Container einen Partikel-Cache, in dem nicht mehr benötigte Partikel zwischengespeichert werden, um nicht dauernd Speicher anzufordern oder freizugeben. Danach kommt der Partike-Emitter, der die Partikel-Positionen berechnet, sie zeichnet sowie neue Partikel hinzufügt und alte löscht. Darum gibt es dann den Partikel-Effekt, der eine Liste von Partikel-Emittern besitzt.
Um die Partikel-Darstellung möglichst flexibel zu machen, gibt es nicht nur eine Start-Farbe und eine End-Farbe sondern ein Appearance-Set. Diese Appearance-Set besteht aus mehreren Appearance-Daten. In einem Appearance-Daten-Block wird die Farbe, die Größe, die Masse (für den Luftwiderstand) sowie ein LifeTime-Stamp gesetzt. Beim Zeichnen wird der aktuelle Appearance-Block anhand der Partikel-Lifetime ausgewählt. Befindet sich ein Partikel zwischen zwei Daten-Blöcken, wird zwischen den beiden Blöcken interpoliert.
Zusätzlich können alle Partikel per Shader gezeichnet werden. Somit konnte ich Soft-Partikel einbauen, was die Qualität extrem verbessert. Gerade dafür ist das Deferred-Shading perfekt, da ich für die Softpartikel die Tiefeninformationen brauche. Bei Softpartikel gibt es keine harte Kante zwischen Geometry und Partikel. Stattdessen wird die Partikelfarbe je nach Tiefendifferenz langsam ausgeblendet. Einen relativ guten Artikel dazu findet man auf der developer.nvidia.com - Seite.
Mal wieder ein Video Da Partikel in Aktion immer besser aussehen als auf Screenshots, habe ich mal wieder ein Video erstellt. In dem Video kann man den ersten Rohbau des ersten Stocks sowie die zweite Lagerhalle bewundern. Im ersten, bereits bekannten Gang, ist des verzögerte Anschalten von Lichtern zu sehen. Zudem kann man die Türen mal in Bewegung (und natürlich die dynamische Schatten) in Aktion sehen. Die Lichter "schieße" ich übrigens aus - daher auch das Ladenkreuz in der Mitte . Beim zerschießen werde ich dann noch Funken sowie eine kleine Explosion hinzufügen und dann schaut das auch schon besser aus. Zum Ende hin habe ich dann ein Feuer in der ersten Lagerhalle gelegt. Das Feuer besteht aus drei Emittern. Der erste kümmert sich um die Flamme an sich. Der zweite Emitter spukt etwas langsamer Rauch aus. Der dritte Emitter schießt einzelne Funken in die Luft.
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Moin,
Pünktlich zum Start des Wochenendes gibt es mal wieder ein Update.
Ich habe heute gemerkt, dass der letzte offizielle (soll heißen im Projekt-Thread) TechDemo-Release bereits 3 Monate her ist. Also habe ich mich mal hingesetzt und den aktuellen Stand mal als Release-Version vorbereitet. Leider sind es nicht, wie ich im Meinungsthread vorher mal überschlagen habe, 12 MB sondern 15 MB geworden. Aber ich denke, dass man das noch verkraften kann
Im aktuellen Release ist die aktuelle - noch unfertige - aber bereits bekannte - Karte .
Steuerung
WSAD: Bewegen
F: Taschenlampe an/ausschalten
Leertaste: springen
Shift: Sprinten (gedrückt halten)
Strg: Durcken/wieder aufstehen
Linke Maustaste: schießen (man sieht zwar noch nichts, jedoch kann man die Lichter im ersten Gang ausschießen
Debug-Steuerung
1 oder 2: Nightvision an/aus (Post-Scene-Effects müssen immer noch an sein)
3: Beleuchtung in den Lagerhallen an/ausschalten (anschalten ist leicht verzögert)
4: Beleuchtung im ersten Gang an/ausschalten (anschalten ist auch leicht verzögert)
5: Beleuchtung im Treppenhaus an/ausschalten
6: Beleuchtung im zweiten Stockwerk an/ausschalten
Enter: Taschenlampe fixieren / wieder in die Hand nehmen
Bekannte Probleme
Man kann durch alle Türen durchlaufen, auch wenn sie nicht offen sind
Im 1. Stock sind keine Räume hinter den Öffnungen
Es gibt noch ein bis zwei Stellen, bei denen man Stecken bleiben kann
System-Voraussetzungen
OpenGL 2.0 - Grafikkarte (sollte schon relativ aktuell sein)
ca. 64 MB Grafikspeicher
ca. 128 MB RAM
2 GHz sollten ausreichen
Zwar wird von den Einstellungen her noch der Forward-Renderer unterstützt, jedoch würde ich zum einen wegen der schlechteren Performance davon abraten, zum anderen sind die Shader vom Forward-Renderer nicht mehr wirklich aktuell.
Ich hoffe, dass diesmal der Deferred-Renderer nicht mehr so viele Probleme macht wie beim letztes Mal. So, nun endlich der Download-Link: hier.
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
So, ich mal mal wieder ein Status-Update.
Im Moment geht es bei mir nicht gut voran. Ich habe im Moment relativ viel anderes zu tun (Arbeit, neue Wohnung, etc. pp.). Daher gibt es jetzt kein extremes Update sondern nur ein paar kleine Sachen (der Text ist länger als die eigentlichen Änderungen )
Script-Engine Ich habe in letzter Zeit viel Arbeit in die Script-Engine investiert. Unter anderem habe ich die Performance sehr verbessert. Beim Aufruf von externen Methoden habe ich die Ausführungsgeschwindigkeit z.B. durch eine simple Umstellung um 30% verbessert. Außerdem habe ich ein paar Spezial-OpCodes eingebaut, die viele Stack-Operationen durch einen OpCode ersetzen.
Im Moment funktioniert die Script-Engine extrem gut und ich bin mit der bisherigen Implementation super zufrieden. Es gibt zwar noch ein paar Kinderkrankheiten im Compiler und auch ein paar Probleme im Optimizer. Jedoch behebe ich die Probleme im Moment On-The-Fly und somit sind die Sachen für nicht sehr hinderlich.
Ich weiß, dass ich im Kommentar-Thread Finalspace eine Debug-Version versprochen habe, jedoch gibt es da ein kleines Problem: in der aktuellen Script-Engine-Version sind die OpCodes nicht mehr kompatibel mit denen, die im damaligen TechDemo-Release in den kompilieren Script-Dateien gespeichert ist. Somit müsste ich für einen Patch einen kompletten Script-Engine-Rollback machen, was sich auf die Produktivität nicht gerade positiv auswirkt. Doch das ist nicht das einzige Problem mit dem damaligen Release. Ich habe das komplette Command-Interface herausgenommen, wodurch die bisherige Kommunikation zwischen Spiellogik und Karte nicht mehr existiert. Den genauen Grund versuche ich im nächsten Part genauer zu beschreiben.
Überarbeitung der Engine-Interna Ich habe mir in letzter Zeit viele Gedanken um ein sinnvolles Client-Server-Modell gemacht. Dabei habe ich eine extremen Design-Fehler entdeckt, den ich im Moment behebe. Es geht um folgendes: Ich habe vor ein paar Posts geschrieben, wie die Spiellogik mit der Karte interagiert. Es war bisher so, dass die Logik nur Commands an die Karte senden kann und bestimmte Sachen abfragen konnte. Das Problem ist jetzt folgendes: die Spiellogik bekommt es nicht mit, wenn sich in der Karte was ändert (z.B. ein Licht geht an). Genau das Problem ist in einem Netzwerk-Spiel ein Problem.
Also habe ich den Command-Modus durch ein komplett anderes Modell ersetzt. Nun habe ich folgendes implementiert: es gibt eine globale Properties-Klasse. Diese Klasse ist eine Art HashMap: über einen Namen (string) kann auf eine einzelne Property zugegriffen werden. Eine einzelne Property speichert dann einen Wert. Intern wird der Wert immer als string gespeichert, jedoch bietet die Property-Klasse Helfer-Methoden, um Werte als Integer, als Float oder als Boolean zu lesen oder zu schreiben.
Das besondere an dieser Klasse ist nun: jede Script-Klasse der Karte kann sich an eine oder mehrere Properties als Subscriber registrieren. Jeder Subscriber bekommt automatisch eine Nachricht sobald sich der Wert der Property geändert hat. Zudem kann jeder den Wert jeder Property ändern. Intern speichert die Properties-Klasse alle Änderungen. Die Spiellogik kann immer Abfragen, welche Properties sich seit dem letzten State-Save geändert haben. Diese können dann einfach in einen string gepackt werden und an den Server gesendet werden (bzw. vom Server zu den Clients gesendet werden). Vom Programm-Ablauf wird es dann so ablaufen: Ein Client will nun das Licht in einem Raum anschalten. Dies geht nur über die Zones. Der Client sendet dann die Anfrage mit der Zone-Id an den Server. Der Server führt dann die Aktion aus. Alle Änderungen, die dann an einen einzelnen Properties gemacht wurden, werden dann als ein sequenzielles Update an alle Clients gesendet. Somit bekommen die Clients nur die Änderungen mit. Jedoch bietet die Property-Klasse auch die Möglichkeit, alle Eigenschaften in einen string zu packen bzw. aus einem String zu laden. Bei einem Connect fragt ein Client dann den aktuellen, kompletten Status ab und bekommt den kompletten Status gesendet.
Das war jetzt zwar sehr theoretisch, jedoch hoffe ich, dass es wenigstens ein wenig verständlich war.
Erweiterung des Netzwerk-Modules In einem der vorherigen Posts habe ich bereits geschieben, dass ich mein urspüngliches Netzwerk-Modell durch ein neueres und flexibleres Modell ersetzt habe. Nun habe ich mich hingesetzt und einen neuen Layer auf das Netzwerk-Modell aufgebaut. Bisher gibt es nur Streams für die Kommunikation. Über die Streams kann alles mögliche gesendet werden und ist somit extrem flexibel. Jedoch habe ich mir eine weitere Schicht überlegt: diese Schicht implementiert ein rudimentäres Command-System. Es gibt eine einfache Command-Klasse. Diese Klasse hat eine Command-Id sowie ein Daten-Stream. In den Daten-Stream können beliebige Daten geschrieben bzw. ausgelesen werden. Jeder Command wird beim Senden zuerst in eine Command-Liste hinzugefügt. Die Liste wird dann an einen Netzwerk-Socket gebunden. Danach schickt die Liste alle Commands über den Socket. Die Listenklasse hat auch eine Funktion, einzelne Commands aus einem Socket auszulesen. Da jeder Command ein Command-Typ-Feld hat, kann so ein einfaches Protokoll aufgebaut werden. Die Command-Klasse kümmert sich intern darum, dass die Daten komplett und korrekt aus dem Socket ausgelesen werden. Die Basis-Command-Liste kümmert sich nur um das Senden und um das Auslesen. Ich habe mir bereits eine weitere Klasse überlegt, die Paket-Sicherheit sowie Paket-Reihenfolge beachtet (wie bei TCP üblich). Jedoch ist diese Klasse noch nicht implementiert. In meiner Engine habe ich UDP sowie TCP-Sockets über einen Stream abstrahiert. Da die Command-Klasse mit der abstrakten Netzwerk-Stream-Klasse arbeitet, kann man z.B auch Commands, die von einer TCP-Verbindung gekommen sind, über UDP zurücksenden. Da das Command-Interface auf das Stream-Interface aufbaut und beide verfügbar sind, kann sich jeder aussuchen, ob er das Command-Interface benutzt, ein eigenes implementiert oder sogar nur mit den Netzwerk-Streams arbeitet.
Um die Command-Methoden zu überprüfen, habe ich mein bisheriges UDP-Chat-Beispiel hergenommen und umgeschrieben. Dabei habe noch ein paar Probleme mit dem UDP-Stream gefunden und behoben. Nun habe ich einen kompletten Chat auf UDP-Basis, der komplett in meiner Script-Engine implementiert ist. Zwar können noch Pakete verloren gehen, aber ein Basis-Chat ist bereits ohne Probleme möglich - mit beliebig vielen Benutzern. Mit dem Command-Interface ist es sogar möglich, neue Sachen wie z.B. ein privaten Chat zwischen zwei Chattern ohne viel Code einzubauen (wahrscheinlich benötige ich für den Einbau eines privaten Chats ca. 50 Script-Quelltext-Zeilen - Delphi muss ich dafür nicht mal anwerfen).
So, viel Text für wenige Ändernungen - jedoch hoffe ich, dass es vielleicht ein paar interessiert
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Sodala es gibt mal wieder etwas neues zu berichten:
Intern - neue Test-Map Es ist zwar jetzt erstmal nichts besonderes, jedoch habe ich für interne Zwecke eine neue, kleine Karte gebastelt. Die Karte an sich ist eigentlich nur eine Erweiterung meiner ersten Test-Karte des Editors. Da ich immer versuche, meine Dateien kompatibel zu halten, war es kein Problem, die alte Karte im Editor zu öffnen. Damit jetzt nicht jeder im Thread den Screenshot suchen muss, hab ich den alten Screenshot nochmal eben verlinkt: zum vergrößern: klicken
Die Karte an sich habe ich einfach etwas vergrößert und das bisherige Mittelstück dupliziert und die beiden Teile an beiden Enden der Karte platziert. Dazu habe ich noch ein paar Lichter hinzugefügt bzw. verschoben und fertig. Der Vorteil der "Test-Karte" ist, dass ich sie schneller laden kann als die bisher bekannte Karte. Zudem habe ich mir viel Platz in der Mitte für alle möglichen Spielereien gelassen . Damit ihr euch ein besseres Bild der Karte machen könnt, habe ich noch drei Screenshots erstellt (die Taschenlampe ist absichtlich so überstrahlend). zum vergrößern: klicken
Sichtbare Gegner ... ... sind immer ganz nützlich . Daher habe ich mich endlich dazu überwunden, mich an den Model-Controller zu setzen. Das ganze Model-System ist sehr abstrakt gehalten und ist somit sehr flexibel. Das hat auch einen Grund: ich wollte für die Engine nicht erst ein eigenes Model-Format inkl. Animationen etc. pp. erstellen. Also habe ich mir gedacht, dass ich einfach bereits bekannte Model-Formate über eine "PlugIn"-Schnittstelle zur Verfügung stelle. Zwar ist die PlugIn-Schnittstelle nur intern - also nicht per dll - jedoch reicht das für mich.
Da unterschiedliche Model-Formate unterschiedliche Animations-Systeme benutzen, habe ich mir ein abstraktes Animations-System überlegt: es gibt eine Animations-Liste, in der einzelne Animationen gespeichert sind. Jede Animation hat dabei einen Namen, über die die Animation dann gesteuert wird. Die Implementierung der Animations-Logik ist dann dem Model-Format bzw. dem Model-Loader vorbehalten. Im Endeffekt sagt die Engine nur, dass z.B. die Animation "Gehen" gestartet werden soll. Was der Model-Loader daraus macht ist ihm überlassen - also ob er die Animation mit Hilfe von Bones erledigt oder ob eine andere Technik verwendet werden soll. Damit die einzelnen Loader die Animationen auch korrekt abspielen können, ruft die Engine in der Main-Loop automatisch eine Update-Methode auf. Mit Hilfe dieser Methode kann der Loader dann die einzelnen Animationen anwenden.
Der Model-Controller kümmert sich jedoch nicht nur um die Animation, sondern auch um das Zeichen. Dazu gehören mehrere Dinge: zum einen natürlich das zeichnen selbst (was im Moment noch komplett per Immediate-Mode gemacht wird *hust* ), zum anderen kümmert sich der Controller auch darum, die Schatten der Lichter als "Veraltet" zu markieren. Dafür geht die Engine einmal pro Frame jedes Licht durch. Bei jedem Licht wird dann das Licht-Frustum an den Controller übergeben. Dieser muss dann sagen: ja, ich befinde mich im Frustum, oder nein: ich bin nicht darin. Wenn der Controller ja sagt, wird der Schatten des Lichtes als veraltet markiert und wird dann beim nächsten Frame aktualisiert. Jedoch kann es natürlich sein, dass sich ein Model nicht bewegt. Somit wäre es etwas sinnlos, den Schatten trotzdem zu aktualisieren. Daher gibt es noch eine einfache Funktion, die sagt - ob sich das Model seit dem letzten Frame bewegt hat.
Das ganze System wird auch schon relativ komplett ausgenutzt (bis auf die Animations-Klasse), da ich bereits einen einfachen MD3-Model-Importer eingebaut habe. Das MD3-Format ist zwar nicht mehr das aktuellste, jedoch reicht es für mich bisher auf jeden Fall. Für den Fall, dass MD3 nicht mehr ausreicht UND ich einen guten Ersatz habe, dann muss ich mich halt hinsetzten und einen neuen Controller basten . Jedoch kann man aus dem MD3-Modellen mit Normal-Maps usw. noch einiges herausholen - und so komplexe Animationen brauche ich eigentlich auch nicht.
Damit sich der Aufwand der Test-Map auch lohnt, habe ich diese bereits für die ersten Tests benutzt . Für den Model-Test lasse ich einfach 5 mal das selbe Modell durch die Karte laufen - querfeldein, ohne Kollisions-Test. Dabei switche ich noch zufällig zwischen dem Schleichen und Renn-Modus der Modelle. Da animierte Modelle auf Screenshots immer etwas, naja, sagen wir "un-animiert" rüberkommen, habe ich mal wieder ein Video erstellt. Dabei ist auch das von mir geplante Ziel-System des Spielers zu sehen: ein Laser-Pointer . Der Laster-Pointer ist einfach nur ein Spot-Light mit einem sehr kleinen Spot-Winkel. Zwar gibt es manchmal noch ein paar Grafikfehler, diese sollten sich jedoch beheben lassen.
Und noch ein paar andere Sachen ... Auch wenn es nicht wirklich viel aussagt, kann ich ja mal noch ein paar Zahlen präsentieren: aktuell hat die Engine an sich 140.000 Code-Zeilen (inkl. Leerzeilen und Kommentaren). Diese Zeilen teilen sich insgesamt in 209 Dateien auf. In jeder Datei befinden sich durchschnittlich (ist nur geraten) 2 bis 3 Klassen (in manchen gibt es nur eine, in anderen auch wieder 10 bis 15).
Und wie der Zufall es will: der Projekt-Thread wird heute genau 1 Jahr alt *freu* . Zwar habe ich die Engine schon vorher angefangen, jedoch kann ich mir den 8. April anhand des Projekt-Thread-Datums gut merken . Zum Einjährigen möchte ich mich bei jedem, der sein Feedback im Kommentar-Thread abgegeben hat, ganz dolle für die Unterstützung und für die konstruktive Kritik bedanken und hoffe, dass auch weiterhin so viele gute Sachen kommen . Des weiteren bedanke ich mich natürlich sehr gerne nochmal für den Quartals- und den Jahres-Award. Für die Entwicklungszeit von einem Jahr bin ich sehr gut voran gekommen (besser als ich je gedacht hätte) und ich hoffe, dass es so gut weiter geht wie bisher.
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo
Im Moment jagt bei mir ein Update das nächste . Aber es gibt mal wieder was zum selbst testen
Neue Tech-Demo mleyen hat ja bereits im Kommentar-Thread und Lord Horazont im Chat gefragt, wann denn mal wieder eine neue Version zum Testen bereit steht. Zwar habe ich zuerst gedacht, dass das noch eine Weile dauern kann, jedoch habe ich mir das heute anders überlegt. Ich hoffe, dass das ganze ausgereift genug zum Testen ist - jedoch gilt wie immer: ich hafte für keine Schäden .
In der neuen Tech-Demo kann man sich die Test-Map mit den neuen Models anschauen. Da die "KI" der Models (wenn man das überhaupt so nennen kann) dümmer ist als ein Stück Brot, sollten jetzt keine großen Sachen erwartet werden. Im Endeffekt laufen die Models nur wild hin und her, wobei sie zufällig zwischen Gehen, Rennen und geducktem Gehen wechseln.
Da einige immer wieder sagen, dass alles etwas zu dunkel ist, habe ich als Bonus einen kleinen "Farbmodus" eingebaut. Jeder Bot hat eine Taschenlampe, mit der er hin- und her-läuft. Jede Taschenlampe bekommt dabei eine zufällige Farbe, wodurch alles etwas bunter wird . Die Taschenlampen lassen sich aber beliebig ein- und ausschalten. Die Taschenlampen sind dabei per Default erstmal aus. Mit K kann man die Taschenlampen der Bots ein- und ausschalten. Ansonsten hat sich die Steuerung zu den letzten Demos hin nicht groß verändert:
WSAD: Bewegen
Leertaste: springen
Shift: rennen
Strg: ducken/wieder aufstehen
1, 2: Nightvision ab/aus
F: Taschenlampe an/aus
Enter: Taschenlampe fixieren/wieder in die Hand nehmen
L: Laster-Pointer an/aus
+: einen neuen "Spieler" hinzufügen
-: einen zufälligen "Spieler" entfernen
K: Taschenlampen der Bots an/ausschalten
Ich habe versucht die Probleme von Finalspace zu beheben, weiß aber nicht ob ich erfolgreich war. Ich kann die Fehler bei mir leider nicht nachvollziehen.
Ach noch was: die Models werden im Forward-Render-Modus nicht korrekt gezeichnet. Das Problem ist bekannt, wird aber erst später behoben. Ich empfehle dringend den Deferred-Renderer zu verwenden. Falls man mal in der Karte stecken bleiben sollte, kann man sich mit R wieder zurücksetzen.
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo mal wieder, ich melde mal wieder den aktuellen Stand
Script-Engine In den letzten Wochen habe ich mich wieder etwas ausführlicher mit der Script-Engine beschäftigt. Ein großer Knackpunkt bisher war die interne Speicherung von Records im Arbeitsspeicher. Records wurden wie Klassen behandelt, somit wurde auch immer nur ein 4-Byte-Pointer als Referenz auf das Record gespeichert. Das war an sich ja nicht das Problem, jedoch bei verschachtelten Records bekam ich dann das Problem, sobald ein Record die Script-Engine verlassen hat und an das Host-Programm übergeben wurde (und natürlich anders herum). Nun habe ich die interne Record-Speicherung komplett geändert, so dass auch verschachtelte Records hintereinander im Speicher liegen. Somit kann ich die Records jetzt ohne Konvertierung an das Host-Programm übergeben und wieder auslesen. Dies ist wichtig, da ich bei Vektoren sowie bei der Kollisions-Abfrage viele verschachtelte Records benutzen will.
Ein weiteres neues Feature in der Script-Engine ist Multi-Threading: ich kann jetzt in einem Script eigene Threads erstellen und Script-Quelltext in diesem Thread abarbeiten lassen. Das ganze ist ähnlich wie in Delphi - jedoch leite ich keine Klasse von TThread im Script ab, sondern von der script-internen Klasse TExecutionContext ab. Für Multi-Threading musste ich viel am internen Layout der RunTime ändern, jedoch denke ich, dass sich das ganze gelohnt hat. Ich weiß zwar noch nicht genau, für welche Zwecke ich das Multi-Threading benutzen will, jedoch fällt mir da bestimmt was ein
Decal-Rendering Als weiteren Punkt habe ich den Renderer nun etwas erweitert. Somit ist es jetzt möglich Decals zu rendern. Decals müssen nicht immer nur Schusslöcher sein, sondern können auch für detailliertere Texturen benutzt werden. Die Decals werden dabei in einem Extra-Render-Pass gezeichnet, wobei ich einen kleinen Polygon-Offset aktiviert und das Schreiben in den Tiefenpuffer deaktiviert habe. Da die Decals das bisherige Material-System nutzen, kann ich für jeden Decal zudem noch einen Normal-Map erstellen. Dadurch werden die späteren Schusslöcher dann auch ins richtige Licht gerückt .
Das ganze ist jedoch noch nicht ganz fertig, da z.B. Schusslöcher noch nicht möglich sind. Dies liegt jetzt jedoch nicht mehr am Renderer, sondern die Möglichkeit zur Erzeugung und Verwaltung der Schusslöcher-Decals ist noch nicht da. Sobald das dann fertig ist, sollte man nun auch Löcher an der Wand sehen können, sobald man darauf geschossen hat. Was zudem noch fehlt ist die Render-Reihenfolge der Decals. Wenn ich mehrere Decal-Layer auf einer Oberfläche unterbringen will, dann habe ich noch keine Kontrolle über die Render-Reihenfolge der Decals. Dies wollte ich einfach über einen Layer-Index im Material lösen, der dann sortiert abgearbeitet wird. Jedoch bin ich mir noch nicht sicher, ob ich den Layer-Index für jeden Decal-Mesh einzeln oder ob ich den Index global über das Material einstellbar mache.
Zum Testen der Decals habe ich die bekannte Testmap mal mit Hilfe von Decals etwas aufgepeppt. Zum einen ist der Boden jetzt nicht mehr ein kompletter Einheitsbrei, sondern mit etwas Dreck überzogen. Zudem sind noch ein paar Gullis und ein paar Linien auf dem Boden. Damit ihr euch das besser vorstellen könnt habe ich mal zwei Screenshots erstellt. Einmal vorher ohne Decals und einmal nachher mit Decals (ach ja, der Schatten ist übrigens von der eigenen Spielerfigur )
Links ohne, rechts mit Decals - zum Vergrößern drauf klicken
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Hallo mal wieder. Ich bring euch mal wieder auf den aktuellen Stand:
Schusslöcher Wie ich im letzten Post bereits geschrieben habe, habe ich den Renderer um Decals erweitert. Das Rendering habe ich noch ein wenig verbessert und auch ein paar kleine Bugs beseitigt. Zum anderen habe ich jetzt einen Manager für dynamische Decals eingebaut. Dadurch ist es nun endlich möglich, Schusslöcher an der Wand zu sehen. Doch ein paar simple Löcher sind ja noch nicht das ganz das Gelbe vom Ei - Funken und Rauch müssen her. Dazu habe ich das Partikel-System noch etwas erweitern müssen. Bisher war ein Partikel-Emitter an eine Position gebunden. Das ist auch immer noch so. Jedoch gibt es jetzt eine weitere Klasse im Partikel-System. Diese Klasse kann den Emittern sozusagen fernsteuern. Das ganze schaut dann so aus: im Editor erstelle ich erstmal ein paar Partikel-Effekte. Die Einstellung verwende ich dann sozusagen als Template. In der Script-Engine erstelle ich dann einfach für jedes Schussloch einen Controller, den ich dann mit dem Emitter verbinde. Dieser Controller modifiziert dann die Startposition und die Richtung der Partikel des Emitters. Zudem hat er noch einen eingebauten Timer, der den Controller automatisch nach Ablauf der angegebenen Zeit löscht. Der Vorteil davon ist: ich habe nur einen komplexen Partikel-Emitter, den ich für jede kleine Staubwolke - die beim Schießen auf eine Wand auftreten kann - fernzusteuern. Das spart Speicher und Rechenzeit.
Im letzten Post habe ich ja schon erwähnt, dass die Schusslöcher Bump-/Parallax-Mapping erlauben sollen: das habe ich mittlerweile auch erfolgreich hinbekommen. Dafür musste ich in meinem Deferred-Shader (der den G-Buffer füllt), noch um einen Parameter erweitern und das wars auch schon. Der Qualitätsgewinn ist wahnsinnig groß. Es schaut dann nicht mehr wie eine einfache, draufgeklebte Textur aus, sondern wirklich wie ein Loch in der Wand. Da Screenshots da nicht so wirklich sinnvoll sind, habe ich wieder einmal ein Video hochgeladen - den Link zum Video gibts unten.
Kleine Erweiterungen Sascha hat im Meinungsthread mal vorgeschlagen, beim Rennen ein Motion-Blur einzubauen. Das habe ich nun umgesetzt - zwar nicht direkt ein Motion-Blur, sondern einfach nur einen Radial-Blur. Dieser wird nur dann aktiviert, wenn der Spieler rennt. Dadurch wirkt es jetzt so, als würde man 100 km/h rennen - aber wenn man selber mal sprinten, kann man sich ja auch kaum auf die Umgebung konzentrieren.
Neues Video Das neue Video habe ich auf YouTube hochgeladen. Ich empfehle sehr, die HD-Version zu nehmen und die Anmerkungen anzuzeigen (damit man versteht, warum ich ein paar Sekunden stehen bleibe ). Am Anfang renne ich noch etwas herum, um den Radial-Blur zu zeigen. Es ist leider etwas dunkel, daher sieht man den Effekt noch nicht so ganz. Aber ich arbeite im Moment an einer neuen Karte, die ich für die ersten Spiel-Tests nehmen will (Screenshots gibt es aber noch nicht ). Da schaut der Blur um einiges besser aus.
Mitglieder in diesem Forum: Google [Bot] und 3 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.