rubygame 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|