Abbildung 1: Der Perlmeister springt vom Himmel. Das zugehörige Video gibt's auf [2]. |
Programmierer von Computerspielen rechnen mit physikalischen Formeln und setzen spezielle Tricks ein, um Grafiken realitätsnah zu animieren. Der als Perl-Wrapper verfügbare Simple DirectMedia Layer (SDL) bietet ein leistungsfähiges Framework, um mit wenigen Zeilen einfache 2D-Welten zu erschaffen.
Als ich neulich das Video meines Tandemfallschirmsprungs von einer ollen VHS-Cassette auf Youtube überspielte und den Link [2] einigen Arbeitskollegen schickte, entbrannte eine heftige Diskussion über die bei einem Sprung relevanten physikalischen Gesetze.
In einem vereinfachten Modell ohne Seitenwindeinfluss startet der Springer mit einer vertikalen Geschwindigkeit vy=0 und beschleunigt sofort aufgrund der Erdanziehung nach unten. Der nach unten wirkenden Erdanziehungskraft wirkt der Luftwiderstand entgegen, der sich mit steigender Fallgeschwindigkeit stetig vergrößert. Abhängig vom Gewicht und Ausmaßen des Springers stellt sich dann bei 180/h ein Kräftegleichgewicht ein und der Springer fällt mit konstanter Geschwindigkeit nach unten. Für den Springer fühlt sich das an, als würde er im Weltall schweben und dieser Zustand hält solange an, bis der Fallschirm geöffnet wird, was sich anfühlt, als zöge einen jemand an einem Seil ruckartig nach oben.
Das Skript skydive
in Listing 1 simuliert das Fallexperiment. Es
stellt den frei fallenden Springer in einem Icon dar, das erst langsam,
dann immer schneller nach unten fällt, bis die konstante Endgeschwindigkeit
von 50 m/s (180 km/h) erreicht ist.
Der User löst den Fallschirm mit einem Tastendruck auf die Taste
``Cursor-UP'' aus, worauf das Icon sich in einen Springer mit offenem
Fallschirm verwandelt, der zunächst stark abbremst und dann langsam
der Erde entgegensegelt.
Abbildung 2: Der Springer beschleunigt nach dem Absprung und erreicht nach einigen Sekunden die konstante Fallgeschwindigkeit. |
Abbildung 3: Der Springer fällt mit 40.96 m/s und ist nicht mehr weit vom Boden entfernt -- allerhöchste Zeit, mit der Taste "Cursor-Up" den Fallschirm zu öffnen! |
Abbildung 4: Der Fallschirm geht auf und bremst den Springer ab. Bei einer sicheren Landung fällt der Springer weniger als 3.1 m/s. |
Das Skript zählt die Sekunden vom Absprung bis zur sicheren Landung. Das Ziel ist es, die Reißleine möglichst spät zu ziehen, aber darauf zu achten, dass die Landegeschwindigkeit bei offenem Fallschirm nicht mehr als 3 m/s (etwa 11 km/h) beträgt, damit der Springer sich nicht verletzt. Das Display zeigt links die verstrichene Zeit in Sekunden und rechts die aktuelle Fallgeschwindigkeit in m/s an. Erreicht der Spieler eine neue Bestzeit, wird diese unterhalb des aktuellen Zählerfeldes eingeblendet und bleibt dort, bis ein neuer Versuch die Bestzeit unterschreitet. Landet der Spieler aber zu schnell, verwandelt sich das Fallschirm-Icon am Boden wieder in einen Springer ohne Fallschirm, um einen missglückten Versuch anzuzeigen. Die Bestzeit bleibt in diesen Fällen unberührt.
Abbildung 5: Sichere Landung mit 3.0 m/s und neuer Rekord mit 17.60 Sekunden! |
Abbildung 6: Gute Zeit, aber ungültiger Versuch, da die Landegeschwindigkeit mit 45.33 m/s viel zu hoch war. |
Die Geschwindigkeit eines aus dem Ruhezustand konstant beschleunigten Körpers beträgt v=a*t. Im Falle eines aus einem Flugzeug springenden Körpers ist die Beschleunigung a gleich der Erdbeschleunigung (9.82 m/s**2) und die Zeit t tickt in Sekunden und startet beim Absprung.
Die der Gravitationskraft entgegen wirkende Luftreibung lässt sich als negative Beschleunigung auslegen, die für vy=0 gleich Null und für vy=vterm gleich der Erdbeschleunigung 9.81m/s**2 ist.
Der Luftwiderstand berechnet sich aus Masse und Geschwindigkeit des Springers sowie dem Reibungskoeffizienten des Springers in Luft. Nach [5] beschleunigt ein 80kg schwerer Erwachsener nach dem Absprung binnen 14 Sekunden auf etwa 190 km/h, wobei er 548 Meter zurücklegt. Anschließend fällt er 3000 Meter pro Minute mit konstanter Geschwindigkeit, bis der Fallschirm sich öffnet.
Diese Endgeschwindigkeit hängt allerdings auch noch von der Fluglage ab. Mit dem Kopf voran ist der Luftwiderstand geringer und es lassen sich leicht über 200km/h erreichen. Details zur Berechnung des Luftwiderstands stehen unter [6]. Bewegt sich der Körper schließlich mit konstanter Geschwindigkeit fort, weil Gravitiations- und Luftreibungskraft sich gegenseitig aufheben, legt er mit der Geschwindigkeit v in der Zeit t die Strecke s=v*t zurück.
In einem animierten Spiel, das 50 Frames pro Sekunde zeichnet, muss man keine Multiplikation ausführen, um die gleichmäßige Fortbewegung einer Figur zwischen zwei Frames auszurechnen. Man dividiert einfach die Geschwindigkeit in m/s durch 50 und addiert das Ergebnis zur aktuellen Position. Wiederholt man das 50 Mal für jeden Frame, ist man am Ende einer Sekunde genau bei s=v*1s, was der physikalischen Formel für die gleichmäßige Fortbewegung entspricht.
Wegen der relativ kleinen Zeitabstände zwischen den einzelnen Frames funktioniert das sogar für die gleichmäßig beschleunigte Bewegung eines zur Erde fallenden Körpers. Zusätzlich zur Position ändert sich allerdings in jedem Frame die Geschwindigkeit des Körpers. Um diese zu berechnen, addiert man einfach jedesmal 1/50 der Erdbeschleunigung zur aktuellen Geschwindigkeit hinzu. Macht man das 50 mal hintereinander, ist man genau bei v=a*1s.
Die Beschleunigung ist allerdings auch nicht konstant. Fällt der
Körper aus dem Flugzeug, ist die Beschleunigung 9.81 m/s**2, wenn man
Effekte wie Seitenwind und Auftrieb einmal vernachlässigt. Mit
steigender Geschwindigkeit hemmt die Luftreibung den Fall, und die
tatsächliche Beschleunigung ist kleiner als die durch die Gravitationskraft
erzeugte. Erreicht der Körper die maximale Fallgeschwindigkeit
vterm
, ist die Beschleunigung gleich Null und der Springer fällt mit
konstanter Geschwindigkeit. Das Spiel löst diese Berechnung mit einem
vereinfachten Verfahren, indem es mit der Funktion deceleration
einen
Wert berechnet, der von der aktuellen Beschleunigung abgezogen wird.
Er ermittelt sich aus dem Verhältnis von aktueller und maximaler
Geschwindigkeit, nimmt also einen linearen Zusammenhang an.
Wird der Fallschirm geöffnet, ist die Beschleunigung sogar negativ.
Allerdings bremst der Fallschirm nicht beliebig stark, das Spiel
limitiert die maximal herrschende Gegenkraft auf 2g.
Was deceleration
treibt, ist physikalisch gesehen zwar Lug und Trug,
aber für das Spiel reicht die Genauigkeit.
Bewegt sich ein Icon durch das Spielfeld, wie ein vom Himmel fallender
Fallschirmspringer, löscht SDL zunächst den alten
Eintrag und zeichnet dann das Bild an der neuen Position. Das Icon ist
schon als Image im Speicher und wird mit der Methode blit()
von einer
Speicherposition an eine andere kopiert. Mit diesem Trick entstehen
Änderungen an der Spieloberfläche mit beeindruckender Geschwindigkeit
und der Benutzer schwebt in der Illusion einer realen Welt.
Um zum Beispiel das Logo des Spiels, eine mit Gimp erstellte PNG-Grafik,
an den oberen Rand des Spielfelds zu zeichnen, lädt das Skript die
Datei logo.png
in Zeile 21 mit dem Konstruktor der
Klasse SDL::Surface von der
Platte in den Speicher. Zeile 39 definiert dann ein Rechteck der Klasse
SDL::Rect mit Länge, Breite, sowie der gewünschten Position der
Grafik im Bild. Die X-Koordinaten laufen von links nach rechts, die
Y-Koordinaten von oben nach unten.
Die Methode blit()
der Grafik in $logo
in Zeile 43 kopiert die Daten
blitzschnell auf die Spieloberfläche $app
.
SDL frischt die Oberfläche allerdings nicht sofort auf, sondern wartet
aus Performance-Gründen, bis der Programmierer mit der Methode update()
den Befehl dazu gibt. So kann SDL viele Rechtecke blitzschnell auf einmal
auffrischen, sodass der Eindruck einer ruhig laufenden Animation entsteht.
001 #!/usr/bin/perl -w 002 use strict; 003 use SDL; 004 use SDLMove; 005 use SDL::TTFont; 006 007 local *main::TEXT_SHADED = \&SDL::Event::TEXT_SHADED; 008 009 my $SPEED_MS = 20; 010 my $FRAMES_PSEC = 1000.0/$SPEED_MS; 011 my $VTERM_FREE = 50; # Terminal speed 012 my $VTERM_PARA = 3; # ... with parachute 013 my $WIDTH = 158; 014 my $HEIGHT = 500; 015 my $G = 9.81; 016 my $MAX_LAND = 3.1; 017 018 my $bg_color = SDL::Color->new( 019 -r => 0, -g => 0, -b => 0 ); 020 my $fg_color = SDL::Color->new( 021 -r => 0xff, -g => 0x0, -b => 0x0 ); 022 023 my $logo = SDL::Surface->new( 024 -name => "logo.png"); 025 # Load player icons 026 my $diver = SDL::Surface->new( 027 -name => "dive.png"); 028 my $para = SDL::Surface->new( 029 -name => "para.png"); 030 031 my $app = SDL::App->new( 032 -title => "Skydive 1.0", -depth => 16, 033 -width => $WIDTH, -height => $HEIGHT); 034 035 my $fontpath = "/usr/X11R6/lib/X11/fonts/TTF/VeraMono.ttf"; 036 $fontpath = "/usr/share/fonts/truetype/ttf-bitstream-vera/VeraMono.ttf" 037 if ! -f $fontpath; 038 039 my $font = SDL::TTFont->new( 040 -name => $fontpath, 041 -size=>15, 042 -bg => $bg_color, -fg => $fg_color); 043 044 my $lrect = SDL::Rect->new( 045 -width => $logo->width, 046 -height => $logo->height, 047 -x => 0, -y => 0); 048 $logo->blit(0, $app, $lrect); 049 $app->update($lrect); 050 051 my $event = new SDL::Event->new(); 052 $event->set_key_repeat(200, 10); 053 054 my $record_time; 055 my $gtime; 056 057 # Next game ... 058 GAME: while(1) { 059 060 my $obj = SDLMove->new( 061 app => $app, 062 bg_color => $bg_color, 063 x => $WIDTH/2 - $diver->width()/2, 064 y => $logo->height, 065 image => $diver, # Start with diver 066 ); 067 068 my $v = 0; 069 my $vterm = $VTERM_FREE; 070 my $start = $app->ticks(); 071 072 while(1) { # Frame loop 073 my $synchro_ticks = $app->ticks; 074 075 # Accelerate 076 $v += ($G - deceleration($v, $vterm)) 077 / $FRAMES_PSEC; 078 # Move player downwards 079 $obj->move("s", $v/$FRAMES_PSEC); 080 081 if($obj->hit_bottom()) { 082 if($v <= $MAX_LAND) { # soft enough? 083 if(! defined $record_time or 084 $gtime < $record_time) { 085 $record_time = $gtime; 086 } 087 nput($app, 0, $lrect->height + 20, 088 $record_time); 089 } else { 090 $obj->wipe(); 091 $obj->image($diver); 092 $obj->move("s", # indicate crash 093 $para->height - $diver->height); 094 } 095 sleep 5; 096 $obj->wipe(); 097 next GAME; 098 } 099 # Process all queued events 100 while ($event->poll != 0) { 101 my $type = $event->type(); 102 exit if $type == SDL::Event::SDL_QUIT(); 103 104 if($type == SDL::Event::SDL_KEYDOWN()) { 105 my $keypressed = $event->key_name; 106 107 if($keypressed eq "left") { 108 $obj->move("w", 0.1); 109 } elsif($keypressed eq "right") { 110 $obj->move("e", 0.1); 111 } elsif($keypressed eq "up") { 112 # deploy parachute 113 $vterm = $VTERM_PARA; 114 $obj->image($para); 115 } elsif($keypressed eq "r") { 116 $obj->wipe(); 117 next GAME; 118 } elsif($keypressed eq "q") { 119 exit 0; # quit 120 } 121 } 122 } 123 $gtime = ($app->ticks - $start)/1000.0; 124 125 nput($app, 0, $lrect->height, $gtime); 126 nput($app, 110, $lrect->height, $v); 127 128 my $wait = $SPEED_MS - 129 ($app->ticks - $synchro_ticks); 130 select undef, undef, 131 undef, $wait/1000.0 if $wait > 0; 132 } 133 } 134 135 ########################################### 136 sub deceleration { 137 ########################################### 138 my($v, $vterm) = @_; 139 140 my $d = $v/$vterm*9.81; 141 142 $d = 0 if $d < 0; 143 $d = 2*$G if $d > 2*$G; 144 145 return $d; 146 } 147 148 ########################################### 149 sub nput { 150 ########################################### 151 my($app, $x, $y, $number) = @_; 152 153 my $rect = SDL::Rect->new( 154 "-height" => $font->height, 155 "-width" => $font->width($number), 156 "-x" => $x, 157 "-y" => $y); 158 159 $app->fill($rect, $bg_color); 160 my $string = sprintf "%-5.2f", $number; 161 $font->print($app, $x, $y, $string); 162 $app->sync(); 163 } 164
Zeile 7 setzt die Geschwindigkeit der Animation auf 20 Millisekunden
pro Frame. Das entspricht 50 Frames pro Sekunde, was die Variable
$FRAMES_PSEC
in Zeile 8 widerspiegelt. Die Endlosschleife ab
Zeile 67 bringt die Frames auf den Bildschirm. Um die Taktrate genau
zu halten, fragt das Skript am Anfang der Schleife mit $app->ticks()
die seit Programmbeginn verstrichenen Anzahl von Millisekunden ab
und speichert sie
in der Variablen $synchro_ticks
ab.
Eine weitere Messprobe am Ende der Schleife bestimmt dann die von
Schleifenanfang bis Schleifenende verstrichenen Millisekunden. Ist diese
kleiner als 20, muss das Skript solange pausieren, bis die anberaumten
20 Millisekunden pro Frame abgelaufen sind.
Mit select()
kann man Pausen mit Millisekunden-Auflösung einlegen,
so läuft die Animation ruckelfrei.
Ist die Differenz zwischen anberaumter zu verstrichener Zeit
gar negativ, hat die Berechnung innerhalb der Schleife
länger als 20 ms gedauert und das Skript muss umgeschrieben oder die
Framerate gesenkt werden.
Auch während skydive
emsig die Hauptschleife durchläuft, kommen
Events wie Tastendrücke, Mausbewegungen oder Klicks auf das
Schließ-Icon des Applikationsfensters bei der Applikation an.
Das in Zeile 46 definierte Objekt der Klasse SDL::Event stellt
die Methode poll()
bereit, die anzeigt, ob überhaupt Events
vorliegen. Den Typ des Events liefert event_type()
.
Der Eventtyp SDL_QUIT liegt zum Beispiel an, wenn der User
das Applikationsfenster mit der Maus schließt. In diesem Fall bricht das
Skript in Zeile 97 einfach mit exit
ab.
Events vom Typ SDL_KEYDOWN signalisieren, dass der User eine Taste des
Keyboards gedrückt hat. Die Methode key_name()
in Zeile 100 findet
heraus, welche Taste es war. Praktischerweise übersetzt SDL die Tastaturcodes
gleich in handliche Strings und so kommt bei gedrückter rechter Cursortaste
der String "right"
an und "q"
, wenn jemand auf ``q'' gedrückt hat.
Die Methode set_key_repeat()
hilft, länger gedrückte Tasten als
wiederholende Eingaben zu verarbeiten. Sie nimmt zwei Parameter entgegen.
Der erste bestimmt, nach wievielen Millisekunden SDL eine konstant
gedrückte Taste auf Dauerfeuer stellt. Der zweite Parameter bestimmt
den zeitlichen Abstand der dann ausgelösten Salven, ebenfalls in
Millisekunden. Für das vorgestellte
Spiel ist das zwar irrelevant, aber wer den Springer mit den nach links
und rechts zeigenden Cursortasten manövrieren will, wird diese Einstellung
zu schätzen wissen.
Drückt der User auf die Cursortaste mit dem Pfeil nach oben, möchte
er den Fallschirm auslösen und die elsif-Bedingung ab Zeile 107 leitet
zwei Aktionen ein: Die Endgeschwindigkeit $VTERM wird von $VTERM_FREE
auf $VTERM_PARA gesetzt und die Methode image()
des Spieler-Objektes
$obj
setzt das Spieler-Icon auf $para
, also das Fallschirm-Icon.
Weitere Tastendrücke sind ``r'' für Restart (also einen Spielabbruch
und -neugbeginn), sowie ``q'', um die Applikation abzubrechen. Für Erweiterungen
sind auch noch die nach links und rechts zeigenden Cursortasten definiert,
die das Spielerobjekt im Fallen nach links und rechts transportieren. Dies
ist zwar in der vorliegenden Version nicht notwendig, könnte aber bei
Erweiterungen nützlich sein.
Die Variable $gtime
gibt die Spielzeit eines gerade beendeten Durchlaufs
an und $record_time
wird aufgefrischt, falls eine neue Rekordzeit erzielt
wurde und die Landung nicht allzu hart verlaufen ist.
Die Applikation selbst liegt im Objekt $app
vom Typ SDL::App, einer
von SDL::Surface abgeleiteten Klasse. Zeichenvorgänge im Applikationsfenster
oder ein Auffrischen der veränderten Rechtecke laufen grundsätzlich über
$app
ab.
Damit der ganze Zirkus um das Verschieben des Spieler-Icons einfacher
vonstatten geht, definiert das Modul SDLMove.pm in Listing 2
einige Hilfsfunktionen. Die Methode image()
setzt das Spieler-Icon
auf das spezifizierte Objekt vom Typ SDL::Surface.
Da SDLMove die Dimensionen der Applikation kennt, kann es mit
hit_bottom()
mitteilen, ob die Spielfigur das untere Ende des
Spielfensters erreicht hat und die aktuelle Runde abgeschlossen ist.
Die Methode wipe()
wischt die Spielerfigur mit einem Schlag vom
Feld, zum Beispiel um einen gescheiterten Fallschirmspringer am Boden
zurück in einen freifallenden zu verwandeln, um anzuzeigen, dass der
aktuelle Versuch wegen zu hoher Aufprallgeschwindigkeit gescheitert ist.
Die Methode move()
bewegt die Spielfigur um die angegebene Anzahl von
Pixeln in eine per Himmelsrichtung (n=North s=South w=West e=East)
angegebene Richtung. Die
Pixels dürfen ruhig auch Bruchteile enthalten, die dann zwar keine
Auswirkung auf die aktuelle Bewegung haben, aber für zukünftige
Aktionen aufkumuliert werden. Bevor die Spielfigur weiterwandert, löscht
SDLMove die alte Repräsentation, damit eine ordentliche Bewegung auf
dem Bildschirm entsteht.
In $VTERM_FREE
liegt mit 50m/s die Höchstgeschwindigkeit im freien
Fall, $VTERM_PARA
gibt mit 3m/s die Sinkrate des Fallschirms an, auf die
sich diese nach einiger Zeit des Segelns einpendelt. In der Sektion nach Zeile
7 lassen sich diese Werte umändern und auch andere Parameter wie die
Höhe und Breite des Animationsfensters verändern.
Um Texte auf der Spieloberfläche darzustellen, jongliert das Modul
SDL::TTFont
mit True Type Fonts. Es baut Zeichenstrings zu gerenderten Textketten
zusammen und hilft, diese in die Spieloberfläche hineinzuschreiben.
Der in Zeile 33 aufgerufene Konstruktor lädt den fixed-Font
VeraMono
, der im Unterverzeichnis TTF des Fontverzeichnisses
meines X-Servers liegt. Die Optionen -fg
und -bg
stellen die
Textfarbe Rot auf schwarzem Hintergrund ein. Die Methode print()
übernimmt das Rendern und die Anzeige auf der Spieloberfläche an
einem bestimmten Punkt an den Koordinaten ($x, $y). Ähnlich wie bei den
vorher besprochenen Rechtecken frischt SDL die Anzeige nicht direkt
nach einem print
-Befehl auf, sondern wartet auf ein nachfolgendes
sync()
auf das $app
-Objekt.
Überschreibt ein weiterer Aufruf allerdings die gleiche Stelle mit
neuem Text, bleibt die ursprüngliche Anzeige bestehen und das Nummerfeld
sieht nach einigen Sekunden aus wie das Cover ``Ghost in the Machine'' von
der 70er-Jahre-Band The Police.
Die ab Zeile 144 definierte Funktion nput
ermittelt deswegen
zunächst die Ausmaße des gerenderten Textstrings und definiert ein
Recheck darauf. Dieses füllt sie dann mit schwarzer Farbe, damit die
Funktion print
anschließend gedankenlos drüberschreiben kann.
01 package SDLMove; 02 use strict; 03 use warnings; 04 use SDL; 05 use SDL::App; 06 07 ########################################### 08 sub new { 09 ########################################### 10 my($class, %options) = @_; 11 12 my $self = { %options }; 13 bless $self, $class; 14 15 $self->image($self->{image}); 16 return $self; 17 } 18 19 ########################################### 20 sub image { 21 ########################################### 22 my($self, $image) = @_; 23 24 $self->{image} = $image; 25 $self->{drect} = SDL::Rect->new( 26 -width => $image->width, 27 -height => $image->height, 28 -x => $self->{x}, 29 -y => $self->{y}, 30 ); 31 } 32 33 ########################################### 34 sub move { 35 ########################################### 36 my($self, $direction, $pixels) = @_; 37 38 my $rect = $self->{drect}; 39 my $app = $self->{app}; 40 41 if($direction eq "w") { # left 42 $self->{x} -= $pixels if $self->{x} > 0; 43 44 } elsif($direction eq "e") { # right 45 $self->{x} += $pixels if $self->{x} < 46 $app->width - $rect->width; 47 48 } elsif($direction eq "n") { # up 49 $self->{y} -= $pixels if $self->{y} > 0; 50 51 } elsif($direction eq "s") { # down 52 $self->{y} += $pixels if $self->{y} < 53 $app->height - $rect->height; 54 } 55 56 $self->{old_rect} = SDL::Rect->new( 57 -height => $rect->height, 58 -width => $rect->width, 59 -x => $rect->x, 60 -y => $rect->y, 61 ); 62 63 $rect->x( $self->{x} ); 64 $rect->y( $self->{y} ); 65 $app->fill($self->{old_rect}, 66 $self->{bg_color}); 67 68 $self->{image}->blit(0, $self->{app}, 69 $rect); 70 $app->update($self->{old_rect}, $rect); 71 } 72 73 ########################################### 74 sub wipe { 75 ########################################### 76 my($self) = @_; 77 78 $self->{app}->fill($self->{drect}, 79 $self->{bg_color}); 80 $self->{app}->update($self->{drect}); 81 } 82 83 ########################################### 84 sub hit_bottom { 85 ########################################### 86 my($self) = @_; 87 88 return $self->{y} > 89 $self->{app}->height - 90 $self->{drect}->height; 91 } 92 93 1;
SDL ist in gängigen Linux-Distributionen meist schon vorhanden,
andernfalls sind die RPMs SDL
, SDL-devel
, SDL_ttf
,
SDL_ttf-devel
und SDL_mixer
zu installieren.
Eine CPAN-Shell erledigt anschließend mit install SDL_perl
die Installation
des Perl-Wrappers mit allen heute verwendeten SDL-Modulen.
Es ist darauf zu achten, SDL_perl
erst nach den
obengenannten Bibliotheken zu installieren, da letzeres sonst Features
with True Type Fonts einfach nicht unterstützt. Die drei verwendeten
Icons logo.png
, dive.png
und para.png
sind mit den Listings auf
dem Download-Server des Linux-Magazins verfügbar. Das Skript sucht die Icons
wenn es hochfährt im aktuellen Verzeichnis und beschwert sich, falls
sie fehlen.
Das Spiel lässt sich mit ein paar Perl-Zeilen leicht erweitern. Wer
sich von Experten Ideen holen möchte, ist gut damit beraten, den
Sourcecode von Frozen Bubble
([4]) zu studieren. Hierbei handelt es sich
um ein mit SDL_Perl geschriebenes Spiel mit professioneller
Animation. Einige Ideen für den heute vorgestellten Prototyp: Es
wäre denkbar, den Springer realitätsnah aus einem sich mit horizontaler
Geschwindigkeit bewegenden Flugzeug abspringen zu lassen. In diesem Fall
bewegt er sich zunächst mit konstanter Geschwindigkeit seitwärts, wird
aber durch die Luftreibung abgebremst. Ziel des Spiels wäre es dann, nicht
nur sanft zu landen, sondern auch eine Markierung am Boden zu treffen oder
Wasser oder Strommasten auszuweichen.
Nach dem Öffnen des Fallschirms kann der Springer langsam hin- und
hersegeln, um Korrekturen vorzunehmen.
Aber natürlich nur, solange der
zufällig aufbrausende Seitenwind ihm nicht einen Strich durch die Rechnung
macht! Und mit SDL_Mixer generierten Soundeffekten wird ein richtiges
Spiel daraus.
Anmerkung: Bitte zusätzlich zu den Listings auch noch die drei Icons logo.png, dive.png und para.png zum Download bereitstellen!
Abbildung 8: |
Abbildung 9: |
Michael Schilliarbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com. |