Construct2 PacMan
Grundlage für den folgenden Text ist das Tutorial unter https://www.scirra.com/tutorials/308/cloning-the-classics-pacman/page-2 das ursprünglich von Stemkoski veröffentlicht wurde. (.capx zum Download) (spielbare Version)
Anhand des klassischen PacMan Spieles kann man viele der Möglichkeiten von Constuct2 zeigen.
- Sprite Strips
- Bewegung des Spielers ohne Kollision mit Hindernissen
- Zusätzlich zufällig gesteuerte Bewegung bei Monstern
Sprite Strips - Das Spielfeld
Das Spielfeld für PacMan besteht aus einer gekachelten Hintergrundgrafik und einer Reihe von Wänden als Hindernis.
Der Hintergrund
Zuerst legen wir ein neues leeres Projekt an und laden dort hinein die folgende Grafik als TiledBackground.
Hinweis: In den Screenshots von Construct2 sieht der Hintergrund etwas komisch aus, breite Linien zwischen den Kacheln. Das hängt wohl damit zusammen, dass ich Windows immer im Emulator unter Linux benutze. Da scheint Construct2 ein Problem mit der Darstellung zu haben. Das stört aber ansonsten nicht weiter.
Wir stellen alle Größen auf 640x640 ein, also für
- Project -> Window Size
- Layouta -> Layout1
- Object types -> TiledBackground
Danach benennen wir den Layer um in Hintergrund und sperren ihn (auf das Schloss klicken). Dann fügen wir einen weiteren Layer ein, nennen ihn Main und machen ihn zum aktiven Layer (anklicken).
Die Wände
In den Layer Main fügen wir nun ein Sprite ein, indem wir auf den freien Bereich klicken. Nun laden wir das Bild nicht in den Image Editor, sondern wir klicken mit der rechten Maustaste in den Kasten Animation frames und wählen aus dem Menü Import Sprite Strip.
In dem erscheinenden Datei-Dialog wählen wir die folgende Abbildung aus.
Nun müssen wir noch angeben, in wieviele Zeilen und Spalten die Abbildung aufgeteilt werden soll, der richtige Wert ist 10 Spalten und 3 Zeilen.
Danach haben wir 31 einzelne Animation frames, den ersten müssen wir löschen, der war schon vor dem Laden der Grafik als leerer Frame vorhanden. Danach sind es noch 30 Frames mit den Nummern von 0 bis 29.
Es ist wesentlich performanter nur die eine Grafikdatei zu laden und sie dann softwaremäßig zu zerteilen, als 30 kleine Abbildungen nacheinander zu laden. Man muss beim Erstellen einer derartigen Grafik nur darauf achten, dass alle Teile gleich groß sind (hier 32x32) und jeweils exakt positioniert.
Wir löschen dann noch die Elemente, die keine Wände sind, als die gelben und schwarzen Kreise. Es bleiben 25 Frames.
Den Grafik-Editor können wir dann schließen und das Sprite umbenennen in Wall.
Nun erzeugen wir insgesamt 25 Instanzen unseres Sprites (Strg+Maus ziehen) und positionieren diese Kopien oberhalb des Spielfeldes. Für das ordentliche Positionieren der Elemente ist es wichtig im Menü unter Views das Häkchen Snap to grid zu aktivieren und für Höhe und Breite jeweils 32 anzugeben.
Jede der 25 Instanzen klicken wir nun nacheinander an vergeben eine jeweils andere Zahl für Initial frame.
Nun können wir uns beliebig Rahmenelemente aus dem oberen Bereich kopieren und daraus unser Spielfeld aufbauen.
Am Ende könnte das Layout-Fenster dann folgendermaßen aussehen.
Bevor wir testen können sollten wir noch einmal das Wall-Sprite aktivieren, im Kontext-Editor Edit Animations wählen und dann unter Properties die Eigenschaft Speed auf 0 stellen.
Bewegung des Spielers
Für den nächsten Schritt brauchen wir wieder viele kleine Grafiken, die wir als Sprite Strip laden.
Layout
Es handelt sich um einen Sprite Strip mit den Abmessungen 14x4, den wir wie beim Wall einladen. Wir löschen dann alle Abbildungen, die keinen PacMan darstellen, es sollten genau 8 Abbildungen übrig bleiben.
Bei den Animation Properties setzen wir Loop auf Yes.
Von der Animation fertigen wir im Animation Editor nun drei Kopien an und benennen die Animationen der Reihe nach A0, A90, A180, A270. In jeder der vier Animationen lassen wir dann nur die beiden Grafiken über, die der Richtungsangabe entsprechen, bei A0 also die beiden die nach Rechts schauen, bei A9 die beiden, die nach Unten schauen.
Nun bekommt PacMan (die Klasse, also rechts unter Objects aktivieren noch die folgenden Instanzenvariablen.
- Direction (number) - Hier wird die Richtung abgelegt, in die PacMan aktuell blickt. Der Startwert ist 0.
- MoveDuration (number) - Hier ist die Geschwindigkeit abgelegt, bzw. wieviele Sekunden PacMan braucht um einen Einheit zurückzulegen (Eine Kachel hat 32 Einheiten). Der Startwert 0.25.
- TargetX (number) - Die x-Komponente des Feldes zu dem PacMan möchte. Startwert ist 0.
- TargetY (number) - Die y-Komponente des Zielfeldes.
Weiter bekommt er noch ein Bullet-Behavior, bei dem wir die Speed auf 0 setzen und Set angle auf No (diese kleine Einstellung hat mich viel Zeit gekostet, auch in der Vorlage ist der Wert leider auf Yes und führt zu komischen Drehungen, eventuell hat sich hier bei einem Constuct2-Update etwas am Verhalten verändert).
Dann fügen wir in unser Spiel noch ein Keyboard-Objekt ein, damit wir auch auf Tastatur-Ereignisse reagieren können.
Dann fügen wir noch die folgende Grafik als Sprite ein und nennen das Sprite CollisionDetector, die Property Visibility von diesem Objekt setzen wir auf Invisible, damit wird es unsichtbar.
Events
Weiter geht es im Event-Sheet. Hier fügen wir zuerst eine globale Variable TileSize ein und geben ihr den numerischen Wert 32. Dann noch eine neue Gruppe, der wir den Namen PacMan Movement geben.
In diese Gruppe kommt nun eine Reihe von einfachen Events.
Event: PacMan -> Bullet Speed = 0
Darunter die folgenden Subevents mit den zugehörigen Actions.
Event: Keyboard -> Up arrow is down Action: PacMan -> SetValue -> Direction to 270
Event: Keyboard -> Down arrow is down Action: PacMan -> SetValue -> Direction to 90
Event: Keyboard -> Left arrow is down Action: PacMan -> SetValue -> Direction to 180
Event: Keyboard -> Right arrow is down Action: PacMan -> SetValue -> Direction to 0
Dann kommt auf gleicher Ebene ein Block mit vier per Oder verknüpften Bedingungen. Dazu fügt man die erste Bedingung an (Insert new event below), aktiviert dann Make 'or' block und fügt dann die anderen Events per Add another condition an.
Event: Keyboard -> Up arrow is down -or- Event: Keyboard -> Down arrow is down -or- Event: Keyboard -> Left arrow is down -or- Event: Keyboard -> Right arrow is down
Nun die Actions dazu:
Action: PacMan -> SetValue -> TargetX to Self.X + cos(Self.Direction) * TileSize Action: PacMan -> SetValue -> TargetY to Self.Y + sin(Self.Direction) * TileSize Action: CollisionDetector -> SetX to PacMan.TargetX Action: CollisionDetector -> SetY to PacMan.TargetY
Das mit den trigonometrischen Funktionen sieht auf den ersten Blick schwierig aus, bei den vorliegenden Winkeln liefern die Funktionen aber nur einen der Werte -1, 0 bzw +1. Damit wird in Abhängigkeit von der Richtung die Position ggf. um 32 (TileSize) erhöht oder erniedrigt.
- sin( 0)=0, cos( 0)=1
- sin( 90)=1, cos( 90)=0
- sin(180)=0, cos(180)=-1
- sin(270)=-1, cos(270)=0
So ist also der CollisionDetector an die Position gesetzt, die PacMan als nächstes besuchen würde. Nun müssen wir noch feststellen, ob es eine Kollision mit den Wänden gibt.
Wir fügen also ein Subevent zu dem mit Oder verknüpften Block ein.
Event: CollisionDetector -> Is overlapping another object, Wall , und Invertieren.
Dazu kommt dann eine Reihe von Actions.
Action: PacMan -> Bullet Set speed to TileSize/Self.MoveDuration Action: PacMan -> Bullet Set angle to PacMan.Direction Action: PacMan -> Set animation to "A" & str(Self.Direction) (play from current frame) Action: PacMan -> Start animation from current frame Action: System -> Wait PacMan.MoveDuration Action: PacMan -> Bullet Set speed to 0 Action: PacMan -> Set X to Self.TargetX Action: PacMan -> Set Y to Self.TargetY Action: PacMan -> Stop animation Action: PacMan -> Set animation frame to 0
In der dritten Action-Zeile steckt ein interessanter Trick. Direction kann einen der Werte 0, 90, 180 oder 270 annehmen. Wandelt man die Zahl in einen String um und setzt ein A davor, so ergeben sich mit A0, A90, A180, A270 die Namen der vier Richtungs-Animationen von PacMan.
Vor den ganzen PacMan-Bereich setzen wir dann noch zur Initialisierung,
Event: System -> On start of layout Action: PacMan -> Bullet set speed to 0
Insgesamt ergibt sich dann folgender Zustand.
Die Monster
Für die Monster benötigen wir erneut den Sprite Strip den wir schon für PacMan benutzt haben. Wir laden ihn erneut ein (14x4) und benennen das Sprite dann Ghost. Wir löschen alle Grafiken, die nichts mit den Monstern zu tun haben (es verbleiben 46 Elemente) und setzen unter Properties den Wert von Loop auf yes und den von Speed auf 6.
Layout
Nun brauchen wir 18 Kopien der Animation, insgesamt also 19 Animationen.
Aus Tradition heißen die Gespenster Blinky (Roter ghost), Pinky (Pinker ghost), Inky (Blauer ghost), and Clyde (Orange ghost). Wir benennen die ersten 16 Animationen also Blinky0, Blinky90, Blinky180, Blinky270, Pinky0, Pinky90, Pinky180, Pinky270, Inky0, etc., bis zu Clyde270. Weiter nennen wir eine Animation Vulnerable, eine weitere VulnerableBlink, und die letzte Eaten.
In diesen 19 Animation müssen wir jetzt jeweils alle Bildelement löschen, die nicht zu der jeweiligen Version gehören, es bleiben immer genau zwei Bilder nach. Man muss sich dabei etwas konzentrieren und genau auf die Farbe und die Position der Augen achten. In de Animation Vulnerable lässt man nur die zwei Bilder mit dem Blauen ghost mit den großen Augen, in der Animation VulnerableBlink zwei Bilder mit dem blauen ghost mit den großen Augen und zwei Bilder mit dem weißen ghost mit den großen Augen, die beiden Farben sollten wecheseln, also muss ein Bild verschoben werden. In der Animation Eaten verbleibt nur ein Bild nur mit den Augen..
Die Bewegung wird nun ähnlich angelegt, wie beim PacMan. Zuerst brauchen wir also wieder ein paar Instanzenvariablen (alle numerisch).
- Direction, numerisch, 90
- MoveDuration, numerisch, 0.33
- TargetX, numerisch, 0
- TargetY, numerisch, 0
und zusätzlich
- GhostName, text
- VulnerableTimer, numerisch, 0
Das Objekt Ghost bekommt noch ein Bullet Behavior und wir setzen unter Properties den Wert von Set angle auf No.
Von Ghost erzeugen wir mittels Strg und Mouse-Drag drei Kopien (insgesamt also vier Instanzen) von Ghost, platzieren sie an unterschiedlichen Stellen und vergeben jeweils einen der Namen:
- Blinky
- Pinky
- Inky
- Clyde
Zum Abschluss fügen wir im Tab Layout ein neues Element ein und zwar ein Array. Dieses Array nennen wir dann AvailableDirections.
Events
Im Event Sheet fügen wir zunächst eine globale Variable (numerisch) mit dem Namen n ein und eine Gruppe Ghost Movement ein.
In diese Gruppe kommen nun die folgenden Events.
Event: System -> For each Ghost
und dann die folgenden Subevents
Event: Ghost -> Bullet speed = 0 Action: AvailableDirections -> Set size to (0,1,1)
dann wieder ein Subevent:
Event: System -> for "loop" from 0 to 3 Action: System -> set n to loopindex("loop")*90 Action: CollisionDetector -> set X to Ghost.X + cos(n) * TileSize Action: CollisionDetector -> set Y to Ghost.Y + sin(n) * TileSize
Und noch ein Subevent:
Event: CollisionDetector -> is overlapping Wall , dann invertieren Action: AvailableDirections -> push front, n on X axis
Nun folgt ein Event auf der gleichen Ebene wie die Schleife, dort also auswählen insert new event below.
Event: System -> Compare two values -> AvailableDirections.Width > 1 Action: System -> set n to (Ghost.Direction + 180) % 360 Action: AvailableDirections -> Delete AvailableDirections.IndexOf(n) from X axis
Und noch ein Event auf der gleichen Ebene
Event: System -> Every tick Action: System -> set n to floor(random( AvailableDirections.Width )) Action: Ghost -> set value Direction to AvailableDirections.At(n)
Und noch ein Event auf der gleichen Ebene:
Event: System -> Every tick Action: Ghost -> Set value TargetX to Self.X + cos(Self.Direction) * TileSize Action: Ghost -> Set value TargetY to Self.Y + sin(Self.Direction) * TileSize Action: Ghost -> Bullet set speed to TileSize / Self.MoveDuration Action: Ghost -> Bullet set angle of motion to Self.Direction Action: System -> Wait Ghost.MoveDuration Action: Ghost -> Bullet set speed to 0 Action: Ghost -> SetX to Self.TargetX Action: Ghost -> SetY to Self.TargetY
Zum Abschluss ergänzen wir noch einige Actions in dem obersten Event System->on start of layout
Action: Ghost -> Bullet set speed to 0 Action: Ghost -> Set animation to Self.GhostName & str(Self.Direction) Action: Ghost -> Set Target X to Self.X Action: Ghost -> Set Target Y to Self.Y
Nun sollten die Geister fröhlich auf dem Spielfeld herum irren und PacMan lässt sich steuern. Es hat aber noch keinen Effekt, wenn sich PacMan und ein Geist berühren.
Es sieht auch etwas komisch aus, das bei den beiden letzten Events die Bedingungen gleich sind (System -> every tick). Hier kann man später noch Elemente dazwischen schieben, die die Bewegungsrichtung der Gespenster geschickter bzw, gefährlicher bestimmen. Momentan gilt absoluter Zufall zur Bestimmung der Richtungsänderung. Immer wenn die Geister durch eine Wand behindert werden ändern sie zufällig ihre Richtung, wobei sie aber nicht zurück gehen, solange es eine Alternative gibt, also keine Sackgasse vorhanden ist.
Kollisionen mit anderen Objekten
Bisher hat PacMan keinerlei Möglichkeit irgendwelche Punkte zu sammeln, aber auch kein Risiko von einem Geist getötet zu werden, das wollen wir nun ändern.
Zuerst fügen wir ein paar weitere globale Variablen ein.
- Score, numerisch, 0
- Lives, numerisch, 3
- GhostsEaten, numerisch, 0
Mit Dots
Jetzt brauchen wir noch die gelben Punkte, die auf dem Spielfeld eingesammelt werden können. Wir importieren also erneut das die Abbildung mit den Wänden als SpriteStrip (10x3) und löschen alle Elemente außer dem kleinen gelben Punkt. Das Sprite nennen wir Dot. Nun noch einmal der gleiche Schritt und wir lassen nur den großen gelben Punkt übrig und nennen das Sprite BigDot. Beide Elemente positionieren wir außerhalb des Layouts.
Nun platzieren wir in jeder Ecke eine Kopie von BigDot und auf allen freien Feldern eine Kopie von Dot.
Das Spielfeld sieht dann folgendermaßen aus.
Sollten die Ghosts sich hinter den Dots hindurch bewegen, so muss man im Layout-Fenster die Klasse Ghost aktivieren (unter Objects) und dann mit der rechten Maustaste auf eine der Instanzen klicken. Dort unter Z Order wählt man dann Send to top of layer.
Wir fügen nun einen dritten Layer ein und nennen ihn GUI, hier soll u.a. der Punktestand angezeigt werden. In dem neuen Layer und im oberen freien Bereich fügen wir nun zwei Objekte vom Typ Text ein, das einen nennen wir TextScore und das andere TextPoints. Beide gestalten wir hinsichtlich Schriftgröße und Schriftfarbe passend.
Dann wieder zurück ins Event-Sheet dort fügen wir in die neue Gruppe ein.
Event: PacMan -> Is overlapping another object, Dot Action: Dot -> Destroy Action: System -> Add to Score, 10 Action: TextPoints -> SetText to str(Score)
Nun auch eine Reaktion für die großen Dots.
Event: PacMan -> Is overlapping another object, BigDot Action: Ghost -> Set animation to "Vulnearable" (play from the beginning) Action: Ghost -> Set value VulnerableTimer to 5 Action: Ghost -> Set value MoveDuration to 0.67 Action: BigDot -> Destroy Action: System -> Set GhostsEaten to 0 Action: System -> Add 50 to Score Action: TextPoints -> SetText to str(Score)
Wenn man jetzt auf ein Feld mit einem BigDot geht, dann gibt es 50 Punkte dazu und alle Ghosts verändern ihre Animation. Diese Veränderung muss natürlich irgendwann auch wieder rückgängig gemacht werden. Irgendwann müssen wir die Ghosts natürlich auch für die entsprechende Zeit verwundbar machen, aber das kommt später.
Außerhalb der Gruppen ergänzen wir ein Event.
Event: Ghost -> Animation Is palying "Vulnearabele" or Event: Ghost -> Animation Is playing "VulnearabeleBlink" Action: Ghost -> subtract dt from VulnerableTimer
Wenn also eine dieser Animationen läuft, dann wird der zugehörige Zähler heruntergezählt. Nun reagieren wir auf den Zählerstand, dazu ein neues Subevent.
Event: Ghost -> Compare instance variable VulnerableTimer <= 1 Action: Ghost -> Set animation to "VulnearableBlink" (play from the beginning)
Nun noch der Fall, dass der Zähler den Wert 0 annimmt, also Insert new event below
Event: Ghost -> Compare instance variable VulnerableTimer <= 0 Action: Ghost -> Set animation to Self.GhostName & str(Self.Direction)
Mit Ghosts
Nun sollen auch Kollisionen mit den Ghosts einen Effekt bekommen. Dafür benötigen wir noch ein paar Abbildungen. Zuerst zwei Textgrafiken
die wir jeweils als Sprite mit einem einzigen Animation Frame laden und von denen wir jeweils eine Instanz unterhalb des Spielfeldes ablegen.
Wir ebnötigen weitere Grafiken, eine für den expodierenden PacMan , eine für explodierende Ghosts . Beide Abbildungen laden wir als Particles Image (nicht Sprite) ins Layout und legen sie oberhalb des Spielfeldes ab und nennen sie ParticlesPacMan und ParticlesGhost. Dann ändern wir noch die Properties folgendermaßen:
- Rate: 80
- Spray cone: 360
- Type: one-shot
und die Initial Properties.
- Speed: 20
- Size: 9
- Opacity: 100
- Grow rate: -8
- Speed randomizer: 80
Particle live time Properties
- Acceleration: -10
- Speed randomizer: 0
- Timeout: 1 bei PacMan und 2 bei Ghosts
Dann fügen wir in der Gruppe eine neues Event ein.
Event: PacMan -> on Collision with another object -> Ghost
Nun müssen wir noch unterscheiden, ob die Ghosts im normalen Zustand sind oder eine der oben eingeführten Animationen läuft. Wir nehmen noch gleich eine dritte mit auf, die im nächsten Schritt eingeführt wird. Ein neues Subevent
Event: Ghost -> Is playing animation "VulnerableBlink" , Invertiert
Add another condition
Event: Ghost -> Is playing animation "Vulnerable" , Invertiert
Add another condition
Event: Ghost -> Is playing animation "Eaten" , Invertiert
Unter dieser Bedingung wird also PacMan bei der Kollision vernichtet.
Action: PacMan -> Spawn another object -> ParticlesPacMan on Layer "Main" Action: PacMan -> Destroy Action: System -> Wait 1 second
Das Verwalten der drei Leben von PacMan gliedern wir aus in eine Gruppe NextLife. Diese Gruppe wird in der letzten Aktion aktiviert, die Zeile kann man aber nur eingeben, wenn die Gruppe vorher schon einmal (zumindest leer) eingerichtet wurde.
Action: System -> Set Group "NextLife" activated
Wir fügen also eine neue Gruppe NextLife an und achten darauf, dass Häkchen bei Active on start nicht gesetzt ist.
Nun brauchen wir noch zwei unsichtbare Platzhalter, für die Ausgangspositionen des neuen PacMan und der neuen Ghosts. Die Ghosts muss man auf alle Fälle auch entfernen, damit es nicht sofort wieder eine Kollision mit PacMan gibt. Für die Platzhalter klonen wir die Klasse CollisionDetector. Dazu klickt man unter Objects die Klasse mit der rechten Maustaste an und wählt Clone. Man bekommt eine etwas kryptische Meldung die damit zu tun hat, dass Klassen nur dann hier aufgeführt werden, wenn mindestens eine Instanz im Layout vorhanden ist. Die Kopien finden sich etwas höher unter Projects. Hier können sie umbenannt werden in GhostHome und PacManHome. Dann zieht man jeweils eine Instanz ins Layout. PacManHome dahin wir im Layout PacMan steht und GhostHome zwischen die beiden Ghosts im inneren Bereich. Sollten die Elemente andere verdecken, so wieder die Z Order einstellen auf Send to bottom of layer. Wichtig ist noch, dass bei beiden Elementen die Property Initial visibility auf Invisible steht.
Nun clonen wir auch noch PacMan, nennen die Kopie PacLives und entfernen aus der Animation das Bild mit dem geschlossenen Mund. Wir entfernen alle Instanzenvariablen und das Bullet-Behavior von PacLive und führen neu eine Instanzenvariable ID (numerisch) ein.
Von PacLives legen wir drei Instanzen nebeneinander in den Bereich hinter der Score-Anzeige, sie bekommen für ihre Instanzenvariable ID nacheinander die Werte 1, 2 bzw. 3.
Nun können wir mit dem Inhalt der Gruppe NextLife weitermache, dazu wieder ein Subevent.
Event: System -> foreach Ghost Action: Ghost -> Spawn another object -> ParticlesGhost on Layer "Main" Action: Ghost -> SetPosition to another object: GhostHome Action: Ghost -> Set animation to: Self.GhostName & str(Self.Direction) Action: Ghost -> Set value TargetX to Self.X Action: Ghost -> Set value TargetY to Self.Y
So damit sind die Ghosts an ihrer alten Position aufgelöst und ihr Neuerscheinen in der Mitte vorbereitet.
Nun müssen wir noch kontrollieren, ob noch Leben für PacMan übrig sind, also Insert new event below.
Event: System -> Compare two values: Lives = 0 Action: System -> Create object: TextLoose on Layer "GUI" at (320, 256)
das wäre dann das Spielende.
Davor gibt es aber noch mehrere Leben für PacMan, also ein neues Event darunter.
Event: PacLives -> Compare instance variables: ID = Lives Action: PacLives -> Spawn another object -> ParticlesPacMan on Layer "Main" Action: PacLives -> Destroy Action: PacManHome -> Spawn another object -> PacMan on Layer "Main" Action: System -> Substract from: Lives 1
Zuletzt müssen wir diesen Block wieder deaktivieren.
Event: System -> every tick Action: System -> set group active -> NextLife, deactivated
Wir wollen auch gewinnen können
Bisher kann man bei dem Spiel nur verlieren, wenn alle Leben aufgebraucht sind. Damit wir gewinnen können brauchen wir etwas Übung, also eine Möglichkeit das Spiel neu zu starten. Gewonnen hat man, wenn alle Dots und BigDots entfernt sind.
Zuerst die Möglichkeit zum Neustart. Dazu fügen wir unterhalb des allerersten Events ein neues Event hinzu.
Event: Keyboard -> on R pressed Action: System -> Goto Layout Layout1 Action: System -> Set variable: Score to 0 Action: System -> Set variable Lives to 3
Nun müssen wir noch überprüfen, ob wir gewonnen haben. Dazu hängen wir ganz am Ende neue Events an:
Event: System -> Compare two values: Dot.Count <= 1 (der außerhalb des Layouts)
add another condition
Event: System -> Compare two values: BigDot.Count <= 1 (der außerhalb des Layouts)
add another condition
Event: System -> Compare two values: TextWin.Count <= 1 Action: System -> Create object: TextWin on Layer "GUI" at (320,256) Action: Ghost -> Spawn on other object -> ParticlesGhost on Layer "Main" Action: Ghost -> Destroy
PacMan vernichtet Ghosts
Immer dann, wenn die Ghosts die Animation Vulnerable oder VulnerableBlink zeigen, dann werden sie bei einer Kollision mit PacMan vernichtet. Das fehlt noch. Wir müssen also in der Gruppe PacMan Collisons unterhalb des vorhandenen Events noch ein Event hinzufügen.
Event: Ghost -> Animation Is playing: Vulnerable
OR
Event: Ghost -> Animation Is playing: VulnerableBlink
Action: Ghost -> Set animation to Eaten Action: Ghost -> Spawn an other object -> ParticlesGhost an Layer "Main" Action: System -> Add 1 to GhostsEaten
später kommen hier die Points zwischen
Action: System -> Add 100*(2^GhostsEaten) to Score Action: TextPoints -> set text to str(Score)
Jetz kann man noch eine kleine Spielerei ergänzen. Wenn PacMan einen Ghost vernichtet, dann soll eine Animation mit der hierbei erzielten Punktzahl starten. Dazu braucht man eine Animation mit den passenden Abbildungen.
Diese Grafiken lädt man dann als einzelne Animationen in ein Sprite namens Points.
Das Sprite bekommt dann noch im Bereich Behavior noch ein Bullet und ein Fade Behavior. Beim Fade Behavior setzen wir folgende Properties.
- Wait time 0.5
- Fade out time 0.5
Beim Bullet Behavior setzen wir
- Speed 16
Nun müssen wir die Points noch aktivieren. Dazu ergänzen wir, nachdem GhostsEaten um 1 erhöht wurde.
Action: Ghost -> Spwan another object Points on Layer "GUI" Action: Points -> Set animation to "P" & str(100 * (2^GhostsEaten)) Action: Points -> Bullet Set Angle of motionto 270 Action: Points -> Bullet set speed to 16
Der Bereich sieht im Event Sheet dann folgendermaßen aus.
Was noch fehlt
In der Vorlage gibt es noch einen Bereich, der mit AI bezeichnet ist. Hier bestimmen die Ghosts ihre neue Richtung nicht zufällig, sondern verfolgen unterschiedliche Strategien.
Das macht das Spiel abwechslungsreicher und schwieriger. Außerdem kann ein Ghost mit der Animation Eaten so wiederbelebt werden. Er verfolgt dann nämlich die Strategie zurück zum Ausgangspunkt zu kommen (Home Priority) und dort wird er dann reaktiviert.
Zusätzlich gibt es dann noch die Fruits, die regelmäßig im Spielfeld auftauchen und bei einer Kollision mit PacMan zusätzliche Punkte bringen.