rubygame 2.4.1 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +98 -0
- data/ROADMAP +0 -13
- data/Rakefile +2 -2
- data/doc/managing_framerate.rdoc +303 -0
- data/ext/body/rubygame_body.so +0 -0
- data/ext/rubygame/rubygame_clock.c +295 -0
- data/ext/rubygame/{rubygame_time.h → rubygame_clock.h} +7 -7
- data/ext/rubygame/rubygame_main.c +2 -2
- data/ext/rubygame/rubygame_screen.c +137 -13
- data/ext/rubygame/rubygame_screen.h +2 -0
- data/lib/rubygame.rb +5 -1
- data/lib/rubygame/clock.rb +291 -109
- data/lib/rubygame/event_handler.rb +22 -3
- data/lib/rubygame/event_hook.rb +10 -10
- data/lib/rubygame/event_triggers.rb +14 -6
- data/lib/rubygame/events/clock_events.rb +58 -0
- data/lib/rubygame/ftor.rb +13 -0
- data/lib/rubygame/mediabag.rb +10 -0
- data/lib/rubygame/shared.rb +36 -0
- data/samples/chimp.rb +3 -2
- data/samples/demo_gl.rb +2 -2
- data/samples/demo_gl_tex.rb +3 -1
- data/samples/demo_rubygame.rb +24 -27
- data/samples/framerate.rb +120 -0
- metadata +11 -4
- data/ext/rubygame/rubygame_time.c +0 -183
data/NEWS
CHANGED
@@ -1,5 +1,103 @@
|
|
1
1
|
= NEWS
|
2
2
|
|
3
|
+
== Rubygame 2.5.0
|
4
|
+
|
5
|
+
Release focus: Clock improvements.
|
6
|
+
|
7
|
+
=== Features
|
8
|
+
|
9
|
+
- New and improved algorithm for calculating framerate.
|
10
|
+
|
11
|
+
- Better Clock compatibility with multithreaded applications:
|
12
|
+
|
13
|
+
- Clock.delay and Clock.wait now accept a new argument: +nice+.
|
14
|
+
If nice is +true+, these methods try to allow other ruby threads
|
15
|
+
to run during the delay. The default behavior is to block other
|
16
|
+
threads, as previous versions of Rubygame do.
|
17
|
+
|
18
|
+
- Added: Clock @nice.
|
19
|
+
This instance variable controls the +nice+ value used while
|
20
|
+
delaying in #tick (if framerate limiting is active).
|
21
|
+
|
22
|
+
- Better control over the balance between CPU usage and accuracy
|
23
|
+
when using Clock's framerate limiting:
|
24
|
+
|
25
|
+
- Added: Clock @granularity.
|
26
|
+
This instance variable controls the granularity value used while
|
27
|
+
delaying in #tick (if framerate limiting is active). Smaller
|
28
|
+
values lower CPU usage, but framerate limiting will be less
|
29
|
+
accurate if the granularity is lower than the system's actual
|
30
|
+
clock granularity. Use Clock#calibrate to find the best value for
|
31
|
+
the current system.
|
32
|
+
|
33
|
+
- Added: Clock#calibrate.
|
34
|
+
Call this after you create a Clock instance calibrate the Clock's
|
35
|
+
granularity to match the system's actual clock granularity. This
|
36
|
+
reduces wasteful CPU usage when using framerate limiting.
|
37
|
+
|
38
|
+
- New ClockTicked event for use with EventHandlers:
|
39
|
+
|
40
|
+
- Added: ClockTicked event class.
|
41
|
+
If you call Clock#enable_tick_events, Clock#tick will return an
|
42
|
+
instance of ClockTicked instead of the raw delay time in milliseconds.
|
43
|
+
|
44
|
+
- Added: TickTrigger event trigger class.
|
45
|
+
Matches ClockTicked events.
|
46
|
+
|
47
|
+
- HasEventHandler#make_magic_hooks accepts the symbol :tick to create a
|
48
|
+
TickTrigger.
|
49
|
+
|
50
|
+
- Added: Clock#frametime.
|
51
|
+
Returns the actual milliseconds per frame recorded by the Clock.
|
52
|
+
|
53
|
+
- New in-depth Clock tutorial, "Managing Framerate".
|
54
|
+
See doc/managing_framerate.rdoc[link:files/doc/managing_framerate_rdoc.html].
|
55
|
+
An accompanying code example has also been added, samples/framerate.rb.
|
56
|
+
|
57
|
+
- A few Screen additions:
|
58
|
+
|
59
|
+
- Added: Screen.get_resolution. Returns the user's desktop resolution.
|
60
|
+
Due to a limitation of SDL, this can ONLY be used when the Rubygame
|
61
|
+
window is closed (i.e. before you have called Screen.new, or after
|
62
|
+
you have called Screen.close).
|
63
|
+
|
64
|
+
- Added: Screen.close. Closes the Rubygame window, if it's open.
|
65
|
+
|
66
|
+
- Added: Screen.open?. True if the Rubygame window is open.
|
67
|
+
|
68
|
+
- Added: Screen.open (alias of Screen.new).
|
69
|
+
|
70
|
+
- Added: HasEventHandler#make_magic_hooks_for.
|
71
|
+
It's like make_magic_hooks, but the hook's owner will be the
|
72
|
+
specified object, instead of the object with the handler.
|
73
|
+
|
74
|
+
=== Other Stuff
|
75
|
+
|
76
|
+
- Various Clock fixes and improvements:
|
77
|
+
|
78
|
+
- Clock#tick when called for the first time, no longer considers the
|
79
|
+
creation time of the Clock instance to be "the previous tick",
|
80
|
+
when checking how much time has passed.
|
81
|
+
|
82
|
+
- Clock#tick won't do any extra delay if the frame has already taken
|
83
|
+
longer than the target.
|
84
|
+
|
85
|
+
- Clock.delay and Clock.wait handle negative numbers more gracefully.
|
86
|
+
Instead of hanging for a long time, they act as if the values were 0.
|
87
|
+
|
88
|
+
- Deprecation: Screen.set_mode and Screen.instance (both aliases of
|
89
|
+
Screen.new) are deprecated and will be removed in Rubygame 3.0.
|
90
|
+
Use Screen.new or Screen.open instead.
|
91
|
+
|
92
|
+
- Deprecation: Ftor is deprecated and will be removed in Rubygame 3.0.
|
93
|
+
A mostly-compatible vector class will be provided at or before that time.
|
94
|
+
|
95
|
+
- Deprecation: MediaBag is deprecated and will be removed in Rubygame 3.0.
|
96
|
+
Use the NamedResource functionality of Music, Sound, and Surface instead.
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
3
101
|
== Rubygame 2.4.1
|
4
102
|
|
5
103
|
Release focus: Bug fixes; Ruby 1.9 compatibility.
|
data/ROADMAP
CHANGED
@@ -6,19 +6,6 @@ keep in mind that specific details may change over time.
|
|
6
6
|
|
7
7
|
== 2.X.0 (_possible_ minor releases before 3.0.0)
|
8
8
|
|
9
|
-
=== Focus: Clock
|
10
|
-
|
11
|
-
- New Events::ClockTicked class with lots of information
|
12
|
-
- Time since last frame, in both seconds and milliseconds
|
13
|
-
- Current time
|
14
|
-
- Current framerate
|
15
|
-
|
16
|
-
- New clock class
|
17
|
-
- Based on Ruby's timer (allows multi-threading)
|
18
|
-
- Better framerate calculation algorithm
|
19
|
-
- #tick returns an instance of Events::Tick
|
20
|
-
|
21
|
-
|
22
9
|
=== Focus: Surface & Rect
|
23
10
|
|
24
11
|
- Surface improvements
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
|
8
8
|
# The version number for Rubygame.
|
9
|
-
RUBYGAME_VERSION = [2,
|
9
|
+
RUBYGAME_VERSION = [2,5,0]
|
10
10
|
|
11
11
|
|
12
12
|
#
|
@@ -379,7 +379,7 @@ rubygame_core = ExtensionModule.new do |core|
|
|
379
379
|
'rubygame_joystick',
|
380
380
|
'rubygame_screen',
|
381
381
|
'rubygame_surface',
|
382
|
-
'
|
382
|
+
'rubygame_clock',
|
383
383
|
]
|
384
384
|
core.create_all_tasks()
|
385
385
|
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
= Managing Framerate
|
2
|
+
|
3
|
+
This is a guide to using the Rubygame::Clock class in your games to
|
4
|
+
monitor and limit the game's framerate. It also gives tips and
|
5
|
+
good practices for dealing with framerate and time units in your
|
6
|
+
application.
|
7
|
+
|
8
|
+
The theory and general information here is applicable to most game
|
9
|
+
development platforms, so you may find this guide useful even if you
|
10
|
+
are not using Rubygame.
|
11
|
+
|
12
|
+
|
13
|
+
== The Short Version
|
14
|
+
|
15
|
+
1. When your game launches, ceate a Clock instance, set the target
|
16
|
+
framerate, enable tick events, and calibrate it.
|
17
|
+
|
18
|
+
2. If your game uses multiple ruby threads, set Clock#nice to true.
|
19
|
+
|
20
|
+
3. Call Clock#tick once per frame. It returns a
|
21
|
+
Rubygame::Events::ClockTicked instance.
|
22
|
+
|
23
|
+
4. Use ClockTicked#seconds to see how much time passed since the
|
24
|
+
previous frame. Use that number to calculate how far your
|
25
|
+
characters moved, etc.
|
26
|
+
|
27
|
+
5. You can check the framerate with Clock#framerate.
|
28
|
+
|
29
|
+
|
30
|
+
=== Usage Example
|
31
|
+
|
32
|
+
require "rubygame"
|
33
|
+
|
34
|
+
$clock = Rubygame::Clock.new()
|
35
|
+
|
36
|
+
# Set the desired framerate.
|
37
|
+
$clock.target_framerate = 20
|
38
|
+
|
39
|
+
# Make Clock#tick return ClockTicked (Rubygame 2.5+)
|
40
|
+
$clock.enable_tick_events()
|
41
|
+
|
42
|
+
# Allow Clock to work nicely with multiple ruby threads.
|
43
|
+
# You can skip this if you don't use multiple threads.
|
44
|
+
$clock.nice = true
|
45
|
+
|
46
|
+
# Optimize clock for this computer.
|
47
|
+
puts "Calibrating... "
|
48
|
+
$clock.calibrate()
|
49
|
+
puts "New granularity: %d ms"%[$clock.granularity]
|
50
|
+
|
51
|
+
i = 0
|
52
|
+
|
53
|
+
catch :quit do
|
54
|
+
loop do
|
55
|
+
|
56
|
+
tick_event = $clock.tick() # call tick once per frame.
|
57
|
+
|
58
|
+
# Give tick_event to an event handler or queue, or whatever.
|
59
|
+
|
60
|
+
fps = $clock.framerate # current framerate
|
61
|
+
diff = tick_event.seconds # time since previous call to tick
|
62
|
+
|
63
|
+
puts "Tick %0.2d = %0.2d fps (%0.4f s since previous)"%[i, fps, diff]
|
64
|
+
|
65
|
+
i += 1
|
66
|
+
throw :quit if( i > 50 )
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
== Background
|
73
|
+
|
74
|
+
Almost all games and other interactive applications have a main loop,
|
75
|
+
code that is repeated many times per second to take user input, update
|
76
|
+
the status of the game, and refresh the screen, among other things.
|
77
|
+
Each repetition of this loop is one *frame*, and the number of frames
|
78
|
+
that occur in one second is called the *framerate* or FPS (frames per
|
79
|
+
second).
|
80
|
+
|
81
|
+
|
82
|
+
== Effects of Framerate
|
83
|
+
|
84
|
+
In general, the higher the framerate, the smoother the motion in your
|
85
|
+
game seems.
|
86
|
+
|
87
|
+
At low framerates (generally less than 12-15 FPS), the game will
|
88
|
+
feel "choppy" or "jerky" to the user. At even lower framerates
|
89
|
+
(less than 5), the user may find it frustrating to interact with the
|
90
|
+
game. Obviously, that's bad!
|
91
|
+
|
92
|
+
High framerates appear smoother, but only up to a point. For example,
|
93
|
+
a user will almost certainly notice a big difference between 5 and 10
|
94
|
+
FPS, but would probably not notice much difference between 50 and 100
|
95
|
+
FPS. Framerates above 60 FPS are usually just a waste, as they use
|
96
|
+
much more CPU power but have very little gained smoothness.
|
97
|
+
|
98
|
+
So, it's best to find a balanced framerate, which will appear smooth to
|
99
|
+
the user, without being wasteful. A good framerate is usually in the
|
100
|
+
range of 20-40 FPS, but varies from game to game. For example, a
|
101
|
+
fast-paced action game may need 60 FPS, while a slow-paced puzzle game
|
102
|
+
may be fine with only 10.
|
103
|
+
|
104
|
+
|
105
|
+
== Framerate Monitoring
|
106
|
+
|
107
|
+
=== How to Enable Framerate Monitoring
|
108
|
+
|
109
|
+
Rubygame's Clock class makes it simple to monitor (measure) framerate.
|
110
|
+
Simply call Clock#tick exactly once in your main game loop. Clock will
|
111
|
+
measure the time since the previous tick. From that information,
|
112
|
+
collected over many frames, it can determine the framerate that your
|
113
|
+
game is running at.
|
114
|
+
|
115
|
+
You can get the average recorded framerate, in frames per second, with
|
116
|
+
Clock#framerate.
|
117
|
+
|
118
|
+
If you prefer, you can get the frametime (the average time it took to
|
119
|
+
make one frame), measured in milliseconds per frame, with
|
120
|
+
Clock#frametime. Clock#frametime is equivalent to 1000.0 /
|
121
|
+
Clock#framerate.
|
122
|
+
|
123
|
+
|
124
|
+
=== Finding Out How Long a Frame Took
|
125
|
+
|
126
|
+
The Clock#tick method returns information about how long it has been
|
127
|
+
since the previous frame. You can use this information to decide how
|
128
|
+
far objects in your game should move. (See the Framerate Independence
|
129
|
+
section, below).
|
130
|
+
|
131
|
+
For backwards compatibility, Clock#tick by default returns the number
|
132
|
+
milliseconds since the previous frame as an integer. But if you call
|
133
|
+
Clock#enable_tick_events (in Rubygame 2.5+), Clock#tick will instead
|
134
|
+
return an instance of ClockTicked containing that information. You can
|
135
|
+
then get that information with either ClockTicked#milliseconds or
|
136
|
+
ClockTicked#seconds, whichever is more convenient for you.
|
137
|
+
|
138
|
+
|
139
|
+
== Framerate Limiting
|
140
|
+
|
141
|
+
=== Why Limit the Framerate?
|
142
|
+
|
143
|
+
If you don't limit the framerate of your game, the main loop will
|
144
|
+
repeat as quickly as the computer can run it. This will make the game
|
145
|
+
run as smoothly as possible, but it will also use as much of the
|
146
|
+
computer's CPU power as it can!
|
147
|
+
|
148
|
+
This can make other applications on the computer run more slowly or
|
149
|
+
choppily. Also, running the CPU full throttle uses more electricity,
|
150
|
+
so laptops and other mobile devices will lose battery charge faster.
|
151
|
+
It can also make the laptop get uncomfortably warm, or trigger the
|
152
|
+
fans on the laptop to turn on, causing unwanted noise.
|
153
|
+
|
154
|
+
Another benefit of framerate limiting is that it can make your game
|
155
|
+
run at a stable, consistent framerate. However, it is poor practice
|
156
|
+
to assume that every frame will take the same amount of time. Instead,
|
157
|
+
you should use the data returned by Clock#tick to see how long the
|
158
|
+
frame took. The "Framerate Independence" section below further
|
159
|
+
explains why and how to do that.
|
160
|
+
|
161
|
+
|
162
|
+
=== How to Enable Framerate Limiting
|
163
|
+
|
164
|
+
Enabling framerate limiting in Rubygame is easy as pie. Just set
|
165
|
+
Clock#target_framerate= to the maximum framerate for your game. So
|
166
|
+
if you wanted your game to stay at 20 frames per second, you would do:
|
167
|
+
|
168
|
+
my_clock = Rubygame::Clock.new
|
169
|
+
my_clock.target_framerate = 20 # frames per second
|
170
|
+
|
171
|
+
There's also another way to set the target framerate:
|
172
|
+
Clock#target_frametime=. Instead of setting the number of frames per
|
173
|
+
second, this allows you to set the number of milliseconds per frame.
|
174
|
+
This has the same effect as setting the framerate, it's just another
|
175
|
+
way of expressing it. So, the following is exactly equivalent to the
|
176
|
+
earlier example:
|
177
|
+
|
178
|
+
my_clock = Rubygame::Clock.new
|
179
|
+
my_clock.target_frametime = 50 # milliseconds per frame
|
180
|
+
|
181
|
+
To use framerate limiting, you also need to call Clock#tick once per
|
182
|
+
frame, the same way you do for framerate monitoring (you should only
|
183
|
+
call it once per frame, even if you're doing both monitoring and
|
184
|
+
limiting). When framerate limiting is enabled, the tick method pauses
|
185
|
+
the program for a brief time. The length of the pause carefully
|
186
|
+
calculated each frame to keep a consistent framerate.
|
187
|
+
|
188
|
+
However, framerate limiting only set the *maximum* framerate. In other
|
189
|
+
words, it can only slow down your game if it's running too fast, not
|
190
|
+
make your game run faster. So if your game naturally runs at 25 FPS,
|
191
|
+
setting a target framerate of 30 FPS will have no effect.
|
192
|
+
|
193
|
+
You can change the target framerate/frametime at any time.
|
194
|
+
Setting the target back to nil will disable framerate limiting.
|
195
|
+
|
196
|
+
|
197
|
+
=== Improving Clock Efficiency
|
198
|
+
|
199
|
+
In Rubygame 2.5 and later, you can call Clock#calibrate to optimize
|
200
|
+
the framerate limiting algorithm for the current computer. This
|
201
|
+
minimizes wasted CPU power by setting Clock#granularity to match
|
202
|
+
the system clock's granularity. The improvement varies by
|
203
|
+
operating system and processor, and will be most noticeable
|
204
|
+
on systems with high-precision system clocks.
|
205
|
+
|
206
|
+
By default, the calibration will take up to a maximum of 0.5 seconds
|
207
|
+
to complete. The needed amount of time will likely decrease in future
|
208
|
+
versions of Rubygame. If you want to calibrate more quickly (but
|
209
|
+
potentially less accurately), you can pass a different maximum time
|
210
|
+
as a parameter to Clock#calibrate.
|
211
|
+
|
212
|
+
You can also set Clock#granularity directly, if you wish. Setting
|
213
|
+
Clock#granularity to 0 will use the least CPU possible, but will
|
214
|
+
lose accuracy.
|
215
|
+
|
216
|
+
|
217
|
+
=== Multithreaded Applications
|
218
|
+
|
219
|
+
By default, Clock's framerate limiting feature will pause *all* ruby
|
220
|
+
threads during the small pause each frame. This is a side effect of
|
221
|
+
the SDL function used for the pause. It also affects Clock.delay and
|
222
|
+
Clock.wait.
|
223
|
+
|
224
|
+
Starting in Rubygame 2.5, Clock has a new attribute, Clock#nice.
|
225
|
+
If true, Clock will try to give the other threads an opportunity to run
|
226
|
+
during the brief pause each frame when performing framerate limiting.
|
227
|
+
|
228
|
+
If you are calling Clock.delay or Clock.wait directly, you can get the
|
229
|
+
same effect by passing true for the +nice+ parameter.
|
230
|
+
|
231
|
+
*NOTE*: There is no guarantee or specification about how often other
|
232
|
+
threads will allowed to run during the delay, or even that they will
|
233
|
+
be allowed run during the delay at all. Setting +nice+ to true simply
|
234
|
+
tells the Clock to *try* to be nice to other threads, if it can do so
|
235
|
+
without losing accuracy.
|
236
|
+
|
237
|
+
|
238
|
+
== Framerate Independence
|
239
|
+
|
240
|
+
=== Why Be Framerate Independent?
|
241
|
+
|
242
|
+
Some programmers are tempted to use framerate limiting to control the
|
243
|
+
speed of the action in their games. In other words, they assume that
|
244
|
+
every frame will take the same amount of time on all computers, so
|
245
|
+
they define movement speeds and other gameplay rates in "per frame"
|
246
|
+
units: move 2 pixel per frame, lose 1% HP per frame when poisoned,
|
247
|
+
etc. This is called "framerate dependence", because the speed of the
|
248
|
+
gameplay depends on how fast the framerate is.
|
249
|
+
|
250
|
+
You should *almost always* avoid this programming practice, because
|
251
|
+
it's not very robust, it usually makes the game more difficult to
|
252
|
+
maintain or change in the future, and can lead to bugs and unwanted
|
253
|
+
behavior.
|
254
|
+
|
255
|
+
For example, if you programmed your game so that the characters move
|
256
|
+
2 pixels per frame, and limited your game's framerate to 30 FPS, the
|
257
|
+
character would move 60 pixels in one second. But if you later decided
|
258
|
+
to change the framerate to 60 FPS, the character would move 120 pixels
|
259
|
+
each second -- twice as fast! You would have to go through your entire
|
260
|
+
game and adjust the speed for every character, which is a hassle.
|
261
|
+
|
262
|
+
Also consider the other end: slow computers. Your game may reliably
|
263
|
+
run at 30 FPS on your computer, but an older or slower computer might
|
264
|
+
only be able to run at 15 FPS. In that case, all the characters would
|
265
|
+
move half as fast, which could make the game a lot less fun. Even worse,
|
266
|
+
if more objects started to appear on the screen, the framerate would
|
267
|
+
start to drop, and the gameplay would get even slower!
|
268
|
+
|
269
|
+
The solution to these problems is simple: define your gameplay in
|
270
|
+
per-second or per-millisecond units, instead of per-frame units.
|
271
|
+
You can then use the frame time to calculate how much the game
|
272
|
+
changed in the past frame. This is called "framerate independence".
|
273
|
+
|
274
|
+
|
275
|
+
=== Framerate Independence with Rubygame
|
276
|
+
|
277
|
+
It's very easy to make your game framerate independent with
|
278
|
+
Rubygame. So, you have no excuse not to do it, not even laziness!
|
279
|
+
|
280
|
+
The first step is making sure all your speeds and other gameplay
|
281
|
+
rates are defined as per-second or per-millisecond units: move 60
|
282
|
+
pixels per second, lose 30% HP per second, etc. The choice to use
|
283
|
+
seconds or milliseconds is up to you, and doesn't make a difference as
|
284
|
+
long as you use the same time scale everywhere.
|
285
|
+
|
286
|
+
The second step is to use the value returned by Clock#tick each frame
|
287
|
+
to find out how long it has been since the previous frame, and use
|
288
|
+
that time to calculate how far the character has moved in the past frame,
|
289
|
+
how much HP they have lost, etc. (I will assume for this tutorial that
|
290
|
+
you have called Clock#enable_tick_events, so that Clock#tick will return
|
291
|
+
a ClockTicked event.)
|
292
|
+
|
293
|
+
This calculation is very simple: just multiply the number of seconds
|
294
|
+
(or milliseconds) by the speed or rate. Remember to use the correct
|
295
|
+
units! If your rates are defined in per-second units, multiply by
|
296
|
+
ClockTicked#seconds; if they are per-millisecond units, multiply by
|
297
|
+
ClockTicked#milliseconds.
|
298
|
+
|
299
|
+
tick_event = Clock.tick
|
300
|
+
movement = speed * tick_event.seconds
|
301
|
+
|
302
|
+
For a full example of how to apply this concept in code, see
|
303
|
+
samples/framerate.rb in the Rubygame source distribution.
|
Binary file
|
@@ -0,0 +1,295 @@
|
|
1
|
+
/*
|
2
|
+
* Functions for getting the time since initialization and delaying execution
|
3
|
+
* for a specified amounts of time.
|
4
|
+
*
|
5
|
+
* --
|
6
|
+
*
|
7
|
+
* Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
8
|
+
* Copyright (C) 2004-2009 John Croisant
|
9
|
+
*
|
10
|
+
* This library is free software; you can redistribute it and/or
|
11
|
+
* modify it under the terms of the GNU Lesser General Public
|
12
|
+
* License as published by the Free Software Foundation; either
|
13
|
+
* version 2.1 of the License, or (at your option) any later version.
|
14
|
+
*
|
15
|
+
* This library is distributed in the hope that it will be useful,
|
16
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
18
|
+
* Lesser General Public License for more details.
|
19
|
+
*
|
20
|
+
* You should have received a copy of the GNU Lesser General Public
|
21
|
+
* License along with this library; if not, write to the Free Software
|
22
|
+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
23
|
+
*
|
24
|
+
* ++
|
25
|
+
*/
|
26
|
+
|
27
|
+
#include "rubygame_shared.h"
|
28
|
+
#include "rubygame_clock.h"
|
29
|
+
|
30
|
+
void Rubygame_Init_Clock();
|
31
|
+
|
32
|
+
VALUE cClock;
|
33
|
+
|
34
|
+
VALUE rbgm_clock_wait(int, VALUE*, VALUE);
|
35
|
+
VALUE rbgm_clock_delay(int, VALUE*, VALUE);
|
36
|
+
VALUE rbgm_clock_runtime(VALUE);
|
37
|
+
|
38
|
+
|
39
|
+
/* Initialize the SDL timer system, if it hasn't been already. */
|
40
|
+
void rg_init_sdl_timer()
|
41
|
+
{
|
42
|
+
if(!SDL_WasInit(SDL_INIT_TIMER))
|
43
|
+
{
|
44
|
+
if(SDL_InitSubSystem(SDL_INIT_TIMER))
|
45
|
+
{
|
46
|
+
rb_raise(eSDLError,"Could not initialize timer system: %s",\
|
47
|
+
SDL_GetError());
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
/* NOTICE: if you change this value "officially", don't forget to update the
|
54
|
+
* documentation for rbgm_time_delay!!
|
55
|
+
*/
|
56
|
+
#define WORST_CLOCK_ACCURACY 12
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
/* Delays for the given amount of time, but possibly split into small
|
61
|
+
* parts. Control is given to ruby between each part, so that other
|
62
|
+
* threads can run.
|
63
|
+
*
|
64
|
+
* delay: How many milliseconds to delay.
|
65
|
+
* nice: If 1 (true), split the delay into smaller parts and allow
|
66
|
+
* other ruby threads to run between each part.
|
67
|
+
*
|
68
|
+
*/
|
69
|
+
Uint32 rg_threaded_delay( Uint32 delay, int nice )
|
70
|
+
{
|
71
|
+
Uint32 start;
|
72
|
+
|
73
|
+
start = SDL_GetTicks();
|
74
|
+
|
75
|
+
if( nice )
|
76
|
+
{
|
77
|
+
while( SDL_GetTicks() < start + delay )
|
78
|
+
{
|
79
|
+
SDL_Delay(1);
|
80
|
+
rb_thread_schedule(); /* give control to ruby */
|
81
|
+
}
|
82
|
+
}
|
83
|
+
else
|
84
|
+
{
|
85
|
+
SDL_Delay( delay );
|
86
|
+
}
|
87
|
+
|
88
|
+
return SDL_GetTicks() - start;
|
89
|
+
}
|
90
|
+
|
91
|
+
|
92
|
+
/*
|
93
|
+
* call-seq:
|
94
|
+
* Clock.wait( time, nice=false ) -> Integer
|
95
|
+
*
|
96
|
+
* time:: The target wait time, in milliseconds.
|
97
|
+
* (Non-negative Integer. Required.)
|
98
|
+
* nice:: If true, try to let other ruby threads run during the delay.
|
99
|
+
* (true or false. Optional.)
|
100
|
+
*
|
101
|
+
* Returns:: The actual wait time, in milliseconds.
|
102
|
+
*
|
103
|
+
* Pause the program for approximately +time+ milliseconds. Both this
|
104
|
+
* function and Clock.delay can be used to slow down the framerate so
|
105
|
+
* that the application doesn't use too much CPU time. See also
|
106
|
+
* Clock#tick for a good and easy way to limit the framerate.
|
107
|
+
*
|
108
|
+
* The accuracy of this function depends on processor scheduling,
|
109
|
+
* which varies with operating system and hardware. The actual delay
|
110
|
+
* time may be up to 10ms longer than +time+. If you need more
|
111
|
+
* accuracy use Clock.delay, which is more accurate but uses slightly
|
112
|
+
* more CPU time.
|
113
|
+
*
|
114
|
+
* If +nice+ is true, this function will try to allow other ruby
|
115
|
+
* threads to run during this function. Otherwise, other ruby threads
|
116
|
+
* will probably also be paused. Setting +nice+ to true is only
|
117
|
+
* useful if your application is multithreaded. It's safe (but
|
118
|
+
* pointless) to use this feature for single threaded applications.
|
119
|
+
*
|
120
|
+
* The Rubygame timer system will be initialized when you call this
|
121
|
+
* function, if it has not been already. See Clock.runtime.
|
122
|
+
*
|
123
|
+
*/
|
124
|
+
VALUE rbgm_clock_wait(int argc, VALUE *argv, VALUE module)
|
125
|
+
{
|
126
|
+
rg_init_sdl_timer();
|
127
|
+
|
128
|
+
VALUE vtime, vnice;
|
129
|
+
|
130
|
+
rb_scan_args(argc,argv,"11", &vtime, &vnice);
|
131
|
+
|
132
|
+
int delay = NUM2INT(vtime);
|
133
|
+
if( delay < 0 )
|
134
|
+
{
|
135
|
+
delay = 0;
|
136
|
+
}
|
137
|
+
|
138
|
+
int nice = (vnice == Qtrue) ? 1 : 0;
|
139
|
+
|
140
|
+
return UINT2NUM( rg_threaded_delay(delay, nice) );
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
/*--
|
146
|
+
* From pygame code, with a few modifications:
|
147
|
+
* - takes 'accuracy' argument
|
148
|
+
* - ruby syntax for raising exceptions
|
149
|
+
* - uses rg_threaded_delay
|
150
|
+
*++
|
151
|
+
*/
|
152
|
+
static Uint32 accurate_delay(Uint32 ticks, Uint32 accuracy, int nice)
|
153
|
+
{
|
154
|
+
Uint32 funcstart;
|
155
|
+
int delay;
|
156
|
+
|
157
|
+
if( accuracy <= 0 )
|
158
|
+
{
|
159
|
+
/* delay with no accuracy is like wait (no busy waiting) */
|
160
|
+
return rg_threaded_delay(ticks, nice);
|
161
|
+
}
|
162
|
+
|
163
|
+
funcstart = SDL_GetTicks();
|
164
|
+
|
165
|
+
if(ticks >= accuracy)
|
166
|
+
{
|
167
|
+
delay = (ticks - 2) - (ticks % accuracy);
|
168
|
+
if(delay >= accuracy)
|
169
|
+
{
|
170
|
+
rg_threaded_delay(delay, nice);
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
do{
|
175
|
+
delay = ticks - (SDL_GetTicks() - funcstart);
|
176
|
+
rb_thread_schedule(); /* give control to ruby */
|
177
|
+
}while(delay > 0);
|
178
|
+
|
179
|
+
return SDL_GetTicks() - funcstart;
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
/*
|
185
|
+
* call-seq:
|
186
|
+
* Clock.delay( time, gran=12, nice=false ) -> Integer
|
187
|
+
*
|
188
|
+
* time:: The target delay time, in milliseconds.
|
189
|
+
* (Non-negative integer. Required.)
|
190
|
+
* gran:: The assumed granularity (in ms) of the system clock.
|
191
|
+
* (Non-negative integer. Optional. Default: 12.)
|
192
|
+
* nice:: If true, try to let other ruby threads run during the delay.
|
193
|
+
* (true or false. Optional. Default: false.)
|
194
|
+
*
|
195
|
+
* Returns:: The actual delay time, in milliseconds.
|
196
|
+
*
|
197
|
+
* Pause the program for +time+ milliseconds. This function is more
|
198
|
+
* accurate than Clock.wait, but uses slightly more CPU time. Both
|
199
|
+
* this function and Clock.wait can be used to slow down the
|
200
|
+
* framerate so that the application doesn't use too much CPU time.
|
201
|
+
* See also Clock#tick for a good and easy way to limit the
|
202
|
+
* framerate.
|
203
|
+
*
|
204
|
+
* This function uses "busy waiting" during the last part
|
205
|
+
* of the delay, for increased accuracy. The value of +gran+ affects
|
206
|
+
* how many milliseconds of the delay are spent in busy waiting, and thus
|
207
|
+
* how much CPU it uses. A smaller +gran+ value uses less CPU, but if
|
208
|
+
* it's smaller than the true system granularity, this function may
|
209
|
+
* delay a few milliseconds too long. The default value (12ms) is very
|
210
|
+
* safe, but a value of approximately 5ms would give a better balance
|
211
|
+
* between accuracy and CPU usage on most modern computers.
|
212
|
+
* A granularity of 0ms makes this method act the same as Clock.wait
|
213
|
+
* (i.e. no busy waiting at all, very low CPU usage).
|
214
|
+
*
|
215
|
+
* If +nice+ is true, this function will try to allow other ruby
|
216
|
+
* threads to run during this function. Otherwise, other ruby threads
|
217
|
+
* will probably also be paused. Setting +nice+ to true is only
|
218
|
+
* useful if your application is multithreaded. It's safe (but
|
219
|
+
* pointless) to use this feature for single threaded applications.
|
220
|
+
*
|
221
|
+
* The Rubygame timer system will be initialized when you call this
|
222
|
+
* function, if it has not been already. See Clock.runtime.
|
223
|
+
*
|
224
|
+
*/
|
225
|
+
VALUE rbgm_clock_delay(int argc, VALUE *argv, VALUE module)
|
226
|
+
{
|
227
|
+
rg_init_sdl_timer();
|
228
|
+
|
229
|
+
VALUE vtime, vgran, vnice;
|
230
|
+
|
231
|
+
rb_scan_args(argc,argv,"12", &vtime, &vgran, &vnice);
|
232
|
+
|
233
|
+
int delay = NUM2INT(vtime);
|
234
|
+
if( delay < 0 )
|
235
|
+
{
|
236
|
+
delay = 0;
|
237
|
+
}
|
238
|
+
|
239
|
+
int gran;
|
240
|
+
if( RTEST(vgran) )
|
241
|
+
{
|
242
|
+
gran = NUM2UINT(vgran);
|
243
|
+
if( gran < 0 )
|
244
|
+
{
|
245
|
+
gran = 0;
|
246
|
+
}
|
247
|
+
}
|
248
|
+
else
|
249
|
+
{
|
250
|
+
gran = WORST_CLOCK_ACCURACY;
|
251
|
+
}
|
252
|
+
|
253
|
+
int nice = (vnice == Qtrue) ? 1 : 0;
|
254
|
+
|
255
|
+
return UINT2NUM( accurate_delay(delay, gran, nice) );
|
256
|
+
}
|
257
|
+
|
258
|
+
|
259
|
+
|
260
|
+
/*
|
261
|
+
* call-seq:
|
262
|
+
* runtime -> Integer
|
263
|
+
*
|
264
|
+
* Return the number of milliseconds since the Rubygame timer system
|
265
|
+
* was initialized.
|
266
|
+
*
|
267
|
+
* The Rubygame timer system will be initialized when you call this function,
|
268
|
+
* if it has not been already.
|
269
|
+
*/
|
270
|
+
VALUE rbgm_clock_runtime( VALUE module )
|
271
|
+
{
|
272
|
+
rg_init_sdl_timer();
|
273
|
+
|
274
|
+
return UINT2NUM(SDL_GetTicks());
|
275
|
+
}
|
276
|
+
|
277
|
+
|
278
|
+
|
279
|
+
void Rubygame_Init_Clock()
|
280
|
+
{
|
281
|
+
#if 0
|
282
|
+
mRubygame = rb_define_module("Rubygame");
|
283
|
+
#endif
|
284
|
+
|
285
|
+
/* Clock class */
|
286
|
+
cClock = rb_define_class_under(mRubygame, "Clock", rb_cObject);
|
287
|
+
|
288
|
+
/* Clock class methods */
|
289
|
+
rb_define_singleton_method(cClock, "wait", rbgm_clock_wait, -1);
|
290
|
+
rb_define_singleton_method(cClock, "delay", rbgm_clock_delay, -1);
|
291
|
+
rb_define_singleton_method(cClock, "runtime",rbgm_clock_runtime, 0);
|
292
|
+
|
293
|
+
/* Clock instance methods are defined in clock.rb. */
|
294
|
+
|
295
|
+
}
|