PageRenderTime 25ms CodeModel.GetById 17ms app.highlight 3ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/pods/SDLx/Controller.pod

http://github.com/PerlGameDev/SDL
Unknown | 277 lines | 190 code | 87 blank | 0 comment | 0 complexity | 3ffc3e39c85652d292984dc3d83e1d85 MD5 | raw file
  1
  2=head1 NAME
  3
  4SDLx::Controller - Handles the loops for events, movement and rendering
  5
  6=head1 CATEGORY
  7
  8Extension, Controller
  9
 10=head1 SYNOPSIS
 11
 12 use SDLx::Controller;
 13
 14 # create our controller object
 15 my $app = SDLx::Controller->new;
 16 
 17 # we could also do:
 18 my $app = SDLx::App->new;
 19 # because App is also a controller
 20
 21 # register some callbacks
 22 $app->add_event_handler( \&on_event );
 23 $app->add_move_handler( \&on_move );
 24 $app->add_show_handler( \&on_show );
 25
 26 # run our game loop
 27 $app->run;
 28
 29=head2 DESCRIPTION
 30
 31The core of an SDL application/game is the main loop, where you handle events
 32and display your elements on the screen until something signals the end of
 33the program. This usually goes in the form of:
 34
 35  while (1) {
 36      ...
 37  }
 38
 39The problem most developers face, besides the repetitive work, is to ensure
 40the screen update is independent of the frame rate. Otherwise, your game will
 41run at different speeds on different machines and this is never good (old
 42MS-DOS games, anyone?).
 43
 44One way to circumvent this is by capping the frame rate so it's the same no
 45matter what, but this is not the right way to do it as it penalizes better
 46hardware.
 47
 48This module provides an industry-proven standard for frame independent
 49movement. It calls the movement handlers based on time (hi-res seconds) rather
 50than frame rate. You can add/remove handlers and control your main loop with
 51ease.
 52
 53=head1 METHODS
 54
 55=head2 new
 56
 57 SDLx::Controller->new(
 58     dt    => 0.5,
 59     min_t => 0,
 60     event => $event_object,
 61 );
 62
 63The C<dt> parameter specifies the length, in seconds, of a full movement step, and defaults to 0.1.
 64The C<dt> can  be anything and the game can still look the same.
 65It is only when you change the C<dt> without changing all the things in the movement step that are being multiplied by the first move argument that it will make a difference.
 66If you lower the C<dt>, everything will move faster than it did with it set higher, and vice-versa.
 67This is useful to add slo-mo and fast-forward features to the game, all you would have to do is change the C<dt>.
 68
 69C<min_t> specifies the minimum time, in seconds, that has to accumulate before any move or show handlers are called, and defaults to 1 / 60.
 70Having the C<min_t> at 1 / 60 ensures that the controller can update the screen at a maximum of 60 times per second.
 71A "V-Sync" such as this is necessary to prevent video "tear", which occurs when the app is updating faster than the monitor can display.
 72Setting it to 0, as seen above, will let the app run as fast as it possibly can.
 73
 74C<delay> specifies a loop delay in millisecs to place on the controller loop. B<NOTE:> Picking a good delay based on the needs can help reduce CPU load and pressure.
 75
 76C<event> is a SDL::Event object that events going to the event callbacks are polled in to. It defaults to C<< SDL::Event->new() >>.
 77
 78All parameters are optional.
 79
 80Returns the new object.
 81
 82=head2 run
 83
 84After creating and setting up your handlers (see below), call this method to
 85activate the main loop. The main loop will run until C<stop> is called.
 86
 87All hooked functions will be called during the main loop, in this order:
 88
 89=over 4
 90
 91=item 1. Events
 92
 93=item 2. Movements
 94
 95=item 3. Displaying
 96
 97=back
 98
 99Please refer to each handler below for information on received arguments.
100Note that the second argument every callback receives is the C<SDLx::Controller> object.
101
102=head2 stop
103
104Returns from the C<run> loop.
105
106=head2 pause
107
108Attempts to pause the application with a call to C<SDL::Events::wait_event>. See L<SDL::Events>.
109
110Takes 1 argument which is a callback. The application waits for the next event with C<wait_event>.
111When one is received, it is passed to the callback as the first argument, along with the C<SDLx::Controller> object as the second argument.
112If the callback then returns a true value, C<pause> will return.
113If the callback returns a false value, C<pause> will repeat the process.
114
115This can be used to easily implement a pause when the app loses focus:
116
117 sub window {
118     my ($e, $app) = @_;
119     if($e->type == SDL_QUIT) {
120         $app->stop;
121         # quit handling is here so that the app
122         # can be stopped while paused
123     }
124     elsif($e->type == SDL_ACTIVEEVENT) {
125         if($e->active_state & SDL_APPINPUTFOCUS) {
126             if($e->active_gain) {
127                 return 1;
128             }
129             else {
130                 $app->pause(\&window);
131                 # recursive, but only once since the window
132                 # can't lose focus again without gaining it first
133             }
134         }
135     }
136     return 0;
137 }
138
139Note: if you implement your own pause function, remember to update C<current_time> to the current time when the application unpauses.
140This should be done with C<Time::HiRes::time>.
141Otherwise, time will accumulate while the application is paused, and many movement steps will be called all at once when it unpauses.
142
143Note 2: a pause will be potentially dangerous to the C<run> cycle (even if you implement your own) unless called by an C<event> callback.
144
145=head2 paused
146
147Returns 1 if the app is paused, undef otherwise.
148This is only useful when used within code that will be run by C<pause>:
149
150 sub pause {
151     # press P to toggle pause
152     
153     my ($e, $app) = @_;
154     if($e->type == SDL_QUIT) {
155         $app->stop;
156         # quit handling is here so that the app
157         # can be stopped while paused
158     }
159     elsif($e->type == SDL_KEYDOWN) {
160         if($e->key_sym == SDLK_p) {
161             # We're paused, so end pause
162             return 1 if $app->paused;
163             
164             # We're not paused, so pause
165             $app->pause(\&pause);
166         }
167     }
168     return 0;
169 }
170
171=head2 add_event_handler
172
173Register a callback to handle events. You can add as many subs as you need.
174Whenever a SDL::Event occurs, all registered callbacks will be triggered in
175order. Returns the order queue number of the added callback.
176
177The first argument passed to registered callbacks is the L<< SDL::Event >> object.
178The second is the C<SDLx::Controller> object.
179
180 sub stop {
181    my ($event, $app) = @_;
182    if($event->type == SDL_QUIT) {
183        $app->stop;
184    }
185 }
186 $app->add_event_handler(\&stop);
187
188=head2 add_move_handler
189
190Register a callback to update your objects. You can add as many subs as
191you need. Returns the order queue number of the added callback.
192
193All registered callbacks will be triggered in order for as many C<dt> as have happened between calls,
194and once more for any remaining time less than C<dt>.
195The first argument passed to the callbacks is the portion of the step, which will be 1 for a full step, and less than 1 for a partial step.
196Movement values should be multiplied by this value.
197The full steps correspond to the amount of C<dt> passed between calls, and the partial step corresponds to the call with the remaining time less than C<dt>.
198The argument can be 0 if no time has passed since the last cycle. If you need to protect against this, set a C<min_t>, or put a C<< return unless $_[0] >> at the start of every move handler.
199
200The second argument passed to the callbacks is the C<SDLx::Controller> object.
201The third is the total amount of time passed since the call of C<run>.
202
203You should use these handlers to update your in-game objects, check collisions, etc.
204so you can check and/or update it as necessary.
205
206 sub move_ball {
207     my ($step, $app, $t) = @_;
208     $ball->move_x( $ball->x_vel * $step );
209     $ball->move_y( $ball->y_vel * $step );
210 }
211
212=head2 add_show_handler
213
214Register a callback to render objects. You can add as many subs as you need.
215Returns the order queue number of the added callback.
216All registered callbacks will be triggered in order, once per run of the C<run> loop.
217
218The first argument passed is the time, in seconds, since the previous call.
219The second is the C<SDLx::Controller> object.
220
221 sub show_ball {
222     my ($delta, $app) = @_;
223     $app->draw_rect(
224         [ $ball->x, $ball->y, $ball->size, $ball->size ],
225         $ball->colour
226     );
227 }
228
229=head2 remove_move_handler( $index )
230
231=head2 remove_event_handler( $index )
232
233=head2 remove_show_handler( $index )
234
235Removes the handler with the given index from the respective calling queue.
236
237You can also pass a coderef.
238The first coderef in the handler list that this matches will be removed.
239
240Returns the removed handler.
241
242=head2 remove_all_move_handlers
243
244=head2 remove_all_event_handlers
245
246=head2 remove_all_show_handlers
247
248Removes all handlers from the respective calling queue.
249
250=head2 remove_all_handlers
251
252Quick access to removing all handlers at once.
253
254=head2 dt
255
256=head2 min_t
257
258=head2 current_time
259
260If an argument is passed, modifies the corresponding value to the argument.
261C<dt> and C<min_t> will keep their old value until the beginning of the next C<run> cycle.
262
263Returns the corresponding value.
264
265=head1 AUTHORS
266
267See L<SDL/AUTHORS>.
268
269=head2 ACKNOWLEDGEMENTS
270
271The idea and base for this module comes from Lazy Foo's L<< Frame Independent
272Movement|http://www.lazyfoo.net/SDL_tutorials/lesson32/index.php >> tutorial,
273and Glenn Fiedler's L<< Fix Your Timestep|http://gafferongames.com/game-physics/fix-your-timestep/ >> article on timing.
274
275
276
277