Advanced Games Physics
1. Kapitel

Basics der Programmierung

Programmieren mit processing oder p5.js

In diesem Kapitel möchte ich Dir die Basics der Programmierung meiner ausführbaren Programmbeispiele vermitteln, um Dir die Nachverfolgung oder eigenständigen Entwicklung solcher Programme zu ermöglichen.
Als die Entwickler von processing sich an die Arbeit machten, hatten sie das Ziel, eine Entwicklungsumgebung für "jedermann" zu schaffen. Die Ansprüche an Programmierkenntnisse sollten möglichst gering gehalten werden. Zu Zeiten der Konzipierung der ersten Version von Processing spielten die Themen "Internet" bzw. "Mobile Endgeräte" noch keine Rolle, was zur Folge hatte, dass die von den Nutzern entwickelten Lösungen in erster Linie lokal auf dem Desktop liefen. In der zweiten Version wurde eine Interpreter zur Verfügung gestellt, der JAVA in JAVASCRIPT convertierte und damit Auftritte im Internet ermöglichte. Allerdings mit dem Mangel, dass sie auf mobilen Endgeräten nicht bedienbar sind, weil die Behandlung von touch-events nicht hinreichend möglich ist. In der dritten Version von Processing steht dieser Converter nun nicht mehr zur Verfügung. Statt dessen wurde eine neue Entwicklungsumgebung, nämlich p5.js ins Leben gerufen. Wie der Appendix '.js' deutlich macht, handelt es sich hierbei um ein javascript-basiertes Tool. Jetzt sind auch Internet-Auftritte auf mobilen Endgeräten möglich. Natürlich verlangt der Umstieg auf p5.js einiges an Umstellungen, die aber relativ schnell zu erlernen sind. In p5.js werden die Unterschiede erläutert und mit Programmbeispielen hinterlegt. Auf meiner Homepage stelle ich beide Programmversionen als download zur Verfügung, auch wenn die Processing-Lösungen nicht immer dem letzten Stand entsprechen. Alle aktuellen Programmbeispiele sind in p5.js geschrieben.

Processing

Processing hat verschiedene Vorteile, von denen die wichtigsten hier genannt sein:
  1. Processing ist ein open source Projekt,
  2. Processing ist Java-basiert und kooperiert mit JavaScript,
  3. Dank JavaScript können web-Applikationen geschrieben werden,
  4. Dank der komfortablen Matrix-Befehle können voneinander unabhängige Koordinatensysteme kreiert werden
  5. Processing verfügt über viele features, die eine Vielzahl von Bewegungsmodi unterstützt
  6. Processing verfügt über multimedia-Fähigkeiten

Es ist nicht meine Absicht, eine detailierten Einführung in "processing" zu geben - dafür gibt es gute Tutorials. Nein, ich möchte nur auf eine Besonderheit eingehen, die manchmal zu Verwirrung führt. zeigt den prinzipiellen Pogrammlauf eines in "processing" geschriebenen Programmes. Zu Beginn des Programms wird, wie üblich, eine Deklaration von Variablen und Klassen ausgeführt. Daran schließt sich eine setup()-Sequenz an, die nur einmal durchlaufen wird. Im Setup werden Variablen mit Startwerten versehen oder Klassen instanziiert. Ist dies erfolgt, beginnt der eigentliche Programmlauf. Und hier liegt der Unterschied zu anderen Objekt orientierten Programmiersprachen: diese als draw() bezeichnete Sequenz wird, wenn nicht das Programm absichtlich gestoppt wird, fortlaufend wiederholt. Und zwar im Takt des Bildschirm-Refreshs. Damit werden bewegte Bilder einfach wie bei einem Film oder Video möglich. Ein Film besteht ja auch aus aufeinander folgenden Bildern, genau das bewirkt die draw() Routine!
Allerdings, der Programmierer muss sich der Tatsache stets bewußt sein, dass die draw() Sequenz ohne sein Zutun immer wieder durchlaufen wird. Alle Nutzerinputs, die eigentlich nur bei Bedarf abgefragt werden, müssen immer durch geeignete Verzweigungen aus dem steten Zyklus heraus vorgenommen werden.
Die Bildschirm-Refresh-Rate kann im setup() voreingestellt werden. Dafür gibt es das Kommando frameRate(). Wird diese Funktion mit dem Wert 50 aufgerufen, bedeutet dies, dass je Sekunde 50-mal das Bild gewechselt wird. Und damit jedes Bild für die Dauer von 1/50 s dargestellt wird. So wird für die Programmausführung ein festes Zeitraster vorgegeben. In der Regel orientiert sich diese Rate an der Physiologie unseres Sehens. Also 25 - 100 mal pro Sekunde wird das Bild auf dem Bildschirm erneuert, also wird die draw() Routine 25 - 100 mal pro Sekunde durchlaufen. Sollte allerdings der in draw() enthaltene Code so umfangreich oder so anspruchsvoll sein, dass die CPU die erforderliche Leistung nicht aufbringen kann, dann kann es zu einer Verringerung der refresh-Rate kommen. Dies hat Konsequenzen für die interne Zeitbasis, die wir zur Berechnung der Zeitfunktionen benötigen.

Prinzipieller Programmablauf

Abb. prinzipieller Programmablauf


Noch ein Hinweis zur zeitabhängigen Darstellung eines bewegten Objektes. Da sich die draw()-Sequenz zyklisch wiederholt, werden ohne geeignete Maßnahmen für alle errechneten Orte das Objekt bleibend(!) dargestellt. Ein Spur entstünde dabei. Um das zu verhindern wird bei zu Beginn jedes draw()-Zyklus das letzte Bild vollständig gelöscht, um anschließend mit den neuen Werten neu gezeichnet zu werden. Der Befehl, der dies ausführt, ist der background()-Befehl.

Viele der hier vorgestellten Programme verwenden Funktionen oder Klassen von Funktionen, die in den unterschiedlichsten Situationen Anwendung finden. Sind diese Programme hinreichend allgemein gehalten, werden sie sinnvoller Weise nicht im Hauptprogramm (draw()) untergebracht, sondern separat in einer Library. So stehen sie allen Programmen zur Verfügung. In Processing können solche Programme mittels Tabs eingebunden werden (siehe ).

Hauptprogramm und Tabs

Abb. Einbindung von Tabs in Processing

Tabs sind die in oben beschrifteten Reiter (z.B. Basics_v2.0). Zweckmäßig ist in diesem Zusammenhang die Einrichtung eines Ordners "Libraries", in dem alle Programme und ihre Versionen allgemeingültiger Aufgaben abgelegt sind. In processing ist es allerdings notwendig, dass die erforderlichen Library-Programme in den Ordner des aufrufenden Hauptprogramms kopiert werden, erst dann stehen Sie als Tab zur Verfügung ()!

In processing müssen alle Unterprogramme physisch im Ordner des Hauptprogramms verfügbar sein! Das bedeutet, größte Sorgfalt beim Editieren der Unterprogramme walten zu lassen, da die Unterprogramme zunächst nur lokal in diesem Ordner abgespeichert sind. Erst, wenn diese Änderung hinreichend getestet wurde, kann sie als neue Version in die Library übernommen werden. Aus diesem Grunde stelle ich keine geschlossene Library für die processing-Versionen meiner Beispielprogramme zum Download zur Verfügung - alle erforderlichen Unterprogramme (Tabs) sind in jedes downloadbaren zip-File eingeschlossen!

Ordnerhierarchie

Abb. Verfügbarmachung von Tabs in Processing



p5.js

Der grundsätzliche Programmablauf von "p5.js" unterscheidet sich nicht von dem in gezeigten Ablauf in "processing". Auch für "p5.js" gibt es ein geeignetes Tutorial.
p5.js erzeugt ein script (javaScript), welches in eine html-Seite eingebunden - eingebettet - wird (). Im Beispiel ist dies das Programm "pGrund_0010mini.js". Des weiteren werden aber auch die Includes, die ebenfalls als scripte vorliegen, geladen. Hier ist zu unterscheiden in die "libraries", die von p5.js zur Verfügung gestellt und als Basis der Programmentwicklung eingefügt werden und die Includes, die den Tabs von Processing gleich zu setzen sind. Also entspricht z.B. pControls_v0.0.js, die alle Buttons, Schieberegler und sensitiven Elemente beinhaltet dem Tab controls_vX.X.pde der processing-Lösungen. Neu ist das file "canCSS.css", welches für das Styling der html-Seite verantwortlich ist.
Mit dem abschließenden <div id="pGrund_0010mini" class="canvasStyle"></div> wird die Ausführung des scriptes gestylt und auf der html-Seite positioniert.


Abb. Verfügbarmachung von Tabs und Aufruf eines p5.js- scripts in einer HTML-Seite

zeigt die zu gehörende Ordnerstruktur. Anders als in Processing werden hier die Includes bzw. die Libraries zentral in einem Ordner gelagert. Der Ordner p5-includes enthält alle Includes, die für den Programm-Ablauf erforderlich sind, aber auch die Laufzeitumgebung p5.js und das Styling css. Die zentrale Ablage dieser Files erleichtert die Pflege der Includes, da eine Änderung alle vergangenen und künftigen Entwicklungen gleichermaßen betrifft.

download p5.js
p5 Library und Includes

Die Programmbeispiele

Einordnung der Programmbeispiele

Alle Programmbeispiele, ob im Fließtext eingebettet oder als ausführbares Programm (erreichbar über das PC-Symbol) stehen zum freien Download zur Verfügung. Damit kann jeder Nutzer dieser site den Code herunter laden, lesen oder auch verwenden. Dabei ist allerdings zu beachten, dass die Codes für beliebige Endgeräte geschrieben sind, also sowohl für Desktop, die mit Maus-Interaktion, als auch für mobile Anwendungen, die mit touch-Bedienung funktionieren müssen. Zwangsläufig wird dadurch der Code umfangreicher als für eine reine Desktop-Lösung. Im wesentlichen betrifft dies die Unterprogramme "basics_vX.x" und "controls_vX.x", aber marginal auch andere Programme und Unterprogramme. Immer dann, wenn z.B. die Funktion getMediaType() aufgerufen wird, wird getestet, welches Endgerät gerade in Benutzung ist. Der Rückgabewert ist eine ganze Zahl mit der Bedeutung: 0: Desktop, 1: mobile landscape bzw. -1: mobile portrait. Mit dieser Information kann das Styling des Programms beeinflusst werden!

Alle Programmbeispiele können auch autonom laufen, wenn das gewünschte Beispielprogramm nach dem download in die nebenstehende Ordnerstruktur geladen wird (). Dazu wird in diesem Beispiel ein übergeordneter Ordner AGP Programme mit dem Unterordner application angelegt. In diesen Ordner wird das heruntergeladene, gezippte Beispielprogramm geladen und entpackt. Außerdem wird in den Ordner AGP Programme das gezippte Include-Verzeichnis p5_includes.zip geladen und entpackt. Dabei wird automatisch der Ordner p5_includes angelegt. Dieser enthält dann die p5-Library, alle verwendeten Include-Files und das Styling-File.
Im gezeigten Beispiel in ist das gezippte File pGrund_0023_Schraeger_Wurf.zip in den Ordner AGP Programme geladen worden. Nach dem Entpacken siehst Du den Ordner pGrund_0023_Schraeger_Wurf, der die Files pGrund_0023_Schraeger_Wurf.js und Pindex.html enthält.
Ein Doppelclick auf das File Pindex.html öffnet in der Regel das Beispielprogramm im Standard-Browser. Das klappt aber nicht immer! Enthält nämlich das Beispielprogramm Images, die hinzugeladen werden müssen, streikt der Browser aus Sicherheitsgründen. Obwohl die Images im gezippten Beispielordner mitgeliefert worden sind. Einzig mögliche Lösung ist die Installation eines lokalen web-Servers, wie z.B. den Apache, der dann das Starten der Beispielprogramme in einem localhost gestattet.
Ordnerstruktur in p5
Abb. Ablage von Includes und Libraries

Strukturierung meiner Programmbeispiele

Die meisten meiner Beispielprogramme sind nach dem gleichen Strickmuster gearbeitet. Sieh Dir noch einmal die , die den Grundaufbau der processing oder p5.js-Programmabläufe zeigt, an. Da siehst Du, dass der unter der draw()-Sequenz befindliche Code maßgeblich für das gesamte Programm ist. Also sollten wir uns die draw()-Sequenz etwas genauer anschauen. Hier befinden sich alle Voreinstellungen, alle Berechnungsschritte, alle User-Interaktionen und alle Darstellungen der Objekte.
Bevor wir auf die draw()-Sequenz näher eingehen, will ich aber noch ein Wort zur setup()-Routine sagen. Dort wird nach der Festlegung des Darstellungsbereiches (size(xmax, ymax) bzw. canvas(xmax, ymax)) mit Hilfe der Funktion evaluateConstants(xpos, ypos) die Position des START-Buttons festgelegt. Die Werte für xpos und ypos bewegen sich in einem Rahmen von 0 bis 100, wobei der Wert 100 dem rechten (xmax) bzw. dem unteren Rand (ymax) entsprechen. Die wirkliche Position des Buttons ergibt sich durch Multiplikation der normierten Werte xGrid bzw. yGrid mit den angegebenen Positionswerten xpos und ypos. Die Werte xGrid und yGrid spannen ein 100 x 100 - Gitternetz auf, dessen Maschen wie im Kapitel 2. Darstellung bewegter Objekte beschrieben, unter Berücksichtigung der Display-Auflösung berechnet worden sind. Viele der nachfolgend benutzten Funktionen greifen auf dieses Gitter zurück.
Und nun zur draw()-Routine. Bei der Analyse meiner Programmbeispiele stellt sich heraus, dass ich drei Grundvarianten in Benutzung habe. Diese Varianten unterscheiden sich eigentlich nur in ihrem Start-/Reset-Verhalten und im Zeitpunkt der Freischaltung von Mitteln zur Nutzerinteraktion.

processing: Start/Reset

Abb. a)
Processing: START/RESET des Programmablaufs

p5.js: Start/Reset

Abb. b)
p5.js: START/RESET des Programmablaufs

p5.js: automatischer Start

Abb. c)
Processing bzw. p5.js: automatischer Programmstart
Meine Programme liegen in den meisten Fällen in zwei Varianten vor: Variante 1 (a) (Processing) bzw. (b)) (p5.js)) und Variante 2 (c)) für beide Entwicklungsumgebungen. Warum zwei Varianten? Weil unterschiedliche Startsituationen vorliegen können:
  1. Variante: Hier wird nach dem Start des Programms auf eine Nutzereingabe, die die Berechnung der Bewegungsabläufe startet, gewartet. Der Nutzer löst die Berechnungen mit der Betätigung des START-Buttons aus. Vor dem START kann der Nutzer per Eingabe mit der Maus bestimmte Bewegungsparameter, wie z.B. die Fallhöhe beim freien Fall oder die Startgeschwindigkeit des zu bewegenden Objektes voreinstellen. Nach dem START ist das nicht immer möglich, weil eine nachträgliche Veränderung der Startparameter ggf. zu falschen Resultaten führen würde. Nach dem Start des Programms, wechselt der START-Button seine Bedeutung und wird zum RESET-Button. Eine Betätigung des RESET-Buttons führt zu einem erneuten Bedeutungswechsel dieses Buttons. Er wird wieder zum START-Button und eine Wiederholung das Experiment mit den alten oder aber auch mit neuen Startparametern wird möglich. Ist das Experiment regulär beendet, weil eine Endbedingung erfüllt ist, wird die Bewegungsberechnung beendet. Diese wird in der Routine finaltest() bzw. endProgram() ausgetestet. Bei Erfüllung der Bedingung wird der Zeitquant dt = 0 gesetzt, was zu einem Einfrieren der Bewegung führt. Mit dem START wird u.a. der Zeitquant dt = 1/frmRate gesetzt, womit eine erneute Bewegung möglich wird.
    Zwischen den beiden Lösungen a) und b) bestehen kleine Unterschiede, die auf die unterschiedlichen Behandlungen von Variablen in Klassen (Processing) bzw. Objekten (p5.js) zurück zu führen sind.
  2. Variante: Das ist die Spielvariante. Mit dem Programmstart wird auch sofort die Berechnung von Bewegungen möglich. Ein "Ende" gibt es nicht, darum auch kein RESET. Die Steuerung des Spielablaufs wird ausschließlich durch Nutzereingaben bestimmt, sofern nicht das Programm selbst Abläufe bestimmt.
Programmtechnisch unterscheiden sich die beiden Varianten darin, dass bei der
  1. Variante mit dem Aufruf der Funktion startProgram() der Startbutton aktiviert und die Berechnung der Bewegungsabläufe beginnt. Diese endet, wenn entweder die Endbedingung (Processing: finalTest() bzw. p5.js: endProgram()) erfüllt ist oder der RESET-Button betätigt wurde.
  2. Variante dieser Aufruf nicht erfolgt, da die Bewegungsberechnung sofort mit dem Start des Programms beginnt. Nach enthält sie die Programmabläufe der 1. Variante nur rudimentär. Als Beispiel sein hier das Programm Flipper genannt. Das zwar die Abfrage der START-Variablen beinhaltet, aber keinen solchen Button hat. Hier wird mit dem START-Ablauf nur eine Parametrisierung des Spiels vorgenommen, die aber ebensogut in der setup()-Routine erledigt werden könnte. Auch eine Abbruchbedingung gibt es, nämlich das Einlochen der Kugel, sie beendet aber nicht das Spiel, sondern stellt den Ausgangszustand wieder her. Hat die Kugel nämlich das Startloch wieder erreicht, kann sie per handle wieder losgeschickt werden.

Aus grundsätzlichen Überlegungen gibt es eine weitere Gliederung der Aufgaben innerhalb der draw()-Funktion. Diese Gliederung ergibt sich u.a. aus der Tatsache, dass
  1. Bewegungsabläufe sinnvoller Weise in einem Abschnitt Bewegungsberechnung in den realen Größen wie Meter m, Geschwindigkeit m/s oder Beschleunigung m/s2 berechnet werden sollten. Diese werden dann
  2. in einem Darstellungsabschnitt mittels einer Maßstab-Umrechnung und anschließender Koordinatentransformation maßstäblich und örtlich korrekt plaziert und dargestellt.
  3. es schließlich noch administrative Aufgaben zu erledigen gibt. Dazu gehören z.B. textliche Ausgaben (Überschriften, Zahlenangaben) aber auch die unterschiedlichsten Bedienelemente (Buttons oder Scrollbars). Diese Elemente haben ja mit der Spiel-Szenerie nichts zu tun. Daher unterliegen sie keiner maßstäblichen oder örtlichen Transformation.
Und hier stehen die Beispielprogramme nach den Abbildungen b und c) zum Download bereit:
run program
download p5-Programm
run program
download p5-Programm




Wenn Du selbst ein Programm entwickeln möchtest

Schaust Du auf die offizielle Seite von p5.js wirst Du Hinweise und Werkzeuge für eine eigene Programmentwicklung finden. Dennoch möchte ich Dir hier eine alternative Möglichkeit für eigene Programme vorstellen, die auf den Funktionen der Dienstprogramme Includes aufbaut. Allerdings sind die hier im download der Includes_min verfügbaren Programme soweit vereinfacht, dass sämtliche Elemente, die für die Touch-Funktionen erforderlich sind, weggelassen wurden. Auch werden nur die Includes zur Verfügung gestellt, die Bezug auf die Bedienelemente (Maus aktv) und Gestaltung (z.B. Darstellung von Zeitfunktionen) haben.

Folgende Includes werden bereit gestellt:

1. pBasics_min
function task
kXi(), kYi() Koordinatentransformation: kartesisch [m] zu intern [pixel], bzgl. Nullpunkt xi0, yi0
iXk(), iYk() Koordinatentransformation: intern [pixel] zu kartesisch [m], bzgl. Nullpunkt xi0, yi0
mouseClicked(),
mousePressed(),
mouseReleased(),
mouseDragged()
Maus-events: Mauskoordinaten werden mit den Systemvariablen mouseX [pixel] bzw. mouseY [pixel] übergeben
keyPressed(),
keyReleased()
Tastaturevents, Resultat wird in der Systemvariablen key übergeben
keyPressed(),
keyReleased()
Tastatur-events
saveStringToFile(),
loadStringFromFile(),
loadError
File-Operationen

2. pConstants_min
function task
setConstants() Constanten für responsives Design:
gridX, gridY [pixel]: 1% von canvasWidth bzw. canvasHeight
normPixelX, normPixelY [pixel]: 0.1% von canvasWidth bzw. canvasHeight
normPixel [pixel]: geometrisches Mittel aus normPixelX und normPixelY
fontSize [pixel]: responsive Schriftgröße basierend auf der Größe von normPixel
buttonWidth, buttonHeight [pixel]: Button-Maße basierend auf gridX bzw. gridY

3. pControls_min   (detailierte Erläuterungen findest Du in den Headern der Funktionen!)
object task
Circle() / Objekt Maus sensitiver Kreis, kartesisches Koordinatensystem
function: inCircle
result: status, Postion [m]
Ring() / Objekt Maus sensitiver Kreisring, kartesisches Koordinatensystem
function: inCircle
result: status, Radius [m], Postion [m]
Arrow() / Objekt Maus sensitiver Vektorpfeil, kartesisches Koordinatensystem
function: moveArrow
result: status, Postion [m], Betrag [m], Winkel [rad]
Handle() / Objekt Maus sensitiver Griff (rotierend), kartesisches Koordinatensystem
function: moveHandle
result: status, Winkel [rad]
ToggleButton() / Objekt Button, Zustand toggelnd, Positionierung im internen Koordinatensystem
function: drawButton
result: Zustand
PushButton() / Objekt Button, triggernd oder andauernd, wenn mousePressed, Positionierung im internen Koordinatensystem
function: drawButton
result: Zustand
ScrollBar() / Objekt Schieberegler, Positionierung im internen Koordinatensystem
function: yValue
result: Wert
Path() / Objekt Bezier-Pfad, kartesisches Koordinatensystem
function: createPath
function: usePath
result: Wertpaare [m]

4. pFramework_min   (detailierte Erläuterungen findest Du in den Headern der Funktionen!)
function / object task
prepareScreen() Headline/Subline, Positionierung im internen Koordinatensystem
displayLine() Textausgabe, Positionierung im internen Koordinatensystem<
formatNumber() Formatierung von Gleitkommazahlen
result: String [Vorkomma, Nachkomma]
displayArrow() Darstellung von Vektoren, kartesisches Koordinatensystem
Diagramm() / Objekt Darstellung von Zeitfunktionen, internes Koordinatensystem
function: prepareWindow
function: drawXScale
function: drawYScale
function: drawTimeLine
Xaxis() / Objekt Darstellung einer x-Achse, internes Koordinatensystem
function: drawXaxis
Yaxis() / Objekt Darstellung einer y-Achse, internes Koordinatensystem
function: drawYaxis
Spring() / Objekt Darstellung einer Feder, kartesisches Koordinatensystem
function: drawSpring
Zur Entwicklung eines javascript-Programms benötigst Du einen Browser wie z.B. Firefox oder Chrome. Und einen geeigneten Editor wie z.B. Notepad++ oder Eclipse. Auch p5.js macht hierzu passende Vorschläge.
Wenn Du das File pTemplate.zip geladen und entpackt hast, findest Du ein komplettes Archiv für eine eigenständige Entwicklung.

Sandbox: Ordnerhierarchie

Abb. Sandbox: Ordnerhierarchie

Damit Du ein eigenes Programm entwickeln und vorführen kannst, ist zunächst der download des gezippten Files pTemplate.zip erforderlich. Dazu legst Du einen Ordner (in AGP sandbox genannt) an, in den das gezippte File pTemplate.zip lädst und dort entpackst. Damit wird eine Ordnerstruktur inclusive Inhalt gemäß erzeugt. Das Programm example.js ist ein Beispielprogramm, das die Nutzung der includierten Dienstprogramme demonstriert und von Dir auch als Grundstock für eigene Entwicklungen weiter verwendet werden kann. Doppelklick auf die index.html öffnet das Beispielprogramm im Standard-Browser.
Möchtest Du das Programm example.js umbenennen, dann vergiss nicht, diese Umbenennung auch in der index.html vorzunehmen!

download p5-Programm
p5: Template
example und includes_min
run program
Beispielanwendung starten
(RETURN nur über Browser-return möglich!)


Arbeiten mit einer state machine

Spiele, insbesondere Computer-Spiele, sind durch eine Vielzahl unterschiedlicher Situationen gekennzeichnet. Dabei sind diese Situationen in ihrem Ablauf oder ihrer Aufeinanderfolge durch vorangegangene Situationen oder Zustände des Spiels bestimmt. Für die Programmierung eines Spiels bedeutet dies, dass die unterschiedlichen Zustände als Programmteil dargestellt sein müssen und, dass die Erkennung von Zustandsübergängen klar definiert sein muss. Dies führt auf die Idee der state-machine.

Die state-machine muss im wesentlichen drei Aufgaben erfüllen:
  1. Erkennen, wenn eine Wechsel des Status erfolgt. Benennen des nunmehr aktuellen Status,
  2. Übergabe der Parameter und Variablenwerte vom vorherigen zum folgenden Status,
  3. Ausführen der Bewegungsberechnung des aktuellen Status.

Der Begriff der state-machine oder Zustandsmaschine ist in der Literatur weit verbreitet und wohl definiert. Meine Verwendung dieses Begriffs ist deutlich enger gefasst. Ich meine mit state-machine den vom Ablauf eines Spiels gesteuerten Wechsel von Spielsituationen. Im folgenden Beispiel des Minigolf-Spiels möchte ich das deutlich machen.
Im weitesten Sinn bedingen alle Handlungen bzw. die dazu erforderlichen Programmteile Zustände. Darum geht es mir aber nicht! Für den Spielablauf sind hier die Wechsel zwischen den Zuständen des Golf-Balls: Rollen auf der Ebene oder der Schräge, Fliegen im schrägen Wurf, Einlochen oder Bewegung bei Hindernissen wichtig. Das Erkennen der Wechsel in den Bewegungen des Golf-Balls und die Übergabe der physikalischen Parameter wie Ort, Geschwindigkeit oder Winkel ist für den kontinuierlichen Bewegungsverlauf des Balls von entscheidender Bedeutung! Die Situationen, die zu solchen Wechseln führen, zu erkennen und die erforderlichen Parameter bereit zu stellen, das ist meine state-machine!

Mini-Golf

Abb. Mini-Golf: Spielszenarium


Ganz kurz zur Spielidee (). Bei dem Minigolf-Spiel handelt es sich um eine vereinfachte Version des Golf-Spiels. Anders als in der Realität kann mein Golfer nur den Schläger (hier Putter genannt) bewegen, er selbst steht unverrückbar an der Abstoßstelle. Der Spieler fasst mit der Maus/linke Maustaste das untere Ende des Putters (hier durch einen grauen Kreis markiert) an und bewegt diesen im Bogen nach rechts. Dabei sind Heben oder Senken des Schlägers in einem engen Bereich möglich, um so den Flug des Golfballs steuern zu können. Mit dem Loslassen der Maustaste beginnt die Bewegung des Putters und in der Folge auch die des Golfballs. Ein Punkt wird gewonnen, wenn es dem Spieler gelingt, den Golfball "einzulochen". Zur Erschwerung des Einlochens
  1. weht ein unstetiger Wind, dessen Stärke und Richtung an dem Lochfähnchen zu ersehen ist,
  2. ist der Boden mit Rasen (grün) oder Sand (gelb) bedeckt,
  3. gibt es einen Wassergraben (blau), der den Lauf des Balles unterbricht.
skizziert mögliche Ballbewegungen (Zustände), die alle mit Hilfe der state-machine zu analysieren sind, um diese endlich als Bewegungs-Parameter im Falle eines Zustandswechsels bereit zu stellen. Im wesentlichen sind es zwei Zustände der Bewegung des Balls und ein Zustand des Putters. Das sind:
Natürlich gilt es noch Roll-Reibungseinflüsse auf der Ebene und Strömungsreibungs- und Windeinflüsse während des Fluges zu beachten.

zeigt die Abhängigkeiten der Zustände von einander. Das Spiel beginnt mit dem Status "start". Hier bewegt der Spieler den Golfschläger aus seiner Ruhelage. Beim Loslassen der Maustaste pendelt der Schläger mit einer Winkelgeschwindigkeit ω, die der Auslenkung proportional ist, in Richtung Golfball. Stoßen Schläger und Golfball zusammen, erfolgt eine Wechselwirkung entsprechend der Stoß/Der freie elastische Stoß. Damit erfolgt der Übergang vom Status "start" zum Status "on flight" (1). Im Übergang (1) werden der Ort centerBall und die Ballgeschwindigkeit vxBall, vyBall übergeben.

Im Zustand "on flight" wirken die Gesetze des schrägen Wurfs unter den Einflüssen von Strömungsreibung und Wind. Während des Fluges wird laufend geprüft, ob und wo der Ball mit dem Grund (Rasen, Wasser, Loch, Sand) kollidiert. Ist dies der Fall, wird die Segment-Nummer des Landeorts, der Abstand s vom Beginn des betreffenden Segments vermerkt. Die Kollision mit dem Grund ist wieder ein Stoß-Ereignis, das eine zum Segment parallele vP bzw. normale Geschwindigkeitskomponente vN liefert. Ist die normale (senkrechte) Komponente groß genug, springt der Ball vom Segment ab (bouncing) und der Status "on flight" wird mit den Übergabewerten (2) erneuert. Das wiederholt sich solange, bis die Bedingung für das Bouncing nicht mehr erfüllt ist, dann erfolgt der Übergang (4) zum Status "on ground". Ergibt die Kollision-Prüfung, dass der Ball aus dem Zustand "on flight" direkt einlocht oder in den Wassergraben fällt, dann erfolgt ein Übergang zu den Prozessen "hole" oder "water".
Mini-Golf

Abb. Mini-Golf: state-machine



Im Zustand "on ground" rollt der Ball auf den Segmenten, die allesamt schiefe Ebenen mit unterschiedlichen Neigungswinkeln "β" darstellen. Rollt der Ball von einem Segment zum nächsten oder auch vorherigen Segment, erfolgt eine permanente Parameterübergabe (6) innerhalb dieses Zustands. In diesem Zustand ist zu prüfen, ob der Ball in den Wassergraben oder das Loch rollt. Auch hier erfolgt ein Übergang zu den Prozessen "hole" oder "water", was zur Beendigung des aktuellen Spielversuchs führt.
Es kann aber auch geschehen, dass der Ball bei großen Geschwindigkeiten und entsprechenden Neigungswinkeln von der Segmentoberfläche abhebt. Dann geht der aktuelle Zustand in den Status "on flight" zurück, wobei wieder eine Parameterübergabe (5) erfolgen muss.
Ist die Ballgeschwindigkeit aber zu klein geworden, bleibt der Ball auf dem Spielfeld liegen. Nur ein "NEW" oder ein "RESET" ermöglicht, ebenso wie nach "hole" oder "water", eine Fortsetzung des Spiels
Dieses Beispiel enthält neben den oben gezeigten Verlinkungen weitere Vorgriffe auf viele der in den folgenden Kapiteln ausgeführten physikalischen und mathematischen Teilaufgaben. Genannt seien hier:
•  Maßstäbe, responsives Design und Koordinatentransformation
•  Kollision, Fehlerbehandlung sowie dezentraler Stoß      und
•  numerische Lösung von Differentialgleichungen

download p5.js

run program