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