graphics 1.0.0b1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.autotest +26 -0
- data/.gemtest +0 -0
- data/History.rdoc +12 -0
- data/Manifest.txt +37 -0
- data/README.rdoc +71 -0
- data/Rakefile +26 -0
- data/examples/boid.rb +653 -0
- data/examples/bounce.rb +86 -0
- data/examples/collision.rb +74 -0
- data/examples/demo.rb +90 -0
- data/examples/editor.rb +70 -0
- data/examples/fluid.rb +246 -0
- data/examples/fluid2.rb +199 -0
- data/examples/lito.rb +108 -0
- data/examples/lito2.rb +110 -0
- data/examples/logo.rb +73 -0
- data/examples/math.rb +42 -0
- data/examples/radar.rb +31 -0
- data/examples/tank.rb +160 -0
- data/examples/tank2.rb +173 -0
- data/examples/targeting.rb +46 -0
- data/examples/vants.rb +69 -0
- data/examples/walker.rb +116 -0
- data/examples/zenspider1.rb +93 -0
- data/examples/zenspider2.rb +123 -0
- data/examples/zenspider3.rb +104 -0
- data/examples/zenspider4.rb +90 -0
- data/examples/zombies.rb +385 -0
- data/lib/graphics.rb +9 -0
- data/lib/graphics/body.rb +216 -0
- data/lib/graphics/extensions.rb +48 -0
- data/lib/graphics/simulation.rb +377 -0
- data/lib/graphics/trail.rb +69 -0
- data/lib/graphics/v.rb +71 -0
- data/resources/images/body.png +0 -0
- data/resources/images/turret.png +0 -0
- data/rubysdl_setup.sh +34 -0
- data/test/test_graphics.rb +408 -0
- metadata +191 -0
- metadata.gz.sig +2 -0
data/lib/graphics.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require "graphics/v"
|
4
|
+
require "graphics/extensions"
|
5
|
+
|
6
|
+
##
|
7
|
+
# A body in the simulation.
|
8
|
+
#
|
9
|
+
# All bodies know their position, their angle, goal angle (optional),
|
10
|
+
# and momentum.
|
11
|
+
|
12
|
+
class Graphics::Body
|
13
|
+
|
14
|
+
# degrees to radians
|
15
|
+
D2R = Graphics::Simulation::D2R
|
16
|
+
|
17
|
+
# radians to degrees
|
18
|
+
R2D = Graphics::Simulation::R2D
|
19
|
+
|
20
|
+
##
|
21
|
+
# The normals for the cardinal directions.
|
22
|
+
|
23
|
+
NORMAL = {
|
24
|
+
:north => 270,
|
25
|
+
:south => 90,
|
26
|
+
:east => 180,
|
27
|
+
:west => 0,
|
28
|
+
}
|
29
|
+
|
30
|
+
attr_accessor :x, :y, :a, :ga, :m, :w # :nodoc:
|
31
|
+
|
32
|
+
##
|
33
|
+
# Create a new body in windowing system +w+ with a random x/y and
|
34
|
+
# everything else zero'd out.
|
35
|
+
|
36
|
+
def initialize w
|
37
|
+
self.w = w
|
38
|
+
|
39
|
+
self.x, self.y = rand(w.w), rand(w.h)
|
40
|
+
self.a = 0.0
|
41
|
+
self.ga = 0.0
|
42
|
+
self.m = 0.0
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect # :nodoc:
|
46
|
+
"%s(%.2fx%.2f @ %.2f°x%.2f == %p @ %p)" %
|
47
|
+
[self.class, x, y, a, m, position, velocity]
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Convert the body to a vector representing its velocity.
|
52
|
+
#
|
53
|
+
# DO NOT modify this vector expecting it to modify the body. It is a
|
54
|
+
# copy.
|
55
|
+
|
56
|
+
def velocity
|
57
|
+
x, y = dx_dy
|
58
|
+
V[x, y]
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Set the body's magnitude and angle from a velocity vector.
|
63
|
+
|
64
|
+
def velocity= o
|
65
|
+
dx, dy = o.x, o.y
|
66
|
+
self.m = Math.sqrt(dx*dx + dy*dy)
|
67
|
+
self.a = Math.atan2(dy, dx) * R2D
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Convert the body to a vector representing its position.
|
72
|
+
#
|
73
|
+
# DO NOT modify this vector expecting it to modify the body. It is a
|
74
|
+
# copy.
|
75
|
+
|
76
|
+
def position
|
77
|
+
V[x, y]
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Set the body's position from a velocity vector.
|
82
|
+
|
83
|
+
def position= o
|
84
|
+
self.x = o.x
|
85
|
+
self.y = o.y
|
86
|
+
end
|
87
|
+
|
88
|
+
def dx_dy # :nodoc:
|
89
|
+
rad = a * D2R
|
90
|
+
dx = Math.cos(rad) * m
|
91
|
+
dy = Math.sin(rad) * m
|
92
|
+
[dx, dy]
|
93
|
+
end
|
94
|
+
|
95
|
+
def m_a # :nodoc:
|
96
|
+
[m, a]
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Turn the body +dir+ degrees.
|
101
|
+
|
102
|
+
def turn dir
|
103
|
+
self.a = (a + dir).degrees if dir
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Move the body via its current angle and momentum.
|
108
|
+
|
109
|
+
def move
|
110
|
+
move_by a, m
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Move the body by a specified angle and momentum.
|
115
|
+
|
116
|
+
def move_by a, m
|
117
|
+
rad = a * D2R
|
118
|
+
self.x += Math.cos(rad) * m
|
119
|
+
self.y += Math.sin(rad) * m
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Keep the body in bounds of the window. If it went out of bounds,
|
124
|
+
# set its position to be on that bound and return the cardinal
|
125
|
+
# direction of the wall it hit.
|
126
|
+
#
|
127
|
+
# See also: NORMALS
|
128
|
+
|
129
|
+
def clip
|
130
|
+
max_h, max_w = w.h, w.w
|
131
|
+
|
132
|
+
if x < 0 then
|
133
|
+
self.x = 0
|
134
|
+
return :west
|
135
|
+
elsif x > max_w then
|
136
|
+
self.x = max_w
|
137
|
+
return :east
|
138
|
+
end
|
139
|
+
|
140
|
+
if y < 0 then
|
141
|
+
self.y = 0
|
142
|
+
return :north
|
143
|
+
elsif y > max_h then
|
144
|
+
self.y = max_h
|
145
|
+
return :south
|
146
|
+
end
|
147
|
+
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Return a random angle 0...360.
|
153
|
+
|
154
|
+
def random_angle
|
155
|
+
360 * rand
|
156
|
+
end
|
157
|
+
|
158
|
+
###
|
159
|
+
# Randomly turn the body inside an arc of +deg+ degrees from where
|
160
|
+
# it is currently facing.
|
161
|
+
|
162
|
+
def random_turn deg
|
163
|
+
rand(deg) - (deg/2)
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# clip and then set the goal angle to the normal plus or minus a
|
168
|
+
# random 45 degrees.
|
169
|
+
|
170
|
+
def clip_off_wall
|
171
|
+
if wall = clip then
|
172
|
+
normal = NORMAL[wall]
|
173
|
+
self.ga = (normal + random_turn(90)).degrees unless (normal - ga).abs < 45
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Like clip, keep the body in bounds of the window, but set the
|
179
|
+
# angle to the angle of reflection. Also slows momentum by 20%.
|
180
|
+
|
181
|
+
def bounce
|
182
|
+
# TODO: rewrite this using clip + NORMAL to clean it up
|
183
|
+
max_h, max_w = w.h, w.w
|
184
|
+
normal = nil
|
185
|
+
|
186
|
+
if x < 0 then
|
187
|
+
self.x, normal = 0, 0
|
188
|
+
elsif x > max_w then
|
189
|
+
self.x, normal = max_w, 180
|
190
|
+
end
|
191
|
+
|
192
|
+
if y < 0 then
|
193
|
+
self.y, normal = 0, 90
|
194
|
+
elsif y > max_h then
|
195
|
+
self.y, normal = max_h, 270
|
196
|
+
end
|
197
|
+
|
198
|
+
if normal then
|
199
|
+
self.a = (2 * normal - 180 - a).degrees
|
200
|
+
self.m *= 0.8
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# Wrap the body if it hits an edge.
|
206
|
+
|
207
|
+
def wrap
|
208
|
+
max_h, max_w = w.h, w.w
|
209
|
+
|
210
|
+
self.x = max_w if x < 0
|
211
|
+
self.y = max_h if y < 0
|
212
|
+
|
213
|
+
self.x = 0 if x > max_w
|
214
|
+
self.y = 0 if y > max_h
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Integer
|
2
|
+
##
|
3
|
+
# Calculate a random chance using easy notation: 1 =~ 50 :: 1 in 50 chance
|
4
|
+
|
5
|
+
def =~ n #
|
6
|
+
rand(n) <= (self - 1)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Numeric
|
11
|
+
##
|
12
|
+
# Is M close to N within a certain delta?
|
13
|
+
|
14
|
+
def close_to? n, delta = 0.01
|
15
|
+
(self - n).abs < delta
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Normalize a number to be within 0...360
|
20
|
+
|
21
|
+
def degrees
|
22
|
+
(self < 0 ? self + 360 : self) % 360
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# I am honestly befuddled by this code, and I wrote it.
|
27
|
+
#
|
28
|
+
# I should probably remove it and start over.
|
29
|
+
#
|
30
|
+
# Consider this method private, even tho it is in use by the demos.
|
31
|
+
|
32
|
+
def relative_angle n, max
|
33
|
+
deltaCW = (self - n).degrees
|
34
|
+
deltaCC = (n - self).degrees
|
35
|
+
|
36
|
+
return if deltaCC < 0.1 || deltaCW < 0.1
|
37
|
+
|
38
|
+
if deltaCC.abs < max then
|
39
|
+
deltaCC
|
40
|
+
elsif deltaCW.close_to? 180 then
|
41
|
+
[-max, max].sample
|
42
|
+
elsif deltaCW < deltaCC then
|
43
|
+
-max
|
44
|
+
else
|
45
|
+
max
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,377 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require "sdl"
|
4
|
+
|
5
|
+
module SDL; end # :nodoc: -- stupid rdoc :(
|
6
|
+
|
7
|
+
##
|
8
|
+
# A simulation. This ties everything together and provides a bunch of
|
9
|
+
# convenience methods to make life easier.
|
10
|
+
|
11
|
+
class Graphics::Simulation
|
12
|
+
|
13
|
+
# degrees to radians
|
14
|
+
D2R = Math::PI / 180.0
|
15
|
+
|
16
|
+
# radians to degrees
|
17
|
+
R2D = 1 / D2R
|
18
|
+
|
19
|
+
# The window the simulation is drawing in.
|
20
|
+
attr_accessor :screen
|
21
|
+
|
22
|
+
# The window width.
|
23
|
+
attr_accessor :w
|
24
|
+
|
25
|
+
# The window height.
|
26
|
+
attr_accessor :h
|
27
|
+
|
28
|
+
# Pause the simulation.
|
29
|
+
attr_accessor :paused
|
30
|
+
|
31
|
+
# The current font for rendering text.
|
32
|
+
attr_accessor :font
|
33
|
+
|
34
|
+
# A hash of color names to their values.
|
35
|
+
|
36
|
+
attr_accessor :color
|
37
|
+
|
38
|
+
# A hash of color values to their rgb values. For text, apparently. *shrug*
|
39
|
+
attr_accessor :rgb
|
40
|
+
|
41
|
+
##
|
42
|
+
# Create a new simulation of a certain width and height. Optionally,
|
43
|
+
# you can set the bits per pixel (0 for current screen settings),
|
44
|
+
# the name of the window, and whether or not to run in full screen mode.
|
45
|
+
#
|
46
|
+
# This also names a bunch colors and hues for convenience.
|
47
|
+
|
48
|
+
def initialize w, h, bpp = 0, name = self.class.name, full = false
|
49
|
+
SDL.init SDL::INIT_VIDEO
|
50
|
+
SDL::TTF.init
|
51
|
+
|
52
|
+
full = full ? SDL::FULLSCREEN : 0
|
53
|
+
|
54
|
+
self.font = SDL::TTF.open("/System/Library/Fonts/Menlo.ttc", 32, 0)
|
55
|
+
|
56
|
+
SDL::WM.set_caption name, name
|
57
|
+
|
58
|
+
self.screen = SDL::Screen.open w, h, bpp, SDL::HWSURFACE|SDL::DOUBLEBUF|full
|
59
|
+
self.w, self.h = screen.w, screen.h
|
60
|
+
|
61
|
+
self.color = {}
|
62
|
+
self.rgb = Hash.new { |hash, k| hash[k] = screen.get_rgb(color[k]) }
|
63
|
+
|
64
|
+
register_color :black, 0, 0, 0
|
65
|
+
register_color :white, 255, 255, 255
|
66
|
+
register_color :red, 255, 0, 0
|
67
|
+
register_color :green, 0, 255, 0
|
68
|
+
register_color :blue, 0, 0, 255
|
69
|
+
register_color :gray, 127, 127, 127
|
70
|
+
register_color :yellow, 255, 255, 0
|
71
|
+
register_color :alpha, 0, 0, 0, 0
|
72
|
+
|
73
|
+
(0..99).each do |n|
|
74
|
+
m = (255 * (n / 100.0)).to_i
|
75
|
+
register_color ("gray%02d" % n).to_sym, m, m, m
|
76
|
+
register_color ("red%02d" % n).to_sym, m, 0, 0
|
77
|
+
register_color ("green%02d" % n).to_sym, 0, m, 0
|
78
|
+
register_color ("blue%02d" % n).to_sym, 0, 0, m
|
79
|
+
end
|
80
|
+
|
81
|
+
self.paused = false
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Name a color w/ rgba values.
|
86
|
+
|
87
|
+
def register_color name, r, g, b, a = 255
|
88
|
+
color[name] = screen.format.map_rgba r, g, b, a
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Return an array populated by instances of +klass+. You can specify
|
93
|
+
# how many to create here or it will access +klass::COUNT+ as the
|
94
|
+
# default.
|
95
|
+
|
96
|
+
def populate klass, n = klass::COUNT
|
97
|
+
n.times.map {
|
98
|
+
o = klass.new self
|
99
|
+
yield o if block_given?
|
100
|
+
o }
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Handle an event. By default only handles the Quit event. Override
|
105
|
+
# if you want to add more handlers. Be sure to call super or you
|
106
|
+
# won't be able to quit.
|
107
|
+
|
108
|
+
def handle_event event, n
|
109
|
+
exit if SDL::Event::Quit === event
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Handle key events. By default handles ESC & Q (quit) and P
|
114
|
+
# (pause). Override this if you want to handle more key events. Be
|
115
|
+
# sure to call super or provide your own means of quitting and/or
|
116
|
+
# pausing.
|
117
|
+
|
118
|
+
def handle_keys
|
119
|
+
exit if SDL::Key.press? SDL::Key::ESCAPE
|
120
|
+
exit if SDL::Key.press? SDL::Key::Q
|
121
|
+
self.paused = !paused if SDL::Key.press? SDL::Key::P
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Run the simulation. This handles all events by polling and
|
126
|
+
# scanning for key presses (multiple keys at once are possible).
|
127
|
+
#
|
128
|
+
# On each tick, call update, then draw the scene.
|
129
|
+
|
130
|
+
def run
|
131
|
+
self.start_time = Time.now
|
132
|
+
n = 0
|
133
|
+
event = nil
|
134
|
+
loop do
|
135
|
+
handle_event event, n while event = SDL::Event.poll
|
136
|
+
SDL::Key.scan
|
137
|
+
handle_keys
|
138
|
+
|
139
|
+
unless paused then
|
140
|
+
update n unless paused
|
141
|
+
|
142
|
+
draw_and_flip n
|
143
|
+
|
144
|
+
n += 1 unless paused
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def draw_and_flip n # :nodoc:
|
150
|
+
self.draw n
|
151
|
+
screen.flip
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# Draw the scene. This is a subclass responsibility and must draw
|
156
|
+
# the entire window (including calling clear).
|
157
|
+
|
158
|
+
def draw n
|
159
|
+
raise NotImplementedError, "Subclass Responsibility"
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Update the simulation. This does nothing by default and must be
|
164
|
+
# overridden by the subclass.
|
165
|
+
|
166
|
+
def update n
|
167
|
+
# do nothing
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Clear the whole screen
|
172
|
+
|
173
|
+
def clear c = :black
|
174
|
+
fast_rect 0, 0, w, h, c
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Draw an antialiased line from x1/y1 to x2/y2 in color c.
|
179
|
+
|
180
|
+
def line x1, y1, x2, y2, c
|
181
|
+
h = self.h
|
182
|
+
screen.draw_line x1, h-y1, x2, h-y2, color[c], :antialiased
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Draw a horizontal line from x1 to x2 at y in color c.
|
187
|
+
|
188
|
+
def hline y, c, x1 = 0, x2 = h
|
189
|
+
line x1, y, x2, y, c
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Draw a vertical line from y1 to y2 at y in color c.
|
194
|
+
|
195
|
+
def vline x, c, y1 = 0, y2 = w
|
196
|
+
line x, y1, x, y2, c
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# Draw a closed form polygon from an array of points in a particular
|
201
|
+
# color.
|
202
|
+
|
203
|
+
def polygon *points, color
|
204
|
+
points << points.first
|
205
|
+
points.each_cons(2) do |p1, p2|
|
206
|
+
w.line(*p1, *p2, color)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Draw a line from x1/y1 to a particular magnitude and angle in color c.
|
212
|
+
|
213
|
+
def angle x1, y1, a, m, c
|
214
|
+
x2, y2 = project x1, y1, a, m
|
215
|
+
line x1, y1, x2, y2, c
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# Draw a rect at x/y with w by h dimensions in color c. Ignores blending.
|
220
|
+
|
221
|
+
def fast_rect x, y, w, h, c
|
222
|
+
screen.fill_rect x, self.h-y-h, w, h, color[c]
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# Draw a point at x/y w/ color c.
|
227
|
+
|
228
|
+
def point x, y, c
|
229
|
+
screen[x, h-y] = color[c]
|
230
|
+
end
|
231
|
+
|
232
|
+
##
|
233
|
+
# Calculate the x/y coordinate offset from x1/y1 with an angle and a
|
234
|
+
# magnitude.
|
235
|
+
|
236
|
+
def project x1, y1, a, m
|
237
|
+
rad = a * D2R
|
238
|
+
[x1 + Math.cos(rad) * m, y1 + Math.sin(rad) * m]
|
239
|
+
end
|
240
|
+
|
241
|
+
##
|
242
|
+
# Draw a rect at x/y with w by h dimensions in color c.
|
243
|
+
|
244
|
+
def rect x, y, w, h, c, fill = false
|
245
|
+
screen.draw_rect x, self.h-y-h, w, h, color[c], fill
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# Draw a circle at x/y with radius r in color c.
|
250
|
+
|
251
|
+
def circle x, y, r, c, fill = false
|
252
|
+
screen.draw_circle x, h-y, r, color[c], fill, :antialiased
|
253
|
+
end
|
254
|
+
|
255
|
+
##
|
256
|
+
# Draw a circle at x/y with radiuses w/h in color c.
|
257
|
+
|
258
|
+
def ellipse x, y, w, h, c, fill = false
|
259
|
+
screen.draw_ellipse x, self.h-y, w, h, color[c], fill, :antialiased
|
260
|
+
end
|
261
|
+
|
262
|
+
##
|
263
|
+
# Draw an antialiased curve from x1/y1 to x2/y2 via control points
|
264
|
+
# cx1/cy1 & cx2/cy2 in color c.
|
265
|
+
|
266
|
+
def bezier x1, y1, cx1, cy1, cx2, cy2, x2, y2, c, l = 7
|
267
|
+
h = self.h
|
268
|
+
screen.draw_bezier x1, h-y1, cx1, h-cy1, cx2, h-cy2, x2, h-y2, l, color[c], :antialiased
|
269
|
+
end
|
270
|
+
|
271
|
+
## Text
|
272
|
+
|
273
|
+
##
|
274
|
+
# Return the w/h of the text s in font f.
|
275
|
+
|
276
|
+
def text_size s, f = font
|
277
|
+
f.text_size s
|
278
|
+
end
|
279
|
+
|
280
|
+
##
|
281
|
+
# Return the rendered text s in color c in font f.
|
282
|
+
|
283
|
+
def render_text s, c, f = font
|
284
|
+
f.render_solid_utf8 s, *rgb[c]
|
285
|
+
end
|
286
|
+
|
287
|
+
##
|
288
|
+
# Draw text s at x/y in color c in font f.
|
289
|
+
|
290
|
+
def text s, x, y, c, f = font
|
291
|
+
f.draw_solid_utf8 screen, s, x, self.h-y-f.height, *rgb[c]
|
292
|
+
end
|
293
|
+
|
294
|
+
##
|
295
|
+
# Print out some extra debugging information underneath the fps line
|
296
|
+
# (if any).
|
297
|
+
|
298
|
+
def debug fmt, *args
|
299
|
+
s = fmt % args
|
300
|
+
text s, 10, h-40-font.height, :white
|
301
|
+
end
|
302
|
+
|
303
|
+
attr_accessor :start_time # :nodoc:
|
304
|
+
|
305
|
+
##
|
306
|
+
# Draw the current frames-per-second in the top left corner in green.
|
307
|
+
|
308
|
+
def fps n
|
309
|
+
secs = Time.now - start_time
|
310
|
+
fps = "%5.1f fps" % [n / secs]
|
311
|
+
text fps, 10, h-font.height, :green
|
312
|
+
end
|
313
|
+
|
314
|
+
### Blitting Methods:
|
315
|
+
|
316
|
+
## utilities for later
|
317
|
+
|
318
|
+
# put_pixel(x, y, color)
|
319
|
+
# []=(x, y, color)
|
320
|
+
# get_pixel(x, y)
|
321
|
+
# [](x, y)
|
322
|
+
# put(src, x, y) # see blit
|
323
|
+
# copy_rect(x,y,w,h)
|
324
|
+
# transform_surface(bgcolor,angle,xscale,yscale,flags)
|
325
|
+
|
326
|
+
##
|
327
|
+
# Load an image at path into a new surface.
|
328
|
+
|
329
|
+
def image path
|
330
|
+
SDL::Surface.load path
|
331
|
+
end
|
332
|
+
|
333
|
+
##
|
334
|
+
# Return the current mouse state: x, y, buttons.
|
335
|
+
|
336
|
+
def mouse
|
337
|
+
r = SDL::Mouse.state
|
338
|
+
r[1] = h-r[1]
|
339
|
+
r
|
340
|
+
end
|
341
|
+
|
342
|
+
##
|
343
|
+
# Draw a bitmap at x/y with an angle and optional x/y scale.
|
344
|
+
|
345
|
+
def blit o, x, y, a°, xs=1, ys=1, opt=0
|
346
|
+
SDL::Surface.transform_blit o, screen, -a°, 1, 1, o.w/2, o.h/2, x, h-y, opt
|
347
|
+
end
|
348
|
+
|
349
|
+
##
|
350
|
+
# Create a new sprite with a given width and height and yield to a
|
351
|
+
# block with the new sprite as the current screen. All drawing
|
352
|
+
# primitives will work and the resulting surface is returned.
|
353
|
+
|
354
|
+
def sprite w, h
|
355
|
+
new_screen = SDL::Surface.new SDL::SWSURFACE, w, h, screen
|
356
|
+
old_screen = screen
|
357
|
+
old_w, old_h = self.w, self.h
|
358
|
+
self.w, self.h = w, h
|
359
|
+
|
360
|
+
self.screen = new_screen
|
361
|
+
yield if block_given?
|
362
|
+
|
363
|
+
new_screen.set_color_key SDL::SRCCOLORKEY, 0
|
364
|
+
|
365
|
+
new_screen
|
366
|
+
ensure
|
367
|
+
self.screen = old_screen
|
368
|
+
self.w, self.h = old_w, old_h
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
if $0 == __FILE__ then
|
373
|
+
SDL.init SDL::INIT_EVERYTHING
|
374
|
+
SDL.set_video_mode(640, 480, 16, SDL::SWSURFACE)
|
375
|
+
sleep 1
|
376
|
+
puts "if you saw a window, it was working"
|
377
|
+
end
|