Antwort erstellen 
:
Complete the task
Benutzername:
Betreff:
Nachrichtentext:
Gib deine Nachricht hier ein. Sie darf nicht mehr als 60000 Zeichen enthalten. 

Smilies
:D :) ;) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :!: :?: :idea: :arrow: :| :mrgreen: :geek: :ugeek: :angel: :clap: :crazy: :eh: :lolno: :problem: :shh: :shifty: :sick: :silent: :think: :thumbdown: :thumbup: :wave: :wtf: :yawn:
Schriftgröße:
Tipp: Formatierungen können schnell auf den markierten Text angewandt werden.  Schriftfarbe
Optionen:
BBCode ist eingeschaltet
[img] ist eingeschaltet
[flash] ist eingeschaltet
[url] ist eingeschaltet
Smilies sind eingeschaltet
BBCode ausschalten
Smilies ausschalten
URLs nicht automatisch verlinken
   

Die letzten Beiträge des Themas - GameStates und IGameState 
Autor Nachricht

Mit Zitat antworten Beitrag Verfasst: Fr 22. Mär 2013, 13:04
GameStates und IGameState
Hallo Leute!

GameStates sind ein wirklich interessantes Gebiet. Eines auf dem mit Interfaces und Delegaten so richtig schön spielen kann. Es geht bei GameStates darum, dass das Spiel 60 Mal in der Sekunde die Methode Update und Draw aufruft. Dabei sollte das Programm möglichst einen Codepfad einlegen, indem nur das berechnet wird, was momentan zu berechnen ist und nur das gezeichnet wird, was gerade zu zeichnen wäre.

Anders als bei einem Windows-Form haben wir nur ein Fenster. Wir zeichnen immer auf dieselbe Tafel ein neues Bild. Mit GameStates sagen wir dem Programm: Zeichne jetzt das Menü A, jetzt doch bitte das Menü B oder das Spielfeld. Ich habe für mein Programm eine eigene Lösung gefunden die für mich recht praktikabel ist. Allerdings ist wohl nur das Interface allgemein weiter verwendbar. Der große Rest muss für jedes Spiel erneut erstellt werden.

Das Interface
Code:
 
internal interface IGameState
  {
    void Update(GameTime time);
    void Draw(GameTime time, GraphicsDevice grapic, SpriteBatch sprite);
    bool ContentLoaded { get; set; }
    void LoadContent();
    void UnloadContent();
  }
 

Wer mit einem Interface nix anfangen kann, dann aber ganz schnell: http://rbwhitaker.wikidot.com/c-sharp-interfaces
Es bestimmt, welche Methoden die Klasse besitzen muss wenn das Interface implementiert wird. Somit kann auch die Klasse in das Interface gecastet werden und so auf die Methoden, Properties und Felder zugegriffen werden. Je nachdem, was im Interface deklariert wurde.

Wir brauchen für ein GameState die Methode Update mit GameTime, die Methode Draw mit GameTime, GraphicsDevice und SpriteBatch zum Zeichnen und zwei Methoden zum Laden und Entladen von Content.

Da mein Spiel EnergyBowl recht klein ist habe ich mich dagegen entschieden jedes Mal die Bilder erst zu laden und danach zu entladen. Dafür ist die Datenmenge einfach zu klein und würde wohl aufwendiger sein, als sie einfach am Anfang des Programms zu laden.


Verwendung
Mein Spiel hat folgenden Code (Auszug):
Code:
 
  internal static IGameState GameState = null;
  // (...)
 
  protected overwrite void LoadContent()
  {
    // Standardroutine zum Aufrufen eines anderen GameStates
    // GameState.UnloadContent(); Da noch nix geladen ist kann auch nix entladen werden.
    GameState = GameStates.Neuigkeiten;
    GameState.LoadContent();
  }
 

Also um ehrlich zu sein habe das in einer anderen Methode stehen weil ich den Content Asynchron lade. Aber das ist jetzt nicht wichtig. Wichtig ist, dass ich über die statische Variable vom Typ IGameState mein Spiel nun instanziierte Klassen zuweisen kann, die die Methoden des Interfaces (auf Deutsch übrigens "Schnittstelle") implementiert haben. So kann ich in der Methode Update und Draw folgenden Code schreiben:
Code:
 
  // Methode Update:
  if (GameState != null) { GameState.Update(gameTime); }
 
  // Methode Draw:
  if (GameState != null) GameState.Draw(gameTime, device, spriteBatch);
 

Theoretisch müsste ich nicht auf null prüfen, weil ich beim Laden des Spieles bereits die Variable zuweise und auch später sie nie auf null gesetzt werden dürfte.


Meine Klasse GameStates
Code:
 
  internal static partial class GameStates
  {
    internal static gsEinzelspieler Einzelspieler = new gsEinzelspieler();
    internal static gsNeuigkeiten Neuigkeiten = new gsNeuigkeiten();      
  }
 

Recht unspektakulär momentan noch. Aber das Ganze befindet sich noch im Test. gsEinzelspieler und gsNeuigkeiten haben natürlich das Interface IGameState implementiert. Sonst würde die Zuweisung nicht funktionieren.

Code:
 
internal static partial class GameStates
{
  internal class gsNeuigkeiten : IGameState
  {
    void IGameState.Update(GameTime time)
    {
    }
 
    void IGameState.Draw(GameTime time, GraphicsDevice grapic, SpriteBatch sprite)
    {
      Menu.DrawHintergrund(sprite);
 
      // Neuigkeiten
      sprite.DrawString(Res.Font_CranberryGin_12, "Alle meine Entchen, schwimmen in dem See...", new Vector2(25, 100), Color.WhiteSmoke);
 
      Menu.Draw(sprite, "Neuigkeiten");
    }
 
    bool IGameState.ContentLoaded { get; set; }
    void IGameState.LoadContent()
    {
      Menu.Enable(true);
    }
    void IGameState.UnloadContent()
    {
      Menu.Enable(false);
    }
  }
}
 

Ich trenne die jeweiligen Klassen voneinander und speichere sie jeweils in eine eigene Codedatei. Die statische Klasse Menu bietet mir übrigens allerhand Werkzeuge um das Menü anzuzeigen, zu zeichnen und übernimmt die Verarbeitung der Events.

Nun, ich greife hier auf die Methoden der statischen Klasse Menu zurück und füge dann zwischen drin den Inhalt ein, der für "Neuigkeiten" spezifisch ist. Beim Laden - ganz wichtig - deaktiviere ich alle Events des Menüs. Sonst könnte man, obwohl das Menü nicht angezeigt wird, Menüs wechseln. Schließlich wird der Code von dem, was angezeigt wird, getrennt.

Code:
 
internal static partial class GameStates
{
  internal class gsEinzelspieler : IGameState
  {
    void IGameState.Update(GameTime time)
    {
      Spielfeld.Update(time);
    }
 
    void IGameState.Draw(GameTime time, GraphicsDevice grapic, SpriteBatch sprite)
    {
      Spielfeld.Draw(time, grapic, sprite);
    }
    bool IGameState.ContentLoaded { get; set; }
    void IGameState.LoadContent()
    {
      LogForm log = Program.log;
      log.EnableBottom(false);
      log.SubscribeEvents();
      Network.StartServer(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 14300), 0, true);
      log.Text = "HOST";
    }
    void IGameState.UnloadContent()
    {
    }
  }
}
 

Hier wieder herum greife ich auf meine statische Klasse Spielfeld zurück. Die bietet mir wieder alle Methoden zum Anzeigen des Spielfeldes. Beim Laden des Contents starte ich eben einen virtuellen Server. Was das ist, das erkläre ich in einem anderem Beitrag.


Um mein Konzept zusammenzufassen:
Für die Anzeige von Menü und Spielfeld habe ich jeweils eine statische Klasse geschrieben, die mir jeweils alle Methoden bieten, die ich für die Anzeige und Funktion des Menüs und des Spielfeldes benötige. Welche Methoden ich genau davon verwende, kann ich über diese instanziierten Klassen gsEinzelspieler und gsNeuigkeiten in den jeweiligen Methoden bestimmen. Das sind natürlich noch nicht alle, es kommen noch mehr als 16 weitere dazu. Aber ich denke, dass ich im Prinzip so weiter machen kann und das Konzept gut funktioniert :)

Schöne Grüße,
Magony


Gehe zu:  
cron
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software
Deutsche Übersetzung durch phpBB.de

Impressum