1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
=pod
=head1 NAME
SDL::Tutorial::Animation - Creating animations with SDL
=head2 CATEGORY
Tutorials
=head1 SYNOPSIS
# to read this tutorial
$ perldoc SDL::Tutorial::Animation
# to create a demo animation program based on this tutorial
$ perl -MSDL::Tutorial::Animation=sdl_anim.pl -e 1
=head1 ANIMATING A RECTANGLE
Now that you can display a rectangle on the screen, the next step is to animate
that rectangle. As with movies, there's no actual motion. Computer animations are just very very fast slideshows. The hard work is creating nearly identical images in every slide (or frame, in graphics terms).
Okay, it's not that difficult.
There is one small difficulty to address, however. Once you blit one surface
onto another, the destination is changed permanently. There's no concept of
layers here unless you write it yourself. If you fail to take this into
account (and just about everyone does at first), you'll end up with blurry
graphics moving around on the screen.
There are two approaches to solve this problem, redrawing the screen on every
frame and saving and restoring the background for every object drawn.
=head2 Redrawing the Screen
Since you have to draw the screen in the right order once to start with it's
pretty easy to make this into a loop and redraw things in the right order for
every frame. Given a L<SDLx::App> object C<$app>, a L<SDL::Rect> C<$rect>, and
a L<SDL::Color> C<$color>, you only have to create a new SDL::Rect C<$bg>,
representing the whole of the background surface and a new mapped color
C<$bg_color>, representing the background color. The colors need to be mapped
to the format of the current display. This is done by L<SDL::Video::map_RGB>.
S< >
my $color = SDL::Video::map_RGB (
$app->format,
$rect_r,
$rect_g,
$rect_b,
);
my $bg_color = SDL::Video::map_RGB (
$app->format,
$bg_r,
$bg_g,
$bg_b,
);
S< >
You can write a C<draw_frame()> function as follows:
S< >
sub draw_frame
{
my ($app, %args) = @_;
SDL::Video::fill_rect($app, $args{bg}, $args{bg_color} );
SDL::Video::fill_rect($app, $args{rect}, $args{rect_color} );
SDL::Video::update_rects($app, $args{bg} );
}
S< >
Since you can change the C<x> and C<y> coordinates of a rect with the C<x()>
and C<y()> methods, you can move a rectangle across the screen with a loop like
this:
S< >
for my $x (0 .. 640)
{
$rect->x( $x );
draw_frame( $app,
bg => $bg, bg_color => $bg_color,
rect => $rect, rect_color => $color,
);
}
S< >
If C<$rect>'s starting y position is 190 and its height and width are 100, the
rectangle (er, square) will move across the middle of the screen.
Provided you can keep track of the proper order in which to redraw rectangles
and provided you don't need the optimal speed necessary (since blitting every
object takes more work than just blitting the portions you need), this works
quite well.
=head2 Undrawing the Updated Rectangle
If you need more speed or want to make a different complexity tradeoff, you can
take a snapshot of the destination rectangle I<before> you blit onto it. That
way, when you need to redraw, you can blit the old snapshot back before
blitting to the new position.
B<Note:> I have no idea how this will work in the face of alpha blending,
which, admittedly, I haven't even mentioned yet. If you don't know what this
means, forget it. If you do know what this means and know why I'm waving my
hands here, feel free to explain what should and what does happen and why. :)
With this technique, the frame-drawing subroutine has to be a little more
complicated. Instead of the background rect, it needs a rect for the previous
position. It also needs to do two updates (or must perform some scary math to
figure out the rectangle of the correct size to C<update()>. No thanks!).
S< >
sub undraw_redraw_rect
{
my ($app, %args) = @_;
SDL::Video::fill_rect($app, $args{old_rect}, $args{bg_color} );
SDL::Video::fill_rect($app, $args{rect}, $args{rect_color} );
SDL::Video::update_rects($app, $args{old_rect} );
SDL::Video::update_rects($app, $args{rect} );
}
S< >
We'll need to create a new SDL::Rect, C<$old_rect>, that is a duplicate of
C<$rect>, at the same position at first. You should already know how to do
this.
As before, the loop to call C<undraw_redraw_rect()> would look something like:
S< >
for my $x (0 .. 640)
{
$rect->x( $x );
undraw_redraw_rect( $app,
rect => $rect, old_rect => $old_rect,
rect_color => $color, bg_color => $bgcolor,
);
$old_rect->x( $x );
}
S< >
If you run this code, you'll probably notice that it's tremendously faster than
the previous version. It may be too fast, where the alternate technique was
just fast enough. There are a couple of good ways to set a fixed animation
speed regardless of the speed of the processor and graphics hardware (provided
they're good enough, which is increasingly often the case), and we'll get to
them soon.
=head1 SEE ALSO
=over 4
=item L<SDL::Tutorial::Drawing>
basic drawing with SDL Perl
=item L<SDL::Tutorial::Images>
animating images
=back
=head1 AUTHOR
chromatic, E<lt>chromatic@wgz.orgE<gt>
Written for and maintained by the Perl SDL project, L<http://sdl.perl.org/>.
See L<SDL/AUTHORS>.
=head1 BUGS
No known bugs.
=head1 COPYRIGHT
Copyright (c) 2003 - 2004, chromatic. All rights reserved. This module is
distributed under the same terms as Perl itself, in the hope that it is useful
but certainly under no guarantee.
|