Aus Linux-Magazin 10/2007

Spielerische Fallschirmflug-Simulation mit Perl

Abbildung 1: Der Perlmeister (unten, mit der verrutschten Maske) springt vom Himmel. Das zugehörige Video ist bei Youtube [2] in ganzer Länge zu betrachten.

Spiele-Programmierer rechnen mit physikalischen Formeln und setzen spezielle Tricks ein, um Grafiken realitätsnah zu animieren. Der als Perl-Wrapper verfügbare Simple Direct Media Layer (SDL) bietet ein leistungsfähiges Framework, um einfache 2D-Welten zu schaffen, hier eine Fallschirmsprung-Simulation.

Als ich neulich das Video meines Tandem-Fallschirmsprungs von einer ollen VHS-Kassette auf Youtube überspielte und den Link [2] einigen Kollegen schickte, entbrannte eine heftige Diskussion über die physikalischen Gesetze, die während des Sprungs wirken. In einem vereinfachten Modell ohne Seitenwindeinfluss startet der Springer mit einer vertikalen Geschwindigkeit vy=0 und beschleunigt sofort aufgrund der Gravitation. Der nach unten wirkenden Erdanziehungskraft stemmt sich der Luftwiderstand entgegen, der mit steigender Fallgeschwindigkeit stetig wächst.

Abhängig vom Gewicht und von den Ausmaßen des Springers stellt sich dann bei etwa 180 km/h Kräftegleichgewicht ein, bei dem die Fallgeschwindigkeit konstant bleibt. Der Springer empfindet das, als würde er im Weltall schweben. Dieser Zustand hält bis zum Öffnen des Fallschirms an, was sich dann so anfühlt, als risse ihn jemand an einem Seil mit Wucht nach oben.

Abbildung 1: Der Perlmeister (unten, mit der verrutschten Maske) springt vom Himmel. Das zugehörige Video ist bei Youtube [2] in ganzer Länge zu betrachten.

Abbildung 1: Der Perlmeister (unten, mit der verrutschten Maske) springt vom Himmel. Das zugehörige Video ist bei Youtube [2] in ganzer Länge zu betrachten.

Freier Plumps

Das Skript »skydive« (Listing 1) simuliert das Experiment. Es stellt den frei fallenden Springer als 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 hoch] aus, worauf das Icon sich in einen Springer mit offenem Fallschirm verwandelt, der zunächst stark abbremst und dann langsam der Erde entgegensegelt.

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 Metern pro Sekunde an (Abbildung 1).

Neue Bestzeiten blendet es unterhalb des aktuellen Zählerfelds ein, wo sie bleiben, bis ein neuer Versuch sie 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 (Abbildung 6). Die Bestzeit bleibt in diesen Fällen unberührt.

Fallphysik

Die Geschwindigkeit eines aus dem Ruhezustand konstant beschleunigten Körpers beträgt vy=a*t. Im Fall einer aus dem Flugzeug springenden Person ist die Beschleunigung a gleich der Erdbeschleunigung (9,82 m/s2), die Zeit t tickt in Sekunden und startet beim Absprung. Die der Gravitation entgegenwirkende Luftreibung ist als negative Beschleunigung zu deuten, die für vy=0 gleich null und für vy=vterm gleich der Erdbeschleunigung 9,81m/s2 ist.

Der Luftwiderstand berechnet sich unter anderem aus der Masse, der Geschwindigkeit sowie dem Reibungskoeffizienten des Springers in der Luft. Nach [5] beschleunigt ein 80 Kilogramm schwerer Erwachsener nach dem Absprung binnen 14 Sekunden auf etwa 190 km/h, wobei er 548 Meter zurücklegt. Anschließend fällt er konstant mit 3000 Metern pro Minute, bis der Fallschirm sich öffnet.

Die Endgeschwindigkeit hängt allerdings auch noch von der Fluglage ab. Mit dem Kopf voran ist der Luftwiderstand geringer als Bauch voran und es lassen sich leicht über 200 km/h erreichen. Details zur Berechnung des Luftwiderstands finden sich unter [6].

Der Körper bewegt sich schließlich mit konstanter Geschwindigkeit fort. Grund dafür ist die Trägheit, derzufolge er in Bewegung bleibt, solange keine äußere Kraft entgegensteht. Die äußeren Kräfte aber – hier die Luftreibung und die Gravitation – heben sich auf. In dieser Situation legt er mit der Geschwindigkeit v in der Zeit t die Strecke s=vterm*t zurück.

Spielphysik

In einer Animation, die 50 Frames pro Sekunde zeichnet, muss man nicht multiplizieren, um die gleichmäßige Bewegung einer Figur zwischen zwei Frames auszurechnen. Wer die Geschwindigkeit in Metern pro Sekunde durch 50 dividiert und das Ergebnis zur aktuellen Position addiert, erhält die neue. 50-mal für jeden Frame wiederholt, kommt am Ende einer Sekunde genau s=v*1s heraus, was der 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 die zu berechnen, wird einfach jedes Mal 1/50stel der Erdbeschleunigung zur aktuellen Geschwindigkeit hinzuaddiert. Nach dem 50sten Mal ergibt sie sich genau bei v=a*1s.

Lug und Trug

Die Beschleunigung ist allerdings nicht konstant. Fällt der Springer aus dem Flugzeug, ist die Beschleunigung 9,81 m/s2, 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 Gravitation erzeugte.

Erreicht der Körper die maximale Fallgeschwindigkeit vterm, ist die Beschleunigung gleich null, der Springer fällt mit konstanter Geschwindigkeit. Das Spiel berechnet dies mit einem vereinfachten Verfahren, indem es mit der Funktion »deceleration« einen Wert ermittelt, den es von der aktuellen Beschleunigung abzieht. Dieser ermittelt sich aus dem Verhältnis von aktueller und maximaler Geschwindigkeit, unterstellt also einen linearen Zusammenhang.

Wird der Fallschirm geöffnet, ist die Beschleunigung sogar negativ. Allerdings bremst der Fallschirm nicht beliebig stark, das Spiel limitiert die herrschende Gegenkraft auf 2g. Was »deceleration« treibt, ist physikalisch gesehen zwar nichts als Lug und Trug, aber für das Spiel reicht die Genauigkeit.

Abbildung 2: Der Springer beschleunigt nach dem Absprung und erreicht nach einigen Sekunden die konstante Fallgeschwindigkeit.

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, den Fallschirm zu öffnen!

Abbildung 3: Der Springer fällt mit 40,96 m/s und ist nicht mehr weit vom Boden entfernt – allerhöchste Zeit, den Fallschirm zu öffnen!

Abbildung 4: Der Fallschirm geht auf und bremst den Springer ab. Bei einer sicheren Landung fällt der Springer mit weniger als 3,1 m/s.

Abbildung 4: Der Fallschirm geht auf und bremst den Springer ab. Bei einer sicheren Landung fällt der Springer mit weniger als 3,1 m/s.

Abbildung 5: Sichere Landung mit 3,0 m/s und neuer Rekord mit 17,60 Sekunden, der eingeblendet bleibt, bis ihn jemand unterschreitet.

Abbildung 5: Sichere Landung mit 3,0 m/s und neuer Rekord mit 17,60 Sekunden, der eingeblendet bleibt, bis ihn jemand unterschreitet.

Abbildung 6: Gute Zeit, aber ungültiger Versuch, da die Landegeschwindigkeit mit 45,33 m/s viel zu hoch war. Der Springer wäre tot.

Abbildung 6: Gute Zeit, aber ungültiger Versuch, da die Landegeschwindigkeit mit 45,33 m/s viel zu hoch war. Der Springer wäre tot.

Blit mal das Image

Bewegt sich ein Icon durch das Spielfeld, wie der vom Himmel fallende Springer, löscht SDL (Simple Direkt Media Layer) zunächst den alten Eintrag und zeichnet das Bild an der neuen Position. Das Icon ist schon als Image im Speicher, die Methode »blit()« kopiert es nur an eine andere Position. Dieser Trick verleiht den Änderungen eine beeindruckende Geschwindigkeit und der Benutzer erhält die Illusion einer realen Welt.

Um zum Beispiel das Logo des Spiels, eine mit Gimp erstellte PNG-Grafik, ins Spielfeld zu zeichnen, lädt das Skript die Datei »logo.png« in Zeile 21 mit dem Konstruktor der Klasse »SDL::Surface« in den Speicher. Zeile 39 definiert dann ein Rechteck der Klasse »SDL::Rect« mit Länge, Breite und gewünschter Position der Grafik. Die x-Koordinaten laufen von links nach rechts, die y-Koordinaten von oben nach unten. Die Methode »blit()« der Grafik in »$logo« (Zeile 43) kopiert die Daten auf die Spielfläche »$app«.

SDL frischt die Oberfläche aber nicht sofort auf, sondern wartet aus Performancegründen, bis per »update()«-Methode den Befehl dazu kommt. So kann SDL viele Rechtecke zugleich auffrischen, damit der Eindruck einer ruhig laufenden Animation entsteht.

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 verstrichene Anzahl von Millisekunden ab und speichert sie in der Variablen »$synchro_ticks«.

Eine weitere Messung am Ende der Schleife bestimmt die von Anfang bis Ende verstrichenen Millisekunden. Ist deren Anzahl kleiner als 20, muss das Skript so lange pausieren, bis die anberaumten 20 Millisekunden abgelaufen sind. Mit »select()« lassen sich Pausen mit Millisekunden-Auflösung einlegen, so läuft die Animation ruckelfrei. Ist die Differenz negativ, hat die Berechnung länger als 20ms gedauert und der Programmierer muss das Skript umschreiben oder die Framerate senken.

Auch während »skydive« emsig die Hauptschleife durchläuft, kommen Events wie Tastendrücke, Mausbewegungen oder Klicks auf das Schließ-Icon des Fensters 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.

Auf Knopfdruck

Den Typ eines Events liefert »event_type()«. Der Event-Typ »SDL_QUIT« entsteht zum Beispiel, wenn der User das Applikationsfenster mit der Maus schließt. Dann 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 raus, welche Taste es war. Praktischerweise übersetzt SDL die Tastaturcodes gleich in handliche Strings, so kommt bei gedrückter rechter Cursortaste der String »right« an, »q«, wenn jemand auf [Q] gedrückt hat.

Die Methode »set_key_repeat()« hilft, länger gedrückte Tasten als wiederholte Eingaben zu verarbeiten. Sie nimmt zwei Parameter entgegen. Der erste bestimmt, nach wie vielen Millisekunden SDL eine konstant gedrückte Taste auf Dauerfeuer stellt. Der zweite Parameter regelt 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, weiß diese Einstellung bestimmt zu schätzen.

Ein Druck auf die Cursortaste mit dem Pfeil nach oben löst den Fallschirm aus. Die »elsif«-Bedingung ab Zeile 107 leitet dann zwei Aktionen ein: Die Endgeschwindigkeit »$VTERM« wird von »$VTERM_FREE« auf »$VTERM_PARA« gesetzt, die Methode »image()« des Spieler-Objekts »$obj« setzt das Spieler-Icon auf das Fallschirm-Icon »$para«.

Weitere Tastendrücke sind [R] für Restart (also Spielabbruch und -neubeginn) sowie [Q], um die Applikation abzubrechen. Für spätere Erweiterungen sind auch die linken und rechten Cursortasten definiert, die das Spieler-Objekt im Fallen nach links und rechts transportieren, was aber in der vorliegenden Version nicht erforderlich ist.

Die Variable »$gtime« gibt die Spielzeit eines gerade beendeten Durchlaufs an und »$record_time« nimmt einen neuen Wert an, falls eine neue Rekordzeit ohne Bauchlandung gelungen ist. Die Applikation selbst liegt im Objekt »$app« vom Typ »SDL::App«, einer von »SDL::Surface« abgeleiteten Klasse. Zeichenvorgänge im Applikationsfenster oder das Auffrischen veränderter Rechtecke laufen grundsätzlich über »$app« ab.

Damit die ganze Luftnummer um das Verschieben des Spieler-Icons einfacher vonstattengeht, 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, um zum Beispiel einen gescheiterten Fallschirmspringer am Boden zurück in einen frei fallenden zu verwandeln.

Die Methode »move()« bewegt die Spielfigur um die angegebene Pixelzahl in eine als Himmelsrichtung (n=North, s=South, w=West, e=East) angegebene Richtung. Die Pixel dürfen ruhig Bruchteile enthalten, die zwar keine Auswirkung auf die aktuelle Bewegung haben, aber das Skript kumuliert sie für künftige Aktionen. Bevor die Spielfigur weiterwandert, löscht SDLMove die alte Repräsentation, damit eine ordentliche Bewegung auf dem Bildschirm entsteht.

Listing 1:
»skydive«

001 #!/usr/bin/perl -w
002 use strict;
003 use SDL;
004 use SDLMove;
005 use SDL::TTFont;
006 
007 my $SPEED_MS  = 20;
008 my $FRAMES_PSEC = 1000.0/$SPEED_MS;
009 my $VTERM_FREE = 50; # Terminal speed
010 my $VTERM_PARA = 3; # ... with parachute
011 my $WIDTH    = 158;
012 my $HEIGHT   = 500;
013 my $G      = 9.81;
014 my $MAX_LAND  = 3.1;
015 
016 my $bg_color = SDL::Color->new(
017  -r => 0, -g => 0, -b => 0 );
018 my $fg_color = SDL::Color->new(
019  -r => 0xff, -g => 0x0, -b => 0x0 );
020 
021 my $logo = SDL::Surface->new(
022        -name => "logo.png");
023  # Load player icons
024 my $diver = SDL::Surface->new(
025        -name => "dive.png");
026 my $para = SDL::Surface->new(
027        -name => "para.png");
028 
029 my $app = SDL::App->new(
030  -title => "Skydive 1.0", -depth => 16,
031  -width => $WIDTH, -height => $HEIGHT);
032 
033 my $font = SDL::TTFont->new(
034  -name =>
035 "/usr/X11R6/lib/X11/fonts/TTF/VeraMono.ttf",
036  -size=>15,
037  -bg => $bg_color, -fg => $fg_color);
038 
039 my $lrect = SDL::Rect->new(
040  -width => $logo->width,
041  -height => $logo->height,
042  -x => 0, -y => 0);
043 $logo->blit(0, $app, $lrect);
044 $app->update($lrect);
045 
046 my $event = new SDL::Event->new();
047 $event->set_key_repeat(200, 10);
048 
049 my $record_time;
050 my $gtime;
051 
052  # Next game ...
053 GAME: while(1) {
054 
055  my $obj = SDLMove->new(
056   app   => $app,
057   bg_color => $bg_color,
058   x => $WIDTH/2 - $diver->width()/2,
059   y => $logo->height,
060   image => $diver, # Start with diver
061  );
062 
063  my $v   = 0;
064  my $vterm = $VTERM_FREE;
065  my $start = $app->ticks();
066 
067  while(1) {  # Frame loop
068   my $synchro_ticks = $app->ticks;
069 
070    # Accelerate
071   $v += ($G - deceleration($v, $vterm))
072      / $FRAMES_PSEC;
073    # Move player downwards
074   $obj->move("s", $v/$FRAMES_PSEC);
075 
076   if($obj->hit_bottom()) {
077    if($v <= $MAX_LAND) { # soft enough?
078     if(! defined $record_time or
079       $gtime < $record_time) {
080       $record_time = $gtime;
081     }
082     nput($app, 0, $lrect->height + 20,
083        $record_time);
084    } else {
085      $obj->wipe();
086      $obj->image($diver);
087      $obj->move("s", # indicate crash
088       $para->height - $diver->height);
089    }
090    sleep 5;
091    $obj->wipe();
092    next GAME;
093   }
094    # Process all queued events
095   while ($eve nt->poll != 0) {
096    my $type = $event->type();
097    exit if $type == SDL_QUIT;
098 
099    if($type == SDL_KEYDOWN) {
100     my $keypressed = $event->key_name;
101 
102     if($keypressed eq "left") {
103       $obj->move("w", 0.1);
104     } elsif($keypressed eq "right") {
105       $obj->move("e", 0.1);
106     } elsif($keypressed eq "up") {
107      # deploy parachute
108      $vterm = $VTERM_PARA;
109      $obj->image($para);
110     } elsif($keypressed eq "r") {
111      $obj->wipe();
112      next GAME;
113     } elsif($keypressed eq "q") {
114      exit 0; # quit
115     }
116    }
117   }
118   $gtime = ($app->ticks - $start)/1000.0;
119 
120   nput($app, 0, $lrect->height, $gtime);
121   nput($app, 110, $lrect->height, $v);
122 
123   my $wait = $SPEED_MS -
124       ($app->ticks - $synchro_ticks);
125   select undef, undef,
126      undef, $wait/1000.0 if $wait > 0;
127  }
128 }
129 
130 ###########################################
131 sub deceleration {
132 ###########################################
133   my($v, $vterm) = @_;
134 
135   my $d = $v/$vterm*9.81;
136 
137   $d = 0 if $d < 0;
138   $d = 2*$G if $d > 2*$G;
139 
140   return $d;
141 }
142 
143 ###########################################
144 sub nput {
145 ###########################################
146  my($app, $x, $y, $number) = @_;
147 
148  my $rect = SDL::Rect->new(
149   "-height" => $font->height,
150   "-width" => $font->width($number),
151   "-x"   => $x,
152   "-y"   => $y);
153 
154  $app->fill($rect, $bg_color);
155  my $string = sprintf "%-5.2f", $number;
156  $font->print($app, $x, $y, $string);
157  $app->sync();
158 }

Listing 2:
»SDLMove.pm«

01 ###########################################
02 package SDLMove;
03 use strict;
04 use warnings;
05 use SDL;
06 use SDL::App;
07 
08 ###########################################
09 sub new {
10 ###########################################
11  my($class, %options) = @_;
12 
13  my $self = { %options };
14  bless $self, $class;
15 
16  $self->image($self->{image});
17  return $self;
18 }
19 
20 ###########################################
21 sub image {
22 ###########################################
23  my($self, $image) = @_;
24 
25  $self->{image} = $image;
26  $self->{drect} = SDL::Rect->new(
27   -width => $image->width,
28   -height => $image->height,
29   -x   => $self->{x},
30   -y   => $self->{y},
31  );
32 }
33 
34 ###########################################
35 sub move {
36 ###########################################
37  my($self, $direction, $pixels) = @_;
38 
39  my $rect = $self->{drect};
40  my $app = $self->{app};
41 
42  if($direction eq "w") {   # left
43   $self->{x} -= $pixels if $self->{x} > 0;
44 
45  } elsif($direction eq "e") { # right
46   $self->{x} += $pixels if $self->{x} <
47     $app->width - $rect->width;
48 
49  } elsif($direction eq "n") { # up
50   $self->{y} -= $pixels if $self->{y} > 0;
51 
52  } elsif($direction eq "s") { # down
53   $self->{y} += $pixels if $self->{y} <
54     $app->height - $rect->height;
55  }
56 
57  $self->{old_rect} = SDL::Rect->new(
58   -height => $rect->height,
59   -width => $rect->width,
60   -x   => $rect->x,
61   -y   => $rect->y,
62  );
63 
64  $rect->x( $self->{x} );
65  $rect->y( $self->{y} );
66  $app->fill($self->{old_rect},
67        $self->{bg_color});
68 
69  $self->{image}->blit(0, $self->{app},
70             $rect);
71  $app->update($self->{old_rect}, $rect);
72 }
73 
74 ###########################################
75 sub wipe {
76 ###########################################
77  my($self) = @_;
78 
79  $self->{app}->fill($self->{drect},
80        $self->{bg_color});
81  $self->{app}->update($self->{drect});
82 }
83 
84 ###########################################
85 sub hit_bottom {
86 ###########################################
87  my($self) = @_;
88 
89  return $self->{y} >
90   $self->{app}->height -
91   $self->{drect}->height;
92 }
93 
94 1;

Konfiguration

Die Yariable »$VTERM_FREE« speichert mit 50 m/s die Höchstgeschwindigkeit im freien Fall, »$VTERM_PARA« gibt mit 3 m/s die Sinkrate des Fallschirms an, auf die sie sich nach einiger Zeit des Segelns einpendelt. In der Sektion ab Zeile 7 von Listing 1 lassen sich diese Werte und auch andere Parameter wie etwa die Höhe und Breite des Animationsfensters verändern.

Um Texte auf der Spieloberfläche darzustellen, jongliert das Modul SDL::TTFont mit Truetype-Fonts. Es baut die Zeichenstrings zu gerenderten Textketten zusammen und hilft dabei, sie in die Spieloberfläche hineinzuschreiben. Der in Zeile 33 aufgerufene Konstruktor lädt den Fixed-Font »VeraMono«, der im Unterverzeichnis »TTF« des Fontverzeichnisses des X-Servers liegt. Die Optionen »-fg« und »-bg« stellen schließlich noch als Textfarbe Rot und Schwarz für den 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 besprochenen Rechtecken frischt SDL die Anzeige nicht direkt nach einem »print«-Befehl auf, sondern wartet, bis ein nachfolgendes »sync()« auf das »$app«-Objekt eintrifft.

Überschreibt ein weiterer Aufruf allerdings die gleiche Stelle mit neuem Text, bleibt die ursprüngliche Anzeige bestehen und das Nummernfeld 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 daher zunächst die Ausmaße des gerenderten Textstrings und definiert ein entsprechendes Rechteck. Dies füllt sie dann mit schwarzer Farbe, damit die Funktion »print« anschließend bedenkenlos überschreiben kann.

Einrichten und Erweiterungen

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 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 oben genannten Bibliotheken zu installieren, andernfalls unterstützt es einfach keine Features mit Truetype-Fonts.

Die drei verwendeten Icons »logo.png«, »dive.png« und »para.png« sind mit den Listings auf dem Download-Server des Linux-Magazins verfügbar [1]. Das Skript sucht die Icons, wenn es hochfährt, im aktuellen Verzeichnis und beschwert sich, falls sie fehlen.

Die Simulation ist mit ein paar Perl-Zeilen leicht zu erweitern. Wer sich von Experten Ideen holen möchte, ist gut damit beraten, den Sourcecode von Frozen Bubble [4] zu studieren. Dabei handelt es sich um ein in 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, doch die Luftreibung bremst ihn ab.

Ziel des Spiels wäre es dann, nicht nur sanft zu landen, sondern auch eine Markierung am Boden zu treffen oder Gewässern oder Strommasten auszuweichen. Nach dem Öffnen des Fallschirms kann der Springer langsam hin und her segeln, um Korrekturen vorzunehmen. Aber natürlich nur, solange der zufällig aufbrausende Seitenwind ihm nicht einen Strich durch die Rechnung macht! Und mit Funktionen aus SDL_Mixer generierte Soundeffekte machen ein richtiges Spiel daraus. (jcb)

Infos

[1] Listings und Icons zu diesem Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2007/10/Perl]

[2] Youtube-Video: [http://youtube.com/watch?v=aRxvsSs0sz4]

[3] André LaMothe, “Tricks of the Windows Game Programming Gurus”: Sams Verlag, Second Edition, 2002

[4] Frozen Bubble: [http://www.frozen-bubble.org]

[5] “Free Fall – Falling Math”: [http://www.greenharbor.com/fffolder/math.html]

[6] Luftwiderstand: [http://en.wikipedia.org/wiki/Drag_(physics)]

[7] Perl-SDL-Tutorial: [http://arstechnica.com/guides/tweaks/games-perl.ars]

Der Autor


Michael Schilli arbeitet 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.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben