ZweiDe
Übersicht

Gehe zu Seite Zurück 1, 2, 3 ... 17, 18, 19, 20 Weiter
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Also ehrlichgesagt weiß ich gerade nicht, wozu du in deinem Projekt, das bisher keine Objekte hat, ein UpdateGrid brauchst, aber gut, das lass ich mal deine Sorge sein ![]() Ein UpdateGrid erfüllt im Wesentlichen den Zweck, Objekte basierend auf ihrer räumlichen Position in ein zweidimensionales Array einzuordnen, um dann bei Dingen wie zB. Kollisionsabfrage nicht durch *alle* Objekte durchiterieren zu müssen sondern sich von vornherein auf einen bestimmten Bereich beschränken kann. Lohnt aber eigentlich erst bei Objektzahlen > 100 und dann natürlich auch nur, wenn die nicht alle auf einem Fleck sitzen. ![]() Das Textur-Kollisionsfeature für deine Hintergrundpixmap würde ich aber an deiner Stelle streichen und Objektkollisionen lieber auf deine bestehenden Abfragemethoden abbilden. Fällt Ingame später wahrscheinlich nicht auf, ist viel schneller und auch leichter zu handhaben. Ist jetzt aber weniger eine ZweiDe- und mehr eine Projektempfehlung. ![]() Die CSScript-Klasse.. puh, das ist ne Weile her, die ist ein Relikt aus dem Rpg-Projekt. Sie ermöglicht es, zur Laufzeit C# Code zu kompilieren und als Scripte für Objekte o.Ä. auszuführen. Grundsätzlich hast du zwei Möglichkeiten, C#-Scripting zu realisierei: 1. Die Präprozessor-Script-Variante In dieser Methode nutzt du die gesamte CSScript-Klasse in all ihrer (zugegebenermaßen nicht jahrelang getesteten) Featurepracht. Das kann gut sein, aber für Nullpunkt habe ich mich beispielsweise bewusst gegen diese Variante entschieden. So werden deine Scriptfiles dann aussehen (Nur ein Beispiel): Code: [AUSKLAPPEN] local double timeTemp; method void Init(ScriptParam par) { this.ChangeDir(); timeTemp.Set(Program.Rnd.NextDouble() * 3.0d); } method void Update(ScriptParam par) { timeTemp.Set(timeTemp.Get() + Program.FrameCount.TimeDelta / 60.0d); if (timeTemp.Get() > 3.0d) { timeTemp.Set(0.0d); this.ChangeDir(); this.ZVel = 5.0d; } } method void CollideTile(ScriptParam par) { this.ChangeDir(); } method void ChangeDir() { this.Control.Reset(); switch (Program.Rnd.Next(4)) { case 0: this.Control.MoveDown(); break; case 1: this.Control.MoveUp(); break; case 2: this.Control.MoveLeft(); break; case 3: this.Control.MoveRight(); break; } switch (Program.Rnd.Next(4)) { case 0: this.Dir = Math.PI; break; case 1: this.Dir = 0.0d; break; case 2: this.Dir = Func.HalfPi * 3.0d; break; case 3: this.Dir = Func.HalfPi; break; } } Mit dem this-Keyword beziehst du dich auf das Objekt, das das Script ausführt (Was ein präprozessor-hack ist), anders als in der C#-Syntax musst du deine Methoden mit "method" markieren und brauchst dich auch nicht um namespaces, Using-Direktiven oder eine umschließende Klasse kümmern. Ferner kannst du mit dem durch den Präprozessor ermöglichen "local"-Keyword Objektlokale Variablen anlegen, oder mit "global" eben globale. Der Zugriff geschieht, wie oben im Beispiel mit ".Get()" und ".Set(..)". Der Grund, warum ich mich gegen diese Variante entschieden habe, ist aber genau dieser Präprozessor - letztendlich war es mir dann doch wichtiger, wirklich den Code zu schreiben, der auch kompiliert wird und damit eine mögliche Fehlerquelle auszuschließen. Für diese Variante initialisierst du zunächst einen Script Compiler mit einer Type-Referenz auf den Typ des Objekts, das das Script ausführen wird (Also den Typ vom Script-"this") und einer string-Liste der Using-Direktiven: Code: [AUSKLAPPEN] CSScript.Compiler scrProv = new CSScript.Compiler( scriptSelfType, new string[] { "System", "Rpg_Project", "ScriptParam = Rpg_Project.GameObj.ScriptParam", "Fetze.Module", "Fetze.Utility", "Fetze.Utility.Color" }); Dann übergibst du ihm eine Liste von Typen, die du für "local" und "global"-Variablen unterstützen willst. Code: [AUSKLAPPEN] scrProv.AddExtSafeTypes( typeof(List<>), typeof(Dictionary<,>), typeof(Stack<>), typeof(Queue<>), typeof(BitArray), typeof(Point<>), typeof(Rect<>), typeof(Vector2D), typeof(Vector3D), typeof(Clr.RGBA), typeof(Clr.RGBAf), typeof(Clr.HSVA)); Und damit ist der Compiler soweit fertig. Mit scrProv.Compile(fileName) lässt du ihn eine Scriptdatei kompilieren. Er liefert dir dann eine CSScript-Instanz, was nicht ein konkretes Script ist sondern nur ein Wrapper für die Script-Assembly. Du brauchst also nicht für jedes Objekt ein eigenes CSScript, nur für jede Scriptdatei. Wenn du nun Scriptmethoden aufrufen willst, brauchst du erstmal einen Funktionspointer darauf, das sind in C# sogenannte "Delegates". Dafür musst du einen Delegate-Typen definieren: Code: [AUSKLAPPEN] public delegate void ScriptDelegate(GameObject parentObj, ScriptParam par);
Damit definierst du die Signatur einer Scriptfunktion. Was ich hier GameObject genannt habe ist der Typ, den du dem Compiler als Typ für seine this-Referenz gegeben hast, ScriptParam ist hier im Beispiel einfach irgendein Parameter-Typ. Du kannst beliebig viele verschiedene Delegates für beliebig viele verschiedene Scriptfunktionstypen definieren, oder aber du definierst nur einen, der einen ScriptParam-Parameter hat, wobei ScriptParam eine abstrakte Klasse ist, von der du ableitest, um dann konkrete parameterdaten zu übergeben. So sparst du dir eine Menge Delegates, aber ist nur eine Möglichkeit von vielen. So erhältst du ein Delegate auf eine Scriptfunktion: Code: [AUSKLAPPEN] ScriptDelegate myMethod = (ScriptDelegate)csScriptInstance.CreateDelegate(typeof(GameObject), "Name_Deiner_Methode"); Um die Scriptfunktion jetzt aufzurufen, machst du folgendes: Code: [AUSKLAPPEN] myMethod(thisObject, paramOne); thisObject ist die Instanz des Objekt, das sich im Script hinter "this" verbirgt, paramOne ist hier irgendwas beispielhaftes für den oben definierten ScriptParam-Parameter. Eine letzte Sache noch: Damit du das Feature mit im Script definierten lokalen und globalen Variablen nutzen kannst, muss dein this-Typ die CSScript.IValueProvider-Schnittstelle implementieren. Das geht zB. so: Code: [AUSKLAPPEN] #region CSScript.IValueProvider implementation private Dictionary<string,object> localScriptVar = new Dictionary<string,object>(); private static Dictionary<string,object> globalScriptVar = new Dictionary<string,object>(); public T GetLocalVar<T>(string var) { object value; if (this.localScriptVar.TryGetValue(var, out value)) { return (T)value; } else { T newVal = default(T); this.localScriptVar.Add(var, newVal); return newVal; } } public T GetGlobalVar<T>(string var) { object value; if (GameObj.globalScriptVar.TryGetValue(var, out value)) { return (T)value; } else { T newVal = default(T); GameObj.globalScriptVar.Add(var, newVal); return newVal; } } public void SetLocalVar<T>(string var, T value) { this.localScriptVar[var] = (object)value; } public void SetGlobalVar<T>(string var, T value) { GameObj.globalScriptVar[var] = (object)value; } #endregion Damit hast du jetzt alles, um die CSScript-Klasse zu nutzen. Oder aber du wählst Methode 2: Vieles von Oben (zB. die Sache mit den Delegates) kannst du auch hier anwenden, aber letztlich ist das hier die Methode, ohne die CSScript-Klasse auszukommen und ohne Präprozessor direkt "echten" C#-Code zu kompilieren. Fürs Scripting musst du nichts beachten, es wird einfach nur eine Codedatei sein, wie du sie in deinem Projekt auch hast. Das Kompilieren des Scripts und die Methode, Delegates zu erstellen, musst du dir aber selbst schreiben. Hier eine kleine Starthilfe, 1:1 aus Nullpunkt: Kompilieren: Code: [AUSKLAPPEN] public void LoadScript(string file) { file = Path.Combine("Content\\Script", file); if (this.compiledScripts.ContainsKey(file)) return; Assembly[] references = AppDomain.CurrentDomain.GetAssemblies(); #region Loading script code FileStream stream = File.OpenRead(file); StreamReader reader = new StreamReader(stream, Encoding.Default, true); string sourceFile = reader.ReadToEnd(); reader.Close(); #endregion #region Compiling CSharpCodeProvider codeProv = new CSharpCodeProvider(); CompilerParameters compArgs = new CompilerParameters(); CompilerResults ret; compArgs.GenerateInMemory = true; compArgs.GenerateExecutable = false; #if DEBUG compArgs.IncludeDebugInformation = true; #else compArgs.IncludeDebugInformation = false; compArgs.CompilerOptions = "/optimize"; #endif for (int i = 0; i < references.Length; i++) { compArgs.ReferencedAssemblies.Add(references[i].Location); } ret = codeProv.CompileAssemblyFromSource(compArgs, sourceFile); if (ret.Errors.Count > 0) { SteApp.Current.ErrorLog.WriteLine( String.Format("Error {2} compiling script '{0}', line {1}: {3}", file, ret.Errors[0].Line, ret.Errors[0].ErrorNumber, ret.Errors[0].ErrorText) ); #if DEBUG System.Diagnostics.Debugger.Break(); #endif return; } #endregion this.compiledScripts.Add(file, ret.CompiledAssembly); } Delegate holen: Code: [AUSKLAPPEN] <...> foreach (Assembly asm in this.compiledScripts.Values) { foreach (Type t in asm.GetTypes()) { method = t.GetMethod( myInfo.name, BindingFlags.Static | BindingFlags.Public, null, new Type[] {myInfo.objType, myInfo.argsType}, null); if (method != null) break; } if (method != null) break; } <...> // Generate delegate if (method != null) { func = (ScriptFunc<T,U>)Delegate.CreateDelegate(typeof(ScriptFunc<T,U>), method); } <...> Kleine Anmerkung dazu: In Nullpunkt nutze ich nur statische Methoden, die einen zusätzlichen parameter namens "self" haben, der das Objekt enthält, das das Script ausführt. Im Prinzip genau das, was das CSScript als Präprozessor-Hack automatisch einbaut. Zum Vergleich, so sieht ein Nullpunkt-Script aus (leicht fürs Beispiel abgeändert): Code: [AUSKLAPPEN] using System; using System.Collections.Generic; using SteCore; namespace Script { public static class General { public static bool TestFunc(GameObject obj, ScriptEventArgs args) { Console.WriteLine("Begin"); Console.WriteLine("Mid"); Console.WriteLine("End"); return true; } } } Bei Rückfragen einfach posten, aber ich sag dir gleich: Die Präprozessor-Variante ist zwar im Rpg-Projekt auch in vielerlei Hinsicht getestet worden, was aber nicht heißt, dass es nicht bisher nicht aufgetretene und daher unberücksichtigte Spezialfälle gibt, in denen der Präprozessor versagt und ein Script unkompilierbar macht, das eigentlich funktionieren sollte. Es *sollte* zwar alles laufen, aber man weiß ja nie. |
||
![]() |
coolo |
![]() Antworten mit Zitat ![]() |
---|---|---|
Viel dank für diese ausführliche Antwort ![]() Zitat: Also ehrlichgesagt weiß ich gerade nicht, wozu du in deinem Projekt, das bisher keine Objekte hat, ein UpdateGrid brauchst, aber gut, das lass ich mal deine Sorge sein Wink
Naja ich bin ja gerade am implementieren dieser und da ich diesmal von Anfang an ein wenig auf Performance achten wollte (die Objekt <> Objekt Kollision in der BMax Version war auf einer Linked List basiert -> sehr langsam) wollte ich gleich ein UpdateGrid verwenden. Zitat: Das Textur-Kollisionsfeature für deine Hintergrundpixmap würde ich aber an deiner Stelle streichen und Objektkollisionen lieber auf deine bestehenden Abfragemethoden abbilden. Fällt Ingame später wahrscheinlich nicht auf, ist viel schneller und auch leichter zu handhaben. Ist jetzt aber weniger eine ZweiDe- und mehr eine Projektempfehlung. Wink
Werde ich machen, war nur ein kleiner Test. Sowie du es hier beschreibst ist die 2. Variante um einiges besser geeignet für meine Zwecke. Deshalb werde ich, auch diese verwenden. Vielen dank ![]() |
||
http://programming-with-design.at/ <-- Der Preis ist heiß!
That's no bug, that's my project! "Eigenzitate sind nur was für Deppen" -Eigenzitat |
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Okay, wenn du später größere Objektzahlen haben wirst, macht ein UpdateGrid schon Sinn. Ich hab in Nullpunkt für Projektile eins, da lohnt sich das bei größeren Schlachten auch mal richtig. Es gibt da das "Normale" UpdateGrid, welche Objekte als Punkt ansieht und das SizedUpdateGrid, wo jedes Objekt ein Kreis mit beliebigem Radius ist. Letzteres hat einen wesentlich höheren Verwaltungsoverhead, aber letztlich eignet es sich für Objektmengen mit hohen Größenunterschieden besser als das Standard-UpdateGrid, weil du bei Abfragen dann keinen pauschalen "Größenpuffer" in den Abfragebereich reinrechnen musst. ![]() |
||
![]() |
cooloBetreff: Scriptsystem funktionstüchtig \o/ |
![]() Antworten mit Zitat ![]() |
---|---|---|
Sooo,
das Scriptsystem von meinem Spiel ist bereits fertig. Dabei habe ich mich stark an Fetze's Erklärung gehalten (welche sehr genau ist, weswegen es nicht schwer war). Dabei habe ich auch gleich das erste Objekt implementiert: Den Spieler. Dieser kann bereits fröhlich umher gehen, springen usw. Das Script für den Spieler sieht folgendermaßen aus: Code: [AUSKLAPPEN] namespace Script {
public class Player { public static IEnumerable<ScriptCondition> Update( ScriptObject self ) { self.setSize( 20, 40 ); while ( true ) { //endlos schleife, welche jedes Frame aufgerufen wird ZweiDe.DrawRect( self.X, self.Y, self.Width, self.Height ); if ( ZweiDe.KeyDown( ZweiDe.KeyID.Left ) ) { self.move( -1f, 0 ); } if ( ZweiDe.KeyDown( ZweiDe.KeyID.Right ) ) { self.move( 1f, 0 ); } if ( ZweiDe.KeyDown( ZweiDe.KeyID.Space ) && self.touchGround()) { self.move( 0, -10 ); } self.updatePhysics( ); yield return new ScriptWaitCondition( 0 ); } } } } Neben der "ScriptWaitCondition" gibt es auch die "ScriptDelegateCondition" Klasse. Diese ist dazu da um Event basiertes Scripting zu ermöglichen. Ein neues Bild lohnt sich im Moment noch nicht, da sich noch nicht viel geändert hat. |
||
http://programming-with-design.at/ <-- Der Preis ist heiß!
That's no bug, that's my project! "Eigenzitate sind nur was für Deppen" -Eigenzitat |
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Sag doch dazu, dass du die Erklärungen von Seite 13 und nicht die hier oben meinst. Ich war grad schon drauf und dran, extrem beeindruckt zu sein, dass du als C#-Neuling in so kurzer Zeit scheinbar von alleine auf die yield-Variante gekommen bist. ![]() Freut mich, dass es so gut von der Hand ging ![]() Was dein Script betrifft, muss ich aber sagen, dass ich es für etwas übertrieben halte, selbst die Update-Funktion eines Objekts über den asynchronen Scripting-Ansatz zu realisieren, d.h. mit yield und in dem Fall einer geschachtelten Schleife - das wird zwar funktionieren, aber letztendlich ist eine Update-Funktion eines Objekts der Inbegriff eines synchronen Scripts: Du kannst exakt bestimmen, wann es aufgerufen wird, wie oft (mit festen Zeitabständen) und in aller Regel gibt es keinen Grund für funktionslokale Verzögerungen. Deinen Initialisierungscode (self.setSize) könntest du auch in eine separate Init-Funktion auslagern. Das soll aber nur ein Anreiz sein, stellenweise hab ichs in Nullpunkt auch so, "weils grad so praktisch war" ^^ Ich feile derweil am neuen HUD. Erstmal wird das ganze soweit möglich mit ZweiDe Primitives realisiert, später hau ich dann Texturen drüber, die da ein wenig mehr Struktur reinbringen sollen. Komme allerdings im Moment kaum dazu, weil ich hier grad besser für Klausuren lernen sollte als hier herumzuprogrammieren.. |
||
![]() |
coolo |
![]() Antworten mit Zitat ![]() |
---|---|---|
Ich werde noch "Event Methoden" implementieren. Also Update, Init und Leave, danke für den Hinweis.
Noch eine kleine Frage: Gibt es einen speziellen Grund weswegen Renderbuffer auf die Bildschirmgröße begrenzt sind? Gibt es eine Möglichkeit dies zu umgehen? Weil eine Map ist eigentlich immer größer als das Grafikfenster. |
||
http://programming-with-design.at/ <-- Der Preis ist heiß!
That's no bug, that's my project! "Eigenzitate sind nur was für Deppen" -Eigenzitat |
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Der Renderbuffer ist nicht auf Bildschirmgröße begrenzt, aber da man damit auf eine Textur rendert, ist das ganze natürlich auf die maximale Texturgröße deiner Grafikkarte beschränkt. Bei größeren Landschaften würde ich eine Einteilung der Landschaft in (zB.) 256x256 große "Tiles" vorschlagen, wobei jedes Tile seine eigene Pixmap und seinen eigenen Renderuffer hat. Ansonsten wirst du wohl nicht über 4096x4096 (oder je nach Rechner auch weitaus weniger) raus kommen und schnell an Performancegrenzen stoßen. Hier mal ne Pro/Contra-Liste der "Tiling"-Variante.
(-) Mehr Verwaltungsaufwand zum Malen und zur Kollisionsabfrage - erstmal betroffene Tiles finden und mit Offsets arbeiten. (+) Optimierte Darstellung (Immer nur die Tiles malen, die sichtbar sind, nicht die gesamte Karte!) (+) Erhöhte Kompatibilität durch kleinere Texturen. (+) Wenn du's geschickt implementierst (Beispiel: Bei weit von der Kamera entfernten Tiles die Textur freigeben und nur die Pixmap behalten, bei Annäherung Textur aus Pixmap generieren) --> Unbegrenzte Kartengröße. Du kannst, wenn er gerade nicht gebraucht wird, je Tile auch den Renderbuffer freigeben und später neu anfordern. Das spart Ressourcen. Musst mal ein wenig herumexperimentieren. Habe noch keine Worms-Like Landschaft umgesetzt, aber mit deinem bisherigen Ansatz bist du durch Grafikkartenschranken doch etwas eingegrenzt (Asche auf mein Haupt für meinen in dieser Hinsicht undurchdachten Umsetzungsvorschlag in unserem PN-Austausch). Mit ein paar Tricks (wie zB. die "Tile"-Variante oder irgendwas anderes) sollte sich da aber auch locker was finden lassen, das dir mehr Möglichkeiten eröffnet. ![]() |
||
![]() |
coolo |
![]() Antworten mit Zitat ![]() |
---|---|---|
Die "in einzelne Tiles zerhacken" Variante habe ich in der BlitzMax Version verwendet. Diese hatte halt den - wie du schon erwähnst- Nachteil, dass es einen größeren Verwaltungsoverhead hat. Ich habe halt gehofft dass das irgendwie anders zu lösen ist.
Naja da werde ich wohl das ganze umschreiben müssen... |
||
http://programming-with-design.at/ <-- Der Preis ist heiß!
That's no bug, that's my project! "Eigenzitate sind nur was für Deppen" -Eigenzitat |
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
![]() Neues HUD ist, was das Optische betrifft, fertig. An Logik und Funktionalität wird noch ein wenig gefeilt, aber im Wesentlichen steht die auch schon. Bin mit dem Resultat soweit sehr zufrieden. ![]() |
||
![]() |
ozzi789 |
![]() Antworten mit Zitat ![]() |
---|---|---|
Oha Fetze ![]() Sieht super aus! |
||
0x2B || ! 0x2B
C# | C++13 | Java 7 | PHP 5 |
![]() |
Starwar |
![]() Antworten mit Zitat ![]() |
---|---|---|
Hallo,
erstmal Danke für ZweiDe. Ich muss echt überlegen ob ich XNA weiter benutze. Vielleicht schreibe ich ja eine Art Game-Klasse für ZweiDe, um das ganze noch etwas übersichtlicher zu machen. Mir ist beim Testen von ZweiDe aufgefallen, dass ein Teil meines PCs anfängt laut zu werden. Es scheint die Festplatte zu sein. Eine weitere Frage an Fetze: Ist es möglich den gesamten nicht-Grafik-Teil zu entfernen, da mich irrKlang stört? Für alle x64 Benutzer von VisualStudio 2008 Express: Wie schalte ich von x64 auf x86 um? Zitat: Extras-> Optionen-> Alle Einstellungen anzeigen-> Projekte und Projektmappen-> Alllgemein -> Erweiterte Buildkonf. anzeigen-> OK
Erstellen -> Konfigurationsmanager -> SPalte Plattform (Dropdown) -> x86 Wenn x86 nicht vorhanden dann: Neu -> Neue Plattform x86, Einstellungen Kopieren von: AnyCPU MFG |
||
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Das Soundzeug in ZweiDe wird intern weder im Hauptmodul (Ausnahme: ShowLogo) noch in irgendeiner Extension verwendet. Wenn du den Soundpart nicht haben willst, kannst du einfach alle Sound-bezogenen Klassen aus ZweiDe löschen und die Referenz auf irrKlang sowie die zugehörigen .dll Dateien in der Projektmappe entfernen. Hab ich für Nullpunkt auch gemacht, das ist jetzt powered by OpenAL. ![]() |
||
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
![]() Im Editor aufgenommene Demoszene featuring "Planet 1". Den Planet hab ich in Photoshop gebastelt, beleuchtet wird er aber dynamisch ingame. So kann ich für Cutscenes dort auch mal die Sonne aufgehen lassen oder ihm für etwas Dynamik eine leichte Drehung spendieren. |
||
![]() |
ZaP |
![]() Antworten mit Zitat ![]() |
---|---|---|
Das ist ein äußerst hübscher Planet. Kannst Du das Fotoladen Bild auch noch posten, zum Vergleich mit Beleuchtung / ohne Beleuchtung? | ||
Starfare: Worklog, Website (download) |
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Das hier ist die Diffusemap des Planeten. Specularmap und Normalmap sind relativ unspektakulär, daher nicht mit dabei. Atmosphäre ebenfalls nicht, die ist ein zweites Objekt und wird nach der Beleuchtung additiv draufgerendert.
![]() Vermutlich wird der Planet ingame in der Form wie oben aber nur in Cutscenes Verwendung finden. Ich habs mal als Hintergrundobjekt ausprobiert, aber das lenkt finde ich zu sehr vom Vordergrundgeschehen ab. Im Hintergrund wird er also erstmal nur in etwa halber Größe und halber Gesamthelligkeit auftauchen. Klingt zwar erstmal doof, wirkt aber immernoch schick. |
||
![]() |
coolo |
![]() Antworten mit Zitat ![]() |
---|---|---|
Wirklich hübsch, sieht fast so aus wie wenn es echtes 3D wäre.
Mein Projekt ist leider etwas still, was daran liegt dass ich einfach (wieder einmal ![]() Viel Glück, dass das bei dir nicht passiert (was ich nicht glaube ![]() |
||
http://programming-with-design.at/ <-- Der Preis ist heiß!
That's no bug, that's my project! "Eigenzitate sind nur was für Deppen" -Eigenzitat |
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
![]() Screenshot der neuen "FF Missiles", einfachen Verfolgungsraketen. Die Gegner-KIs verwenden sie noch nicht, weil die im Moment noch keine Logik zum Auswählen von Waffen drin haben, aber das steht als nächstes auf meiner ToDo. Was man hier schön beobachten kann sind die neuen Particle Trails, eine Spezialversion von Partikeln, die man in so ziemlich jedem Spacegame beobachten kann. Meist werden sie, wie hier, für Waffen oder Antriebe verwendet. Im Prinzip besteht ein Trail aus einer Verkettung von Partikeln, die Partikel selbst verhalten sich jedoch exakt wie alle anderen Partikel auch. Der einzige Unterschied besteht darin, wie die Partikel gerendert werden. Was man hier leider nicht sehen kann: Ein Particle Trail hat je nach Partikeltyp eine Textur, die sich vom Emitter ausgehend "herauszieht", jeder Partikel im Trail agiert dabei als Knotenpunkt für Texturkoordinaten. Ebenso wirken sich Größe und Färbung der Partikel auf die jeweiligen Knotenvertices aus, sodass es auch kein Problem ist, Trails zu realisieren, die sich wie Rauchsäulen verhalten, o.Ä. Die bereits explodierte Rakete im Bild ist am Schild detoniert, der den meisten Schaden abgefangen hat. Raketen verursachen komplementär zu den Standard-Lasergeschossen nur einen Bruchteil ihres Schadens am Schild. Nur wenn sie auf die Rüstung treffen, entfaltet sich ihr volles Zerstörungspotenzial. Was man hier schön beobachten kann: Nachdem die erste Rakete durch ihre Explosion den Schild durchbrochen hat, reagiert die zweite nicht mehr auf die Kollisionsform des Schildes sondern wird auch optisch direkt an der Hülle detonieren. Dieses Detail fällt an sich kaum auf, macht aber einen deutlichen Unterschied im Spielgefühl. So sieht man nicht nur an den Energiebalken (und Effekten), wo eine Waffe getroffen hat, sondern wirklich auch daran, wo sie aufschlägt. Die FF Missiles haben natürlich auch eine Munitionsbeschränkung, die im HUD angezeigt wird. Im Moment liegt die maximale Munition pro Raketenwerfer bei 5 und in der Regel feuert man zwei Raketenwerfer gleichzeitig ab, macht also 5x2 Schuss. Wenn die Munition leer ist.. tja.. da hab ich mir ne Weile Gedanken drüber gemacht. Variante 1 wäre gewesen, es wie im Vorbild FreeSpace zu machen und per Funk Munitionstransporter anzufordern, die dann einfliegen, das Schiff suchen, andocken, aufladen. Da ein aktives Kommunikationssystem gar nicht geplant ist, und der Docking-Code eine ganze Weile brauchen würde, fiel das weg. Ich hab mich dann für Variante 2 entschieden: Bestimmte Schiffe gelten als Munitionstransporter (zB. das große Transportschiff einige Posts zuvor) und wenn man sich nah genug bei ihnen aufhält und die Relativgeschwindigkeit äußerst gering ist (Parallelkurs, gleiche Geschwindigkeit wie Transporter), wird die eigene Munition langsam aufgeladen. Dazu gibts auch nen Effekt im HUD, der das veranschaulicht. Ansonsten gibts noch zu vermelden, dass im Spiel nun endlich keine Musik aus StarTrade mehr enthalten ist. Ein Hobbykomponist namens Tim Suleymanov hat mir nach ein, zwei Monaten zusammenarbeit nun zwei Songs abgeliefert, die die bisherigen für Background- und Combat-Situationen ersetzen. Ich bin sehr zufrieden damit, aber ihr werdet das noch selbst in Aktion erleben können, wenn der nächste Release soweit ist ![]() |
||
![]() |
ZaP |
![]() Antworten mit Zitat ![]() |
---|---|---|
Die Particle Trails sind toll. Ich wollte sowas auch mal versuchen, aber mir ist kein guter Ansatz eingefallen. Wieviele Partikel hat der Trail von der Rakete auf dem Bild ungefähr? | ||
Starfare: Worklog, Website (download) |
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Hab nicht gezählt, dürften aber rund 15 - 20 sein. Weil jeder Partikel als Knotenpunkt für zwei Vertices, Texturkoordinaten, Farbe, Trailbreite und -ausrichtung fungiert, wird der Trail um so weicher, je mehr es sind. Für die meisten Trails dürften 10 - 20 Partikel ausreichen, für einige wenige vielleicht noch weniger, für andere besonders reaktive vielleicht noch mehr. Hab da noch nicht so viel mit herumexperimentiert ![]() |
||
![]() |
Fetze |
![]() Antworten mit Zitat ![]() |
---|---|---|
Obwohl ich eigentlich für Klausuren lernen sollte, bastel ich nebenher ein wenig am Content. Hab heute meinen ersten Asteroiden modelliert und texturiert. So sieht er im Editor aus:
![]() In der Nahaufnahme kann man schön die bläulichen Reflexionen eines von mir im Hintergrund platzierten Gegenlichts sehen. Ich dachte da an metallische Ablagerungen, die ich als unregelmäßige Specular-Verteilung über den Asteroiden realisiert hab. ![]() |
||
Gehe zu Seite Zurück 1, 2, 3 ... 17, 18, 19, 20 Weiter
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group