AC.AL Version 1.3 und Linux

Leider habe ich länger nichts mehr geschrieben, was allerdings nicht heißt, das ich untätig gewesen bin ;)

Ich bin stolz die neue Version von AC.AL präsentieren zu können (v. 1.3), welche nun auch auf Linux funktioniert!

Für den nächsten Release ist Music-Streaming und evtl. auch schon MacOS-Support geplant. Mal schauen, was die Zeit so mit sich bringt.

Den Release könnt ihr wie immer auf der Codeplex-Seite herunterladen:
http://acal.codeplex.com/

P.S.: Wer einen guten Artikel zu native invokes unter Windows und Linux mit .NET und Mono sucht, findet hier einen ausgezeichneten Artikel:
http://rogue-modron.blogspot.com/2011/11/invoking-native.html

Version 1.0 von AC.AL veröffentlicht

Die letzten Tage war ich dabei, meinen OpenAL Wrapper für .NET mal wieder etwas aufzuarbeiten.

Da er recht übersichtlich und einfach zu benutzen ist, dachte ich mir das vielleicht ein paar Programmierer dort draußen, die in das Thema neu einsteigen, diesen als Grundlage gebrauchen könnten.

Neben der Wrapper-Funktion unterstützt die Bibliothek auch RIFF Wave und Ogg Vorbis Dateien.

Nun habe ich mich entschlossen ihn als OpenSource bei Codeplex zu veröffentlichen! Lizenz ist die MIT-Lizenz, was soviel bedeutet wie das ihr alles damit machen könnt, solange der Copyright- und der Lizenz-Hinweis mit dem Projekt mitgeliefert werden.

Über Credits und Kritik jeglicher Art bin ich natürlich immer dankbar und hoffe das es bei einigen Verwendung finden kann. :)

Aber genug des Geschwafels ;) , hier ist der Link zur Codeplex-Seite:
http://acal.codeplex.com/

Vlc für Videos in Spielen

Die letzten Tage hatte ich mal wieder einiges in der Firma zu knobeln. Für Videos in Spielen benutzen wir auf Windows Vlc, doch das Problem war, das die .NET Interop Bibliothek seit 2+ Jahren nicht mehr aktualisiert wurde und schon gar nicht mit der neuesten Vlc Version kompatibel ist. Mal wird das Video zu klein abgespielt, mal ist der Bildschirm komplett schwarz und manchmal öffnet sich ein extra Fenster, in welchem das Video abgespielt wird. Somit war es völlig unbrauchbar. Allerdings hatten wir bisher noch keine gute Alternative gefunden.

Doch dann hat sich ein netter kleiner Blogeintrag, von 2009 doch auch aktualisiert, gefunden. Dieser hat sich zum Ziel genommen gehabt, einen Kontrapunkt zu den vielen Outdated oder total überdimensionierten Bibliotheken zu setzten, indem er einfach nur einen Startpunkt zum eigenen kleinen Interop liefert.

Gesagt getan war in kurzer Zeit ein kleiner aber feiner Wrapper gebaut und alle Probleme haben sich in Nichts aufgelöst.

Fazit: Auch wenn man oft davor zurückschreckt, es lohnt sich oft auch mal das Rad neu zu erfinden um die beste Lösung zu erhalten. Gerade da Vlc nunmal nicht mit dem Gedanken der Spieleprogrammierung geschaffen wurde, ist es sinnvoll sich die Features herauszupicken, die man braucht.

Ein Großer Dank geht hier an Helyar.net für den wunderbaren Blogpost.

Part 1
Part 2

Ich hoffe es hilft, diese Lösung etwas mehr zu verbreiten und wenn ich Zeit finde, werde ich mal einen kleinen Interop speziell für Spiele in .NET erstellen und hier veröffentlichen.

Stay tuned.

Short: Boolean und die Größe – Fixed

In den letzten Tagen war ich auf der Suche nach einer Lösung für das Boolean Problem und habe nun eine recht gute gefunden.

Erst einmal möchte ich noch ein Beispiel für die standard Größe zeigen:

1
2
3
4
struct testStruct
{
  bool bool1, bool2, bool3, bool4, bool5, bool6, bool7, bool8, bool9, bool10;
}
struct testStruct
{
  bool bool1, bool2, bool3, bool4, bool5, bool6, bool7, bool8, bool9, bool10;
}

Wenn wir das Ganze mit Marshal.SizeOf ausgeben lassen, erhalten wir 40 bytes.
Nun hatte ich mich an die StructLayout-Möglichkeiten erinnert und ausprobiert, was es noch gibt. Für das Wrappen von Typen in Unmanaged Typen gibt es ein Attribut, names “MarshalAs”, mit der Eigenschaft “UnmanagedType”. Das ist auch schon eine mögliche Lösung. Wir fügen in einem Struct einfach das Attribut zu den boolean Variablen hinzu und geben als UnmanagedType “I1″ an.

1
2
3
4
5
struct testStruct
{
  [MarshalAs(UnmanagedType.I1)]
  bool bool1, bool2, bool3, bool4, bool5, bool6, bool7, bool8, bool9, bool10;
}
struct testStruct
{
  [MarshalAs(UnmanagedType.I1)]
  bool bool1, bool2, bool3, bool4, bool5, bool6, bool7, bool8, bool9, bool10;
}

Wenn wir das ganze nun wieder überprüfen, erhalten wir 10. Demnach hat nun jeder boolean ein einzelnes Byte, so wie wir uns das gewünscht hatten. :)

Nun hat man aber nicht immer ein Struct in welchem wir booleans benutzen und das Attribut reicht in Klassen nicht allein. Damit das Ganze auch für Klassen funktioniert, müssen wir der Klasse das “StructLayout” Attribut anfügen und als “LayoutKind” “Sequential” wählen.

So kann man nun seine Structs auch wieder Speichereffizienter nutzen ;)

Wissenswert: Boolean und die Größe

Jeder kennt es, jeder benutzt es und viele erschrecken sich, wenn sie merken wieviel Speicher eigentlich eine boolean Variable verwendet.
Ein Boolean kann 2 Status annehmen, True oder False, 1 oder 0. Da denkt man eigentlich das ein Bit völlig ausreichen würde oder wenn man in der kleinsten Einheit in vielen Programmiersprachen ausgeht, einem Byte.
Doch auf normalen Rechnern wird für einen boolean entweder 32 Bit oder 64 Bit verwendet, je nach Architektur. Das heißt ein boolean verwendet 4 oder 8 Byte! Begründet wird dies mit der besseren/schnelleren Verwendbarkeit der Variable im CPU.

Jetzt werden einige Sagen, wenn ich sizeof(bool) ausgeben lasse, kommt dort 1, also ein Byte. Das ist soweit richtig, da C# einen boolean wie ein byte handhabt, doch wenn man sich einmal die Größe einer boolean Variable mit Marshal.SizeOf anzeigen lässt, steht bei 32 Bit Systemen dort 4.

1
2
bool test = true;
Marshal.SizeOf(test);
bool test = true;
Marshal.SizeOf(test);

Der Unterschied hier ist, das sizeof(bool) die C# Variante der Größe wiedergibt und Marshal.SizeOf die Unmanaged Größe angibt. Demnach kann man davon ausgehen, das intern ein C# Boolean auch als 4/8 Byte gespeichert wird.

Auch Wikipedia sagt zu diesem Thema:

[...]Aus technischen Gründen wird jedoch in der Regel eine andere Repräsentation gewählt, meist abhängig von der Wortbreite des konkreten Systems.[...]
Quelle: Wikipedia

Daher muss man sich Gedanken machen, wenn es um die Größe von z.b. einem struct geht, ob man nicht lieber eine byte Variable verwendet. Dies ist zwar nicht so sicher da man mehr als nur 2 Status darin speichern kann, doch spart es recht viel Speicher. Und wenn man oft structs hin und her kopieren muss, oder neue Instanzen erstellen muss, kann das schonmal perfomance fressen.

Short: Button Status erweitern

Zurzeit überarbeite ich den Input-Teil meiner Engine und möchte nun eine Möglichkeit präsentieren, wie man den doch recht simplen ButtonState von XNA erweiter kann.

Erst einmal zur Erinnerung: Die ButtonState Enumeration für z.b. den Mouse LeftButton bietet nur zwei States an. Pressed und Released. Für den Anfang mag das vollkommen ausreichend sein, doch wenn man erweiterte UI Features wie das Ändern des Buttonbildes beim Klick einbaut möchte man oft mehr State-Informationen haben.

Daher habe ich nun folgende States zusammengefasst:

  • NotPressed: Der Button ist nicht gedrückt, er wurde noch nie gedrückt, oder der vorherige Status war Released.
  • JustPressed: Der Button wurde gerade gedrückt, der vorherige Status war NotPressed.
  • Pressed: Der Button ist weiterhin gedrückt, der vorherige Status war JustPressed.
  • Released: Der Button wurde losgelassen und der vorherige Status war JustPressed oder Pressed.

Das sind nun schon einige Möglichkeiten mehr, auf die man zurückgreifen kann. Man kann überprüfen ob der Button schnell geklickt wurde (JustPressed dann Released), oder mit JustPressed beginnt z.b. ein Dragging, bei Pressed wird das Dragging fortgesetzt und bei Released beendet. Da finden sich einige Anwendungsfälle.

Aber wie kann man mit hilfe der zwei States von XNA nun diese vier setzten? Das ist recht einfach. Dafür habe ich mir folgende Methode gebaut, welche für alle Buttons verwendbar ist:

1
2
3
4
5
6
7
8
9
10
11
12
13
private ExtendedButtonState HandleButton(bool pressed, bool previousPressed)
{
  if (pressed && previousPressed == false)
    return ExtendedButtonState.JustPressed;
 
  if (pressed && previousPressed)
    return ExtendedButtonState.Pressed;
 
  if (pressed == false && previousPressed)
    return ExtendedButtonState.Released;
 
  return ExtendedButtonState.NotPressed;
}
private ExtendedButtonState HandleButton(bool pressed, bool previousPressed)
{
  if (pressed && previousPressed == false)
    return ExtendedButtonState.JustPressed;

  if (pressed && previousPressed)
    return ExtendedButtonState.Pressed;

  if (pressed == false && previousPressed)
    return ExtendedButtonState.Released;

  return ExtendedButtonState.NotPressed;
}

Die Methode bekommt ganz einfach einen boolean, welcher angibt ob der aktuelle ButtonState Pressed ist und einen weiteren boolean, ob der letzte State Pressed war. Mehr brauchen wir gar nicht.

Dann überprüfen wir:

  • Ist der Button jetzt gedrückt und war vorher nicht gedrückt? Also JustPressed.
  • Ist der Button gedrückt und wurde vorher schon gedrückt? Also Pressed.
  • Ist der Button nicht mehr gedrückt und war vorher gedrückt? Dann Released.
  • Und wenn nichts davon zutrifft, dann haben wir den NotPressed Status.

Und schon hat man eine sehr viel detailierte Übersicht über den aktuellen Button-Status. Hier noch kurz der Aufruf der Methode:

1
2
3
4
5
6
7
8
LeftButton = HandleButton(state.LeftButton == ButtonState.Pressed,
  lastState.LeftButton == ButtonState.Pressed);
 
MiddleButton = HandleButton(state.MiddleButton == ButtonState.Pressed,
  lastState.MiddleButton == ButtonState.Pressed);
 
RightButton = HandleButton(state.RightButton == ButtonState.Pressed,
  lastState.RightButton == ButtonState.Pressed);
LeftButton = HandleButton(state.LeftButton == ButtonState.Pressed,
  lastState.LeftButton == ButtonState.Pressed);

MiddleButton = HandleButton(state.MiddleButton == ButtonState.Pressed,
  lastState.MiddleButton == ButtonState.Pressed);

RightButton = HandleButton(state.RightButton == ButtonState.Pressed,
  lastState.RightButton == ButtonState.Pressed);

Short: Videos für XNA konvertieren

Da wir in der Firma unseren kompletten Content automatisiert in die richtigen Formate, für die jeweiligen Platformen, konvertieren lassen, war ich auf der Suche nach einem passenden Tool für XNA Videos. Das Problem bei der Sache ist, dass XNA ganz spezielle Anforderungen für Video Content hat. Das Video muss als SinglePass WMV9 enkodiert sein, muss einen Audiokanal haben und darf keine variable Bitrate besitzen. Das sind schon einige Restriktionen.

Normalerweise ist das Konvertieren von Videos kein Problem, man öffnet einfach den Movie Maker oder ähnliche Programme und speichert das richtige Format. Da wir aber alles automatisiert machen, wurde die Suche schwieriger. Das Program musste entweder per Kommandozeile steuerbar sein oder man müsste eine Programmieranbindung haben. Und das ist schon das größte Problem. Klasse Programme wie ffmpeg haben WMV9 nicht integriert da Microsoft die Hand über das Format hält und es auch keine richtige Dokumentation darüber gibt.

Nach langer Suche hat sich dann aber doch eine Möglichkeit gefunden und das sogar direkt von Microsoft selbst. Das Program Expression Encoder liefert neben dem Program auch ein SDK für C# mit, womit man direkt im eigenen Program die Videos konvertieren kann und alle möglichen Einstellungen vornehmen kann. Und am besten ist, es kann WMV9.

Damit hat sich das Problem gelöst und für alle die ebenfalls davor stehen, hier einmal ein kleiner Codeschnipsel, wie man Videos für XNA enkodieren kann:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Job job = new Job();
MediaItem item = new MediaItem(SourceFilePath);
job.MediaItems.Add(item);
job.OutputDirectory = "C:\\";
job.CreateSubfolder = false;
 
item.OutputFormat.VideoProfile = new AdvancedVC1VideoProfile()
{
  Size = item.MainMediaFile.VideoStreams[0].VideoSize,
  Bitrate = new ConstantBitrate(1000),
};
 
item.OutputFormat.AudioProfile = new WmaAudioProfile()
{
  Channels = 2,
};
 
job.Encode();
Job job = new Job();
MediaItem item = new MediaItem(SourceFilePath);
job.MediaItems.Add(item);
job.OutputDirectory = "C:\\";
job.CreateSubfolder = false;

item.OutputFormat.VideoProfile = new AdvancedVC1VideoProfile()
{
  Size = item.MainMediaFile.VideoStreams[0].VideoSize,
  Bitrate = new ConstantBitrate(1000),
};

item.OutputFormat.AudioProfile = new WmaAudioProfile()
{
  Channels = 2,
};

job.Encode();

Und falls man noch als Zusatzinformation wissen möchte, wie lang das Video ist, oder aber auch andere Information, kann man das über das MediaItem machen:

1
float timeLength = (float)item.FileDuration.TotalSeconds;
float timeLength = (float)item.FileDuration.TotalSeconds;

Short: Die Notfalltextur

Manchmal, wenn man einfach nur mal eben einen Grafiktest machen will, oder auch generell falls eine Content Datei fehlt steht man immer vor der Frage: was macht man? Sucht man sich irgendwoher eine kleine Textur zum testen raus, oder lässt man das Spiel abstürzen wenn der Content fehlt?

Die Lösung lautet CheckerMap, oder aber die “Notfalltextur”.

Hierbei handelt es sich um eine zur Laufzeit generierte, 4×4 Pixel große Schachbrettmuster-Textur, welche fehlende Bilddaten zum testen ersetzten kann.

Meist benutzt man dazu einfaches Grau und Silber. Das Schachbrettmuster daher, damit man z.b. die UV-Koordinaten, oder Übergänge auch mit der Testtextur erkennen kann.

In diesem Beispiel wollen wir die CheckerMap mit Hilfe des XNA-Frameworks erstellen.

Da die Textur keinerlei Alpha oder besondere Farbenvielfalt benötigt, können wir zusätzlich die Texturgröße im Speicher ziemlich gering halten. Benutzen tun wir das Bildformat Bgr565, welches 5 bits für blau und rot verwendet und 6 bits für den grünen Kanal. Demnach benötigen wir pro Pixel nur 16 bits, wodurch wir perfekt den ushort Datentyp verwenden können. Für das “Silber” verwenden wir dann den ushort Wert “38034″ und für das “Grau” den Wert “12678″.

Aber nun zum Code:

1
Texture2D texture = new Texture2D(GraphicsDevice, 4, 4, false, SurfaceFormat.Bgr565);
Texture2D texture = new Texture2D(GraphicsDevice, 4, 4, false, SurfaceFormat.Bgr565);

Dieser Einzeiler erstellt uns eine neue Instanz von Texture2D welche 4×4 Pixel groß ist, keine Mipmaps generiert (was bei der CheckerMap unnötig wäre) und das Format Bgr565 für die Daten benutzt.

Aber noch haben wir eine leere Textur, deshalb füllen wir sie nun mit unserem Schachbrettmuster. Dafür speichern wir uns erst einmal unsere zwei Pixel-UShorts:

1
2
ushort silver = 38034;
ushort gray = 12678;
ushort silver = 38034;
ushort gray = 12678;

und füllen jetzt die Textur:

1
2
3
4
5
6
7
texture.SetData<ushort>(new ushort[]
{
  gray, silver, gray, silver,
  silver, gray, silver, gray,
  gray, silver, gray, silver,
  silver, gray, silver, gray,
});
texture.SetData<ushort>(new ushort[]
{
  gray, silver, gray, silver,
  silver, gray, silver, gray,
  gray, silver, gray, silver,
  silver, gray, silver, gray,
});

Und schon haben wir eine wunderschöne, naja zweckmäßige, Textur falls es mal schnell gehen soll. Wenn man eine eigene kleine Engine schreibt ist es hilfreich diese Hilfstextur direkt darin zu intergrieren, damit man sie immer ohne große Umschweife zur Verfügung hat.

Eine wichtige Sache abschließend wäre noch das Filtering der Textur! Da es nur 4×4 Pixel sind wird die Textur in fast allen Fällen skaliert. Dadurch wird die Textur bei Standardshadern schnell schwammig und verliert das Schachbrettmuster. Daher ist es wichtig immer für diese Textur den MinFilter und MagFilter auf Nearest zu stellen. Somit ist das Muster in jeder Größe garantiert. :)

Short: Arrays über Properties erreichen

Kürzlich tauchte die Frage auf, ob es gut sei, ein Array über ein Property nach außen hin erreichbar zu machen und ob dieses dann durch das Property kopiert werden würde.

Dazu erstmal etwas grundlegendes über Arrays. Auch wenn die Schreibweise so gut wie immer so oder so ähnlich aussieht wie int[] oder string[], handelt es sich immer noch um eine Instanz der Klasse Array. Demnach wird das Array beim Property als Referenz übergeben, da dies bei Klassen bekanntermaßen automatisch geschieht. Demnach ist es also kein Problem, ein Array per Property, oder auch Methode erreichbar zu machen.

Wirklich kein Problem? In Bezug auf das Kopieren, ja. Doch birgt dieses Unterfangen ganz andere Fallstricke, welche immer wieder in unbedacht geschriebenem Code zu sehen sind. Da es sich um eine Referenz handelt, muss zwar nichts kopiert werden, doch spiegelt sich auch jegliche Änderung an den Elementen des Arrays auf das interne Array zurück! Das ist gerade dann ein Problem, wenn dadurch Statusänderungen von wichtigen Objekten im Spiel möglich werden, welche eigentlich nur im Objekt selbst gehandhabt werden sollen.

Das heißt also abschließend: Es kommt immer auf den Fall an, wann man ein Array (und das gilt für alle anderen Referenz-Typen auch) nach außen hin verfügbar macht. Wenn es 100% um Performance geht, wie z.b. bei sich ständig ändernden und großen Vertex Arrays, dann ist es besser direkt das Array als Referenz zu übergeben, da das Kopieren immer viel Zeit in Anspruch nimmt. Hier sollte man auf die Vernunft des Benutzers vertrauen und bei Zweifeln evtl. eine Notiz als Kommentar hinterlegt werden. Doch wenn Performance keine Rolle spielt, sollte man lieber den Kopier-Weg vorziehen, da so das ganze System weniger fehleranfällig arbeiten kann.

Short: Netzwerk Nachrichten identifizieren

In diesem Short möchte ich auf die Identifikation von Netzwerknachrichten in Spielen, aber auch generell eingehen.

Kürzlich hatte ich mal wieder ein bisschen in altem Code gestöbert und bin dabei auf eine Netzwerk-Implementierung gestoßen. Als ich sah, wie ich damals die verschiedenen Nachrichten identifiziert hatte, kam mir glatt das Grauen. Deshalb möchte ich nun eine effizientere Methode vorstellen, damit hoffentlich ein paar Programmierer die in das Thema einsteigen nicht den gleichen Fehler begehen.

Aber wie hatte ich nun die Nachrichten aufgebaut? In Spielen ist es sinnvoll den Typ der Nachricht zu markieren, wie z.b. eine Nachricht die für den Chat gedacht ist. Dafür hatte ich dann folgendes geschrieben:

Send(“ChatMessage|” + msgText);

Ja, da kommt einem die Gänsehaut ^^, doch aus Fehlern lernt man. Was hier falsch gelaufen ist, ist die Länge der Nachricht, gerade in größerem Maßstab gedacht, wenn sekündlich Chat Nachrichten geschrieben werden. Eine Nachricht mit einer String zu identifizieren ist weder effizient, noch zielführend, da eine String eine große Anzahl von bytes in den Stream schreiben müssen. Daher hier nun ein Lösungsansatz.

Wenn man sich überlegt, wie viele Nachrichtentypen man benötigt wird man häufig eine recht kleine Zahl haben, z.b. 64. Da die Netzwerk Nachrichten als byte Stream versendet werden, was ist da nicht naheliegender als ein extra byte (0-255) welcher den Typ angibt?

Das heißt einfach das man die Nachrichtendaten am Anfang um ein byte erweitert, den entprechend auf der Empfägerseite wieder ausließt und den Rest der Nachricht in das gewünschte Format konvertiert.

Einige werden jetzt vielleicht sagen: “Das ist doch total unübersichtlich, einfach eine Zahl!”. Das stimmt durchaus, doch wenn man z.b. in C# arbeitet, kann man sich eines netten kleinen Tricks bedienen.

Man erstellt ein enum z.b. “enum MessageType” und schreibt dort mit Namen und Beschreibung den Typen auf und leitet diesen enum dann von byte ab. Natürlich muss dann auch für jeden Wert im enum ein eindeutiger byte Wert zugeordnet werden.

1
2
3
4
5
public enum MessageType : byte
{
  LoginMessage = 0,
  ChatMessage = 1,
}
public enum MessageType : byte
{
  LoginMessage = 0,
  ChatMessage = 1,
}

Und schon hat man detailierte ID’s, ohne großen Speicherbedarf zu benötigen. Und falls einem ein byte doch nicht reichen sollte, nimmt man z.b. einfach einen ushort.