minigl 2.2.2 → 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +19 -19
- data/README.md +35 -36
- data/Rakefile +11 -11
- data/lib/minigl.rb +4 -4
- data/lib/minigl/forms.rb +1485 -1485
- data/lib/minigl/game_object.rb +379 -379
- data/lib/minigl/global.rb +729 -729
- data/lib/minigl/map.rb +256 -256
- data/lib/minigl/movement.rb +585 -585
- data/lib/minigl/text.rb +190 -188
- data/test/data/img/barbg.svg +73 -73
- data/test/data/img/barfg.svg +106 -106
- data/test/data/img/square.svg +66 -66
- data/test/data/img/square2.svg +66 -66
- data/test/data/img/square3.svg +76 -76
- data/test/data/img/tile1.svg +66 -66
- data/test/data/img/tile2.svg +66 -66
- data/test/game.rb +150 -150
- data/test/game_object_tests.rb +108 -108
- data/test/iso_game.rb +39 -39
- data/test/map_tests.rb +57 -57
- data/test/mov_game.rb +76 -76
- data/test/movement_tests.rb +86 -86
- data/test/res_tests.rb +45 -45
- data/test/vector_tests.rb +55 -55
- metadata +24 -24
data/lib/minigl/game_object.rb
CHANGED
@@ -1,379 +1,379 @@
|
|
1
|
-
require_relative 'movement'
|
2
|
-
|
3
|
-
module MiniGL
|
4
|
-
# This class represents an (optionally animated) image inside the game screen.
|
5
|
-
class Sprite
|
6
|
-
# The index of the current sprite in the spritesheet being drawn.
|
7
|
-
attr_reader :img_index
|
8
|
-
|
9
|
-
# The x-coordinate of the image in the screen.
|
10
|
-
attr_accessor :x
|
11
|
-
|
12
|
-
# The y-coordinate of the image in the screen.
|
13
|
-
attr_accessor :y
|
14
|
-
|
15
|
-
# Creates a new sprite.
|
16
|
-
#
|
17
|
-
# Parameters:
|
18
|
-
# [x] The x-coordinate in the screen (or map) where the sprite will be
|
19
|
-
# drawn. This can be modified later via the +x+ attribute.
|
20
|
-
# [y] The y-coordinate in the screen (or map) where the sprite will be
|
21
|
-
# drawn. This can be modified later via the +y+ attribute.
|
22
|
-
# [img] The path to a PNG image or spritesheet, following the MiniGL
|
23
|
-
# convention: images must be inside a 'data/img' directory, relative
|
24
|
-
# to the code file, and you must only provide the file name, without
|
25
|
-
# extension, in this case. If the image is inside a subdirectory of
|
26
|
-
# 'data/img', you must prefix the file name with each subdirectory
|
27
|
-
# name, followed by an underscore (so the file and directories names
|
28
|
-
# must not contain underscores). For example, if your image is
|
29
|
-
# 'data/img/sprite/1.png', you must provide <code>"sprite_1"</code>
|
30
|
-
# or +:sprite_1+.
|
31
|
-
# [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
|
32
|
-
# image is not a spritesheet.
|
33
|
-
# [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
|
34
|
-
# image is not a spritesheet.
|
35
|
-
def initialize(x, y, img, sprite_cols = nil, sprite_rows = nil, retro = nil)
|
36
|
-
@x = x; @y = y
|
37
|
-
retro = Res.retro_images if retro.nil?
|
38
|
-
@img =
|
39
|
-
if sprite_cols.nil?
|
40
|
-
[Res.img(img, false, false, '.png', retro)]
|
41
|
-
else
|
42
|
-
Res.imgs img, sprite_cols, sprite_rows, false, '.png', retro
|
43
|
-
end
|
44
|
-
@anim_counter = 0
|
45
|
-
@img_index = 0
|
46
|
-
@index_index = 0
|
47
|
-
@animate_once_control = 0
|
48
|
-
end
|
49
|
-
|
50
|
-
# Performs time checking to update the image index according to the
|
51
|
-
# sequence of indices and the interval.
|
52
|
-
#
|
53
|
-
# Parameters:
|
54
|
-
# [indices] The sequence of image indices used in the animation. The
|
55
|
-
# indices are determined from left to right, and from top to
|
56
|
-
# bottom, inside the spritesheet. All indices must be in the
|
57
|
-
# interval <code>0..(sprite_cols * sprite_rows)</code>.
|
58
|
-
# [interval] The amount of frames between each change in the image index.
|
59
|
-
# A frame will usually represent 1/60 second (roughly 17
|
60
|
-
# milliseconds).
|
61
|
-
def animate(indices, interval)
|
62
|
-
@animate_once_control = 0 if @animate_once_control != 0
|
63
|
-
|
64
|
-
@anim_counter += 1
|
65
|
-
if @anim_counter >= interval
|
66
|
-
@index_index += 1
|
67
|
-
@index_index = 0 if @index_index >= indices.length
|
68
|
-
@img_index = indices[@index_index]
|
69
|
-
@anim_counter = 0
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Causes the sprite to animate through the +indices+ array exactly once,
|
74
|
-
# so that the animation stops once it reaches the last index in the array.
|
75
|
-
# Subsequent calls with the same parameters will have no effect, but if
|
76
|
-
# the index or interval changes, or if +set_animation+ is called, then a
|
77
|
-
# new animation cycle will begin.
|
78
|
-
#
|
79
|
-
# Parameters:
|
80
|
-
# [indices] The sequence of image indices used in the animation. See
|
81
|
-
# +animate+ for details.
|
82
|
-
# [interval] The amount of frames between each change in the image index.
|
83
|
-
# See +animate+ for details.
|
84
|
-
def animate_once(indices, interval)
|
85
|
-
if @animate_once_control == 2
|
86
|
-
return if indices == @animate_once_indices && interval == @animate_once_interval
|
87
|
-
@animate_once_control = 0
|
88
|
-
end
|
89
|
-
|
90
|
-
unless @animate_once_control == 1
|
91
|
-
@anim_counter = 0
|
92
|
-
@img_index = indices[0]
|
93
|
-
@index_index = 0
|
94
|
-
@animate_once_indices = indices
|
95
|
-
@animate_once_interval = interval
|
96
|
-
@animate_once_control = 1
|
97
|
-
return
|
98
|
-
end
|
99
|
-
|
100
|
-
@anim_counter += 1
|
101
|
-
return unless @anim_counter >= interval
|
102
|
-
|
103
|
-
@index_index += 1
|
104
|
-
@img_index = indices[@index_index]
|
105
|
-
@anim_counter = 0
|
106
|
-
@animate_once_control = 2 if @index_index == indices.length - 1
|
107
|
-
end
|
108
|
-
|
109
|
-
# Resets the animation timer and immediately changes the image index to
|
110
|
-
# the specified value.
|
111
|
-
#
|
112
|
-
# Parameters:
|
113
|
-
# [index] The image index to be set.
|
114
|
-
def set_animation(index)
|
115
|
-
@anim_counter = 0
|
116
|
-
@img_index = index
|
117
|
-
@index_index = 0
|
118
|
-
@animate_once_control = 0
|
119
|
-
end
|
120
|
-
|
121
|
-
# Draws the sprite in the screen
|
122
|
-
#
|
123
|
-
# Parameters:
|
124
|
-
# [map] A Map object, relative to which the sprite will be drawn (the x
|
125
|
-
# and y coordinates of the sprite will be changed according to the
|
126
|
-
# position of the camera).
|
127
|
-
# [scale_x] A scale factor to be applied horizontally to the image.
|
128
|
-
# [scale_y] A scale factor to be applied vertically to the image.
|
129
|
-
# [alpha] The opacity with which the image will be drawn. Valid values
|
130
|
-
# vary from 0 (fully transparent) to 255 (fully opaque).
|
131
|
-
# [color] A color filter to apply to the image. A white (0xffffff) filter
|
132
|
-
# will keep all colors unchanged, while a black (0x000000) filter
|
133
|
-
# will turn all colors to black. A red (0xff0000) filter will keep
|
134
|
-
# reddish colors with slight or no change, whereas bluish colors
|
135
|
-
# will be darkened, for example.
|
136
|
-
# [angle] A rotation, in degrees, to be applied to the image, relative to
|
137
|
-
# its center.
|
138
|
-
# [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
|
139
|
-
# to draw it vertically flipped.
|
140
|
-
# [z_index] The z-order to draw the object. Objects with larger z-orders
|
141
|
-
# will be drawn on top of the ones with smaller z-orders.
|
142
|
-
# [round] Specify whether the drawing coordinates should be rounded to an
|
143
|
-
# integer before drawing, to avoid little distortions of the image.
|
144
|
-
# Only applies when the image is not rotated.
|
145
|
-
#
|
146
|
-
# *Obs.:* This method accepts named parameters.
|
147
|
-
def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
|
148
|
-
if map.is_a? Hash
|
149
|
-
scale_x = map.fetch(:scale_x, 1)
|
150
|
-
scale_y = map.fetch(:scale_y, 1)
|
151
|
-
alpha = map.fetch(:alpha, 0xff)
|
152
|
-
color = map.fetch(:color, 0xffffff)
|
153
|
-
angle = map.fetch(:angle, nil)
|
154
|
-
flip = map.fetch(:flip, nil)
|
155
|
-
z_index = map.fetch(:z_index, 0)
|
156
|
-
round = map.fetch(:round, false)
|
157
|
-
map = map.fetch(:map, nil)
|
158
|
-
end
|
159
|
-
|
160
|
-
color = (alpha << 24) | color
|
161
|
-
if angle
|
162
|
-
@img[@img_index].draw_rot @x - (map ? map.cam.x : 0) + @img[0].width * scale_x * 0.5,
|
163
|
-
@y - (map ? map.cam.y : 0) + @img[0].height * scale_y * 0.5,
|
164
|
-
z_index, angle, 0.5, 0.5, (flip == :horiz ? -scale_x : scale_x),
|
165
|
-
(flip == :vert ? -scale_y : scale_y), color
|
166
|
-
else
|
167
|
-
x = @x - (map ? map.cam.x : 0) + (flip == :horiz ? scale_x * @img[0].width : 0)
|
168
|
-
y = @y - (map ? map.cam.y : 0) + (flip == :vert ? scale_y * @img[0].height : 0)
|
169
|
-
@img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
|
170
|
-
z_index, (flip == :horiz ? -scale_x : scale_x),
|
171
|
-
(flip == :vert ? -scale_y : scale_y), color
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# Returns whether this sprite is visible in the given map (i.e., in the
|
176
|
-
# viewport determined by the camera of the given map). If no map is given,
|
177
|
-
# returns whether the sprite is visible on the screen.
|
178
|
-
def visible?(map = nil)
|
179
|
-
r = Rectangle.new @x, @y, @img[0].width, @img[0].height
|
180
|
-
return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
|
181
|
-
map.cam.intersect? r
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
# This class represents an object with a set of properties and methods
|
186
|
-
# commonly used in games. It defines an object with a rectangular bounding
|
187
|
-
# box, and having all the attributes required for using the Movement module.
|
188
|
-
class GameObject < Sprite
|
189
|
-
include Movement
|
190
|
-
|
191
|
-
# Creates a new game object.
|
192
|
-
#
|
193
|
-
# Parameters:
|
194
|
-
# [x] The x-coordinate of the object's bounding box. This can be modified
|
195
|
-
# later via the +x+ attribute.
|
196
|
-
# [y] The y-coordinate of the object's bounding box. This can be modified
|
197
|
-
# later via the +y+ attribute.
|
198
|
-
# [w] The width of the object's bounding box.
|
199
|
-
# [h] The height of the object's bounding box.
|
200
|
-
# [img] The image or spritesheet for the object.
|
201
|
-
# [img_gap] A Vector object representing the difference between the top
|
202
|
-
# left corner of the bounding box and the coordinates of the
|
203
|
-
# image. For example, an object with <code>x = 100</code>,
|
204
|
-
# <code>y = 50</code> and <code>img_gap = Vector.new(-5, -5)</code>
|
205
|
-
# will be drawn at position (95, 45) of the screen.
|
206
|
-
# [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
|
207
|
-
# image is not a spritesheet.
|
208
|
-
# [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
|
209
|
-
# image is not a spritesheet.
|
210
|
-
# [mass] The mass of the object. Details on how it is used can be found
|
211
|
-
# in the Movement module.
|
212
|
-
def initialize(x, y, w, h, img, img_gap = nil, sprite_cols = nil, sprite_rows = nil, mass = 1.0, retro = nil)
|
213
|
-
super x, y, img, sprite_cols, sprite_rows, retro
|
214
|
-
@w = w; @h = h
|
215
|
-
@img_gap =
|
216
|
-
if img_gap.nil?
|
217
|
-
Vector.new 0, 0
|
218
|
-
else
|
219
|
-
img_gap
|
220
|
-
end
|
221
|
-
@mass = mass
|
222
|
-
@speed = Vector.new 0, 0
|
223
|
-
@max_speed = Vector.new 15, 15
|
224
|
-
@stored_forces = Vector.new 0, 0
|
225
|
-
end
|
226
|
-
|
227
|
-
# Draws the game object in the screen.
|
228
|
-
#
|
229
|
-
# Parameters:
|
230
|
-
# [map] A Map object, relative to which the object will be drawn (the x
|
231
|
-
# and y coordinates of the image will be changed according to the
|
232
|
-
# position of the camera).
|
233
|
-
# [scale_x] A scale factor to be applied horizontally to the image.
|
234
|
-
# [scale_y] A scale factor to be applied vertically to the image.
|
235
|
-
# [alpha] The opacity with which the image will be drawn. Valid values
|
236
|
-
# vary from 0 (fully transparent) to 255 (fully opaque).
|
237
|
-
# [color] A color filter to apply to the image. A white (0xffffff) filter
|
238
|
-
# will keep all colors unchanged, while a black (0x000000) filter
|
239
|
-
# will turn all colors to black. A red (0xff0000) filter will keep
|
240
|
-
# reddish colors with slight or no change, whereas bluish colors
|
241
|
-
# will be darkened, for example.
|
242
|
-
# [angle] A rotation, in degrees, to be applied to the image, relative to
|
243
|
-
# its center.
|
244
|
-
# [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
|
245
|
-
# to draw it vertically flipped.
|
246
|
-
# [z_index] The z-order to draw the object. Objects with larger z-orders
|
247
|
-
# will be drawn on top of the ones with smaller z-orders.
|
248
|
-
# [round] Specify whether the drawing coordinates should be rounded to an
|
249
|
-
# integer before drawing, to avoid little distortions of the image.
|
250
|
-
# Only applies when the image is not rotated.
|
251
|
-
#
|
252
|
-
# *Obs.:* This method accepts named parameters.
|
253
|
-
def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
|
254
|
-
if map.is_a? Hash
|
255
|
-
scale_x = map.fetch(:scale_x, 1)
|
256
|
-
scale_y = map.fetch(:scale_y, 1)
|
257
|
-
alpha = map.fetch(:alpha, 0xff)
|
258
|
-
color = map.fetch(:color, 0xffffff)
|
259
|
-
angle = map.fetch(:angle, nil)
|
260
|
-
flip = map.fetch(:flip, nil)
|
261
|
-
z_index = map.fetch(:z_index, 0)
|
262
|
-
round = map.fetch(:round, false)
|
263
|
-
map = map.fetch(:map, nil)
|
264
|
-
end
|
265
|
-
|
266
|
-
color = (alpha << 24) | color
|
267
|
-
if angle
|
268
|
-
center_x = @x + @w * 0.5
|
269
|
-
center_y = @y + @h * 0.5
|
270
|
-
o_x = center_x - @x - @img_gap.x
|
271
|
-
o_y = center_y - @y - @img_gap.y
|
272
|
-
@img[@img_index].draw_rot @x + (flip == :horiz ? -1 : 1) * (@img_gap.x + o_x) - (map ? map.cam.x : 0),
|
273
|
-
@y + (flip == :vert ? -1 : 1) * (@img_gap.y + o_y) - (map ? map.cam.y : 0),
|
274
|
-
z_index, angle, o_x.to_f / (@img[0].width * scale_x), o_y.to_f / (@img[0].height * scale_y),
|
275
|
-
(flip == :horiz ? -scale_x : scale_x), (flip == :vert ? -scale_y : scale_y), color
|
276
|
-
else
|
277
|
-
x = @x + (flip == :horiz ? -1 : 1) * @img_gap.x - (map ? map.cam.x : 0) + (flip == :horiz ? @w : 0)
|
278
|
-
y = @y + (flip == :vert ? -1 : 1) * @img_gap.y - (map ? map.cam.y : 0) + (flip == :vert ? @h : 0)
|
279
|
-
@img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
|
280
|
-
z_index, (flip == :horiz ? -scale_x : scale_x),
|
281
|
-
(flip == :vert ? -scale_y : scale_y), color
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
# Returns whether this object is visible in the given map (i.e., in the
|
286
|
-
# viewport determined by the camera of the given map). If no map is given,
|
287
|
-
# returns whether the object is visible on the screen.
|
288
|
-
def visible?(map = nil)
|
289
|
-
r = Rectangle.new @x.round + @img_gap.x, @y.round + @img_gap.y, @img[0].width, @img[0].height
|
290
|
-
return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
|
291
|
-
map.cam.intersect? r
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
# Represents a visual effect, i.e., a graphic - usually animated - that shows
|
296
|
-
# up in the screen, lasts for a given time and "disappears". You should
|
297
|
-
# explicitly dispose of references to effects whose attribute +dead+ is set
|
298
|
-
# to +true+.
|
299
|
-
class Effect < Sprite
|
300
|
-
# This is +true+ when the effect's lifetime has already passed.
|
301
|
-
attr_reader :dead
|
302
|
-
|
303
|
-
# Creates a new Effect.
|
304
|
-
#
|
305
|
-
# Parameters:
|
306
|
-
# [x] The x-coordinate in the screen (or map) where the effect will be
|
307
|
-
# drawn. This can be modified later via the +x+ attribute.
|
308
|
-
# [y] The y-coordinate in the screen (or map) where the effect will be
|
309
|
-
# drawn. This can be modified later via the +y+ attribute.
|
310
|
-
# [img] The image or spritesheet to use for this effect (see Sprite for
|
311
|
-
# details on spritesheets).
|
312
|
-
# [sprite_cols] (see Sprite)
|
313
|
-
# [sprite_rows] (see Sprite)
|
314
|
-
# [interval] The interval between steps of the animation, in updates.
|
315
|
-
# [indices] The indices to use in the animation. See Sprite#animate for
|
316
|
-
# details. If +nil+, it will be the sequence from 0 to
|
317
|
-
# <code>sprite_cols * sprite_rows - 1</code>.
|
318
|
-
# [lifetime] The lifetime of the effect, in updates. After +update+ is
|
319
|
-
# called this number of times, the effect will no longer
|
320
|
-
# be visible, even when +draw+ is called, and the +dead+ flag
|
321
|
-
# will be set to +true+, so you get to know when to dispose
|
322
|
-
# of the Effect object. If +nil+, it will be set to
|
323
|
-
# <code>@indices.length * interval</code>, i.e., the exact time
|
324
|
-
# needed for one animation cycle to complete.
|
325
|
-
# [sound] The id of a sound to be played when the effect is created (id must
|
326
|
-
# be given in the format specified for the +Res.sound+ method).
|
327
|
-
# [sound_ext] Extension of the sound file, if a sound is given. Default is
|
328
|
-
# '.wav'.
|
329
|
-
# [sound_volume] The volume (from 0 to 1) to play the sound, if any. Default
|
330
|
-
# is 1.
|
331
|
-
#
|
332
|
-
# *Obs.:* This method accepts named parameters, but +x+, +y+ and +img+ are
|
333
|
-
# mandatory.
|
334
|
-
def initialize(x, y = nil, img = nil, sprite_cols = nil, sprite_rows = nil, interval = 10, indices = nil, lifetime = nil,
|
335
|
-
sound = nil, sound_ext = '.wav', sound_volume = 1)
|
336
|
-
if x.is_a? Hash
|
337
|
-
y = x[:y]
|
338
|
-
img = x[:img]
|
339
|
-
sprite_cols = x.fetch(:sprite_cols, nil)
|
340
|
-
sprite_rows = x.fetch(:sprite_rows, nil)
|
341
|
-
interval = x.fetch(:interval, 10)
|
342
|
-
indices = x.fetch(:indices, nil)
|
343
|
-
lifetime = x.fetch(:lifetime, nil)
|
344
|
-
sound = x.fetch(:sound, nil)
|
345
|
-
sound_ext = x.fetch(:sound_ext, '.wav')
|
346
|
-
sound_volume = x.fetch(:sound_volume, 1)
|
347
|
-
x = x[:x]
|
348
|
-
end
|
349
|
-
|
350
|
-
super x, y, img, sprite_cols, sprite_rows
|
351
|
-
@timer = 0
|
352
|
-
if indices
|
353
|
-
@indices = indices
|
354
|
-
else
|
355
|
-
@indices = *(0..(@img.length - 1))
|
356
|
-
end
|
357
|
-
@interval = interval
|
358
|
-
if lifetime
|
359
|
-
@lifetime = lifetime
|
360
|
-
else
|
361
|
-
@lifetime = @indices.length * interval
|
362
|
-
end
|
363
|
-
Res.sound(sound, false, sound_ext).play(sound_volume) if sound
|
364
|
-
end
|
365
|
-
|
366
|
-
# Updates the effect, animating and counting its remaining lifetime.
|
367
|
-
def update
|
368
|
-
unless @dead
|
369
|
-
animate @indices, @interval
|
370
|
-
@timer += 1
|
371
|
-
@dead = true if @timer == @lifetime
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, z_index = 0)
|
376
|
-
super unless @dead
|
377
|
-
end
|
378
|
-
end
|
379
|
-
end
|
1
|
+
require_relative 'movement'
|
2
|
+
|
3
|
+
module MiniGL
|
4
|
+
# This class represents an (optionally animated) image inside the game screen.
|
5
|
+
class Sprite
|
6
|
+
# The index of the current sprite in the spritesheet being drawn.
|
7
|
+
attr_reader :img_index
|
8
|
+
|
9
|
+
# The x-coordinate of the image in the screen.
|
10
|
+
attr_accessor :x
|
11
|
+
|
12
|
+
# The y-coordinate of the image in the screen.
|
13
|
+
attr_accessor :y
|
14
|
+
|
15
|
+
# Creates a new sprite.
|
16
|
+
#
|
17
|
+
# Parameters:
|
18
|
+
# [x] The x-coordinate in the screen (or map) where the sprite will be
|
19
|
+
# drawn. This can be modified later via the +x+ attribute.
|
20
|
+
# [y] The y-coordinate in the screen (or map) where the sprite will be
|
21
|
+
# drawn. This can be modified later via the +y+ attribute.
|
22
|
+
# [img] The path to a PNG image or spritesheet, following the MiniGL
|
23
|
+
# convention: images must be inside a 'data/img' directory, relative
|
24
|
+
# to the code file, and you must only provide the file name, without
|
25
|
+
# extension, in this case. If the image is inside a subdirectory of
|
26
|
+
# 'data/img', you must prefix the file name with each subdirectory
|
27
|
+
# name, followed by an underscore (so the file and directories names
|
28
|
+
# must not contain underscores). For example, if your image is
|
29
|
+
# 'data/img/sprite/1.png', you must provide <code>"sprite_1"</code>
|
30
|
+
# or +:sprite_1+.
|
31
|
+
# [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
|
32
|
+
# image is not a spritesheet.
|
33
|
+
# [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
|
34
|
+
# image is not a spritesheet.
|
35
|
+
def initialize(x, y, img, sprite_cols = nil, sprite_rows = nil, retro = nil)
|
36
|
+
@x = x; @y = y
|
37
|
+
retro = Res.retro_images if retro.nil?
|
38
|
+
@img =
|
39
|
+
if sprite_cols.nil?
|
40
|
+
[Res.img(img, false, false, '.png', retro)]
|
41
|
+
else
|
42
|
+
Res.imgs img, sprite_cols, sprite_rows, false, '.png', retro
|
43
|
+
end
|
44
|
+
@anim_counter = 0
|
45
|
+
@img_index = 0
|
46
|
+
@index_index = 0
|
47
|
+
@animate_once_control = 0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Performs time checking to update the image index according to the
|
51
|
+
# sequence of indices and the interval.
|
52
|
+
#
|
53
|
+
# Parameters:
|
54
|
+
# [indices] The sequence of image indices used in the animation. The
|
55
|
+
# indices are determined from left to right, and from top to
|
56
|
+
# bottom, inside the spritesheet. All indices must be in the
|
57
|
+
# interval <code>0..(sprite_cols * sprite_rows)</code>.
|
58
|
+
# [interval] The amount of frames between each change in the image index.
|
59
|
+
# A frame will usually represent 1/60 second (roughly 17
|
60
|
+
# milliseconds).
|
61
|
+
def animate(indices, interval)
|
62
|
+
@animate_once_control = 0 if @animate_once_control != 0
|
63
|
+
|
64
|
+
@anim_counter += 1
|
65
|
+
if @anim_counter >= interval
|
66
|
+
@index_index += 1
|
67
|
+
@index_index = 0 if @index_index >= indices.length
|
68
|
+
@img_index = indices[@index_index]
|
69
|
+
@anim_counter = 0
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Causes the sprite to animate through the +indices+ array exactly once,
|
74
|
+
# so that the animation stops once it reaches the last index in the array.
|
75
|
+
# Subsequent calls with the same parameters will have no effect, but if
|
76
|
+
# the index or interval changes, or if +set_animation+ is called, then a
|
77
|
+
# new animation cycle will begin.
|
78
|
+
#
|
79
|
+
# Parameters:
|
80
|
+
# [indices] The sequence of image indices used in the animation. See
|
81
|
+
# +animate+ for details.
|
82
|
+
# [interval] The amount of frames between each change in the image index.
|
83
|
+
# See +animate+ for details.
|
84
|
+
def animate_once(indices, interval)
|
85
|
+
if @animate_once_control == 2
|
86
|
+
return if indices == @animate_once_indices && interval == @animate_once_interval
|
87
|
+
@animate_once_control = 0
|
88
|
+
end
|
89
|
+
|
90
|
+
unless @animate_once_control == 1
|
91
|
+
@anim_counter = 0
|
92
|
+
@img_index = indices[0]
|
93
|
+
@index_index = 0
|
94
|
+
@animate_once_indices = indices
|
95
|
+
@animate_once_interval = interval
|
96
|
+
@animate_once_control = 1
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
100
|
+
@anim_counter += 1
|
101
|
+
return unless @anim_counter >= interval
|
102
|
+
|
103
|
+
@index_index += 1
|
104
|
+
@img_index = indices[@index_index]
|
105
|
+
@anim_counter = 0
|
106
|
+
@animate_once_control = 2 if @index_index == indices.length - 1
|
107
|
+
end
|
108
|
+
|
109
|
+
# Resets the animation timer and immediately changes the image index to
|
110
|
+
# the specified value.
|
111
|
+
#
|
112
|
+
# Parameters:
|
113
|
+
# [index] The image index to be set.
|
114
|
+
def set_animation(index)
|
115
|
+
@anim_counter = 0
|
116
|
+
@img_index = index
|
117
|
+
@index_index = 0
|
118
|
+
@animate_once_control = 0
|
119
|
+
end
|
120
|
+
|
121
|
+
# Draws the sprite in the screen
|
122
|
+
#
|
123
|
+
# Parameters:
|
124
|
+
# [map] A Map object, relative to which the sprite will be drawn (the x
|
125
|
+
# and y coordinates of the sprite will be changed according to the
|
126
|
+
# position of the camera).
|
127
|
+
# [scale_x] A scale factor to be applied horizontally to the image.
|
128
|
+
# [scale_y] A scale factor to be applied vertically to the image.
|
129
|
+
# [alpha] The opacity with which the image will be drawn. Valid values
|
130
|
+
# vary from 0 (fully transparent) to 255 (fully opaque).
|
131
|
+
# [color] A color filter to apply to the image. A white (0xffffff) filter
|
132
|
+
# will keep all colors unchanged, while a black (0x000000) filter
|
133
|
+
# will turn all colors to black. A red (0xff0000) filter will keep
|
134
|
+
# reddish colors with slight or no change, whereas bluish colors
|
135
|
+
# will be darkened, for example.
|
136
|
+
# [angle] A rotation, in degrees, to be applied to the image, relative to
|
137
|
+
# its center.
|
138
|
+
# [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
|
139
|
+
# to draw it vertically flipped.
|
140
|
+
# [z_index] The z-order to draw the object. Objects with larger z-orders
|
141
|
+
# will be drawn on top of the ones with smaller z-orders.
|
142
|
+
# [round] Specify whether the drawing coordinates should be rounded to an
|
143
|
+
# integer before drawing, to avoid little distortions of the image.
|
144
|
+
# Only applies when the image is not rotated.
|
145
|
+
#
|
146
|
+
# *Obs.:* This method accepts named parameters.
|
147
|
+
def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
|
148
|
+
if map.is_a? Hash
|
149
|
+
scale_x = map.fetch(:scale_x, 1)
|
150
|
+
scale_y = map.fetch(:scale_y, 1)
|
151
|
+
alpha = map.fetch(:alpha, 0xff)
|
152
|
+
color = map.fetch(:color, 0xffffff)
|
153
|
+
angle = map.fetch(:angle, nil)
|
154
|
+
flip = map.fetch(:flip, nil)
|
155
|
+
z_index = map.fetch(:z_index, 0)
|
156
|
+
round = map.fetch(:round, false)
|
157
|
+
map = map.fetch(:map, nil)
|
158
|
+
end
|
159
|
+
|
160
|
+
color = (alpha << 24) | color
|
161
|
+
if angle
|
162
|
+
@img[@img_index].draw_rot @x - (map ? map.cam.x : 0) + @img[0].width * scale_x * 0.5,
|
163
|
+
@y - (map ? map.cam.y : 0) + @img[0].height * scale_y * 0.5,
|
164
|
+
z_index, angle, 0.5, 0.5, (flip == :horiz ? -scale_x : scale_x),
|
165
|
+
(flip == :vert ? -scale_y : scale_y), color
|
166
|
+
else
|
167
|
+
x = @x - (map ? map.cam.x : 0) + (flip == :horiz ? scale_x * @img[0].width : 0)
|
168
|
+
y = @y - (map ? map.cam.y : 0) + (flip == :vert ? scale_y * @img[0].height : 0)
|
169
|
+
@img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
|
170
|
+
z_index, (flip == :horiz ? -scale_x : scale_x),
|
171
|
+
(flip == :vert ? -scale_y : scale_y), color
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns whether this sprite is visible in the given map (i.e., in the
|
176
|
+
# viewport determined by the camera of the given map). If no map is given,
|
177
|
+
# returns whether the sprite is visible on the screen.
|
178
|
+
def visible?(map = nil)
|
179
|
+
r = Rectangle.new @x, @y, @img[0].width, @img[0].height
|
180
|
+
return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
|
181
|
+
map.cam.intersect? r
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# This class represents an object with a set of properties and methods
|
186
|
+
# commonly used in games. It defines an object with a rectangular bounding
|
187
|
+
# box, and having all the attributes required for using the Movement module.
|
188
|
+
class GameObject < Sprite
|
189
|
+
include Movement
|
190
|
+
|
191
|
+
# Creates a new game object.
|
192
|
+
#
|
193
|
+
# Parameters:
|
194
|
+
# [x] The x-coordinate of the object's bounding box. This can be modified
|
195
|
+
# later via the +x+ attribute.
|
196
|
+
# [y] The y-coordinate of the object's bounding box. This can be modified
|
197
|
+
# later via the +y+ attribute.
|
198
|
+
# [w] The width of the object's bounding box.
|
199
|
+
# [h] The height of the object's bounding box.
|
200
|
+
# [img] The image or spritesheet for the object.
|
201
|
+
# [img_gap] A Vector object representing the difference between the top
|
202
|
+
# left corner of the bounding box and the coordinates of the
|
203
|
+
# image. For example, an object with <code>x = 100</code>,
|
204
|
+
# <code>y = 50</code> and <code>img_gap = Vector.new(-5, -5)</code>
|
205
|
+
# will be drawn at position (95, 45) of the screen.
|
206
|
+
# [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
|
207
|
+
# image is not a spritesheet.
|
208
|
+
# [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
|
209
|
+
# image is not a spritesheet.
|
210
|
+
# [mass] The mass of the object. Details on how it is used can be found
|
211
|
+
# in the Movement module.
|
212
|
+
def initialize(x, y, w, h, img, img_gap = nil, sprite_cols = nil, sprite_rows = nil, mass = 1.0, retro = nil)
|
213
|
+
super x, y, img, sprite_cols, sprite_rows, retro
|
214
|
+
@w = w; @h = h
|
215
|
+
@img_gap =
|
216
|
+
if img_gap.nil?
|
217
|
+
Vector.new 0, 0
|
218
|
+
else
|
219
|
+
img_gap
|
220
|
+
end
|
221
|
+
@mass = mass
|
222
|
+
@speed = Vector.new 0, 0
|
223
|
+
@max_speed = Vector.new 15, 15
|
224
|
+
@stored_forces = Vector.new 0, 0
|
225
|
+
end
|
226
|
+
|
227
|
+
# Draws the game object in the screen.
|
228
|
+
#
|
229
|
+
# Parameters:
|
230
|
+
# [map] A Map object, relative to which the object will be drawn (the x
|
231
|
+
# and y coordinates of the image will be changed according to the
|
232
|
+
# position of the camera).
|
233
|
+
# [scale_x] A scale factor to be applied horizontally to the image.
|
234
|
+
# [scale_y] A scale factor to be applied vertically to the image.
|
235
|
+
# [alpha] The opacity with which the image will be drawn. Valid values
|
236
|
+
# vary from 0 (fully transparent) to 255 (fully opaque).
|
237
|
+
# [color] A color filter to apply to the image. A white (0xffffff) filter
|
238
|
+
# will keep all colors unchanged, while a black (0x000000) filter
|
239
|
+
# will turn all colors to black. A red (0xff0000) filter will keep
|
240
|
+
# reddish colors with slight or no change, whereas bluish colors
|
241
|
+
# will be darkened, for example.
|
242
|
+
# [angle] A rotation, in degrees, to be applied to the image, relative to
|
243
|
+
# its center.
|
244
|
+
# [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
|
245
|
+
# to draw it vertically flipped.
|
246
|
+
# [z_index] The z-order to draw the object. Objects with larger z-orders
|
247
|
+
# will be drawn on top of the ones with smaller z-orders.
|
248
|
+
# [round] Specify whether the drawing coordinates should be rounded to an
|
249
|
+
# integer before drawing, to avoid little distortions of the image.
|
250
|
+
# Only applies when the image is not rotated.
|
251
|
+
#
|
252
|
+
# *Obs.:* This method accepts named parameters.
|
253
|
+
def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
|
254
|
+
if map.is_a? Hash
|
255
|
+
scale_x = map.fetch(:scale_x, 1)
|
256
|
+
scale_y = map.fetch(:scale_y, 1)
|
257
|
+
alpha = map.fetch(:alpha, 0xff)
|
258
|
+
color = map.fetch(:color, 0xffffff)
|
259
|
+
angle = map.fetch(:angle, nil)
|
260
|
+
flip = map.fetch(:flip, nil)
|
261
|
+
z_index = map.fetch(:z_index, 0)
|
262
|
+
round = map.fetch(:round, false)
|
263
|
+
map = map.fetch(:map, nil)
|
264
|
+
end
|
265
|
+
|
266
|
+
color = (alpha << 24) | color
|
267
|
+
if angle
|
268
|
+
center_x = @x + @w * 0.5
|
269
|
+
center_y = @y + @h * 0.5
|
270
|
+
o_x = center_x - @x - @img_gap.x
|
271
|
+
o_y = center_y - @y - @img_gap.y
|
272
|
+
@img[@img_index].draw_rot @x + (flip == :horiz ? -1 : 1) * (@img_gap.x + o_x) - (map ? map.cam.x : 0),
|
273
|
+
@y + (flip == :vert ? -1 : 1) * (@img_gap.y + o_y) - (map ? map.cam.y : 0),
|
274
|
+
z_index, angle, o_x.to_f / (@img[0].width * scale_x), o_y.to_f / (@img[0].height * scale_y),
|
275
|
+
(flip == :horiz ? -scale_x : scale_x), (flip == :vert ? -scale_y : scale_y), color
|
276
|
+
else
|
277
|
+
x = @x + (flip == :horiz ? -1 : 1) * @img_gap.x - (map ? map.cam.x : 0) + (flip == :horiz ? @w : 0)
|
278
|
+
y = @y + (flip == :vert ? -1 : 1) * @img_gap.y - (map ? map.cam.y : 0) + (flip == :vert ? @h : 0)
|
279
|
+
@img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
|
280
|
+
z_index, (flip == :horiz ? -scale_x : scale_x),
|
281
|
+
(flip == :vert ? -scale_y : scale_y), color
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Returns whether this object is visible in the given map (i.e., in the
|
286
|
+
# viewport determined by the camera of the given map). If no map is given,
|
287
|
+
# returns whether the object is visible on the screen.
|
288
|
+
def visible?(map = nil)
|
289
|
+
r = Rectangle.new @x.round + @img_gap.x, @y.round + @img_gap.y, @img[0].width, @img[0].height
|
290
|
+
return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
|
291
|
+
map.cam.intersect? r
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Represents a visual effect, i.e., a graphic - usually animated - that shows
|
296
|
+
# up in the screen, lasts for a given time and "disappears". You should
|
297
|
+
# explicitly dispose of references to effects whose attribute +dead+ is set
|
298
|
+
# to +true+.
|
299
|
+
class Effect < Sprite
|
300
|
+
# This is +true+ when the effect's lifetime has already passed.
|
301
|
+
attr_reader :dead
|
302
|
+
|
303
|
+
# Creates a new Effect.
|
304
|
+
#
|
305
|
+
# Parameters:
|
306
|
+
# [x] The x-coordinate in the screen (or map) where the effect will be
|
307
|
+
# drawn. This can be modified later via the +x+ attribute.
|
308
|
+
# [y] The y-coordinate in the screen (or map) where the effect will be
|
309
|
+
# drawn. This can be modified later via the +y+ attribute.
|
310
|
+
# [img] The image or spritesheet to use for this effect (see Sprite for
|
311
|
+
# details on spritesheets).
|
312
|
+
# [sprite_cols] (see Sprite)
|
313
|
+
# [sprite_rows] (see Sprite)
|
314
|
+
# [interval] The interval between steps of the animation, in updates.
|
315
|
+
# [indices] The indices to use in the animation. See Sprite#animate for
|
316
|
+
# details. If +nil+, it will be the sequence from 0 to
|
317
|
+
# <code>sprite_cols * sprite_rows - 1</code>.
|
318
|
+
# [lifetime] The lifetime of the effect, in updates. After +update+ is
|
319
|
+
# called this number of times, the effect will no longer
|
320
|
+
# be visible, even when +draw+ is called, and the +dead+ flag
|
321
|
+
# will be set to +true+, so you get to know when to dispose
|
322
|
+
# of the Effect object. If +nil+, it will be set to
|
323
|
+
# <code>@indices.length * interval</code>, i.e., the exact time
|
324
|
+
# needed for one animation cycle to complete.
|
325
|
+
# [sound] The id of a sound to be played when the effect is created (id must
|
326
|
+
# be given in the format specified for the +Res.sound+ method).
|
327
|
+
# [sound_ext] Extension of the sound file, if a sound is given. Default is
|
328
|
+
# '.wav'.
|
329
|
+
# [sound_volume] The volume (from 0 to 1) to play the sound, if any. Default
|
330
|
+
# is 1.
|
331
|
+
#
|
332
|
+
# *Obs.:* This method accepts named parameters, but +x+, +y+ and +img+ are
|
333
|
+
# mandatory.
|
334
|
+
def initialize(x, y = nil, img = nil, sprite_cols = nil, sprite_rows = nil, interval = 10, indices = nil, lifetime = nil,
|
335
|
+
sound = nil, sound_ext = '.wav', sound_volume = 1)
|
336
|
+
if x.is_a? Hash
|
337
|
+
y = x[:y]
|
338
|
+
img = x[:img]
|
339
|
+
sprite_cols = x.fetch(:sprite_cols, nil)
|
340
|
+
sprite_rows = x.fetch(:sprite_rows, nil)
|
341
|
+
interval = x.fetch(:interval, 10)
|
342
|
+
indices = x.fetch(:indices, nil)
|
343
|
+
lifetime = x.fetch(:lifetime, nil)
|
344
|
+
sound = x.fetch(:sound, nil)
|
345
|
+
sound_ext = x.fetch(:sound_ext, '.wav')
|
346
|
+
sound_volume = x.fetch(:sound_volume, 1)
|
347
|
+
x = x[:x]
|
348
|
+
end
|
349
|
+
|
350
|
+
super x, y, img, sprite_cols, sprite_rows
|
351
|
+
@timer = 0
|
352
|
+
if indices
|
353
|
+
@indices = indices
|
354
|
+
else
|
355
|
+
@indices = *(0..(@img.length - 1))
|
356
|
+
end
|
357
|
+
@interval = interval
|
358
|
+
if lifetime
|
359
|
+
@lifetime = lifetime
|
360
|
+
else
|
361
|
+
@lifetime = @indices.length * interval
|
362
|
+
end
|
363
|
+
Res.sound(sound, false, sound_ext).play(sound_volume) if sound
|
364
|
+
end
|
365
|
+
|
366
|
+
# Updates the effect, animating and counting its remaining lifetime.
|
367
|
+
def update
|
368
|
+
unless @dead
|
369
|
+
animate @indices, @interval
|
370
|
+
@timer += 1
|
371
|
+
@dead = true if @timer == @lifetime
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, z_index = 0)
|
376
|
+
super unless @dead
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|