Aus Das deutschsprachige Scratch-Wiki

Eine Spiel-Engine (deutsch etwa Spielwerk) ist ein spezielles Framework für Computerspiele, die den Spielverlauf steuert und für die visuelle Darstellung des Spieleablaufes verantwortlich ist.

Spiel-Engines für Jump'n Run Spiele

Für Jump'n Run Spiele sind zwei Faktoren entscheidend: Der Level und das Engine. Hier bestimmt die Engine z.B. wann sich ein Objekt wie bewegt wann ein bestimmtes Ereignis eintritt. Zu einer solchen Engine gehören mehrere Faktoren, z.B.

  • Die Schwerkraft
  • Dass das Objekt nicht Abrupt stehen bleibt wenn man aufhört, es zu bewegen (Geschmeidigkeit)
  • Dass das Objekt an Wänden abprallt, und nicht hindurchfliegt (Wände)
  • Dass die Figur ohne Springen zu müssen einen Hügel hoch kommt (Höhenüberwindung)

Eventuell kann man noch Wall-Jumping hinzufügen, es ist aber nicht nötig.

Hier ein sehr gutes Platformengine mit 1 Skript und 1 Objekt und Wall-Jumping:


1s1s Platform Engine


Schau' dir dieses Projekt auf der Scratch-Webseite an...


Dieses Engine läuft zudem auch sehr flüssig, was mit daran liegt, dass es nur ein Objekt und ein Skript hat. Deshalb wird hier jetzt auch ein Engine mit nur einem Objekt und einem Skript erklärt.

Die Schwerkraft

Die Schwerkraft wird ja schon auf der dazugehörigen Seite erklärt, aber dort fehlen einige Details, die benötigt werden, um ein perfektes Platformengine zu machen.Diese Details werden hier erklärt.

Hier ist das Originale Skript aus der Seite zu Schwerkraft:

Wenn gf angeklickt
setze [SpeedY v] auf (-5)
wiederhole fortlaufend
falls <nicht <wird Farbe [#000000] berührt?>> // Boden wird berührt
ändere [SpeedY v] um (-0.1)
end
falls <wird Farbe [#000000] berührt?> // Boden wird nicht berührt
setze [SpeedY v] auf (0)
end
ändere y um (SpeedY)
end

Und hier ist die Verbesserung:

Wenn gf angeklickt
setze [SpeedY v] auf (-5)
wiederhole fortlaufend
ändere y um (-0.5) // Hilft gegen einen Zittereffekt des Objekts
falls <nicht <wird Farbe [#000000] berührt?>> // Boden wird berührt
ändere [SpeedY v] um (-0.1)
end
ändere y um (0.5) // Hilft gegen einen Zittereffekt des Objekts
falls <wird Farbe [#000000] berührt?> // Boden wird nicht berührt
ändere y um ((SpeedY)*(-1)) // Hilft gegen Einsinken in den Boden
setze [SpeedY v] auf (0)
end
ändere y um (SpeedY)
end
Feststeck-Effekt.png

Jetzt wird erklärt, wie diese Verbesserungen überhaupt funktionierten.

Einsink-Effekt

Der Einsink-Effekt kommt so zustande: Das Objekt kann sich ja immer nur in Pixeln fortbewegen. Wenn die Variable "SpeedY" auf -10 steht, geht das Objekt immer 10 Pixel runter. Wenn das Objekt knapp über dem Boden ist, z.B. drei Pixel, und dann 10 heruntergeht, steckt es ja mit sieben Pixeln im Boden.

So funktionieren Gegenmittel:

  1. Wenn das Objekt in den Boden hinein fällt, Merkt das Objekt das und geht wieder in die Position zurück, auf die es vorher war (bevor es in den Boden hineingefallen ist) Wenn es an diese Position angekommen ist, stellt sich SpeedY auf 0, so, dass das Objekt knapp über den Boden anfängt zu fallen, und deshalb mit niedrigerer Geschwindigkeit in den Boden rammt, deshalb kommt das Objekt auch wieder knapper darüber zurück... Und das so oft bis das Objekt sicher auf dem Boden steht.
  1. Alternativ kann das Objekt einfach Fall-Geschwindigkeit auf 0 setzen und solange ein Pixel aufsteigen, bis es nicht mehr die Bodenfarbe berührt (wie z.B. hier). Als Nebeneffekt kann das Objekt dann auch schräge Plattformen in Bodenfarbe hochgehen (ohne die Fall-Geschwindigkeit dabei zu verändern) oder an Stangen in Bodenfarbe "hochklettern". Allerdings bedeutet das dann auch, dass man keine Wände programmieren kann.

Zitter-Effekt

Ein Zitter-Effekt der Spielfigur kann auftreten, wenn man nicht bedenkt, dass der Block <wird Farbe [ ] berührt?> nur dann wahr ausgibt, wenn die Farbe und die Spielfigur sich wirklich "berühren", womit quasi "überdecken" gemeint ist, und nicht, wenn sie nur aneinandergrenzen. Das bedeutet, dass, wenn das Objekt sicher steht, es ja nur angrenzt an die Farbe, und deshalb das Programm denkt, dass es nicht auf dem Boden ist. Das bewirkt dass das Programm SpeedY hochfährt, dann im Boden steckt, wieder rauskommt, wieder nur angrenzt und dann wieder reinfällt... also genau dieser unerwünschte "Zitter-Effekt"


Gegenmittel:

Es gibt verschiedene Möglichkeiten diesen Zittereffekt zu vermeiden, eine einfache ist folgende: Wenn geprüft wird, ob das Objekt auf dem Boden steht, wird das Objekt am Anfang um 0.5 Pixel nach unten bewegt und am Ende wieder 0.5 hoch. Das hat den Sinn, dass das Objekt wenn es nur angrenzt, dannach auf der Farbe draufliegt, und der <wird Farbe [ ] berührt?> wahr wiedergeben kann. Warum ausgerechnet 0.5? Nun, aus zwei Gründen: Erstens, weil wenn es 0.5 Pixel runtergeht, erkennt es der Block nur wenn es angrenzt als berührt, wenn es aber ein Pixel darüber ist nicht. Bei 1 Pixel, würde das zwar auch funktionieren, aber dann würde der Block auch wahr wiedergeben wenn das Objekt einen Pixel darüber ist.

Geschmeidigkeit

Das Skript sollte etwa so aussehen:

Wenn gf angeklickt
wiederhole fortlaufend
falls <Taste [Pfeil nach links v] gedrückt?> dann
ändere [SpeedX v] um (-0.8)
end
falls <Taste [Pfeil nach rechts v] gedrückt?> dann
ändere [SpeedX v] um (0.8)
end
ändere x um (SpeedX)
setze [SpeedX v] auf ((SpeedX) * (0.9))
end


Es hat etwa das selbe Prinzip wie die Schwerkraft. Hier gigt es auch eine Variable "Speed" nur mit X hintendran, also "SpeedX". Diese Variable gibt an mit welcher Geschwindigkeit sich das Objekt in welcher Richtung auf der Achse X fortbewegen soll. Wird die rechte Pfeiltaste gedrückt, wird SpeedX erhöht, wird die linke Pfeiltaste gedrückt, wird SpeedX verringert.

Wände

Höhenüberwindung

Das Skript unterscheidet sich jenachdem, ob es Wall-jumping beinhaltet oder nicht.

Mit Wall-Jumping

falls <wird Farbe [#000000] berührt?> dann // Die Menge der Falls-Blöcke unterscheidet sich nach den Pixeln, die das Objekt in Höhe überwinden können soll
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (-6)
falls <Taste [Pfeil nach oben v] gedrückt?> dann
setze [SpeedY v] auf [10]
falls <(SpeedX) > [0]
setze [SpeedX v] auf [-10]
sonst
setze [SpeedX v] auf [10]
end
sonst
ändere x um ((SpeedX) * (-1))
setze [SpeedX v] auf [0]
end
end
end
end
end
end
end

Das funktioniert so: Wenn der Spieler nach rechts bewegt wird, und dort plötzlich eine Rampe steht, sieht der Spieler das erst mal als Mauer (da sich dort eine Erhebung befindet) Dann wird (in diesem Beispiel) 6 mal je einmal einen Pixel nach oben bewegt und geprüft ob dort auch Farbe/Grund ist. Wenn ja, bleibt die Figur da, und es hat sich erledigt: die Figur ist die Rampe hochgegangen und alles ist gut. Wenn aber nach den 6 Pixeln überprüfen immer noch Wand da ist, tritt folgendes ein: Das Objekt/Spieler erkennt es (korrekterweise) als Wand und steht schon wieder vor einer Frage: Soll es Wall-Jumping machen oder einfach gegen die Wand fliegen und runterfallen? Sofern man Die Pfeil-nach-oben Taste gedrückt hat, macht die Figur Wall-Jumping, hat man diese Taste nicht gedrückt, fällt sie runter. Zuerst wird SpeedY auf 10 gesetzt weil beim Wall-Jumping ja gejumpt; gesprungen wird. Dannach wird mit <(SpeedX) > (0)> geprüft, ob die Figur sich momentan nach rechts oder nach links bewegt. Wenn sie sich nach links bewegt, wird SpeedX auf 10 gesetzt, so das die Figur nach rechts springt (weil sie ja von der Wand abprallen soll), wenn sie sich nach rechts bewegt, wird SpeedX auf -10 (nach links) gesetzt. Wenn die Pfeil-nach-oben Taste nicht gedrückt wird, passiert im Grunde dasselbe wie beim Springen gegen die Decke (siehe Einsinkeffekt warum das sein muss), nur dass halt SpeedX gesetzt wird und nicht SpeedY.

Ohne Wall-Jumping

falls <wird Farbe [#000000] berührt?> dann // Die Menge der Falls-Blöcke unterscheidet sich nach den Pixeln, die das Objekt in Höhe überwinden können soll
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (1)
falls <wird Farbe [#000000] berührt?> dann
ändere y um (-6)
ändere x um ((SpeedX) * (-1))
setze [SpeedX v] auf [0]
end
end
end
end
end
end

Das funktioniert so: Wenn der Spieler nach rechts bewegt wird, und dort plötzlich eine Rampe steht, sieht der Spieler das erst mal als Mauer (da sich dort eine Erhebung befindet) Dann wird (in diesem Beispiel) 6 mal je einmal einen Pixel nach oben bewegt und geprüft ob dort auch Farbe/Grund ist. Wenn ja, bleibt die Figur da, und es hat sich erledigt: die Figur ist die Rampe hochgegangen und alles ist gut. Wenn aber nach den 6 Pixeln überprüfen immer noch Wand da ist passiert im Grunde dasselbe wie beim Springen gegen die Decke (siehe Einsinkeffekt warum das sein muss), nur dass halt SpeedX gesetzt wird und nicht SpeedY.

Achtung: Bitte gewöhne dir nicht diesen Stil an, mehrere Wenn-Blöcke zu verschachteln! Es ist platzsparender und zeugt von besseren Programmierstil, den wiederhole-Block zu benutzen, allerdings ist dieser etwas zu langsam für unsere Zwecke hier. Benutze die Wenn-Methode, wenn es nicht anders geht!


Code zum Einbinden ins Forum:
[wiki=de:Engine]Engine[/wiki]