Spielanleitung

Ergänzend zur kürzlichen Vorstellung von iOSLibSpriteKit ein kleiner Einstieg in die grundlegendsten Grundlagen der SpriteKit-Programmierung, wenn Sie mögen. Und zwar in diesem Fall eine einfache Sprite-Animation, so wie diese hier:

Fangen Sie am besten damit an, die Bibliothek herunterzuladen. Wenn Sie sich bei Github nicht anmelden wollen, reicht dafür ein Klick auf „Download ZIP“.

Erstellen Sie ein neues iOS-Projekt in Xojo und kopieren Sie sowohl SpriteKit- als auch iOSLib-Ordner aus der Bibliothek hinein. Nun suchen Sie sich eine hübsche, kleine Grafiksequenz, die ein Objekt oder Wesen in verschiedenen Phasen seiner Bewegung zeigt, oder erstellen Sie selbst ein paar solche Bilder, wenn Sie mausmalerisch begabt sind. Ich greife hier in diesem Beispiel auf kostenfreie Grafiken von GameArtGuppy zurück, wie auch dieses ganze Beispiel eine direkte Umsetzung des Swift-Tutorials von raywenderlich.com ist, und bei beiden möchte ich mich herzlich für die Erlaubnis der Grafik-Abbildung und des Ideenklaus bedanken. Letztere Site ist eh eine hervorragende Quelle von iOS-Tutorials; schauen Sie mal rein, wenn Sie das Thema interessiert. Und erstere bietet sehr günstige und teilweise kostenlose Spielegrafiken.

In der Projektbibliothek finden Sie jetzt ganz unten das Platzhaltersymbol für einen iOSLibSKViewer. Das ist der Canvas-Ersatz, auf dem SpriteKit ausgeführt wird. Ziehen Sie ihn auf ihre Projekt-View, nennen Sie ihn SKSPriteView1 und positionieren Sie ihn bildschirmfüllend. Das kennen Sie ja mittlerweile zur Genüge (und falls nicht, schauen Sie sich eines der Einleitungsvideos auf der Xojo-Site an).

Regieanweisungen

Einem ImageView, der ja erst einmal genauso ausschaut, weist man ein Bild zu, damit er seinem Namen auch gerecht wird. Bei einem SKView-Objekt ist das nicht anders, nur reicht da natürlich kein Bild, es soll ja etwas passieren. Das Bild-Derivat eines SKViews ist eine Szene, auf Apple SKScene, in iOSLib entsprechend iOSLibSKScene genannt.

Wenn Sie eine SKScene verwenden, gibt diese diverse Events heraus für das, was auf dem Bildschirm geschieht. Das heißt, bei einer rein nativen Implementation müssen Sie einerseits das View-Objekt beobachten und dessen Events abfangen als auch die Events der Scene. Das war mir etwas umständlich, deshalb gibt es für iOSLibSKViewer eine Scene-Unterklasse mit dem sehr handlichen Namen iOSLibSKSceneWithInterface. Diese bringt, wie der Name vermuten lässt, ein Kommunikationsinterface mit sich, das ihre Events auf den View umlenkt.

Lassen Sie uns also mal eine neue Scene erstellen – aber wo? Der Open-Event des Views wird zu einem Zeitpunkt ausgelöst, an dem das View noch nicht völlig initialisiert ist. Insbesondere dessen Größe ist dem System zu diesem Zeitpunkt noch unbekannt. Die Scene erwartet aber eine Größe im Constructor …

(Rechts)Klicken Sie daher auf den View und fügen Sie einen Event-Handler für den LayoutSubviews-Event ein. LayoutSubviews feuert, wenn ein View seine Unterviews in Form bringen will, etwa weil das Gerät gedreht wurde oder weil der View sich zum ersten Mal aufbaut. Wenn Ihr Spiel also sowohl horizontal als auch vertikal funktionieren soll, können Sie hier auf Veränderungen reagieren. Das ist mir jetzt zu viel Feinheit, ich bleibe bei einer Initialisierung. Geben Sie daher in den Code-Editor des Event-Handlers

If me.Scene = Nil then ErstelleSzene

ein. Scene ist die gerade laufende Szene des SKViews, und da am Anfang noch keine läuft, können wir hier die Startsequenz einbauen.

Fügen Sie nun eine Methode zum View. Deren Namen können Sie sich schon denken: ErstelleSzene.

Und dort legen Sie nun wie folgt los:

Dim LaufSzene As New iOSLibSKSceneWithInterface (SKSPriteView1)
LaufSzene.name = "Animation"

Es wird hier also eine neue Scene kreiert, durch die Angabe des Views in dessen Größe. Sie können hier aber auch eine andere Größe in Form eines NSRect (demnächst auch Xojo.Core.Rect) angeben.

Mit der 2. Zeile geben Sie der Scene einen Namen. SpriteKit erleichtert die Handhabung von Spites ganz ungemein: Sie können im Prinzip alles durch seinen Namen finden. Der Name kann dabei einzigartig sein – dann finden Sie eben ein eindeutiges Objekt – oder mehrfach vorhanden, um etwa eine ganze Reihe zur gleichen Klasse gehöriger Objekte zu finden.

Jetzt wollen wir aber auch etwas abbilden. Falls Sie den Bären von GameArtGuppy geladen haben, öffnen Sie den Ordner und suchen Sie darin den Ordner TextureAtlas. Darin finden Sie mehrere Animationsphasen eines Bären in verschiedenen Auflösungen. Wenn Sie die Grafik auf iPhone-Größe ausprobieren wollen, dann entfernen Sie die ~ipad-Anhänge von den Dateinamen. Sonst wird der Bär nur auf dem Pad dargestellt.

Kopieren Sie jetzt diesen ganzen Atlas-Ordner in Ihr Projekt. Wenn Sie andere Bilder verwenden, benennen Sie sie am besten so, dass Sie die Reihenfolge schnell erkennen können.

Zurück zur Methode. Ergänzen Sie diese um die Zeilen

Dim Bär As New iOSLibSKSpriteNode ("bear1")
Bär.Position = NSPoint (SpriteKitView1.Width / 2, SpriteKitView1.Height / 2)
Bär.Name = "Bär"

Mit der ersten Zeilen wird eine SKSpriteNode erstellt. Node? Knoten? Ja genau. Sie können sich eine SpriteKit-Szene als große Baumstruktur vorstellen. Alles, was darauf geschieht, ist mit der Wurzel des Ganzen, der Scene, verknotet. Das Basisobjekt nennt sich SKNode bzw. hier iOSLibSKNode und liegt in diversen Geschmacksrichtungen vor. Die Namensgebende ist aber fraglos die SKSpriteNode – die stellt ein Bild dar, oder vielmehr eine Textur. (Wenn Sie sich Textur hier der Einfachheit halber mit „Bild, das für die schnelle Darstellung vorbereitet ist“ übersetzen, ist das gar nicht mal so verkehrt). Wenn wir jetzt keinen Bären hätten, sondern z.B. einen Atomkern in einer Physiksimulation, könnte dieser Kern eigene Kinder – Unterknoten – haben: die Elektronen. Die könnten aus dieser Struktur gelöst und einem anderen Elternteil zugeordnet werden oder sich auch selbstständig machen. Was natürlich nicht heißen soll, dass der Bär keine Kinder haben könnte, nur kreisen diese seltener in konzentrischen Bahnen um ihn, was die Knotenstruktur dann weniger erkennbar macht …

Und der Kern, die Szene? Ist auch eine Unterklasse einer Unterklasse einer SKNode.

Aber zurück zum (Quell)Text: Mit der ersten Zeile wird eine neue SpriteNode erschaffen, eine Textur des Bildes anhand seines Namens (ohne Extension) erzeugt und der Node zugewiesen.

Die zweite Zeile positioniert den Sprite in der Mitte des Views. Praktischerweise befindet sich das Zentrum einer Node standardmäßig in dessen Mitte, sodass ich nichts für Weite oder Höhe des Bären berechnen muss.

Und die dritte Zeile kennen Sie schon: Die Node bekommt einen Namen.

Wenn Sie jetzt das Programm ausführen ließen, würden Sie noch nichts sehen. Damit eine Node erscheint, muss sie der aktuellen Szene als Kind (bzw. Kindeskindeskindeskind oder wohin auch immer) zugeordnet werden. Machen Sie das mit

Laufszene.AddChild Bär
SpriteKitView1.PresentScene LaufSzene

Klicken Sie nun mal auf Run. Da steht ein Bär auf dem iDevice herum, oder?

Wie Sie sich sicher denken können: Die erste Zeile hat den Bären als Kind zur Szene hinzugefügt, und die zweite Zeile lädt die Szene in den View und stellt sie dar.

Allerdings: Jetzt steht er rum, und uns ging es doch um Animation. Nun denn, beenden Sie den Simulator und fügen Sie eine neue Methode ein: AnimiereBär

Und darin nun:

Dim LaufSzene As iOSLibSKScene = SKSPriteView1.Scene
Dim Bär as iOSLibSKNode = LaufSzene.ChildNode("Bär")

Dim Animation As iOSLibSKAction = iOSLibSKAction.AnimateWithTextures _
(iOSLibSKTexture.TextureArray ("bear1", "bear2", "bear3", "bear4", _
"bear5", "bear6", "bear7", "bear8"), 0.1)

Bär.RunActionWithKey iOSLibSKAction.RepeatAction (Animation), _
"LaufBär"

Hier passiert folgendes: Zunächst holen wir uns die aktuelle Scene durch die Scene-Property des Views. (Ich überprüfe hier nicht, ob sie überhaupt vorhanden ist oder die richtige – in einem echten Projekt sollen Sie das lieber tun!)

In der zweiten Zeile lassen wir uns den Bären ganz bequem durch seinen Namen liefern. Sie könnten zwar den in der vorigen Methode deklarierten Bären als globale Property speichern – Sie müssen aber gar nicht! Wie gesagt, SpriteKit ist da sehr komfortabel eingerichtet.

Und dann wird eine Aktion erstellt. Das ist ein neues SpriteKit-Objekt, eine SKAction. Da es so viele Constructors für Actions gibt (SKAction-Referenz), erstellen Sie eine iOSLibSKAction nicht mit New, sondern benutzen eine der zahlreichen geshareten Methoden zur Erstellung einer solchen. In diesem Fall heißt die Aktion AnimateWithTextures, und das erklärt so ziemlich alles: Es wird hiermit eine Abfolge von Texturen auf den Sprite gelegt und die Wartezeit zwischen zwei Bildern dazu angegeben. Der iOSLibSKTexture-TextureArray-Methode übergeben Sie dazu einfach eine Liste der Bilder in Reihenfolge, und um den Rest kümmert sich die Methode.

Die letzte Zeile nun weist dem Bären dieses Aktionsobjekt zu und gibt ihm dabei auch gleich einen Namen, unter dem es wiederzufinden ist.

Wenn Sie den Aufruf zu dieser Methode als letzte Zeile von ErstelleSzene hinzufügen – also

AnimiereBär,

dann können Sie das Programm auch wieder starten und sollten nun einen Bären sehen, der auf der Stelle läuft. Schon viel besser, aber zur richtigen Bewegung fehlt noch die richtige Bewegung, oder?

Löschen Sie die letzte Zeile wieder, damit der Bär nicht von alleine startet, und lassen Sie uns in uns gehen: Ziel ist, dass der Bär zu einer Stelle läuft, auf die der Spieler getippt hat. Einen Tap bekommen wir vom SKView geliefert. (Rechts)Klicken Sie dieses wieder im Navigator an und fügen Sie einen Event-Handler für den TouchesEnded-Event ein. TouchesEnded? Genau, dieser Event wird ausgelöst, wenn eine Benutzerberührung beendet wurde. Es gibt auch das Gegenstück, TouchesBegan – aber in der Regel gilt ein Tap erst bei seinem Ende.

Und dort fügen Sie bitte folgende Zeilen ein:

Dim Laufszene as iOSLibSKScene = SKSPriteView1.Scene
Dim Bär as iOSLibSKNode = SKSPriteView1.scene.ChildNode("Bär")

Dim Touch as ioslibSKtouch = ioslibSKtouch.MakeFromPtr _
(touchset.AllObjects.PtrAtIndex(0))

Erklärung bis hier: Wir holen uns zunächst die Scene und dann den Bären als Kind der Szene, das kennen Sie.

Der TouchesEnded-Event liefert ggf. ein ganzes Array von Berührungen. Das interessiert uns nicht, wir nehmen die Erstbeste aus dem Array (was im Zweifelsfall auch die einzige ist, die darin existiert).

Cave vectorem!

Weiter geht’s mit

Dim Ort As NSPoint = Touch.LocationInNode (Laufszene)

Das rechnet uns die Positionsangabe des Touches auf die Position innerhalb der Scene um. Ganz wichtig, falls Ihre Scene scrollt oder skaliert!

Jetzt müssen wir ein bisschen Mathematik bemühen, um Richtung und Geschwindigkeit des Bären zu bestimmen:

Dim Speed As Double = SKSPriteView1.Width / 3
Dim Differenz As NSPoint = Ort.VectorSubtract (Bär.Position)
Dim Abstand As Double = Differenz.Magnitude
Dim Dauer As Double = Abstand / Speed
Dim Richtung As Double = If (Differenz.x < 0, 1, -1)
Bär.XScale = abs (Bär.XScale) * Richtung

Uffa! Was passiert hier?
Zunächst einmal lege ich beliebig eine Geschwindigkeit fest. Ich möchte, dass der Bär 3 Sekunden benötigt, um den Bildschirm in der Horizontalen zu durchqueren. Das ist Zeile 1.

Die zweite Zeile berechnet einen Vektor, indem die aktuelle Position des Bären von der Position der Berührung abgezogen wird. Falls Ihnen das zu verwirrend ist: Schauen Sie mal in dieses RayWenderlich-Tutorial. Dort werden die mathematischen Grundlagen anskizziert, und Sie finden da auch einen Link zu einer Site, die das alles noch viel gründlicher (und einfach!) darstellt.

Entsprechend dieser Grundlagen wird mit Zeile 3 die Länge des Vektors bestimmt, also der zurückzulegende Weg, und gemäß Zeit = Weg / Geschwindigkeit die Animationsdauer in Zeile 4.

Als nächstes schauen wir noch, ob sich der Punkt rechts oder links des Bären befindet, um ihn ggf. horizontal zu spiegeln. Was die letzten beiden Zeilen machen.

 

Alles verdaut? Das war wirklich das Schwerigste in SpriteKit! Dann zum Rest der Methode:

If Bär.ActionForKey ("BewegeBär") nil then _
Bär.RemoveActionForKey("BewegeBär")

If Bär.ActionForKey ("LaufBär") = NIL then AnimiereBär
Dim Bewege As iOSLibSKAction = iOSLibSKAction.MoveTo _
(Ort, Dauer)

Dim Block As New iOSBlock (AddressOf Angekommen)
Dim Stoppe As iOSLibSKAction = iOSLibSKAction.RunBlock (block)
Dim Aktionssequenz As iOSLibSKAction = iOSLibSKAction.Sequence _
(Bewege, Stoppe)

Bär.RunActionWithKey (Aktionssequenz, "BewegeBär")

Ich glaube, vieles kann man so im Klartext schon lesen, oder?
Wir schauen also, ob der Bär eine Aktion namens BewegeBär ausführt – sich also gerade noch irgendwohin bewegt – und entfernen diese ggf. Er soll ja jetzt zur neuen Stelle laufen.

Wenn der Bär nicht mehr animiert wird, erzeugen wir einfach eine neue Animationssequenz (ja, das ginge auch eleganter, etwa durch Puffern in einer Property), damit die Texturanimation wieder spielt.

Dann kommt eine neue Aktion ins Spiel: MoveTo. Die erwartet eine Position und eine Dauer, und diese haben wir ja gerade berechnet.

Die nächsten Zeilen erstellen wieder eine Aktion, nämlich diesmal eine, die einen iOSBlock aufruft. Falls Sie damit noch keine Berührung hatten: Ein iOSBlock ist eine verpackte Xojo-Methode, die dem Betriebssystem zum Ausführen übergeben wird. Sie können hier also völlig frei alles hineinprogrammieren. Die Methode Angekommen gibt es noch nicht, aber das wird nicht mehr lange so sein.

Und zum Abschluss noch eine Aktion: Sequence. So ein Aktionsobjekt dient zum sequenziellen Verketten von Aktionen. Das haben Sie sich sicher schon gedacht. Ein parallel arbeitendes Pendant gibt es auch: Group.

Diese Sequenz hängen wir mit dem Namen BewegeBär (das ist der Name, dessen Aktion wir anfänglich ggf. gelöscht hatten, wissen Sie noch?) an den Bären.

Fehlt noch eine einzige Sache: Der Block. Erstellen Sie in der View noch eine neue Methode mit Namen Angekommen und geben Sie dort folgendes ein:

Dim Bär As iOSLibSKNode = SKSPriteView1.ChildNode("Bär")
Bär.RemoveActionForKey "BewegeBär"

Was hier passiert, kennen Sie schon: Wir holen uns den Bären und löschen beide Bewegungsaktionen. Der Bär soll ja auch wirklich dort ankommen, wo er hinsoll, und nicht auf der Stelle weitermarschieren.

 

Und? Funktioniert’s?

Sollten Sie auf den Geschmack gekommen sein, dann experimentieren Sie ein wenig. Die Sprachreferenz ist ziemlich vollständig und sollte Ihnen einen kleinen Einblick in die Möglichkeiten liefern. Hat der Bär z. B wirklich Kinder? Wollen Sie Bäume und Sträucher in Vorder- und Hintergrund mitscrollen lassen? (Hinweis: ZPosition bei den Nodes!) Hintergrundmusik? Naturgeräusche? Ein ganzes Spiel?

Legen Sie los, und lassen Sie mich sehr gerne wissen, was daraus entsteht. Und vor allem auch, wo noch Bugs stecken!

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s