29f94efbced2be72b1294e97b2196f60b544c94d
[sdlgit/SDL_perl.git] / lib / SDL / Tutorial / LunarLander.pm
1 =head1 NAME
2
3 Lunar Lander - a small tutorial on Perl SDL
4
5 =head1 INTRODUCTION
6
7 This is a quick introduction to Perl and SDL (Simple DirectMedia
8 Layer, a cross-platform multimedia programming library). We'll write
9 a small game-- Lunar Lander-- in 100 lines of code, or less.
10
11 =head2 PREPARATION
12
13 You'll need SDL_Perl.
14
15 If you are using Debian or Ubuntu, it's probably easier to install
16 the module via apt-get:
17
18     apt-get install libsdl-perl
19
20 or for the bleeding edge with tons of bug fixes.
21    
22    perl -MCPAN -e "install SDL"
23
24 If you are using other Linux distro, look for the corresponding
25 package. If you can't find it you'll have to compile it (and deal
26 with all the dependencies) yourself.
27
28 The point is that it is strongly recommend that you start with a
29 packaged module from your distribution. Avoid compiling SDL_Perl
30 if you can.
31
32 =head2 FIRST VERSION
33
34 We'll start with a text version of the game.
35
36 "What?", you may ask. "I thought it was a SDL tutorial".
37
38 Yes, it is -- thank you for reminding me. But we'll leave the SDL part for
39 later. We must build the game logic first!
40
41 One of the traps of game programming is focusing too much on the interface.
42 If we start with a simpler simulation, we can worry with the presentation
43 later.
44
45 So, here's the initial code:
46
47     #!/usr/bin/perl
48
49     use strict;
50     use warnings;
51
52     my $height   = 1000; # m
53     my $velocity = 0;    # m/s
54     my $gravity  = 1;    # m/s^2
55
56     my $t = 0;
57
58     while ( $height > 0 ) {
59         print "at $t s height = $height m, velocity = $velocity m/s\n";
60
61         $height   = $height - $velocity;
62         $velocity = $velocity + $gravity;
63         $t        = $t + 1;
64     }
65
66     if ( $velocity > 10 ) {
67         print "CRASH!!!\n";
68     } else {
69         print "You landed on the surface safely! :-D\n";
70     }
71
72 Run the code and you'll see something like this:
73
74     at 0 s height = 1000 m, velocity = 0 m/s
75     at 1 s height = 1000 m, velocity = 1 m/s
76     at 2 s height = 999 m, velocity = 2 m/s
77     at 3 s height = 997 m, velocity = 3 m/s
78     at 4 s height = 994 m, velocity = 4 m/s
79     at 5 s height = 990 m, velocity = 5 m/s
80     ...
81     at 43 s height = 97 m, velocity = 43 m/s
82     at 44 s height = 54 m, velocity = 44 m/s
83     at 45 s height = 10 m, velocity = 45 m/s
84
85     CRASH!!!
86
87 "What happened? How do I control the ship???"
88
89 =head2 CONTROLLING THE SHIP
90
91 The problem with our first spaceship is that it had no controls!
92
93 So, let's fix this problem, making the spaceship scriptable. (We 
94 could write some code to handle keyboard and joysticks now, but 
95 an scriptable spaceship will be easier to start. Remember, focus
96 on the game logic!)
97
98 So, create add this simple script to the end of your file:
99
100     __DATA__
101     at 41s, accelerate 10 m/s^2 up
102     at 43s, 10 m/s^2
103     at 45s, 10
104     at 47s, 10
105     at 49s, 10
106
107 The script is straightforward: it simply states a time when we
108 will push the spaceship up with a given acceleration. It accepts
109 free text: any two numbers you type will work.
110
111 We can parse the script using this regular expression:
112
113     my $script_re = qr/(\d+) \D+ (\d+)/x;
114
115 And we can build a hash of ( time => acceleration ) with:
116
117     my %up = map { $_ =~ $script_re } <DATA>;
118
119 So the middle section of the program will become:
120
121     my $script_re = qr/(\d+) \D+ (\d+)/x;
122     my %up = map { $_ =~ $script_re } <DATA>;
123
124     while ( $height > 0 ) {
125         print "at $t s height = $height m, velocity = $velocity m/s\n";
126
127         if ( $up{$t} ) {
128             my $a = $up{$t};
129             print "(accellerating $a m/s^2)\n";
130             $velocity = $velocity - $a;
131         }
132
133         $height   = $height - $velocity;
134         $velocity = $velocity + $gravity;
135         $t        = $t + 1;
136     }
137
138 That's it!
139
140 Try to run the program, and the ship should land safely:
141
142     ./lunar.pl autopilot.txt 
143     at 0 s height = 1000 m, velocity = 0 m/s
144     at 1 s height = 1000 m, velocity = 1 m/s
145     at 2 s height = 999 m, velocity = 2 m/s
146     at 3 s height = 997 m, velocity = 3 m/s
147     at 4 s height = 994 m, velocity = 4 m/s
148     at 5 s height = 990 m, velocity = 5 m/s
149     ...
150     at 54 s height = 19 m, velocity = 4 m/s
151     at 55 s height = 15 m, velocity = 5 m/s
152     at 56 s height = 10 m, velocity = 6 m/s
153     at 57 s height = 4 m, velocity = 7 m/s
154
155     You landed on the surface safely! :-D
156
157 Cool, but...
158
159 =head2 HOW ABOUT THE GRAPHICS?
160
161 Okay, okay... now that we have a working prototype, we can work on
162 the graphics. But, first of all, we'll need...
163
164 =head3 THE GRAPHICS
165
166 Yes, the graphics.
167
168 We won't use anything fancy here, just two images: a large one, for
169 the background, and a smaller one for the spaceship.
170
171 Create the images using the Gimp, or use the images provided by
172 this tutorial; Save these images in a subdirectory called "images":
173 ("C<images/background.jpg>" and "C<images/ship.png>").
174
175 =head2 USING SDL
176
177 First step: use the required libraries:
178
179     use SDL; #needed to get all constants
180     use SDL::App;
181     use SDL::Surface;
182     use SDL::Rect;
183
184 Second step: initialize C<SDL::App>:
185
186     my $app = SDL::App->new(
187         -title  => "Lunar Lander",
188         -width  => 800,
189         -height => 600,
190         -depth  => 32,
191     );
192
193 Third step: load the images and create the necessary "rectangles":
194
195     my $background = SDL::Surface->new( -name => 'images/background.jpg', );
196     my $ship       = SDL::Surface->new( -name => 'images/ship.png', );
197
198     my $background_rect = SDL::Rect->new(
199         -height => $background->height(),
200         -width  => $background->width(),
201     );
202
203     my $ship_rect = SDL::Rect->new(
204         -height => $ship->height(),
205         -width  => $ship->width(),
206     );
207
208 Fourth step: create a sub to draw the spaceship and background:
209
210     sub draw {
211         my ( $x, $y ) = @_; # spaceship position
212
213         # fix $y for screen resolution
214         $y = 450 * ( 1000 - $y ) / 1000;
215
216         # background
217         $background->blit( $background_rect, $app, $background_rect );
218
219         # ship
220         my $ship_dest_rect = SDL::Rect->new(
221             -height => $ship->height(),
222             -width  => $ship->width(),
223             -x      => $x,
224             -y      => $y,
225         );
226
227         $ship->blit( $ship_rect, $app, $ship_dest_rect );
228
229         $app->update($background_rect);
230     }
231
232 Note that this sub first combines all the bitmaps, using a blit
233 ("Block Image Transfer") operation -- which is quite fast, but does
234 not update the display.
235
236 The combined image is displayed in the last line. This process of
237 combining first, and displaying later, avoids that annoying fading
238 between cycles ("flickering").
239
240 Finally, add the following lines to the end of the main loop, so that
241 we call the C<draw()> function with the correct spaceship
242 coordinates:
243
244     while ( $height > 0 ) {
245
246         # ...
247
248         draw( 100, $height );
249         $app->delay(10);
250     }
251
252 That's it!
253
254 Run the program and watch the spaceship landing safely on the surface
255 of the moon.
256
257 =head1 COPYRIGHT & LICENSE
258
259 Copyright 2009 Nelson Ferraz, all rights reserved.
260
261 This program is free software; you can redistribute it and/or modify it
262 under the same terms as Perl itself.
263