gamebox 0.4.0.rc5 → 0.4.0.rc11

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 (93) hide show
  1. data/README.md +205 -127
  2. data/bin/gamebox +49 -3
  3. data/bin/gb +87 -0
  4. data/gamebox.gemspec +4 -3
  5. data/lib/gamebox.rb +1 -1
  6. data/lib/gamebox/actors/collidable_debugger.rb +4 -4
  7. data/lib/gamebox/actors/icon.rb +7 -0
  8. data/lib/gamebox/actors/label.rb +41 -42
  9. data/lib/gamebox/behaviors/animated.rb +6 -0
  10. data/lib/gamebox/behaviors/audible.rb +1 -2
  11. data/lib/gamebox/behaviors/collidable.rb +1 -1
  12. data/lib/gamebox/behaviors/graphical.rb +8 -4
  13. data/lib/gamebox/behaviors/physical.rb +6 -1
  14. data/lib/gamebox/behaviors/positioned.rb +4 -11
  15. data/lib/gamebox/behaviors/projectile.rb +8 -0
  16. data/lib/gamebox/behaviors/visible.rb +3 -3
  17. data/lib/gamebox/core/aabb_tree.rb +1 -1
  18. data/lib/gamebox/core/actor.rb +37 -50
  19. data/lib/gamebox/core/actor_definition.rb +41 -0
  20. data/lib/gamebox/core/actor_view.rb +6 -21
  21. data/lib/gamebox/core/actor_view_definition.rb +19 -0
  22. data/lib/gamebox/core/actor_view_factory.rb +9 -3
  23. data/lib/gamebox/core/behavior.rb +8 -27
  24. data/lib/gamebox/core/behavior_definition.rb +24 -0
  25. data/lib/gamebox/core/config_manager.rb +45 -30
  26. data/lib/gamebox/core/configuration.rb +5 -0
  27. data/lib/gamebox/core/core.rb +4 -0
  28. data/lib/gamebox/core/debug_helpers.rb +46 -0
  29. data/lib/gamebox/core/director.rb +32 -5
  30. data/lib/gamebox/core/event_symbols.rb +214 -0
  31. data/lib/gamebox/core/game.rb +1 -1
  32. data/lib/gamebox/core/input_manager.rb +1 -4
  33. data/lib/gamebox/core/input_mapper.rb +85 -0
  34. data/lib/gamebox/core/physics.rb +7 -3
  35. data/lib/gamebox/core/physics_manager.rb +5 -1
  36. data/lib/gamebox/core/renderer.rb +72 -0
  37. data/lib/gamebox/core/stage.rb +25 -81
  38. data/lib/gamebox/core/stage_definition.rb +60 -0
  39. data/lib/gamebox/core/stage_factory.rb +56 -0
  40. data/lib/gamebox/core/stage_manager.rb +5 -11
  41. data/lib/gamebox/core/timer_manager.rb +6 -2
  42. data/lib/gamebox/core/viewport.rb +12 -5
  43. data/lib/gamebox/core/wrapped_screen.rb +8 -5
  44. data/lib/gamebox/gamebox_application.rb +21 -19
  45. data/lib/gamebox/lib/array_ext.rb +9 -0
  46. data/lib/gamebox/lib/observable_attributes.rb +24 -0
  47. data/lib/gamebox/lib/vector2.rb +432 -0
  48. data/lib/gamebox/post_setup_handlers/file_watcher.rb +37 -0
  49. data/lib/gamebox/post_setup_handlers/gamebox_debug_helpers.rb +13 -0
  50. data/lib/gamebox/post_setup_handlers/pry_remote_server.rb +29 -0
  51. data/lib/gamebox/spec/helper.rb +165 -17
  52. data/lib/gamebox/tasks/gamebox_tasks.rake +27 -12
  53. data/lib/gamebox/version.rb +1 -1
  54. data/lib/gamebox/views/graphical_actor_view.rb +4 -5
  55. data/script/perf_aabb.rb +13 -8
  56. data/spec/acceptance/animation_spec.rb +1 -3
  57. data/spec/acceptance/basic_actor_lifecycle_spec.rb +1 -1
  58. data/spec/acceptance/fps_actor_spec.rb +8 -12
  59. data/spec/acceptance/input_mapper_spec.rb +17 -24
  60. data/spec/acceptance/update_ordering_spec.rb +64 -0
  61. data/spec/actors/label_spec.rb +90 -5
  62. data/spec/behaviors/animated_spec.rb +1 -1
  63. data/spec/behaviors/collidable_spec.rb +7 -15
  64. data/spec/behaviors/positioned_spec.rb +12 -5
  65. data/spec/core/actor_spec.rb +31 -3
  66. data/spec/core/actor_view_spec.rb +1 -1
  67. data/spec/core/behavior_spec.rb +3 -0
  68. data/spec/core/configuration_spec.rb +49 -2
  69. data/spec/core/input_mapper_spec.rb +7 -0
  70. data/spec/core/renderer_spec.rb +89 -0
  71. data/spec/core/stage_definition_spec.rb +41 -0
  72. data/spec/core/stage_manager_spec.rb +11 -11
  73. data/spec/core/stage_spec.rb +38 -78
  74. data/spec/core/viewport_spec.rb +5 -2
  75. data/spec/core/wrapped_screen_spec.rb +18 -12
  76. data/spec/views/graphical_actor_view_spec.rb +33 -62
  77. data/templates/actor_template.erb +11 -0
  78. data/templates/app/README.md +1 -0
  79. data/templates/app/src/actors/{player.rb → player_actor.rb} +3 -1
  80. data/templates/app/src/behaviors/.gitkeep +0 -0
  81. data/templates/app/src/stages/demo_stage.rb +14 -0
  82. data/templates/behavior_template.erb +13 -0
  83. data/templates/stage_template.erb +13 -0
  84. metadata +60 -21
  85. data/component_generators/actor_generator.rb +0 -17
  86. data/lib/gamebox/actors/emitter.rb +0 -12
  87. data/lib/gamebox/behaviors/emitting.rb +0 -48
  88. data/lib/gamebox/behaviors/input_mapper.rb +0 -11
  89. data/lib/gamebox/lib/ftor.rb +0 -372
  90. data/spec/actors/emitter_spec.rb +0 -5
  91. data/templates/app/NEXT_STEPS.txt +0 -1
  92. data/templates/app/README.rdoc +0 -24
  93. data/templates/app/src/demo_stage.rb +0 -7
@@ -1,7 +1,7 @@
1
1
  class StageManager
2
2
 
3
- construct_with :input_manager, :config_manager, :backstage,
4
- :this_object_context
3
+ construct_with :input_manager, :config_manager,
4
+ :stage_factory, :this_object_context
5
5
 
6
6
  attr_reader :stage_names, :stage_opts
7
7
 
@@ -78,13 +78,13 @@ class StageManager
78
78
  @stage = stage_name
79
79
  @stage_args = args
80
80
  @stages[@stage] = create_stage(@stage, @stage_opts[@stage_names.index(@stage)])
81
- @stages[@stage].curtain_raising *args
81
+ @stages[@stage].curtain_up *args
82
82
  end
83
83
 
84
84
  def shutdown_current_stage(*args)
85
85
  if @stage and @stages and @stages[@stage]
86
86
  current_stage = @stages[@stage]
87
- current_stage.curtain_dropping *args
87
+ current_stage.curtain_down *args
88
88
  input_manager.clear_hooks(current_stage)
89
89
  @stages.delete @stage
90
90
  @stage = nil
@@ -93,13 +93,7 @@ class StageManager
93
93
  end
94
94
 
95
95
  def create_stage(name, opts)
96
- stage_instance = nil
97
- this_object_context.in_subcontext do |stage_context|
98
- name_or_klass = opts[:class] || name
99
- stage_instance = stage_context["#{name_or_klass}_stage"]
100
- stage_context[:stage] = stage_instance
101
- end
102
- stage_instance.configure(backstage, opts)
96
+ stage_instance = stage_factory.build(name, opts)
103
97
 
104
98
  stage_instance.when :next_stage do |*args|
105
99
  next_stage *args
@@ -3,6 +3,7 @@ class TimerManager
3
3
  def initialize
4
4
  @timers ||= {}
5
5
  @dead_timers = []
6
+ @callbacks = []
6
7
  end
7
8
 
8
9
  # add block to be executed every interval_ms millis
@@ -23,18 +24,21 @@ class TimerManager
23
24
 
24
25
  # update each timers counts, call any blocks that are over their interval
25
26
  def update(time_delta)
26
- # TODO handle overwriting the same timer name...
27
+ @callbacks.clear
28
+ @dead_timers.clear
29
+
27
30
  @timers.each do |name, timer_hash|
28
31
  timer_hash[:count] += time_delta
29
32
  if timer_hash[:count] > timer_hash[:interval_ms]
30
33
  timer_hash[:count] -= timer_hash[:interval_ms]
31
- timer_hash[:callback].call
34
+ @callbacks << timer_hash[:callback]
32
35
  @dead_timers << name unless timer_hash[:recurring]
33
36
  end
34
37
  end
35
38
  @dead_timers.each do |name|
36
39
  remove_timer name
37
40
  end
41
+ @callbacks.each &:call
38
42
  end
39
43
 
40
44
  def pause
@@ -3,9 +3,11 @@
3
3
  class Viewport
4
4
  extend Publisher
5
5
  can_fire :scrolled
6
+
7
+ construct_with :config_manager
6
8
 
7
9
  attr_accessor :x_offset, :y_offset, :follow_target, :width,
8
- :height, :x_offset_range, :y_offset_range, :boundary
10
+ :height, :x_offset_range, :y_offset_range, :boundary, :rotation
9
11
 
10
12
  attr_reader :speed
11
13
 
@@ -13,13 +15,18 @@ class Viewport
13
15
  "xoff:#{@x_offset} yoff:#{@y_offset}"
14
16
  end
15
17
 
16
- def initialize(width, height)
18
+ def initialize
19
+ res = config_manager[:screen_resolution]
20
+ @width = res[0]
21
+ @height = res[1]
22
+ reset
23
+ end
24
+
25
+ def reset
26
+ @rotation = 0
17
27
  @speed = 1
18
28
  @x_offset = 0
19
29
  @y_offset = 0
20
-
21
- @width = width
22
- @height = height
23
30
  end
24
31
 
25
32
  def scroll(x_delta,y_delta)
@@ -4,16 +4,15 @@ class WrappedScreen
4
4
  def initialize
5
5
  width, height = *config_manager[:screen_resolution]
6
6
  fullscreen = config_manager[:fullscreen]
7
- needs_cursor = config_manager[:needs_cursor]
8
7
  @screen = HookedGosuWindow.new width, height, fullscreen
9
8
  @screen.tap do |screen|
10
- screen.caption = config_manager[:title]
11
- screen.needs_cursor = config_manager[:needs_cursor]
9
+ screen.caption = Gamebox.configuration.game_name
10
+ screen.needs_cursor = Gamebox.configuration.needs_cursor?
12
11
  end
13
12
  end
14
13
 
15
- def method_missing(name,*args)
16
- @screen.send name, *args
14
+ def method_missing(name,*args, &blk)
15
+ @screen.send name, *args, &blk
17
16
  end
18
17
 
19
18
  def width
@@ -129,5 +128,9 @@ class WrappedScreen
129
128
  def draw_image(image, x, y, z, x_scale = 1, y_scale = 1, color = 0xffffffff, mode = :default)
130
129
  image.draw x, y, z, x_scale, y_scale, color, mode
131
130
  end
131
+
132
+ def draw_rotated_image(image, x, y, z, angle, center_x = 0.5, center_y = 0.5, x_scale = 1, y_scale = 1, color = 0xffffffff, mode = :default)
133
+ image.draw_rot x, y, z, angle, center_x, center_y, x_scale, y_scale, color, mode
134
+ end
132
135
  end
133
136
 
@@ -3,12 +3,13 @@ $: << "#{File.dirname(__FILE__)}/../config"
3
3
 
4
4
  begin
5
5
  # optional file
6
- require "environment"
6
+ require "environment"
7
7
  rescue LoadError => err
8
8
  end
9
9
 
10
10
  class GameboxApp
11
11
  attr_reader :context, :game
12
+
12
13
  def self.run(argv,env)
13
14
  GameboxApp.new.start argv, env
14
15
  end
@@ -17,19 +18,16 @@ class GameboxApp
17
18
  @context = Conject.default_object_context
18
19
  end
19
20
 
20
- def setup
21
- @game = @context[:game]
22
- @game.configure
23
- @config_manager = @context[:config_manager]
24
- setup_debug_server if @config_manager[:debug_server] || ARGV.include?("-debug-server")
21
+ def start(argv,env)
22
+ setup(argv,env)
23
+ main_loop
24
+ shutdown
25
25
  end
26
26
 
27
- def setup_debug_server
28
- Thread.new do
29
- loop do
30
- binding.remote_pry
31
- end
32
- end
27
+ def setup(argv,env)
28
+ @game = @context[:game]
29
+ @game.configure
30
+ self.class.post_setup_handlers.each { |handler| handler.setup(argv, env, @context[:config_manager]) }
33
31
  end
34
32
 
35
33
  def main_loop
@@ -38,18 +36,22 @@ class GameboxApp
38
36
  @input_manager.show
39
37
  end
40
38
 
41
- def shutdown
42
- end
43
-
44
- def start(argv,env)
45
- setup
39
+ def shutdown ; end
46
40
 
47
- main_loop
41
+ def self.register_post_setup_handler(handler)
42
+ post_setup_handlers.push handler
43
+ end
48
44
 
49
- shutdown
45
+ def self.post_setup_handlers
46
+ @post_setup_handlers ||= [ ]
50
47
  end
48
+
51
49
  end
52
50
 
51
+ GameboxApp.register_post_setup_handler PostSetupHandlers::FileWatcher
52
+ GameboxApp.register_post_setup_handler PostSetupHandlers::GameboxAppAddDebugHelpers
53
+ GameboxApp.register_post_setup_handler PostSetupHandlers::PryRemoteServer
54
+
53
55
  if $0 == __FILE__
54
56
  GameboxApp.run ARGV, ENV
55
57
  end
@@ -0,0 +1,9 @@
1
+ class Array
2
+ def self.wrap(thing)
3
+ if thing.is_a? Array
4
+ thing
5
+ else
6
+ [thing]
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,29 @@
1
1
  module ObservableAttributes
2
2
 
3
+ # atomically update all attributes
4
+ # it updates all of the values; then triggers the events
5
+ # my_actor.update_attributes(x: 5, y: 7)
6
+ # will emit both x_changed and y_changed after setting _both_ values
7
+ def update_attributes(attributes)
8
+ # TODO put this in kvo gem; there is too much internal knowlege of where
9
+ # kvo stores stuff
10
+ #
11
+ # self.kvo_set('x', new_val, silent: true)
12
+ # self.changed('x', old_val, new_val)
13
+ old_values = attributes.keys.inject({}) do |hash, attr_name|
14
+ hash[attr_name] = self.send(attr_name)
15
+ hash
16
+ end
17
+
18
+ attributes.each do |name, val|
19
+ self.instance_variable_set("@kvo_#{name}", val)
20
+ end
21
+
22
+ attributes.each do |name, val|
23
+ fire "#{name}_changed".to_sym, old_values[name], val
24
+ end
25
+ end
26
+
3
27
  def has_attributes(*names)
4
28
  if names.first.is_a? Hash
5
29
  names.first.each do |name, default|
@@ -0,0 +1,432 @@
1
+ #++
2
+ # Copyright (C) 2008-2010 John Croisant
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ # of the Software, and to permit persons to whom the Software is furnished to do
9
+ # so, subject to the following conditions: #
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software. #
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+
22
+ # The Vector2 class implements two-dimensional vectors.
23
+ # It is used to represent positions, movements, and velocities
24
+ # in 2D space.
25
+ #
26
+ class Vector2
27
+ include Enumerable
28
+
29
+ RAD_TO_DEG = 180.0 / Math::PI
30
+ DEG_TO_RAD = Math::PI / 180.0
31
+
32
+ class << self
33
+
34
+ alias :[] :new
35
+
36
+
37
+ # Creates a new Vector2 from an angle in radians and a
38
+ # magnitude. Use #new_polar_deg for degrees.
39
+ #
40
+ def new_polar( angle_rad, magnitude )
41
+ self.new( Math::cos(angle_rad)*magnitude,
42
+ Math::sin(angle_rad)*magnitude )
43
+ end
44
+
45
+
46
+ # Creates a new Vector2 from an angle in degrees and a
47
+ # magnitude. Use #new_polar for radians.
48
+ #
49
+ def new_polar_deg( angle_deg, magnitude )
50
+ self.new_polar( angle_deg * DEG_TO_RAD, magnitude )
51
+ end
52
+
53
+
54
+ # call-seq:
55
+ # Vector2.many( [x1,y1], [x2,y2], ... )
56
+ #
57
+ # Converts multiple [x,y] Arrays to Vector2s.
58
+ # Returns the resulting vectors in an Array.
59
+ #
60
+ def many( *pairs )
61
+ pairs.collect { |pair| self.new(*pair) }
62
+ end
63
+
64
+ end
65
+
66
+
67
+ # Creates a new Vector2 with the given x and y values.
68
+ def initialize( x, y )
69
+ @x, @y = x.to_f, y.to_f
70
+ end
71
+
72
+ attr_reader :x, :y
73
+
74
+ def x=( value )
75
+ raise "can't modify frozen object" if frozen?
76
+ @x = value.to_f
77
+ @hash = nil
78
+ self
79
+ end
80
+
81
+ def y=( value )
82
+ raise "can't modify frozen object" if frozen?
83
+ @y = value.to_f
84
+ @hash = nil
85
+ self
86
+ end
87
+
88
+
89
+ # Sets this vector's x and y components.
90
+ def set!( x, y )
91
+ raise "can't modify frozen object" if frozen?
92
+ @x = x.to_f
93
+ @y = y.to_f
94
+ @hash = nil
95
+ self
96
+ end
97
+
98
+
99
+ # Sets this vector's angle (in radians) and magnitude.
100
+ # Use #set_polar_deg! for degrees.
101
+ #
102
+ def set_polar!( angle_rad, mag )
103
+ raise "can't modify frozen object" if frozen?
104
+ @x = Math::cos(angle_rad) * mag
105
+ @y = Math::sin(angle_rad) * mag
106
+ @hash = nil
107
+ self
108
+ end
109
+
110
+
111
+ # Sets this vector's angle (in degrees) and magnitude.
112
+ # Use #set_polar! for radians.
113
+ #
114
+ def set_polar_deg!( angle_deg, mag )
115
+ set_polar!( angle_deg * DEG_TO_RAD, mag )
116
+ self
117
+ end
118
+
119
+
120
+ # Adds the given vector to this one and return the
121
+ # resulting vector.
122
+ #
123
+ def +( vector )
124
+ self.class.new( @x + vector.at(0), @y + vector.at(1) )
125
+ end
126
+
127
+ # call-seq:
128
+ # move!( [x,y] )
129
+ # move!( x,y )
130
+ #
131
+ # Moves the vector by the given x and y amounts.
132
+ def move!( x, y=nil )
133
+ raise "can't modify frozen object" if frozen?
134
+ if y.nil?
135
+ a = x.to_ary
136
+ @x += a[0]
137
+ @y += a[1]
138
+ else
139
+ @x += x
140
+ @y += y
141
+ end
142
+ @hash = nil
143
+ self
144
+ end
145
+
146
+ # call-seq:
147
+ # move( [x,y] )
148
+ # move( x,y )
149
+ #
150
+ # Like #move!, but returns a new vector.
151
+ def move( x, y=nil )
152
+ self.dup.move!(x, y)
153
+ end
154
+
155
+
156
+ # Subtracts the given vector from this one and return
157
+ # the resulting vector.
158
+ #
159
+ def -( vector )
160
+ self.class.new( @x - vector.at(0), @y - vector.at(1) )
161
+ end
162
+
163
+
164
+ # Returns the opposite of this vector, i.e. Vector2[-x, -y].
165
+ def -@
166
+ self.class.new( -@x, -@y )
167
+ end
168
+
169
+ # Reverses the vector's direction, i.e. Vector2[-x, -y].
170
+ def reverse!
171
+ raise "can't modify frozen object" if frozen?
172
+ @x, @y = -@x, -@y
173
+ @hash = nil
174
+ self
175
+ end
176
+
177
+ # Like #reverse!, but returns a new vector.
178
+ def reverse
179
+ self.dup.reverse!
180
+ end
181
+
182
+
183
+ # Multiplies this vector by the given scalar (Numeric),
184
+ # and return the resulting vector.
185
+ #
186
+ def *( scalar )
187
+ self.class.new( @x * scalar, @y * scalar )
188
+ end
189
+
190
+
191
+ # True if the given vector's x and y components are
192
+ # equal to this vector's components (within a small margin
193
+ # of error to compensate for floating point imprecision).
194
+ #
195
+ def ==( vector )
196
+ _nearly_equal?(@x, vector.at(0)) and _nearly_equal?(@y, vector.at(1))
197
+ end
198
+
199
+
200
+ # Returns a component of this vector as if it were an
201
+ # [x,y] Array.
202
+ #
203
+ def []( index )
204
+ [@x, @y][index]
205
+ end
206
+
207
+ alias :at :[]
208
+
209
+
210
+ def hash # :nodoc:
211
+ @hash ||= (((@x - @x.modulo(1E-10)).hash << 2) +
212
+ ((@y - @y.modulo(1E-10)).hash << 1) +
213
+ self.class.hash)
214
+ end
215
+
216
+
217
+ # Iterates over this vector as if it were an [x,y] Array.
218
+ #
219
+ def each( &block )
220
+ [@x, @y].each( &block )
221
+ end
222
+
223
+
224
+ # Returns the angle of this vector, relative to the positive
225
+ # X axis, in radians. Use #angle_deg for degrees.
226
+ #
227
+ def angle
228
+ Math.atan2( @y, @x )
229
+ end
230
+
231
+
232
+ # Sets the vector's angle in radians. The vector keeps the same
233
+ # magnitude as before.
234
+ #
235
+ def angle=( angle_rad )
236
+ raise "can't modify frozen object" if frozen?
237
+ m = magnitude
238
+ @x = Math::cos(angle_rad) * m
239
+ @y = Math::sin(angle_rad) * m
240
+ @hash = nil
241
+ self
242
+ end
243
+
244
+
245
+ # Returns the angle of this vector relative to the other vector,
246
+ # in radians. Use #angle_deg_with for degrees.
247
+ #
248
+ def angle_with( vector )
249
+ Math.acos( udot(vector) )
250
+ end
251
+
252
+
253
+ # Returns the angle of this vector, relative to the positive
254
+ # X axis, in degrees. Use #angle for radians.
255
+ #
256
+ def angle_deg
257
+ angle * RAD_TO_DEG
258
+ end
259
+
260
+
261
+ # Sets the vector's angle in degrees. The vector keeps the same
262
+ # magnitude as before.
263
+ #
264
+ def angle_deg=( angle_deg )
265
+ self.angle = angle_deg * DEG_TO_RAD
266
+ self
267
+ end
268
+
269
+
270
+ # Returns the angle of this vector relative to the other vector,
271
+ # in degrees. Use #angle_with for radians.
272
+ #
273
+ def angle_deg_with( vector )
274
+ angle_with(vector) * RAD_TO_DEG
275
+ end
276
+
277
+
278
+ # Returns the dot product between this vector and the other vector.
279
+ def dot( vector )
280
+ (@x * vector.at(0)) + (@y * vector.at(1))
281
+ end
282
+
283
+
284
+ # Returns the magnitude (distance) of this vector.
285
+ def magnitude
286
+ Math.hypot( @x, @y )
287
+ end
288
+
289
+
290
+ # Sets the vector's magnitude (distance). The vector keeps the
291
+ # same angle as before.
292
+ #
293
+ def magnitude=( mag )
294
+ raise "can't modify frozen object" if frozen?
295
+ angle_rad = angle
296
+ @x = Math::cos(angle_rad) * mag
297
+ @y = Math::sin(angle_rad) * mag
298
+ @hash = nil
299
+ self
300
+ end
301
+
302
+
303
+ # Returns a copy of this vector, but rotated 90 degrees
304
+ # counter-clockwise.
305
+ #
306
+ def perp
307
+ self.class.new( -@y, @x )
308
+ end
309
+
310
+
311
+ # Sets this vector to the vector projection (aka vector resolute)
312
+ # of this vector onto the other vector. See also #projected_onto.
313
+ #
314
+ def project_onto!( vector )
315
+ raise "can't modify frozen object" if frozen?
316
+ @x, @y = *(vector * vector.dot(self) * (1/vector.magnitude**2))
317
+ @hash = nil
318
+ self
319
+ end
320
+
321
+ # Like #project_onto!, but returns a new vector.
322
+ def projected_onto( vector )
323
+ dup.project_onto!( vector )
324
+ end
325
+
326
+
327
+ # Rotates the vector the given number of radians.
328
+ # Use #rotate_deg! for degrees.
329
+ #
330
+ def rotate!( angle_rad )
331
+ self.angle += angle_rad
332
+ self
333
+ end
334
+
335
+ # Like #rotate!, but returns a new vector.
336
+ def rotate( angle_rad )
337
+ dup.rotate!( angle_rad )
338
+ end
339
+
340
+
341
+ # Rotates the vector the given number of degrees.
342
+ # Use #rotate for radians.
343
+ #
344
+ def rotate_deg!( angle_deg )
345
+ self.angle_deg += angle_deg
346
+ self
347
+ end
348
+
349
+ # Like #rotate_deg!, but returns a new vector.
350
+ def rotate_deg( angle_deg )
351
+ dup.rotate_deg!( angle_deg )
352
+ end
353
+
354
+
355
+ # call-seq:
356
+ # scale!( scale )
357
+ # scale!( scale_x, scale_y )
358
+ #
359
+ # Multiplies this vector's x and y values.
360
+ #
361
+ # If one number is given, the vector will be equal to
362
+ # Vector2[x*scale, y*scale]. If two numbers are given, it will be
363
+ # equal to Vector2[x*scale_x, y*scale_y].
364
+ #
365
+ # Example:
366
+ #
367
+ # v = Vector2[1.5,2.5]
368
+ # v.scale!( 2 ) # => Vector2[3,5]
369
+ # v.scale!( 3, 4 ) # => Vector2[9,20]
370
+ #
371
+ def scale!( scale_x, scale_y = scale_x )
372
+ raise "can't modify frozen object" if frozen?
373
+ @x, @y = @x * scale_x, @y * scale_y
374
+ @hash = nil
375
+ self
376
+ end
377
+
378
+ # Like #scale!, but returns a new vector.
379
+ def scale( scale_x, scale_y = scale_x )
380
+ dup.scale!(scale_x, scale_y)
381
+ end
382
+
383
+
384
+ # Returns this vector as an [x,y] Array.
385
+ def to_ary
386
+ [@x, @y]
387
+ end
388
+
389
+ alias :to_a :to_ary
390
+
391
+
392
+ def to_s
393
+ "Vector2[#{@x}, #{@y}]"
394
+ end
395
+
396
+ alias :inspect :to_s
397
+
398
+
399
+ # Returns the dot product of this vector's #unit and the other
400
+ # vector's #unit.
401
+ #
402
+ def udot( vector )
403
+ unit.dot( vector.unit )
404
+ end
405
+
406
+
407
+ # Sets this vector's magnitude to 1.
408
+ def unit!
409
+ raise "can't modify frozen object" if frozen?
410
+ scale = 1/magnitude
411
+ @x, @y = @x * scale, @y * scale
412
+ @hash = nil
413
+ self
414
+ end
415
+
416
+ alias :normalize! :unit!
417
+
418
+ # Like #unit!, but returns a new vector.
419
+ def unit
420
+ self.dup.unit!
421
+ end
422
+
423
+ alias :normalized :unit
424
+
425
+
426
+ private
427
+
428
+ def _nearly_equal?( a, b, threshold=1E-10 ) # :nodoc:
429
+ (a - b).abs <= threshold
430
+ end
431
+
432
+ end