[Sharpex2D][Deprecated] Tutorialübersicht

    • C#
    • .NET (FX) 4.0

    Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von ThuCommix.

      [Sharpex2D][Deprecated] Tutorialübersicht

      Sharpex2D


      Da das arbeiten mit Sharpex2D Eingewöhnung braucht, werde ich hier eine Tutorialübersicht erstellen. Damit versuche ich alle möglichen Kniffe abzudecken, und euch alles so gut wie möglich zu erklären. Falls es Fragen gibt, könnt ihr diese natürlich stellen.


      Übersicht

      Überarbeitung läuft ...

      Initialisierung
      Die Game-Klasse
      Der ContentManager
      Der SoundManager
      Erweitertes debuggen
      Der InputManager
      Plugins laden und verwalten
      Der SceneManager
      Benutzen der ScriptEngine (CSharpScript)
      Benutzen der ScriptEngine (VBScript)
      Verwenden des XBox Controllers

      Rendern eines Schriftzuges


      Development Thread

      Sharpex2D Development

      Dieser Beitrag wurde bereits 33 mal editiert, zuletzt von „ThuCommix“ ()

      Einführung in Sharpex2D


      Sharpex2D oder kurz SGL, ist eine Komponenten bassierende 2D GameEngine - Das heißt der Entwickler kann fast alle Komponenten austauschen oder abrufen. So kann man z.B eigene Klassen an den GameLoop koppeln, und entkoppeln. SGL ist sehr abstrakt gehalten weswegen es möglich ist, nahe zu jede Komponente auszutauschen, darunter fällt beispielsweise der SoundManager, die InputDevices, der PhysicProvider, der GameLoop und der Renderer. Aufgrund dessen gibt es schon mehrere Erweiterungsbibliotheken die diese Engine neue Möglichkeiten geben. Darunter fällt der DirectX11Renderer sowie der Sharpex2DSoundLib.


      Core-Features:

      • Rendering
      • Input handling
      • Contentmanager
      • Events
      • Lokalisierung
      • Abstraktes Soundsystem
      • Netzwerkdienste (TCP, UDP)
      • Physik
      • Szenenverwaltung
      • Abstraktes Font und Texture System
      • Serializer für die gebräuchlichsten Typen
      • Einfache Mathe Bibliothek
      • Erweitertes Debugging und Logging
      • Script support
      • UI Verwaltung
      • Pathfinding

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ThuCommix“ ()

      Initialisierung von Sharpex2D

      Sharpex2D bietet eine statische Klasse mit den Namen SGL im Namensraum Sharpex2D an. Die eigentliche Initialisierung wird mithilfe des SGLInitializer gehändelt, in welcher eingestellt wird, wie hoch die Auflösung sein soll, welcher GameLoop Typ verwendet werden soll, und schließlich die Instanz eurer Game-Klasse. Nach erfolgreicher Initialisierung muss die Methode "Run" ausgeführt werden. Diese erwartet als Parameter einen IRenderer und einen MediaInitializer. Ersteres ist für das Rendering eurer Objekte zuständig, während sich der MediaInitializer sich um Sound und Video kümmert. Standardmäßig wird ein GdiRenderer sowie ein WaveSoundInitializer bereitgestellt. Allerdings gibt es bereits Erweiterungen, die den DirectX Support aktivieren. Desweiteren wurde für SGL eine SoundEngine geschrieben, welche auf CSCore aufbaut. Dadurch würd DirectSound, WaveOut und Wasapi bereitgestellt.

      Schauen wir uns doch mal die Initialisierungsmethoden an:

      C#-Quellcode

      1. SGL.Initalize(SGLInitializer.Default(new MyGame()));


      Normalerweise wird beim Instanzieren eines SGLInitializer die Game-Klasse und ein RenderTarget verlangt. Mit der Methode Default muss man nur eine Game-Klasse angeben, während das RenderTarget aus dem MainWindowHandle des aktuellen Prozesses geladen wird. Deswegen ist alternativ auch dies möglich:

      C#-Quellcode

      1. SGL.Initialize(new SGLInitializer(new MyGame(), RenderTarget.Default()));


      Ist es möglich, das ihr dem RenderTarget ganz gezielt ein Handle zuweist, dies funktioniert so:

      C#-Quellcode

      1. SGL.Initialize(new SGLInitializer(new MyGame(), RenderTarget.FromHandle(handle));


      Optional könnt ihr im SGLInitializer noch einstellen, wie hoch die Auflösung sein soll, wieviele FPS erreicht werden sollen, und welcher GameLoop verwendet werden soll - Ein Diagramm findet ihr hier.

      Nachdem alles initialisiert ist, muss wie oben bereits erwähnt, die Methode "Run" ausgeführt werden. Da wir die Parameter bereits besprochen haben, erspare ich mir hier weitere Erläuterungen.

      C#-Quellcode

      1. SGL.Run(new GdiRenderer(), new MediaInitializer(new IWaveInitializer()));


      Möchtet ihr sowieso keinen Ton verwenden, könnt ihr einfach nur new MediaInitializer() übergeben. Wie genau die Game-Klasse aussieht erfahrt ihr im nächsten Post.

      Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „ThuCommix“ ()

      Die Game-Klasse

      Die Game-Klasse ist die zentrale Verwaltung deines Spiels. Dort werden OnRender sowie OnTick aufgerufen in der du dein Spiel updatest bzw. zeichnest. Diese Klasse muss von "Game" erben und dessen Methoden implementieren. Klassendiagramm

      Desweiteren sind nützliche Komponenten wie der InputManager, der SoundProvider sowie ContentManager und SceneManager hinterlegt, zu diesen kommen wir später. Vorerst möchten ich nur auf die Methoden näher eingehen. In der OnInitialize Methode sollten Objekte deines Spiels instanziert werden, diese Methode wird vor dem OnLoadContent aufgerufen. Im OnLoadContent wird das Content-Handling abgewickelt, das bedeutet das hier Texturen, Sounds o.a. geladen werden. Als Gegenstück wird OnUnload dazu verwendet, um Resourcen freizugeben, bevor das Spiel beenden wird. Abschließend wird OnClose aufgerufen - in dieser Methode können Daten gespeichert werden bevor das Spiel sich schließt.
      Im OnRenderer werden die Parameter IRenderer und Float übergeben. Wie du vielleicht schon an dem Klassendiagramm erkennen kannst, ist der Renderer dafür da um Figuren, Texte oder Texturen zu zeichnen. Der Float gibt an, wie viel zeit seit dem letzten Update (Tick) vergangen ist.

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ThuCommix“ ()

      Der ContentManager

      Der ContentManager ist eine Hauptkomponente von Sharpex2D. Über ihn ist es möglich, Texturen, Sounds und Typefaces zu laden. Dies ist aber nicht alles, über die Methode Extend kann er erweitert werden, sodass auch eigene Klasse geladen werden können - Doch dazu später mehr.
      Der ContentManager würd über die Property "Content" in der Game-Klasse sichtbar gemacht, nun können wir anfangen unsere Resourcen zu laden:

      C#-Quellcode

      1. var resource = Content.Load<GdiTexture>("tree.png");


      C#-Quellcode

      1. var resource = Content.Load<Sound>("buzz.wav");


      Wir rufen Load auf, und übergeben in den eckigen Klammern den Typ unserer Resource, in die runden Klammern schreiben wir nun den Dateipfad ausgehend vom Ordner "Content". (Dieser wird automatisch nach den ersten mal debuggen erstellt.) Das ist eigentlich schon alles was man brauch um Resourcen zu laden. Seit der Version 0.1.525 ist es möglich, Erweiterungen zum ContentManager hinzuzufügen. Dafür muss eure Klasse von IContent erben und es muss eine Klasse geben, die eine solche Klasse generieren kann. Diese Klasse erbt von IContentExtension und muss die Eigenschaften "ContentType", "Guid", und die Methode Create() zur Verfügung stellen. Nehmen wir an, ihr möchtet Questbescreibungen über den ContentManager laden, dazu müsst ihr erstmal eine Klasse Quest erstellen, diese lasst ihr von IContent laden. Meine Quest Klasse wird einen Titel und eine Description haben, der aufbau der Datei wird so aussehen:

      Quellcode

      1. Epische Quest Teil 2
      2. Vernichte den blauen Riesen.


      In Zeile 1 steh der Titel und in Zeile zwei die Description. Die Quest Klasse sieht so aus:

      C#-Quellcode

      1. public class Quest : IContent
      2. {
      3. public string Title { set; get; }
      4. public string Description { set; get; }
      5. }


      Als nächstes erstellen wir unsere Loader Klasse:

      C#-Quellcode

      1. public class QuestLoader : IContentExtension
      2. {
      3. public IContent Create(string path)
      4. {
      5. var quest = new Quest();
      6. var content = File.ReadAllLines(path);
      7. quest.Title = content[0];
      8. quest.Description = content[1];
      9. return quest;
      10. }
      11. public Guid Guid { get; private set; }
      12. public Type ContentType { get; private set; }
      13. public QuestLoader()
      14. {
      15. ContentType = typeof (Quest);
      16. Guid = new Guid("20D6BA87-A8EA-44F6-BC87-A7EB2229F38A");
      17. }
      18. }


      In der Methode Create, wird die QuestDatei geöffnet, und die erste und zweite Zeile ausgelesen, danach wird die Quest zurück an den ContentManager gegeben und Ihr erhaltet diese. Vorher müsst Ihr allerdings die Erweiterung zum ContentManager hinzufügen.

      C#-Quellcode

      1. public override void OnLoadContent()
      2. {
      3. Content.Extend(new QuestLoader());
      4. }


      Anschließend könnt ihr eure Quest laden:

      C#-Quellcode

      1. var quest = Content.Load<Quest>("quest.txt");



      Dateien
      • TestGame.zip

        (360,52 kB, 218 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ThuCommix“ ()

      Der SoundManager

      Im Thema Initialisierung haben wir bereits geklärt, das der zweite Parameter von SGL.Run einen SoundInitializer benötigt, da geben wir nun statt null den WaveSoundInitializer an. Natürlich können wir auch jede andere Klasse angeben die ISoundInitializer implementiert, somit wird es möglich auch andere SoundEngines zu verwenden - ein Beispiel hierfür wäre CSCore in Kombination mit der SoundProvider.dll (siehe Github).

      Der SoundManager erwartet bei der Funktion Play() einen Sound, und den PlayMode. Der PlayMode setzt sich aus 2 Optionen zusammen, entweder der Sound wird einmal abgespielt (=None) oder er wird wiederholt (=Loop). Der Sound lässt sich ganz einfach über den ContentManager erstellen:

      C#-Quellcode

      1. var sound = Content.Load<Sound>("pfad im content folder");


      Alternativ bietet die Klasse "Sound" auch eine Factory, die eine Datei außerhalb des Contentbereiches laden kann:

      C#-Quellcode

      1. var sound = Sound.Factory.Create("pfad zur datei");


      Nun haben wir alles um unsere Wavedatei abzuspielen.

      C#-Quellcode

      1. SoundManager.Play(sound, PlayMode.Loop);


      Der SoundManager unterstützt weitere Funktionen wie: Pause, Resume, Seek, Balance, Volume, Length und Mute. Siehe dazu dieses Klassendiagramm.

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ThuCommix“ ()

      Erweitertes debuggen

      In SGL gibt es verschiedene Methoden um das Spiel zu überwachen und nützliche Informationen zu speichern. Im Namespace Debug bzw Debug.Logging befinden sich die Klassen "Log" und "ProcessStatus". Log ist eine statische Klasse und besitzt die Methode "Next" mit ihr wird der log aufgenommen, dabei stehen folgende Parameter zur Verfügung.

      C#-Quellcode

      1. Log.Next(string, LogLevel, LogMode);


      Der erste Parameter übergibt logischerweise eure Meldung die ihr ausgeben wollt. LogLevel gibt die Stufe der Meldung an, dies geht von Info - Critical. Anschließend folgt der LogMode. Dieser gibt an inwiefern die Nachricht ausgegeben wird. Entweder im Standard out Stream oder im Standard error Stream. Um Resourcen zu sparen kann allerdings auch "None" angegeben werden. Um die Logs abzurufen, muss die Methode "GetEntries" verwendet werden, diese gibt einen Array bestehend aus "LogEntry" zurück. Jeder LogEntry besitzt eine Message, Level, und Time Eigenschaft.

      Mit ProcessStatus kann man sich die aktuelle Ram- und CPU Auslastung sowie die Anzahl aller laufenden Threads anschauen. Dies kann dazu verwendet werden, um bestimmte Prozesse im Spiel zu überwachen die Performance hungrig sind.

      C#-Quellcode

      1. var pStatus = new ProcessStatus();
      2. Log.Next(string.Format("{0}%", pStatus.CpuUsage), LogLevel.Info, LogMode.None);

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ThuCommix“ ()

      Der InputManager

      Der InputManager wird bei der Initialisierung von SGL instanziert und ist in der Game-Klasse sowie über SGL.Components verfügbar. Es werden zwei Eigenschaften, Keyboard und Mouse zur verfügung gestellt über die ihr Tasten und Buttons abfragen könnt. Allerdings gibt es auch die Möglichkeit andere Devices über InputManager.Get<Typ> anzusprechen. Diese müssen IDevice implementieren und vorher via Add dem InputManager hinzugefügt werden.

      Über das Keyboard Interface kann nun beispielsweise IsKeyPressed aufgerufen werden. Diese Methode erwartet einen Key als Parameter der überprüft werden soll. Ist dieser gedrückt wurden, gibt die Methode True zurück.

      C#-Quellcode

      1. if(InputManager.Keyboard.IsKeyPressed(Keys.Up))
      2. {
      3. player.MoveUp();
      4. }


      Das Mouse Interface bietet Methoden für gedrückte und losgelassene Tasten.

      C#-Quellcode

      1. if(InputManager.Mouse.IsButtonReleased(MouseButtons.Left))
      2. {
      3. }


      C#-Quellcode

      1. if(InputManager.Mouse.IsButtonPressed(MouseButtons.Left))
      2. {
      3. //charge attack
      4. }


      Über SetStandardKeyboard und SetStandardMouse kann das Device der Eigenschaft Keyboard und Mouse problemlos gewechselt werden.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ThuCommix“ ()

      Plugins laden und verwalten

      Der PluginLoader befindlich im Namespace "Plugin" durchsucht eine Assembly nach einen ganz bestimmten Typ und gibt ihn zurück.

      C#-Quellcode

      1. var pluginLoader = new PluginLoader();
      2. var plugin = pluginLoader.Load<MyClass>(@"c:\assembly.dll");


      Um die Plugins besser aufzubewahren können sie in einen PluginContainer<T> gespeichert werden.

      C#-Quellcode

      1. var container = new PluginContainer<MyClass>();
      2. container.Add(plugin);


      Das Plugin kann nun einfach über Get abgerufen werden.

      C#-Quellcode

      1. var plugin = container.Get<MyClass>();

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ThuCommix“ ()

      Der SceneManager

      Das spätere Spiel muss gut strukturiert sein um die Wartbarkeit zu garantieren. Genau dafür wurde der SceneManager geschaffen der einzelne Szenen vom Typ IScene verwaltet. Der SceneManager wird in der Game-Klasse unter den Namen "SceneManager" sichtbar gemacht. Eine Szene besitzt 4 wichtige Methoden. Zu einem die Render und Tick Methode, und die LoadContent sowie Initialize Methode. Außerdem bietet sie ein EntityEnvironment welches eine Auflistung aller Entity (Spielobjekte) dieser Szene darstellt. Szenen sind quasi vom Aufbau ähnlich der Game-Klasse, jedoch ist es möglich spezielle Szenen wie z.B Pause, Menu oder Inventar abzukapseln.
      Durch den SceneManager kann eine Szene aktiv geschalten werden. Ist eine Szene aktiv wird sie geupdatet und gerendert.

      C#-Quellcode

      1. SceneManager.SetScene(SceneManager.Get<PauseScene>());


      Das hinzufügen einer Szene funktioniert über die Add Methode. Es ist nötig den SceneManager von der Game-Klasse aus zu updaten und zu rendern, da der SceneManager nicht direkt an den GameLoop gekapselt ist.

      C#-Quellcode

      1. SceneManager.Tick(elapsed);

      C#-Quellcode

      1. SceneManager.Render(renderer, elapsed);


      Wird eine Szene neu hinzugefügt wird automatisch die Methode Initialize und anschließend LoadContent aufgerufen.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ThuCommix“ ()

      Schriftzüge mit Typeface


      Das Aussehen eines Schriftzuges wird durch sein Typeface beschrieben. Typeface besitzt Eigenschaften für FamilyName, Size und Style (Regular, Italic usw) und löst SpriteFont ab. So könnt Ihr ein Typeface erstellen:


      C#-Quellcode

      1. var typeface = new Typeface{FamilyName = "Arial", Size = 8, Style = TypefaceStyle.Regular};



      Um nun einen Schriftzug zu erstellen, müssen wir uns klar werden, welchen Renderer wir benutzt haben. Zum GdiRenderer gehört eine GdiFont, zu anderen Renderern ihre eigene Font.

      C#-Quellcode

      1. var gdiFont = new GdiFont(typeface);

      C#-Quellcode

      1. renderer.DrawString("I'm a GdiFont.", _gdiFont, new Vector2(230, 200), Color.Black);


      Entspricht die Font nicht der des Renderers wird eine ArgumentException geworfen.


      Resultat: gerenderter Schriftzug

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ThuCommix“ ()

      Benutzen der ScriptEngine (CSharpScript)

      Die Benutzung der ScriptEngine hinsichtlich CSharpScript ist sehr einfach gehalten. Ihr bemötigt einen ScriptHost, dieser verlangt bei der Initialisierung einen IScriptEvaluator.


      C#-Quellcode

      1. _scriptHost = new ScriptHost(new CSharpScriptEvaluator());


      Der CSharpScriptEvaluator wird nun automatisch die benötigten Komponenten aktivieren, um CSharpScripts über den ContentManager zu laden, und zu kompilieren. Das laden über den ContentManager läuft standardmäßig so ab:

      C#-Quellcode

      1. _testScript = Content.Load<CSharpScript>("myscript.cs");


      Jeden Script sollte noch eine eindeutige Guid zugewiesen werden, falls man später Buffering im Evaluator aktivieren möchte. Dies hat den Vorteil dass, das Script nicht jedes mal neu kompiliert werden muss.

      Ein Test Script, wie später auch im an gehangenen Beispiel, könnte nun so aussehen.

      C#-Quellcode

      1. using ScriptTesting;
      2. using SharpexGL.Framework.Scripting;
      3. using SharpexGL.Framework.Debug.Logging;
      4. namespace MyScript
      5. {
      6. public class Script : IScriptEntry
      7. {
      8. public void Main(params object[] objects)
      9. {
      10. System.Threading.Thread.Sleep(200);
      11. ((Player) objects[0]).Position = new SharpexGL.Framework.Math.Vector2(50, 50);
      12. System.Threading.Thread.Sleep(200);
      13. ((Player)objects[0]).Position = new SharpexGL.Framework.Math.Vector2(111, 50);
      14. System.Threading.Thread.Sleep(200);
      15. ((Player)objects[0]).Position = new SharpexGL.Framework.Math.Vector2(222, 50);
      16. Log.Next("Teleported by Script.", LogLevel.Info, LogMode.StandardOut);
      17. }
      18. }
      19. }


      Eure Klasse muss von IScriptEntry erben, weswegen auch der entsprechende Using gesetzt werden muss. Falls als Parameter Objekte aus euren Spiel übergeben werden, muss der entsprechende Using ebenfalls gesetzt werden.

      Nun muss das Script nur noch erfolgreich aktiviert werden.

      C#-Quellcode

      1. _scriptHost.Execute(_testScript, _player);


      Nach der Angabe des Scripts erfolgen die ganzen Objekte die ihr den Script übergeben möchtet, in diesen Falle übergebe ich eine Player-Klasse, dessen Position vom Script manipuliert werden soll. Falls nun keine ScriptException geworfen wird, sollte alles funktionieren.

      Bild



      Eine Beispielanwendung findet ihr im Anhang.
      Dateien
      • ScriptTesting.zip

        (322,19 kB, 129 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ThuCommix“ ()

      Benutzen der ScriptEngine (VBScript)

      Das Prinzip funktioniert genau wie hier beschrieben, nur das ihr VBScript in Kombination mit einen VBScriptEvaluator verwendet.

      C#-Quellcode

      1. _scriptHost = new ScriptHost(new VBScriptEvaluator());
      2. _testScript = Content.Load<VBScript>("myscript.vb");
      3. _scriptHost.Execute(_testScript, _player);

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ThuCommix“ ()

      Verwenden des XBOX Controllers

      In der Initialize Methode unserer Game-Klasse fügen wir den InputManager ein neues Device hinzu. Dies funktioniert über die Add-Methode. Bevor wir allerdings unseren Controller verknüpfen können, müssen wir uns über den Slot Informieren. Ein Xbox Controller kann an einem PC, einen der 4 Slots besetzen. Falls wir nur einen Controller besitzen ist das Slot 1 mit dem Index 0. (Slot - 1 = Index)

      C#-Quellcode

      1. Input.Add(XboxController.Retrieve(0));


      Der InputManager wird nun automatisch versuchen das Gerät zu initialisieren. Falls kein Fehler auftritt muss das Gerät nun via Input.Get<XboxController>().IsEnabled aktiviert werden.

      C#-Quellcode

      1. var _controller = Input.Get<XboxController>();
      2. _controller.IsEnabled = true;


      Anschließend können wir in der Tick Methode die vielen Tasten und Schalter über Eigenschaften erreichen. Ein KlassenDiagramm findet ihr hier.

      C#-Quellcode

      1. if (_controller.IsStartPressed)
      2. {
      3. _world.New();
      4. }


      C#-Quellcode

      1. if (_controller.IsAPressed)
      2. {
      3. _bird.MoveUp();
      4. }


      Falls unser Charakter stirbt oder an etwas anstößt möchten wir den Spieler ein haptischen Feedback liefern. Dies geschieht über die Methode Vibrate. Ihr übergeben wir die Motorstärke (Links, Rechts) sowie die Zeitspanne.

      C#-Quellcode

      1. _controller.Vibrate(0.5f, 0.5f, TimeSpan.FromSeconds(1));


      Der Rest der XboxController Klasse sollte sich anhand des Klassendiagramms bzw. an den Namen der Eigenschaften erklären. Falls ihr einen XboxController besitzt, könnt ihr das kleine Testspiel im Anhang ausprobieren. Mit A fliegt der Vogel höher, und mit der Start Taste wird das Spiel neu gestartet. Euer Controller muss sich im Slot 1 befinden.
      Bilder
      • PkHHAufrorlo.png

        19,15 kB, 181×790, 154 mal angesehen
      Dateien
      • XboxSample.zip

        (122,81 kB, 142 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ThuCommix“ ()