• Das Tutorial, was du grade angefangen hast zu lesen, dreht sich um SDL und die Verwendung von SDL in C#, bzw. in allen .Net-Sprachen.


    Vorwort - Hier steht noch nichts sehr wichtiges drin. Wenn du willst, kannst du direkt zum nächsten Punkt springen, aber ich fände es toll, wenn du es lesen würdest. Hab mir ja Mühe gegeben^^
    Was ist SDL?
    SDL steht für Simple DirectMedia Layer
    Wieso SDL?
    SDL ist eine Bibliothek, die für alle großen Sprachen umgesetzt wurde. Das heisst, dass die Verwendung unter vielen Sprachen ähnlich ist. Ich bin C++ Quellcodes das erste mal mit SDL in Berührung gekommen, habe in Python gelernt, damit umzugehen und konnte mein Wissen fast problemlos in C# umsetzen, da die Befehle genau die selben sind.
    SDL bietet Schnittstellen zur Grafikausgabe(2D und mit z.B. OpenGL 3D), zur Tonausgabe(Selbst auf CDs kann man damit zugreifen, das werde ich aber nicht erklären ;)), für Eingabe(Tastatur, Maus, Joystick...) sowie zur Netzwerkkommunikation. (Es gibt aber sicherlich auch bessere Frameworks für Netzwerk^^)
    Alles in allem beinhaltet SDL also alles, was man zum Spieleprogrammieren braucht!
    Wieso nicht XNA?
    Ganz einfach: Während die aktuelle Version von XNA bereits .NET 4.0 benötigt, (Afaik. Aber mindestens .NET 3.5 ;)) kommt SDL.NET auch locker mit .NET 2.0 klar, was hilfreich sein kann, wenn man das ganze mit Mono machen will, denn es wird auch kein DirectX vorrausgesetzt, womit SDL-Programme auch auf sehr alten Rechnern laufen können. Das sollte aber für uns ziemlich unwichtig sein ;)
    Des weiteren kann man SDL wie bereits gesagt auch in anderen Sprachen einsetzen, was mit dem XNA-Framework nicht möglich ist. Ursprünglich wollte ich das Tutorial für Python schreiben, aber ich bezweifle, dass sich hier viele für Python interessieren.
    Ein Nachteil gegenüber XNA ist, dass die Migration auf Zune oder die XBox360 wesentlich komplizierter, eigentlich für den Normaluser unmöglich ist. Normaluser bist du, wenn du keinen Zugang zu speziellen Dev-Tools für Konsolen hast :P
    Brauche ich für das Tutorial Vorkenntnisse?
    Ich gehe davon aus, dass du bereits ein wenig Ahnung von C# und .NET hast, du solltest auf jeden Fall damit umgehen können. Ausserdem solltest du ein wenig Mathe können^^ Ich werde vorraussichtlich nicht bspw. Integral- oder Vektorrechnung verwenden - Aber die Grundregeln solltest du beherrschen.
    Und wann gehts jetzt los?
    Jetzt.


    Vorbereitung - Erfahrene Benutzer können sich das ganze klemmen und einfach die Zusammenfassung lesen!
    Zuallererst brauchen wir die Bibliothek:
    http://sourceforge.net/projects/cs-sdl/files/SDL.NET/
    Als allererstes brauchen wir Visual Studio. Wie gesagt funktioniert das auch mit Mono, ich werde aber hier Visual Studio 2008 Express verwenden, da ich zu faul bin, die 2010er Version zu registrieren oder gar eine Profiversion zu saugen.
    Wenn wir jetzt also unsere Entwicklungsumgebung haben, erstellen wir ein neues Programm und wählen "Konsolenanwendung" aus.


    Als erstes müssen wir ein paar Verweise hinzufügen. Fangen wir einfach mit System.Drawing an. Im Projektmappenexplorer Rechtsklick auf "Verweise" und "Verweis hinzufügen" auswählen. Unter dem Reiter ".NET" musst du die Assembly "System.Drawing" hinzufügen. Wenn du das getan hast, kannst du nun SDL.NET einfügen. Unter dem Reiter "Durchsuchen" wählst du die Assemblies "sdlDoNet.dll" und "Tao.SDL" aus dem "bin"-Ordner vom SDL.NET Paket aus. Nun musst du aus dem "lib" Ordner von dem Archiv alle Dlls nehmen und in den Ausgabeordner(bei mir bin/debug, bzw. bin/release) kopieren. Dazu musst du die Projektmappe allerdings gespeichert haben!
    Das sieht nun nicht schön aus, aber es ist das, was wir brauchen.
    Zusammenfassung

    • Download:
      http://sourceforge.net/projects/cs-sdl/files/SDL.NET/
    • Konsolenanwendung erstellen
    • Assembly System.Drawing hinzufügen
    • DLLs aus dem bin/ Ordner des SDL.NET Paketes als Verweise hinzufügen
    • DLLs aus dem lib/ Ordner des SDL.NET Paketes in den Ausgabeordner kopieren


    Bitte beachtet die Lizensierung der Bibliotheken, da SDL unter GPL lizensiert ist!


    Erster Sourcecode
    Aus der Mainmethode erstellen wir erstmal eine Instanz der Klasse Program. Das sieht dann aus wie folgt:

    Code
    1. namespace Tutorial.SDL{ class Program { static void Main(string[] args) { Program app = new Program(); }
    2. public Program() { Video.SetVideoMode(500, 400); } }}


    In dem hier gezeigten Code erstellen wir auch gleichzeitig ein Fenster mit den Maßen 500x400. Dafür ist - unglaublich - Der Befehl Video.SetVideoMode() zuständig. Die Klasse Video ist für jegliche Video- bzw. Fensterarbeit zuständig.
    Die möglichen Parameter für SetVideoMode() sind folgende:

    • Keine Parameter: Es wird ein großes Fenster(Auflösung ist vermutlich die deines Bildschirms) angezeigt, dessen Größe unveränderlich ist und schwarz gefüllt ist.
    • (int width, int height): Es wird ein Fenster angezeigt, dessen Größe unveränderlich ist und schwarz gefüllt ist. Die Parameter geben die Auflösung des Fensters an.
    • (int width, int height, bool resizeable): resizeable gibt an, ob die Größe des Fensters verändert werden kann.
    • (int width, int height, bool resizeable, bool OpenGL): openGL gibt an, ob das Fenster OpenGL gerendert werden soll.
    • ([...], bool fullscreen): fullscreen gibt an, ob das Fenster im Vollbildmodus angezeigt werden soll.
    • ([...], bool hardwareSurface): hardwareSurface gibt an, ob das Bild über die Grafikkarte gerendert werden sollte. Aus Stabilitätsgründen sollte dieser Wert auf false stehen, es sei denn, du weist wirklich, was du tust!
    • ([...], bool frame): Wenn frame true ist, wird ein Rahmen um das Fenster gezeichnet. (Standard!) Wenn fullscreen true ist, wird dies ignoriert und der Rahmen einfach weggelassen.
    • (int width, int height, int bitsPerPixel): bitsPerPixel kann ich nicht besser erklären als der Name es schon selber tut. Wenn du nicht weißt, was das heißt, versuch es einfach zu umgehen
    • Die restlichen bool-Variablen sind auch für die Varianten mit bpp vorhanden und haben die selbe Bedeutung.


    Wenn du jetzt auf F5 drückst bzw. über die Toolbar das debugging startest, wirst du sehen, dass das Bild nur ganz kurz aufflackert und dann das Programm sich wieder beendet. Um das zu umgehen, musst du

    Code
    1. Events.Run();

    aufrufen. Was das bedeutet, erkläre ich gleich.
    Wenn du den Fenstertitel ändern willst, hast du dafür die Property WindowCaption(string).
    Das Icon oben links kannst du mit der Funktion WindowIcon() (void) setzen.
    Der Zyklus
    Wie bereits erwähnt, musst du, damit sich das Fenster nicht direkt wieder schließt, die Funktion

    Code
    1. Events.Run();

    aufrufen. Doch wofür steht das Objekt Events?
    Events ist ein sealed Member von SdlDotNet.Core. Die Klasse ist für jegliches Aktualisieren zuständig(Nicht das zeichnen!) und organisiert den Event-queue. Ich gebe eine kurze Zusammenfassung der wichtigsten Events, Methoden und Eigenschaften. (ein ! davor steht für ein Event und wird nicht mitgeschrieben!)

    • FPS (int) - Gibt die aktuellen FPS zurück und setzt die gewünschte FPS-Rate
    • TargetFPS (int) - Gibt die gewünschte FPS-Rate zurück und setzt diese auch.
    • Close() (void) - Schließt alle Untersysteme von SDL
    • PushUserEvent(UserEventArgs) (void) - Setzt ein User-Event an den Event-Queue. Ich definiere es hier nicht näher, das vertiefe ich später.
    • QuitApplication() (void) - Beendet das Programm
    • Start() (void) - Startet den Event-Queue. Ohne diesen Befehl wird der framerate-ticker nicht gestartet und das Fenster schließt sich sofort.
    • Wait() (void) - Stoppt den ticker solange, bis ein Event vorhanden ist. Für Performance-Zwecke sehr gut zu benutzen. Ich werde es hier vorerst nicht benutzen, aber ihr solltet es wissen ;)
    • ! AppActivate - Wird ausgelöst, wenn die App aktiv oder inaktiv wird.
    • ! KeyboardDown - Wird ausgelöst, wenn eine Taste auf der Tastatur gedrückt wurde. Die Starttaste wird nicht abgedeckt. Alle anderen Tasten werden als KeyboardEventArgs an den Eventhandler weitergegeben.
    • ! KeyboardUp - Wird ausgelöst, wenn eine Taste auf der Tastatur losgelassen wurde. Siehe oben.
    • ! MouseButtonDown - Wird ausgelöst, wenn mit der Maus geklickt wurde.
    • ! MouseButtonUp - Wird ausgelöst, wenn eine Maustaste losgelassen wurde.
    • ! MouseMotion - Wird ausgelöst, wenn sich die Maus bewegt.
    • ! Quit - Wird ausgelöst, wenn das Fenster geschlossen werden soll. Um einen reibungslosen Ablauf zu garantieren, sollte im Handler Events.QuitApplication() aufgerufen werden.
    • ! Tick - Wird jeden Frame ausgelöst.
    • ! UserEvent - Wird ausgelöst, wenn ein UserEvent kommt.
    • ! VideoResize - Wird ausgelöst, wenn die Größe des Fensters verändert wird.


    Tipp: Um ein gutes Tastaturhandling zu erreichen, kann eine boolsche Variable eingeführt werden, die anzeigt, ob eine Taste gedrückt wurde, bzw. ein Array/eine Liste, in dem alle Tasten die gedrückt wurden gespeichert wurden. Diese Tasten werden beim KeyboardUp-Event wieder gelöscht, damit man auch in anderen Frames weiß, ob eine Taste gedrückt wurde.


    2D-Grafik
    Bevor ich dich mit Code bombardiere, möchte ich dir ein bischen Theorie vermitteln. In SDL werden Flächen zum draufzeichnen verwendet, sogenannte Surfaces. Auf solchen Surfaces werden alle grafischen Operationen angewandt, bspw. Pixel, Rechtecke, Sprites oder andere Surfaces können auf Surfaces gezeichnet werden. A propos Sprites: Du weißt nicht, was Sprites sind? Wikipedia-Link
    Sprites können alle Objekte sein, allerdings werden Sprites für alle beweglichen Dinge verwendet, bspw. Spielfiguren. Sprites unterscheiden sich in der Hinsicht von Surfaces, dass auf Sprites nicht gezeichnet werden kann, sowie einige andere Funktionen für Surfaces für Sprites nicht verfügbar sind. Dafür gibt es für Sprites Funktionen, die es für Surfaces nicht gibt, bspw. eine einfache Überprüfung, ob sich zwei Sprites überschneiden.
    Man zeichnet also alles auf diesen Surfaces. Zum zeichnen von Grafiken (Bitmap aus dem Namespace System.Drawing), Sprites, Surfaces, ... stellt SDL im allgemeinen die Funktion blit() bereit. Diese kann man mit den zu zeichnenden Objekten und der gewünschten Position als Parameter aufrufen. Probier dich daran einfach aus, das ist wirklich einfach ;)
    Aber das einfache blitten (kA, ob das überhaupt richtig ist, blit kann man nicht übersetzen, aber es bedeutet, einen rechteckigen Ausschnitt im Grafik-Speicher zu verschieben) reicht nicht aus, um etwas auf den Bildschirm zu bringen. Wenn man sich die Methoden durchliest, kann man sehen, dass es eine Funktion Update() gibt. Diese bewirkt, dass die vorberechnete Fläche auch wirklich übernommen wird. Ohne Update() wird die Fläche also berechnet, aber nicht verarbeitet!
    Sprites und Surfaces können einfach als Objekt erstellt werden (Namespaces SdlDotNet.Graphics und SdlDotNet.Graphics.Sprites) und je nach belieben verwendet werden, die restlichen Methoden und Eigenschaften sind selbsterklärend (es sei denn, ich komme nochmal auf sie zurück)
    Aber wie genau zeichnet man nun auf den Bildschirm? Der Bildschirm hat eine ganz eigene Surface, auf die man über Video.Screen zugreifen kann. Dies ist ein Surface-Objekt, das in eine Variable geschmissen werden kann. Danach werden alle Arbeiten, die auf dieser Surface gemacht werden, direkt auf den Bildschirm übertragen. Der Vorteil (oder auch der Nachteil, da es das OOP-Prinzip zerstört) ist, dass aus jeder x-beliebigen Klasse auf Video.Screen zugegriffen werden kann und auch global jede Kopie davon automatisch aktualisiert wird.
    Ich komme nun zu etwas wesentlich interesanterem: Animierten Sprites. Dazu muss ich gleich drei Klassen einführen: AnimationCollection, AnimationDictionary und AnimatedSprite.
    Eine AnimationCollection hält alle Frames, die zu einer bestimmten Animation dazugehören. Des weiteren kann man einer Collection auch einen Namen geben. Folgend ein Code-Beispiel:

    Code
    1. AnimationCollection walk = new AnimationCollection(); // Erstellt ein neues Objektwalk.Add("tilesheet.png", new Size(16,16)); // Lädt alle Sprites aus den Spritesheet tilesheet.png, wobei alle Sprites als 16x16 angenommen werden. Möchte man nur ein Sprite hinzufügen, reicht es, eine einzelne Surface hinzuzufügen.
    2. walk.Delay = 1000; // Wartet 1sek zwischen jedem Frame
    3. AnimationCollection run = new AnimationCollection(); //Das selbe hierrun.Add("run.bmp", new Size(32, 32));run.Delay = 250;
    4. AnimationCollection stop = new AnimationCollection(); // Und hierstop.Add(new Surface("stop.bmp"));


    Die beschriebenen Befehle sind netterweise selbsterklärend. Das sind aber auch schon alle, die du brauchst, um eine AnimationCollection zu erstellen. Aber was fängt man jetzt damit an? Dazu komme ich jetzt. Wir können die verschiedenen Collections in Dictionaries werfen. Lustig, oder?
    Mal vorher zur Zusammenfassung: Eine AnimationCollection enthält alle Frames einer Animation. Eine AnimationCollection enthält alle Animationen und ein AnimatedSprite stellt einfach nur den Sprite dar.

    Code
    1. AnimationDictionary animations = new AnimationDictionary();
    2. animations.Add("Walk", walk); //Füge eine AnimationCollection hinzu und gebe ihr ein Namenanimations.Add("Stop", stop);
    3. animations["Walk"].Delay = 2000; // Ändere im Nachhinein eine Eigenschaft


    Extrem einfach, oder?
    Um jetzt das ganze zu einem Sprite hinzuzufügen, nutzen wir die bereits vorhandene Animations-Eigenschaft von der Klasse animatedSprite:

    Code
    1. AnimatedSprite hero = new AnimatedSprite();hero.Animations.Add(animations);hero.CurrentAnimation = "Walk";


    Die Aufrufe erstellen einen animierten Sprite, fügen die bereits vorhandenen Sprites hinzu und setzen die aktuelle Animation. Beim zeichnen wird dann automatisch das passende gezeichnet.
    Sound
    Sound ist wichtig. Das solltest du wissen^^
    SDL kann folgende Formate abspielen: Wave-Dateien, Ogg-Dateien, Midi-Dateien, VOC-Dateien sowie MikMod-Dateiformate. Leider ist mp3 nicht unterstützt, da mp3 eigentlich kein freies Format ist.
    Um einen Sound zu erstellen, benutzt man die Klasse Sound. Code-Beispiel:

    Code
    1. Sound scream = new Sound("scream.ogg");scream.Play();


    So einfach wird ein Sound abgespielt. Da das aber nicht alles sein soll, kann man mit der Play-Methode auch festlegen, wie oft das wiederholt wird. Auch kann man über die Volume-Eigenschaft die Lautstärke einstellen. Da das aber ziemlich selbsterklärend ist, komme ich zu etwas wesentlich interessanterem: Channels. Wer ein wenig geschaut hat, wird bemerkt haben, dass die Funktion Play() ein Objekt Channel zurückgibt. Channels bieten erweiterte Funktionen, um die Lautstärke zu setzen, um eine Position im Raum zu verschaffen (Souround-Sound, Woohoo), und vieles mehr. Beachtet bitte, dass per Default maximal 8 Channel bespielt werden können, d.h. es können max. 8 Sounds und Musiken gleichzeitig abgespielt werden. Um die Anzahl zu erhöhen, könnt ihr folgende Variable verwenden:

    Code
    1. Mixer.ChannelsAllocated = 100;

    würde also die maximal gleichzeitig bespielbaren Channels auf 100 setzen. Bitte beachtet, dass mehr Channels mehr Speicherverbrauch bedeuten!
    Die Klasse Mixer stellt Funktionen für alle Channels bereit. Ist sozusagen der Master-Controller ;)


    Mehr folgt bald. Unten seht ihr eine TODO-Liste!


    Beispiele
    Das hier ist mal etwas, was ich mir dabei so zusammengeschrieben habe. Stil ist total schlecht, das weiß ich, aber es soll erstmal nur dazu dienen, es euch zu zeigen ;)


    TODO:

    • Sprite-Dragging
    • AudioChannels weiterführen(evtl.)
    • OpenGL
    • Partikel
    • Videos
    • Fonts
    • Irgendwie ist 'ne Menge formatierung verloren gegangen. Ich werde das übermorgen Updaten :(
  • Das Tutorial ist wirklich gut, vor allem der Vor-Nachteile Bereich gefällt mir, darauf sollte immer eingegangen werden, wenn man etwas benutzt, das auch Alternativen hätte...
    Bis auf ein paar winzige Rechtschreibfehler ist auch alles in Ordnung, es lässt sich flüssig lesen und du hast alles schön angeordnet. Bis auf den Code, das solltest du wirklich noch fixen, auch wenn die meisten ihn später sowieso nur reinkopiern, wär es schön, wenn du die Formatierung von Quellcode etwas verbessern könntest, dann kriegste 10/10 :)

  • Ja, wie gesagt, ne Menge Formatierung ist eben verschwunden >.<
    Als ich es geschrieben hatte, waren da schöne Farben für Schlüsselwörter drin, Absatzweise besser strukturiert, nachdem ich es reinkopiert hatte, war alles in einer Zeile und vollkommen ohne Formatierung >.>


    Achja, zum Code: Mir ist aufgefallen (jetzt hier, vom Schulrechner) dass der letzte Bsp-Code noch ein wenig älter zu sein scheint, ich glaube, ich hatte daran noch etwas im Nachhinein verändert, was aber irgendwie fehlt.


    Der letzte Crappige Code ist mir btw. gar nicht aufgefallen :( Ich könnte schwören, dass der in der Vorschau noch hingehauen hat.


    Ja, also TODO-List steht ja mit drunter, ich würde auch gerne noch ein paar Techniken mit einfügen, aber ich denke, dass das eher in ein eigenes Tut gehört. Aber ich bin erstmal mit hacken beschäftigt, tomtom haut mich sonst :P


    /E: Well, noch nicht ganz perfekt, aber es reicht, oder?

  • Ich habe das in meinem Tutorialpost schon geschrieben bzw. darum gebeten, Farbcodes innerhalb des ['code][/code]-Tags zu aktivieren... Ja, meine Farbformatierungen für Schlüsselwörter waren auch alle weg...


    Ja, das ist schon viel besser! Jetzt gefällt mir das Tutorial um einiges besser :)

  • Ich weiß, es ist schon uralt... Aber damit auch alle was davon haben schreib ich es lieber hier anstatt einer PM.


    Also, bei der Verwendung von Keyboard.IsKeyPressed(Key.F1) bekomme ich eine TypeInitialisationException mit der Message, die in etwa so aussah "Input.Keyboard hat eine Ausnahme verursacht".


    Hat jemand ne Ahnung woran das liegen könnte?

    Du möchtest mich mal treffen? Dann sag hallo und besuche mich an der FAU in Erlangen.

  • Super, das hilft gar nicht. Codebeispiel, das bei mir funktioniert:

    Funktioniert perfekt. Entweder ist deine Bibliothek kaputt oder du solltest wirklich den Code so posten, dass man auch was mit anfangen kann.

  • Er will damit vermutlich aussagen, dass dein Code, sorry, totaler Mist ist. Es ist unübersichtlicher Spaghetticode (der btw unvollständig ist). Aber letztendlich habe ich kein Interesse daran, den Code zu bemängeln :D. Sehe ich das richtig, dass du SDL nur verwendest, um herauszufinden, ob eine auf dem Form Taste gedrückt ist? Da würde sich eher anbieten, das KeyPress-Event vom Form zu nehmen, da SDL in diesem Fall nicht dafür gedacht ist.

  • Naja erstens ists nicht mein Code (sondern von nem Kumpel) zweitens ist er nicht dazu gemacht, dass ihn andere lesen, drittens wusst ich auf die Schnelle nicht wie man Tastenabfragen auf der Tastatur machen kann und da haben mir schnelle Recherchen nicht geholfen.


    Naja jedenfalls zum KeyPress-Event: Funktioniert das auch, wenn kein Fokus auf der Form ist, sondern auf einem anderem Fenster?

    Du möchtest mich mal treffen? Dann sag hallo und besuche mich an der FAU in Erlangen.