XNAPong Phase 2

Aus Das Sopra Wiki

Zurück zu Phase 0-1

Phase 2 - Bewegung

  • Die Grundlage der Bewegung soll ein Tastendruck des Spielers sein
  • Bewegung = Änderung der Position über die Zeit
  • Zeit wird durch den Update() Loop modelliert

Da die Update() Methode in der Game Klasse regelmässig aufgerufen wird, kann man alle Vorgänge die über die Zeit ablaufen sollen in dieser Methode kontrollieren. Es bietet sich häufig an von hier aus zu weiteren Methoden zu verzweigen. Wir fügen in diesem Fall der GameKlasse eine Methode namens 'HandlePlayerInput()' hinzu und rufen diese auch direkt in Update() auf:

protected override void Update(GameTime gameTime)
{
    HandlePlayerInput();
    base.Update(gameTime);
}
private void HandlePlayerInput()
{
    if (Keyboard.GetState().IsKeyDown(Keys.W))
        leftPaddle.Y -= 2;
    if (Keyboard.GetState().IsKeyDown(Keys.S))
        leftPaddle.Y += 2;

    if (Keyboard.GetState().IsKeyDown(Keys.Up))
        rightPaddle.Y -= 2;
    if (Keyboard.GetState().IsKeyDown(Keys.Down))
        rightPaddle.Y += 2;
}

Dieser Code greift auf die von XNA zur Verfügung gestellte Keyboard Klasse zurück. Aus dieser wird der aktuelle Zustand der Tastatur in jedem Frame (Update() Zyklus, d.h. bei jedem Aufruf von Update()) ausgelesen und festgestellt ob eine der angefragten Tasten gedrückt war. Falls ein Tastendruck festgestellt wurde wird die Position des entsprechenden Paddels verändert.

Jetzt fehlt noch der Ball und man könnte schon mit dem Spiel beginnen. Also braucht es noch eine Textur und ein weiteres Rechteck. Meine Balltextur heisst 'pongBall.png' und ist mit 512x512 Pixeln etwas überdimensioniert wie leicht an dem Skalierungsfaktor von 1/12 beim Rectangle für den Ball zu sehen ist.

  • Bei den Memberdefinitionen in Game
private Texture2D ballTexture;
private Rectangle ball;
  • In LoadContent()
ballTexture = Content.Load<Texture2D>("pongBall");
ball = new Rectangle(viewportWidth / 2, viewportHeigth / 2, ballTexture.Width / 12, ballTexture.Height / 12);
  • Schliesslich in Draw(), zwischen spriteBatch.Begin() und spriteBatch.End()
spriteBatch.Draw(ballTexture, ball, Color.White);

'F5' belohnt uns jetzt mit einer Ansicht wie wir es von Pong gewohnt sind: Zwei Paddel und ein Ball. Allerdings bewegt sich der Ball noch nicht. Wie auch, da seine Position momentan nirgends verändert wird. Das muss sich ändern. Dazu benötigen wir die Richtung mit der sich der Ball aktuell fortbewegt:

  • Bei den Memberdefinitionen in Game
private Vector2 ballDirection;
  • In LoadContent()
ballDirection = new Vector2(2,1);
  • In Update()
ball.X += (int)ballDirection.X;
ball.Y += (int)ballDirection.Y;

Die letzten Änderungen bewirken, dass die Position des Balls bei jedem Update um den in ballDirection gespeicherten Wert verschoben wird. Aber der Ball lässt sich aktuell noch nicht aufhalten. Er fliegt einfach durch die Paddel hindurch, weil eine Form von Kollisionserkennung fehlt. Für Rectangle bietet XNA die bequeme Methode rectangle.Intersects():

  • Wiederum in Update()
if (ball.Intersects(rightPaddle) || ball.Intersects(leftPaddle))
    ballDirection.X *= -1;

Kehrt die die Richtung des Balles bei Kollision mit einem Paddel um. Und offenbart direkt das Problem, dass der Ball am unteren Bildschirmrand genauso entschwinden kann wie am oberen. Das kann durch einige weitere Abfragen behoben werden:

  • Immer noch Update()
if (ball.Y < 0 || ball.Y > GraphicsDevice.Viewport.Height - ball.Height)
    ballDirection.Y *= -1;

Jetzt kann man mit 'F5' ein schon fast fertiges Pong bewundern.

Weiter mit Phase 3