Einen Sprite bewegen - Whac-A-Mole
Inhaltsverzeichnis |
Die Idee
Bei Whac-A-Mole handelt es sich um ein Spiel aus den frühen 70iger Jahren. Es geht dabei darum, einem armen Maulwurf, der aus einem seiner Löcher herausschaut, eins überzubraten. Dabei kann sich der Maulwurf nur auf seine Schnelligkeit verlassen. Er erscheint nämlich ständig an einem anderen Loch, an dem ihn sein Jäger zu erwischen versucht. Für das Smartphone soll das Spiel so umgesetzt werden, dass das Berühren des ständig woanders auftauchenden Maulwurfs das Punktekonto erhöht. Fehlversuche werden ebenfalls gezählt. Der Spieler hat 60 Sekunden Zeit um möglichst viele Treffer zu erzielen. Realisiert wird dies durch einen auf dem Display angezeigten Countdown. Zusätzlich enthält die Spieloberfläche drei Buttons für "Start", "Reset" und "Beenden".
Das Design
Der Designentwurf
Components | Properties | |
Spielfeld | Width: Fill parent...; Height: 300px | Canvas |
ImageSprite | Picture:(Bilddatei) | Sprite mit beliebigem Maulwurfbild (lange Kante ca. 45px) |
LabelAbstand0 | Width: Fill parent...; Height:20px | Abstandhalter |
HorizontalArrangement1 | Width: Fill parent... | horizontale Anordnung der Labels für die Trefferanzeige |
Treffer | Text: Treffer:; Width:60px; | Label "Treffer:" |
TrefferAnzeige | Text: 0:; Width:30px; | Label für die erzielten Treffer |
LabelAbstand1 | Width: Fill parent...; Height:20px | Abstandhalter |
HorizontalArrangement2 | Width: Fill parent... | horizontale Anordnung der Labels für die Fehleranzeige |
Fehler | FontBold; Text: Fehler:; Width:60px; | Label "Fehler:" |
FehlerAnzeige | FontBold; Text: 0:; Width:30px; | Label für die Zahl der Fehlversuche |
LabelAbstand2 | Width: Fill parent...; Height:20px | Abstandhalter |
HorizontalArrangement3 | Width: Fill parent...; | horizontale Anordnung des Reset Buttons und des Countdowns |
ButtonReset | FontBold; Text:Reset; Width-60px; Height-40px | Reset Button |
LabelAbstand3 | Width:50px; | Abstandhalter |
LabelCountdown | FontBold; Text: 60; Width:50px; | Label für die Anzeige des Countdowns |
LabelAbstand4 | Height:20px; | Abstandhalter |
HorizontalArrangement4 | Width: Fill parent...; | horizontale Anordnung von Start und Beenden Button |
ButtonStart | FontBold; Text: Start; Width:Fill parent...; Height:40px; | Start Button |
LabelAbstand5 | Width:10px; | Abstandhalter |
ButtonBeenden | FontBold; Text: Beenden; Width:Fill parent...; Height:40px; | Beenden Button |
Clock1 | TimerInterval:550; | Timer für die Bewegung des Maulwurfs |
Clock2 | TimerInterval:1000; | Timer für den Countdown |
Blocks
Als erstes benötigen wir eine Prozedur (= Methode) mit der wir den Sprite bewegen können. Den entsprechenden Block finden wir im Built-In Block Procedures. Wir nennen die Prozedur MaulwurfBewegen. Eine selbst erstellte Prozedur können wir an beliebiger Stelle mit call NameDerMethode aufrufen.
Im Rumpf nutzen wir die Methode .MoveTo aus dem Kontextmenü des Sprites. Diese Methode hat zwei Slots für die x- und die y-Koordinate der Zielposition. Diese zwei Werte sind Zufallszahlen, die eine Koordinate auf dem Spielfeld beschreiben, d.h. zwischen 0 und der Breite bzw. Höhe des Spielfeldes minus die Breite bzw. Höhe des Sprites. Den Block für die Zufallszahlen findet man im Built-In Block Math, die Getter für die Spielfeld- bzw. Spritemaße im jeweiligen Kontextmenü. Es sei noch einmal daran erinnert, dass das Koordinatensystem eines Monitors oder Displays den Nullpunkt links oben hat.
Als nächstes rufen wir mit whenScreen.initialize do sozusagen den Konstruktor der Screen auf. Da in den Properties der Komponente nur Grundfarben verwendet werden können, wird hier die Hintergrundfarbe des Spielfeldes festgelegt. Im Slot des Setters für die Hintergrundfarbe des Spielfeldes verwenden wir die Methode make color aus dem Built-In Block colors, der die Eingabe des RGB-Wertes einer Farbe ermöglicht. Zu einem späteren Zeitpunkt nehmen wir hier noch zwei weitere Initialisierungen vor.
Die Clock1-Komponente ist verantwortlich für die Bewegung des Sprites. Dazu verwenden wir den Block whenClock1.Timer do aus dem Kontextmenü der Clock1. Im Rumpf rufen wir nun unsere oben erstellte Methode MaulwurfBewegen auf. Der Timer ruft diese Methode nach jedem Zeitintervall, das wir in den Properties auf 550 ms voreingestellt haben, neu auf, d.h. der Maulwurf erscheint alle 550 ms an einem anderen Ort des Spielfeldes.
Im nächsten Schritt wollen wir nun den Kern der Spielidee umsetzen - das Treffen des Maulwurfs. Genauer gesagt lautete die Idee: Wird der Maulwurf berührt, erhöhen wir die Trefferzahl um eins, andernfalls die Fehlerzahl. Wir benötigen zur Umsetzung also eine zweiseitige bedingte Anweisung. Den entsprechenden Block müssen wir uns zuerst zusammenbauen. Im Built-In Block Control finden wir an erster Stelle den Block für die einseitige bedingte Anweisung. Klicken wir nun links oben auf das blaue Icon, erscheint ein Kontextmenü. Hier ziehen wir den else-Block aus dem grauen Bereich nach rechts in den if-Block und erhalten dadurch die zweiseitige bedingte Anweisung, die wir im Rumpf des Event Handlers when Spielfeld .touched do aus dem Kontextmenü des Spielfeldes verwenden können.
Als Bedingung verwenden wir den Getter von touchedSprite. Ist die Bedingung erfüllt, erhöhen wir die Trefferanzeige um eins. Wir brauchen dazu den Setter des Labels TrefferAnzeige, den Getter des Labels TrefferAnzeige, den Block für die Addition aus dem Built-In Block Math und einen Block für die Zahl, die wir ebenfalls dort finden. Ist die Bedingung nicht erfüllt, erhöhen wir die Fehleranzeige um eins. Die Vorgehensweise bei der Kombination der Blocks ist analog.
Damit hätten wir bereits ein funktionsfähiges Spiel. Doch einige Feinheiten Fehlen noch. Das Spiel wird erst durch eine Zeitbeschränkung interessant. Zu diesem Zweck bauen wir nun einen Countdown ein. Wir beginnen wieder mit dem Block whenClock2.Timer do aus dem Kontextmenü der Clock2. Das Zeitintervall ist auf 1000 ms voreingestellt, so dass wir den Timer als Sekundenzähler Nutzen können: Jedesmal wenn der Timer abgelaufen ist (nach 1 sec), soll der Wert des Countdown Labels um eins verringert werden. Hat er den Wert 0 erreicht, wird der Timer deaktiviert und der Sprite unsichtbar. Letzteres setzen wir in einer einseitigen bedingten Anweisung um. Die dort benötigten logischen Operationen bzw. booleschen Werte finden wir im Built-In Block Logic.
Nun müssen wir noch den Buttons entsprechendes Verhalten zuweisen. Wir verwenden dazu wieder die bekannten Event Handler. Wird der Start - Button gedrückt, soll der Timer von Clock2 aktiviert werden, der Sprite sichtbar werden und die Countdown-Anzeige auf den Wert 60 gesetzt werden.
Wird der Reset - Button gedrückt werden zusätzlich die Treffer- bzw. die Fehleranzeige auf 0 gesetzt.
Nun ergänzen wir noch den Initialisierungsblock für die Screen. Wenn wir die App aufrufen, soll der Maulwurf unsichtbar sein und der Countdown deaktiviert sein. So kann der Spieler über den Start - Button den Beginn des Spiels selbst bestimmen.
Als letztes weisen wir dem Event Handler des Beenden - Buttons noch das Schließen der Anwendung zu. Den entsprechenden Block finden wir im Built-In Block Control.
Erweiterungsaufgaben
- Das Spiel soll mit verschiedenen Levels gespielt werden, die durch verschiedene Zeitintervalle der Clock1 bestimmt werden.
- Wird der Maulwurf getroffen, gibt er einen kurzen Schrei o.ä. von sich. Passende Sounddateien finden sich z.B. hier
- Ein weiterer Sprite (z.B. eine Blume) wird in das Spiel eingeführt und taucht in unregelmäßigen Abständen auf dem Spielfeld auf. Wird dieser Sprite versehentlich getroffen gibt es Strafpunkte oder das Spiel ist beendet.
- Die verschiedenen Levels kombinieren sich aus den obigen Aufgaben, d.h. zusätzlicher Sprite erst auf höherem Level. Treffer auf höherem Level ergeben mehr Punkte.