gamebox 0.1.1 → 0.2.1

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.
Files changed (74) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +9 -0
  3. data/History.txt +5 -0
  4. data/README.txt +4 -3
  5. data/Rakefile +12 -4
  6. data/TODO.txt +1 -5
  7. data/VERSION +1 -1
  8. data/docs/getting_started.rdoc +2 -2
  9. data/gamebox.gemspec +26 -10
  10. data/lib/gamebox/actor.rb +10 -3
  11. data/lib/gamebox/actor_factory.rb +2 -2
  12. data/lib/gamebox/actor_view.rb +14 -11
  13. data/lib/gamebox/actors/collidable_debugger.rb +35 -0
  14. data/lib/gamebox/actors/curtain.rb +2 -2
  15. data/lib/gamebox/actors/logo.rb +0 -6
  16. data/lib/gamebox/actors/score.rb +2 -5
  17. data/lib/gamebox/actors/spatial_debugger.rb +47 -0
  18. data/lib/gamebox/actors/svg_actor.rb +4 -6
  19. data/lib/gamebox/arbiter.rb +108 -15
  20. data/lib/gamebox/behavior.rb +29 -1
  21. data/lib/gamebox/behaviors/animated.rb +14 -23
  22. data/lib/gamebox/behaviors/audible.rb +1 -12
  23. data/lib/gamebox/behaviors/collidable.rb +29 -22
  24. data/lib/gamebox/behaviors/collidable/aabb_collidable.rb +61 -0
  25. data/lib/gamebox/behaviors/collidable/circle_collidable.rb +17 -0
  26. data/lib/gamebox/behaviors/collidable/collidable_shape.rb +35 -0
  27. data/lib/gamebox/behaviors/collidable/polygon_collidable.rb +85 -0
  28. data/lib/gamebox/behaviors/graphical.rb +13 -10
  29. data/lib/gamebox/behaviors/layered.rb +6 -20
  30. data/lib/gamebox/behaviors/physical.rb +116 -93
  31. data/lib/gamebox/class_finder.rb +6 -2
  32. data/lib/gamebox/config_manager.rb +24 -4
  33. data/lib/gamebox/data/config/objects.yml +5 -3
  34. data/lib/gamebox/ftor.rb +372 -0
  35. data/lib/gamebox/gamebox_application.rb +2 -8
  36. data/lib/gamebox/hooked_gosu_window.rb +30 -0
  37. data/lib/gamebox/input_manager.rb +78 -79
  38. data/lib/gamebox/lib/code_statistics.rb +1 -1
  39. data/lib/gamebox/lib/numbers_ext.rb +1 -1
  40. data/lib/gamebox/lib/rect.rb +612 -0
  41. data/lib/gamebox/physical_stage.rb +12 -2
  42. data/lib/gamebox/physics.rb +14 -3
  43. data/lib/gamebox/resource_manager.rb +28 -65
  44. data/lib/gamebox/sound_manager.rb +7 -13
  45. data/lib/gamebox/spatial_hash.rb +60 -14
  46. data/lib/gamebox/spatial_stagehand.rb +19 -0
  47. data/lib/gamebox/stage.rb +16 -1
  48. data/lib/gamebox/stage_manager.rb +1 -1
  49. data/lib/gamebox/svg_document.rb +1 -0
  50. data/lib/gamebox/tasks/gamebox_tasks.rb +23 -11
  51. data/lib/gamebox/templates/template_app/.gitignore +1 -0
  52. data/lib/gamebox/templates/template_app/Gemfile +1 -1
  53. data/lib/gamebox/templates/template_app/Rakefile +6 -21
  54. data/lib/gamebox/templates/template_app/config/environment.rb +14 -0
  55. data/lib/gamebox/templates/template_app/config/game.yml +2 -1
  56. data/lib/gamebox/templates/template_app/script/generate +0 -3
  57. data/lib/gamebox/templates/template_app/src/demo_stage.rb +1 -11
  58. data/lib/gamebox/templates/template_app/src/game.rb +0 -1
  59. data/lib/gamebox/templates/template_app/src/my_actor.rb +2 -2
  60. data/lib/gamebox/version.rb +1 -1
  61. data/lib/gamebox/views/graphical_actor_view.rb +15 -9
  62. data/lib/gamebox/wrapped_screen.rb +114 -7
  63. data/load_paths.rb +20 -0
  64. data/script/perf_spatial_hash.rb +73 -0
  65. data/spec/actor_view_spec.rb +1 -1
  66. data/spec/arbiter_spec.rb +264 -0
  67. data/spec/behavior_spec.rb +19 -2
  68. data/spec/collidable_spec.rb +105 -5
  69. data/spec/helper.rb +1 -1
  70. data/spec/label_spec.rb +1 -1
  71. data/spec/resource_manager_spec.rb +1 -1
  72. data/spec/spatial_hash_spec.rb +1 -1
  73. metadata +52 -10
  74. data/lib/gamebox/lib/surface_ext.rb +0 -76
@@ -7,17 +7,21 @@ module ClassFinder
7
7
 
8
8
  begin
9
9
  klass = Object.const_get(klass_name)
10
- rescue NameError
10
+ rescue NameError => ex
11
11
  # not there yet
12
+ log(:warn, ex)
12
13
  begin
13
14
  require "#{name}"
14
15
  rescue LoadError => ex
15
16
  # maybe its included somewhere else
17
+ log(:warn, ex)
16
18
  ensure
17
19
  begin
18
20
  klass = Object.const_get(klass_name)
19
- rescue
21
+ rescue Exception => ex
20
22
  # leave this alone.. maybe there isnt a NameView
23
+ log(:warn, ex)
24
+
21
25
  end
22
26
  end
23
27
  end
@@ -1,15 +1,14 @@
1
1
  class ConfigManager
2
2
 
3
3
  attr_accessor :settings
4
- constructor :resource_manager
5
4
  GAME_SETTINGS_FILE = "game"
6
5
 
7
- def setup
8
- @settings = @resource_manager.load_config(GAME_SETTINGS_FILE)
6
+ def initialize
7
+ @settings = load_config(GAME_SETTINGS_FILE)
9
8
  end
10
9
 
11
10
  def save
12
- @resource_manager.save_settings(GAME_SETTINGS_FILE, @settings)
11
+ save_settings(GAME_SETTINGS_FILE, @settings)
13
12
  end
14
13
 
15
14
  def [](key)
@@ -19,4 +18,25 @@ class ConfigManager
19
18
  def []=(key,val)
20
19
  @settings[key] = val
21
20
  end
21
+
22
+ # TODO make this path include that app name?
23
+ def load_config(name)
24
+ conf = YAML::load_file(CONFIG_PATH + name + ".yml")
25
+ user_file = "#{ENV['HOME']}/.gamebox/#{name}.yml"
26
+ if File.exist? user_file
27
+ user_conf = YAML::load_file user_file
28
+ conf = conf.merge user_conf
29
+ end
30
+ conf
31
+ end
32
+
33
+ def save_settings(name, settings)
34
+ user_gamebox_dir = "#{ENV['HOME']}/.gamebox"
35
+ FileUtils.mkdir_p user_gamebox_dir
36
+ user_file = "#{ENV['HOME']}/.gamebox/#{name}.yml"
37
+ File.open user_file, "w" do |f|
38
+ f.write settings.to_yaml
39
+ end
40
+ end
41
+
22
42
  end
@@ -5,6 +5,8 @@ game:
5
5
  - sound_manager
6
6
  - stage_manager
7
7
  resource_manager:
8
+ compose:
9
+ - wrapped_screen
8
10
  stage_manager:
9
11
  compose:
10
12
  - input_manager
@@ -18,13 +20,13 @@ sound_manager:
18
20
  - config_manager
19
21
  input_manager:
20
22
  compose:
23
+ - wrapped_screen
21
24
  - config_manager
22
25
  wrapped_screen:
23
26
  compose:
24
27
  - config_manager
28
+ config_manager:
25
29
  actor_factory:
26
30
  compose:
27
31
  - input_manager
28
- config_manager:
29
- compose:
30
- - resource_manager
32
+ - wrapped_screen
@@ -0,0 +1,372 @@
1
+ # Ftor ("Fake vecTOR"), a vector-like class for 2D position/movement.
2
+ #--
3
+ # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
4
+ # Copyright (C) 2004-2007 John Croisant
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License as published by the Free Software Foundation; either
9
+ # version 2.1 of the License, or (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with this library; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+ #++
20
+
21
+
22
+ # *NOTE*: Ftor is DEPRECATED and will be removed in Rubygame 3.0!
23
+ # A mostly-compatible vector class will be provided at or before
24
+ # that time.
25
+ #
26
+ # *NOTE*: you must require 'rubygame/ftor' manually to gain access to
27
+ # Rubygame::Ftor. It is not imported with Rubygame by default!
28
+ #
29
+ # Ftor ("Fake vecTOR"), a vector-like class for 2D position/movement.
30
+ #
31
+ # (NB: See #angle for an important note about why angles appear to be the
32
+ # opposite of what you may expect.)
33
+ #
34
+ # Ftor is useful for storing 2D coordinates (x,y) as well as
35
+ # vector quantities such as velocity and acceleration (representationally,
36
+ # points and vectors are equivalent.) Although Ftors are always represented
37
+ # internally as Cartesian coordinates (x, y), it is possible to deal with an
38
+ # Ftor as polar coordinates (#angle, #magnitude) instead.
39
+ # See #new_am and #set_am!, for example.
40
+ #
41
+ # Ftor is a "fake" vector because it has certain convenient properties which
42
+ # differ from "true" vectors (i.e. vectors in a strict mathematical sense).
43
+ #
44
+ # Unlike vectors, Ftors may be multiplied or divided to another Ftor. This is
45
+ # equivalent to multiplying or dividing each component by the corresponding
46
+ # component in the second Ftor. If you like, you can think of this feature as
47
+ # scaling each component of the Ftor by a separate factor:
48
+ #
49
+ # Ftor(a,b) * Ftor(c,d) = Ftor(a*c, b*d)
50
+ #
51
+ # Of course, Ftors also have the usual vector behavior for addition/subraction
52
+ # between two Ftors, and multiplication/division of an Ftor by a scalar:
53
+ #
54
+ # Ftor(a,b) + Ftor(c,d) = Ftor(a+c, b+d)
55
+ # Ftor(a,b) * n = Ftor(a*n, b*n)
56
+ #
57
+ # Additionally, Ftor contains functions for manipulating itself.
58
+ # You can both get and set such properties as #angle, #magnitude, #unit,
59
+ # and #normal, and the Ftor will change in-place as needed. For example,
60
+ # if you set #angle=, the vector will change to have the new angle,
61
+ # but keeps the same magnitude as before.
62
+ #
63
+ # Ftor attempts to save processing time (at the expense of memory) by
64
+ # storing secondary properties (angle, magnitude, etc.) whenever they are
65
+ # calculated,so that they need not be calculated repeatedly. If the vector
66
+ # changes, the properties will be calculated again the next time they
67
+ # are needed.
68
+ # (In future versions, it may be possible to disable this feature for
69
+ # certain Ftors, for example if they will change very often, to save memory.)
70
+ #
71
+ class Ftor
72
+ PI = Math::PI
73
+ HALF_PI = PI*0.5
74
+ THREE_HALF_PI = PI*1.5
75
+ TWO_PI = PI*2
76
+
77
+ # Create a new Ftor by specifying its x and y components. See also #new_am
78
+ # and #new_from_to.
79
+ def initialize(x,y)
80
+ @x, @y = x, y
81
+ end
82
+
83
+ # Create a new Ftor by specifying its #angle (in radians) and #magnitude.
84
+ # See also #new.
85
+ def self.new_am(a,m)
86
+ v = self.new(1,0)
87
+ v.a, v.m = a, m
88
+ return v
89
+ end
90
+
91
+ # Returns a new Ftor which represents the difference in position of two
92
+ # points +p1+ and +p2+. (+p1+ and +p2+ can be Ftors, size-2 Arrays, or
93
+ # anything else which has two numerical components and responds to #[].)
94
+ #
95
+ # In other words, assuming +v+ is the Ftor returned by this function:
96
+ # p1 + v = p2
97
+ def self.new_from_to(p1,p2)
98
+ return self.new(p2[0]-p1[0],p2[1]-p1[1])
99
+ end
100
+
101
+ attr_reader :x # The x component of the Ftor.
102
+ # Set the x component of the Ftor.
103
+ def x=(value)
104
+ @x = value
105
+ _clear()
106
+ end
107
+
108
+ attr_reader :y # The y component of the Ftor.
109
+ # Set the y component of the Ftor.
110
+ def y=(value)
111
+ @y = value
112
+ _clear()
113
+ end
114
+
115
+ # Modify the x and y components of the Ftor in-place
116
+ def set!(x,y)
117
+ @x, @y = x,y
118
+ _clear()
119
+ end
120
+
121
+ # Modify the #angle (in radians) and #magnitude of the Ftor in-place
122
+ def set_am!(a,m)
123
+ self.angle, self.magnitude = a, m
124
+ end
125
+
126
+ # Same as #to_s, but this Ftor's #object_id is also displayed.
127
+ def inspect
128
+ "#<#{self.class}:#{object_id}: %f, %f>"%[@x,@y]
129
+ end
130
+
131
+ # Display this Ftor in the format: "#<Ftor: [x, y]>". x and y are displayed
132
+ # as floats at full precision. See also #pp.
133
+ def to_s
134
+ "#<#{self.class}: [%f, %f]>"%[@x,@y]
135
+ end
136
+
137
+ # "Pretty print". Same as #to_s, but components are displayed as rounded
138
+ # floats to 3 decimal places, for easy viewing.
139
+ def pretty
140
+ "#<#{self.class}: [%0.3f, %0.3f]>"%[@x,@y]
141
+ end
142
+
143
+ # Same as #to_s_am, but this Ftor's #object_id is also displayed.
144
+ def inspect_am
145
+ "#<#{self.class}:AM:#{object_id}: %f, %f>"%[angle(),magnitude()]
146
+ end
147
+
148
+ # Display this Ftor in the format: "#<Ftor:AM: [angle, magnitude]>".
149
+ # angle and magnitude are displayed as floats at full precision.
150
+ # See also #to_s and #pp_am.
151
+ def to_s_am
152
+ "#<#{self.class}:AM: [%f, %f]>"%[angle(),magnitude()]
153
+ end
154
+
155
+ # "Pretty print" with angle and magnitude.
156
+ # Same as #to_s_am, but components are displayed as rounded floats to 3
157
+ # decimal places, for easy viewing.
158
+ def pretty_am
159
+ "#<#{self.class}:AM: [%0.3f, %0.3f]>"%[angle(),magnitude()]
160
+ end
161
+
162
+ # Returns an Array of this Ftor's components, [x,y].
163
+ def to_a
164
+ [@x,@y]
165
+ end
166
+ alias :to_ary :to_a
167
+
168
+ # Return the +i+th component of this Ftor, as if it were the Array
169
+ # returned by #to_a.
170
+ def [](i)
171
+ [@x,@y][i]
172
+ end
173
+
174
+ # True if this Ftor is equal to +other+, when both have been converted to
175
+ # Arrays via #to_a. In other words, a component-by-component equality check.
176
+ def ==(other)
177
+ to_a() == other.to_a
178
+ end
179
+
180
+ # The reverse of this Ftor. I.e., all components are negated. See also
181
+ # #reverse!.
182
+ def -@
183
+ self.class.new(-@x,-@y)
184
+ end
185
+
186
+ # Like #-@, but reverses this Ftor in-place.
187
+ def reverse!
188
+ set!(-@x,-@y)
189
+ end
190
+
191
+ # Perform vector addition with this Ftor and +other+, adding them on a
192
+ # component-by-component basis, like so:
193
+ # Ftor(a,b) + Ftor(c,d) = Ftor(a+c, b+d)
194
+ def +(other)
195
+ return self.class.new(@x+other[0],@y+other[1])
196
+ end
197
+
198
+ # Like #+, but performs subtraction instead of addition.
199
+ def -(other)
200
+ return self.class.new(@x-other[0],@y-other[1])
201
+ end
202
+
203
+ # Perform multiplication of this Ftor by the scalar +other+, like so:
204
+ # Ftor(a,b) * n = Ftor(a*n, b*n)
205
+ #
206
+ # However, if this causes TypeError, attempt to extract indices 0 and 1
207
+ # with +other+'s #[] operator, and multiply them into the corresponding
208
+ # components of this Ftor, like so:
209
+ # Ftor(a,b) * Ftor(c,d) = Ftor(a*c, b*d)
210
+ # Ftor(a,b) * [c,d] = Ftor(a*c, b*d)
211
+ def *(other)
212
+ return self.class.new(@x*other,@y*other)
213
+ rescue TypeError
214
+ return self.class.new(@x*other[0],@y*other[1])
215
+ end
216
+
217
+ # Like #*, but performs division instead of multiplication.
218
+ def /(other)
219
+ x, y = @x.to_f, @y.to_f
220
+ return self.class.new(x/other,y/other)
221
+ rescue TypeError
222
+ return self.class.new(x/other[0],y/other[1])
223
+ end
224
+
225
+ # Return the angle (radians) this Ftor forms with the positive X axis.
226
+ # This is the same as the Ftor's angle in a polar coordinate system.
227
+ #
228
+ # *IMPORTANT*: Because the positive Y axis on the Rubygame::Screen points
229
+ # *downwards*, an angle in the range 0..PI will appear to point *downwards*,
230
+ # rather than upwards!
231
+ # This also means that positive rotation will appear *clockwise*, and
232
+ # negative rotation will appear *counterclockwise*!
233
+ # This is the opposite of what you would expect in geometry class!
234
+ def angle
235
+ @angle or @angle = Math.atan2(@y,@x)
236
+ end
237
+
238
+ # Set the angle (radians) of this Ftor from the positive X axis.
239
+ # Magnitude is preserved.
240
+ def angle=(a)
241
+ m = magnitude()
242
+ set!( Math.cos(a)*m, Math.sin(a)*m )
243
+ end
244
+
245
+ alias :a :angle
246
+ alias :a= :angle= ;
247
+
248
+ # Returns the magnitude of the Ftor, i.e. its length from tail to head.
249
+ # This is the same as the Ftor's magnitude in a polar coordinate system.
250
+ def magnitude
251
+ @magnitude or @magnitude = Math.hypot(@x,@y)
252
+ end
253
+
254
+ # Modifies the #magnitude of the Ftor, preserving its #angle.
255
+ #
256
+ # In other words, the Ftor will point in the same direction, but it will
257
+ # be a different length from tail to head.
258
+ def magnitude=(m)
259
+ new = unit() * m
260
+ set!(new.x, new.y)
261
+ end
262
+
263
+ alias :m :magnitude
264
+ alias :m= :magnitude= ;
265
+
266
+ # Return a new unit Ftor which is perpendicular to this Ftor (rotated by
267
+ # pi/2 radians, to be specific).
268
+ def normal
269
+ @normal or @normal = unit().rotate(HALF_PI)
270
+ end
271
+
272
+ # Rotate this Ftor in-place, so that it is perpendicular to +other+.
273
+ # This Ftor will be at an angle of -pi/2 to +other+.
274
+ def normal=(other)
275
+ set!( *(self.class.new(*other).unit().rotate(-HALF_PI) * magnitude()) )
276
+ end
277
+
278
+ alias :n :normal
279
+ alias :n= :normal= ;
280
+
281
+ # Return the unit vector of the Ftor, i.e. an Ftor with the same direction,
282
+ # but a #magnitude of 1. (This is sometimes called a "normalized" vector,
283
+ # not to be confused with a vector's #normal.)
284
+ def unit
285
+ m = magnitude().to_f
286
+ @unit or @unit = Ftor.new(@x/m, @y/m)
287
+ end
288
+
289
+ # Rotates this Ftor in-place, so that its #unit vector matches the unit
290
+ # vector of the given Ftor.
291
+ #
292
+ # In other words, changes the #angle of this Ftor to be the same as the angle
293
+ # of the given Ftor, but this Ftor's #magnitude does not change.
294
+ #--
295
+ # TODO: investigate efficiency of using `self.angle = other.angle` instead
296
+ #++
297
+ def unit=(other)
298
+ set!( *(self.class.new(*other).unit() * magnitude()) )
299
+ end
300
+
301
+ alias :u :unit
302
+ alias :u= :unit=
303
+ alias :align! :unit=;
304
+
305
+ # Return the dot product (aka inner product) of this Ftor and +other+.
306
+ # The dot product of two vectors +v1+ and +v2+ is:
307
+ # v1.x * v2.x + v1.y * v2.y
308
+ def dot(other)
309
+ @x*other[0] + @y*other[1]
310
+ end
311
+
312
+ # Return the #dot product of #unit vectors of this Ftor and +other+.
313
+ def udot(other)
314
+ unit().dot(self.class.new(*other).unit)
315
+ end
316
+
317
+ #Return the difference in angles (radians) between this Ftor and +other+.
318
+ def angle_with(other)
319
+ Math.acos( self.udot(other) )
320
+ end
321
+
322
+ # Rotate this Ftor in-place by +angle+ (radians). This is the same as
323
+ # adding +angle+ to this Ftor's #angle.
324
+ #
325
+ #--
326
+ # , with one important difference:
327
+ # This method will be much more efficient when rotating at a right angle,
328
+ # i.e.rotating by any multiple of PI/2 radians from -2*PI to 2*PI radians.
329
+ #
330
+ # For convenience, and to ensure exactitude, several numerical constants
331
+ # have been defined for multiples of PI/2:
332
+ # * Ftor::PI:: (same as Math::PI)
333
+ # * Ftor::HALF_PI:: PI * 0.5 (or PI/2)
334
+ # * Ftor::THREE_HALF_PI:: PI * 1.5 (or 3*PI/2)
335
+ # * Ftor::TWO_PI:: PI * 2
336
+ #++
337
+ #
338
+ # *IMPORTANT*: Positive rotation will appear *clockwise*, and negative
339
+ # rotation will appear *counterclockwise*! See #angle for the reason.
340
+ def rotate!(angle)
341
+ # case(angle)
342
+ # when HALF_PI, -THREE_HALF_PI
343
+ # self.set!(@y,-@x)
344
+ # when THREE_HALF_PI, -HALF_PI
345
+ # self.set!(-@y,@x)
346
+ # when PI, -PI
347
+ # self.set!(@y,-@x)
348
+ # when 0, TWO_PI, -TWO_PI
349
+ # self.set!(@y,-@x)
350
+ # else
351
+ self.a += angle
352
+ # end
353
+ return self
354
+ end
355
+
356
+ # Like #rotate!, but returns a duplicate instead of rotating this Ftor
357
+ # in-place.
358
+ def rotate(radians)
359
+ self.dup.rotate!(radians)
360
+ end
361
+
362
+ # Clears stored values for #angle, #magnitude, #normal, and #unit,
363
+ # so that they will be recalculated the next time they are needed.
364
+ # Intended for internal use, but might be useful in other situations.
365
+ def _clear
366
+ @angle = nil
367
+ @magnitude = nil
368
+ @normal = nil
369
+ @unit = nil
370
+ return self
371
+ end
372
+ end