Swift für Kinder, Teil II

In den ersten Lektionen habe ich meinen Kindern gezeigt, was Code ist und wie man einfache Veränderungen an vorhandenen Code durchführen kann (Swift für Kinder, Teil I). Heute geht es mit if und for weiter.

Update 21.10.2016: ZIP-Archiv mit Code aktualisiert für Swift 3 final.

Dritte Einheit (Wiederholung, Projekt spritekit5)

Zur Wiederholung haben wir in das Programm ein neues Element eingefügt: Einen Astronauten. Also Bitmap ausschneiden und in Assets.xcassets einfügen. Ich habe die folgende Zeile eingefügt:

let a = SKSpriteNode(imageNamed: "astronaut")

Jetzt der Auftrag:

  • den Astronauten sichtbar machen
  • den Astronauten langsam drehen

Der resultierende Code in didMove(to view:):

// Projekt spritekit5a
a.position = CGPoint(x:500, y:500)
a.zPosition = 2
addChild(a)
a.xScale=0.5
a.yScale=0.5

Und in update:

unserWinkel = unserWinkel + 1
a.zRotation=winkel(unserWinkel)

Vierte Einheit (if, Projekt spritekit6)

Die update-Methode enthält momentan den folgenden Code, um die Position des Raumschiffs zu ändern:

unserX = unserX + 10
unserY = 200
spaceship2.position = CGPoint(x: unserX, y: unserY)

unserX, unserY und unserWinkel haben wir schon das letzte Mal als Eigenschaften der Klasse definiert, ohne dass ich die Hintergründe erläutert habe. Es sind einfach Variablen, fertig.

Das Problem: In der obigen Form fliegt das Raumschiff nach kurzer Zeit aus dem Bildschirm heraus und verschwindet auf Nimmerwiedersehen. Wie lässt sich die Bewegung stoppen?

Die Lösung: Ich habe den Kindern erklärt, wie wir die Bewegung mit if beenden können, sobald x den Grenzwert 1800 erreicht:

if unserX<1800 {
  unserX = unserX + 10
  unserY = 200
  spaceship2.position = CGPoint(x: unserX, y: unserY)
}

Aufgabenstellung: Wie könnten wir es schaffen, dass das Raumschiff zuerst nach links fliegt, dann nach oben? Und dass das Raumschiff immer nach vorne zeigt (anfänglich also nach links, dann nach oben)?

Diese Aufgabenstellung war zu schwierig, wir haben den folgenden Code gemeinsam entwickelt (diesmal die gesamte update-Methode):

override func update(_ currentTime: TimeInterval) {
  if unserX<1800 {
    unserX = unserX + 10
    unserY = 200
    spaceship2.position = CGPoint(x: unserX, y: unserY)
    spaceship2.zRotation = winkel(-90)
  } else {
    unserY = unserY + 10
    spaceship2.position = CGPoint(x: unserX, y: unserY)
    spaceship2.zRotation = winkel(0)
  }
}

Das Raumschiff fliegt zuerst nach links, dann nach oben, und verlässt schließlich den Bildschirm in die unendlichen Weiten des Weltalls …

Siebte Einheit (for und Zufallszahlen, Projekt spritekit7/8)

Vor der siebten Einheit habe ich ein neues Projekt vorbereitet, dessen didMove-Methode so aussieht:

override func didMove(to view: SKView) {
  // nicht ändern
  setup(view)

  // hier geht's los
  spaceship = SKSpriteNode(imageNamed: "Spaceship")
  spaceship.position = CGPoint(x: zufall(von: 200, bis: 1800),
                               y: zufall(von: 200, bis: 1300))
  spaceship.color = zufallsfarbe()
  spaceship.colorBlendFactor = 1
  spaceship.zPosition = 1
  self.addChild(spaceship)
}

Zum spielerischen Kennenlernen der Zufallszahlen habe ich die folgenden drei Methoden vorbereitet (aber den Kindern nicht erläutert, sondern als gegeben vorausgesetzt):

func zufall() -> CGFloat {
  return CGFloat(drand48())
}
func zufall(von: Int, bis: Int) -> Int {
  return von + Int(arc4random_uniform(UInt32(bis-von)))
}
func zufallsfarbe() -> SKColor {
  return SKColor(red: zufall(), green: zufall(), blue: zufall(), alpha: 1)
}

Wir haben eine Weile mit den Zufallszahlen gespielt. Was passiert, wenn wir als X-Koordinate zufall(von:0, bis:3000) verwenden? (Das Raumschiff ist manchmal nicht sichtbar, weil es außerhalb des Bildschirms liegt) Etc.

Dann war mein Vorschlag: Eigentlich wäre es nett, wenn wir zwei Raumschiffe hätten. Kein Problem, Cmd+C / Cmd+V kennen meine Kinder schon. Und drei Raumschiffe? Auch kein Problem. Aber als ich dann 100 Raumschiffe haben wollte, wurde klar, dass Kopieren und Einfügen keine perfekte Lösung ist.

An dieser Stelle habe ich Ihnen die for-Schleife in der einfachsten Form vorgestellt:

override func didMove(to view: SKView) {
  // nicht ändern
  setup(view)

  // hier geht's los
  for i in 1...10 {
    print(i)
    spaceship = SKSpriteNode(imageNamed: "Spaceship")
    spaceship.position = CGPoint(x: zufall(von: 200, bis: 1800),
                                 y: zufall(von: 200, bis: 1300))
    spaceship.color = zufallsfarbe()
    spaceship.colorBlendFactor = 1
    spaceship.zPosition = CGFloat(i)
    self.addChild(spaceship)
  }
}
Eine Schleife erzeugt 10 zufällig platzierte Raumschiffe in zufälligen Farben
Eine Schleife erzeugt 10 zufällig platzierte Raumschiffe in zufälligen Farben

Die Idee der Schleife war gleich klar, und natürlich haben meine Kinder sofort probiert, was passiert, wenn man nicht 10, sondern 1000 oder auch gleich 1000000 Raumschiffe erzeugt. (Das dauert etwas länger …)

Wie ist es gelaufen?

Alles in allem war es mehr ein Vorzeigen von mir denn ein selbstständiges Programmieren meiner Kindern. Dafür ist die Begeisterung ungebrochen.

Das Konzept von else war schwer verdaulich.

Immer wieder tauchte die Frage auf, warum sich am Bildschirm nichts ändert, obwohl wir in der update-Methode unserX, unserY oder unserWinkel verändern. Das Problem bestand darin, dass wohl die Variablen verändert wurden, dass die Kinder aber vergaßen, danach auch die SKSpriteNode-Eigenschaften position bzw. zRotation zu ändern (also spaceship.position = CGPoint(...) bzw. spaceship.zRotation = winkel(unserWinkel)).

Ein bisschen irritiert waren die Kinder, dass wir trotz vieler Raumschiffe nicht viele Variablen (spaceship1, spaceship2, spaceship3 etc.) brauchen. Tja, stimmt irgendwie, vor allem wenn wir die Raumschiffe später noch einmal bearbeiten möchten. Aber mit Arrays wollte ich noch ein wenig warten :-)

Den Sinn von zPosition habe ich erklärt, aber warum spaceship.zPosition = i nicht funktioniert, warum hier also CGFloat(i) notwendig ist, lässt sich nur schwer vermitteln. Da könnte doch der Computer etwas schlauer sein, oder?

Die Pausen von ein bis zwei Wochen, bis wir alle drei gemeinsam Zeit für neue Experimente finden, sind zu lang. Sommer und Ferien sind vielleicht doch nicht der ideale Zeitpunkt, zumindest wenn das Wetter so gut ist wie momentan gerade …

Download

Die ZIP-Datei enthält die Raumschiff-App in den Stadien 5 bis 8. Die Dateien sind jetzt (ab 21.10.2016) aktuell zu Swift 3 / Xcode 8.

ZIP-Datei