... Vorausgesetzt eine Ebene ist als Active oder als Loading gekennzeichnet, dann bekommt die erste Ebene den Zuschlag bezüglich des Eingabefokus. Es sei denn, die CanGetFocus-Eigenschaft des Screens gibt ein false zurück. Head-up Displays, die dem Spieler Auskunft über Lebensenergie und Waffenwahl geben, brauchen meist keine Eingaben verarbeiten. In diesem Fall würde das Screen-Objekt den Eingabefokus verweigern und die darunter liegende Ebene bekommt den Zuschlag. Für die Verarbeitung der Benutzereingaben ist die HandleInput()-Methode vorgesehen (Abschnitt Eingabeverarbeitung).
Ebenen rendern
Im Gegensatz zur Aktualisierung der Logik bedarf es beim Rendern der Ebenen einer umgekehrten Vorgehensweise. Begründet ist dies in der Realisierung von Transparenzeffekten im XNA Framework. Alle Zeichenoperationen münden in einem sequentiellen Speicherbereich (Back Buffer), der den Farbwert eines jeden Pixels speichert. Bei (semi) transparenten Objekten kombiniert das XNA Framework die Farbwerte des aktuellen Objekts mit dem korrespondierenden Pixel im Back Buffer anhand der Gewichtung durch den Alpha-Wert. Wenn die Objekte in falscher Reihenfolge gerendert werden, existiert der Farbwert der unteren Ebene nicht im Back Buffer. Die Konsequenz: Der Transparenzeffekt bleibt aus oder liefert falsche Ergebnisse. Abhilfe schafft die umgekehrte Iteration (von der obersten zur untersten Ebene). Gerendert werden alle Ebenen, indem der Screen Manager deren Draw-Methode aufruft und dabei sowohl ein GameTimeund SpriteBatch-Objekt übergibt. Nicht geeignet sind Ebenen, die den Hidden- Status besitzen.
Listing 3:
public override void Update(GameTime oGameTime){this.Input.Update();if(this.Cursor != null && this.Cursor.Enabled)this.Cursor.Update(oGameTime, this.Input);Screen oRenderScreen;bool bHasFocus = this.Game.IsActivebool bIsHidden = false;m_oScreensCopy.Clear();for (int i = 0; i < m_oScreens.Count; i++)m_oScreensCopy.Add(m_oScreens[i]);while (m_oScreensCopy.Count > 0){oRenderScreen = m_oScreensCopy[m_oScreensCopy.Count - 1];m_oScreensCopy.RemoveAt(m_oScreensCopy.Count - 1);oRenderScreen.Update(oGameTime, bIsHidden,bHasFocus);if (oRenderScreen.State == ScreenStates.Active ||oRenderScreen.State == ScreenStates.Loading){if (bHasFocus && oRenderScreen.CanGetFocus){oRenderScreen.HandleInput(this.Input);bHasFocus = false;}if (oRenderScreen.IsOpaque)bIsHidden = true;}}base.Update(oGameTime);}
Interaktionen dank XINPUT
XINPUT stellt ein statisches Programmiermodell zur Verfügung, über das Benutzereingaben abgefragt werden können. Es bedarf keiner Initialisierung oder einer Sicherstellung, ob ein Gerät angeschlossen ist oder nicht. XINPUT verarbeitet Tastatur- und Mauseingaben sowie die Aktionen des Xbox-360-Controllers. Das Framework feuert keine Ereignisse, wenn eine Aktion registriert wurde. Es obliegt der Anwendung, den Status pro Frame abzufragen. Die Gewohnheiten der Konsolen- und PC-Spieler gehen grundsätzlich auseinander. Zwar unterstützt die Xbox 360 den Anschluss einer USB-Tastatur, dennoch überwiegt die Anzahl derjenigen Konsolenspieler recht deutlich, die keine Tastatur an ihrer Xbox 360 angeschlossen haben. Für beide Plattformen muss das Game Play deshalb auf die Hauptperipheriegeräte abgestimmt und bei der Entwicklung berücksichtigt werden. Drei Klassen dienen als Mensch-Maschine- Schnittstelle:
- GamePad
- Keyboard
- Mouse
Alle diese drei Klassen besitzen eine statische Methode GetState, die eine Game- PadState-, KeyboardState- oder Mouse- State-Struktur zurückgeben. Während die Methode bei Tastatur und Maus keine Argumente erwartet, muss den Varianten des Xbox-360-Controllers eine Konstante der PlayerIndex-Enumeration übergeben werden, die einen von vier möglichen Controllern selektiert.
Instanziierung und Konfiguration des Screen Managers (Listing 4):
protected override void Initialize(){m_oScreenManager = new ScreenManager(this);m_oScreenManager.AddScreen(new BackgroundScreen());this.Components.Add(m_oScreenManager);base.Initialize();}
Boolesche Werte oder ButtonState .Pressed bzw. ButtonState.Released teilen den Status digitaler Komponenten eines Peripheriegeräts mit. Als digital gelten alle Tasten, die entweder gedrückt oder nicht gedrückt sein können. Analoge Eingaben erlauben Abstufungen zwischen den zwei Stati. Zu den analogen Komponenten gehört der sog. Thumb Stick des Xbox- 360-Controllers (Abbildung 3) oder das Mausrad. Jene Komponenten repräsentieren die Strukturen entweder als float oder als Vector2.
Eingabeverarbeitung mit der Hilfsklasse
In WinForms-Awendungen kümmert sich das Betriebssystem um die Eingabeverarbeitung und stellt z.B. sicher, dass ein Tastendruck nur einmal das Click-Ereignis auslöst. Innerhalb einer XNA-Anwendung passiert es schnell, dass eine Taste über mehrere Frames hinweg gedrückt ist und die Anwendung daher binnen weniger Millisekunden mehrmals die Fire- Methode aufruft. Insbesondere bei der Menüführung führt das zu Problemen. Tätigt der Benutzer die [Escape]-Taste, um zum vorherigen Menü zu wechseln, und hält die Taste über drei Frames hinweg gedrückt, springt die Anwendung drei Bildschirme zurück. Damit das nicht passiert, gibt es eine Hilfsklasse InputManager, von der der Screen Manager jedem HandleInput-Aufruf eine Instanz mitgibt (die Klasse ist nicht Bestandteil des XNA Framework). Sie speichert von jedem Eingabegerät den aktuellen und den vorherigen Status. Diverse Eigenschaften wie MenuSelect geben einen booleschen Wert zurück, der angibt ob einer derartige Aktion ausgeführt wurde. Registriert die Hilfsklasse einen Druck auf die [Escape]- Taste im aktuellen Frame und einen nicht gedrückten Zustand im vorherigen Frame, hat eine Bestätigung stattgefunden. Auch die IsKeyPressed-Methode gehört zur InputManager-Klasse. Die Methode prüft für eine spezifische Taste die beiden Zustände des aktuellen und letzten Frames.
Prüfung, ob eine Menüselektion stattgefunden hat (Listing 5):
public bool MenuSelect{get{return (IsKeyPressed(Keys.Enter) || IsKeyPressed(Keys.Space) ||(m_oCurrentGamePadState.Buttons.A ==ButtonState.Pressed &&m_oPreviewGamePadState.Buttons.A ==ButtonState.Released) ||(m_oCurrentGamePadState.Buttons.Start ==ButtonState.Pressed &&m_oPreviewGamePadState.Buttons.Start ==ButtonState.Released));}}
Die Implementierung eines Screens an einem Beispiel
Die Screen-Klasse ist eine abstrakte Basisklasse. Eine Ableitung muss mindestens die Draw-Methode implementieren. Eine Klasse, BackgroundScreen, hat die Aufgabe einen permanenten Hintergrund darzustellen (Abbildung 4):
Standardmäßig blendet sich ein Screen selbst aus, wenn eine höhere Ebene nicht durchsichtig ist, um Ressourcen zu sparen. Indem die Methode Update überschrieben wird, lässt sich die Logik individualisieren.
Exemplarische Implementierung eines Screens zur Darstellung eines Hintergrundbilds (Listing 6):
public class BackgroundScreen : Screen{private Texture2D m_oTexture = null;public override void LoadGraphicsContent(bool bLoadAllContent){if (bLoadAllContent)m_oTexture = this.Content.Load<Texture2D>(“Content\\Textures\\background“);}public override void Update(GameTime oGameTime,bool IsHidden, bool bHasFocus{ }public override void Draw(GameTime oGameTime,SpriteBatch oSprite){oSprite.Begin();oSprite.Draw(m_oTexture, new Rectangle(0, 0, this.Device.Viewport.Width,this.Device.Viewport.Height), Color.White);oSprite.End();}




