gamebox 0.2.1 → 0.3.2

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 (95) hide show
  1. data/Gemfile +1 -8
  2. data/Rakefile +13 -37
  3. data/TODO.txt +27 -27
  4. data/docs/getting_started.rdoc +2 -2
  5. data/gamebox.gemspec +33 -191
  6. data/lib/gamebox.rb +18 -14
  7. data/lib/gamebox/actor.rb +37 -27
  8. data/lib/gamebox/actor_factory.rb +4 -5
  9. data/lib/gamebox/actor_view.rb +8 -0
  10. data/lib/gamebox/actors/collidable_debugger.rb +2 -2
  11. data/lib/gamebox/actors/curtain.rb +3 -3
  12. data/lib/gamebox/actors/emitter.rb +51 -0
  13. data/lib/gamebox/actors/label.rb +27 -7
  14. data/lib/gamebox/actors/logo.rb +1 -1
  15. data/lib/gamebox/actors/spatial_debugger.rb +25 -10
  16. data/lib/gamebox/arbiter.rb +61 -34
  17. data/lib/gamebox/behavior.rb +3 -3
  18. data/lib/gamebox/behaviors/animated.rb +1 -1
  19. data/lib/gamebox/behaviors/audible.rb +1 -1
  20. data/lib/gamebox/behaviors/collidable.rb +9 -4
  21. data/lib/gamebox/behaviors/collidable/aabb_collidable.rb +26 -1
  22. data/lib/gamebox/behaviors/collidable/circle_collidable.rb +3 -3
  23. data/lib/gamebox/behaviors/collidable/polygon_collidable.rb +1 -1
  24. data/lib/gamebox/behaviors/graphical.rb +30 -4
  25. data/lib/gamebox/behaviors/layered.rb +1 -1
  26. data/lib/gamebox/behaviors/physical.rb +113 -30
  27. data/lib/gamebox/behaviors/timed.rb +33 -0
  28. data/lib/gamebox/behaviors/updatable.rb +1 -1
  29. data/lib/gamebox/class_finder.rb +1 -21
  30. data/lib/gamebox/console_app.rb +33 -31
  31. data/lib/gamebox/constants.rb +481 -0
  32. data/lib/gamebox/data/config/objects.yml +7 -0
  33. data/lib/gamebox/gamebox_application.rb +10 -33
  34. data/lib/gamebox/gamebox_generator.rb +32 -32
  35. data/lib/gamebox/input_manager.rb +73 -32
  36. data/lib/gamebox/lib/inflector.rb +1 -1
  37. data/lib/gamebox/lib/range_ext.rb +5 -0
  38. data/lib/gamebox/lib/rect.rb +548 -548
  39. data/lib/gamebox/lib/sorted_list.rb +1 -1
  40. data/lib/gamebox/lib/symbol_ext.rb +8 -0
  41. data/lib/gamebox/physical_director.rb +1 -1
  42. data/lib/gamebox/physical_stage.rb +3 -3
  43. data/lib/gamebox/physics.rb +0 -3
  44. data/lib/gamebox/resource_manager.rb +22 -17
  45. data/lib/gamebox/sound_manager.rb +3 -2
  46. data/lib/gamebox/spatial_hash.rb +60 -31
  47. data/lib/gamebox/spatial_stagehand.rb +30 -6
  48. data/lib/gamebox/spec/helper.rb +7 -7
  49. data/lib/gamebox/stage.rb +18 -19
  50. data/lib/gamebox/stage_manager.rb +33 -23
  51. data/lib/gamebox/stagehand.rb +3 -0
  52. data/lib/gamebox/svg_document.rb +1 -1
  53. data/lib/gamebox/tasks/gamebox_tasks.rake +133 -0
  54. data/lib/gamebox/templates/actor.erb +0 -2
  55. data/lib/gamebox/templates/actor_view.erb +1 -3
  56. data/lib/gamebox/templates/template_app/Gemfile +3 -2
  57. data/lib/gamebox/templates/template_app/Rakefile +3 -8
  58. data/lib/gamebox/templates/template_app/config/environment.rb +7 -39
  59. data/lib/gamebox/templates/template_app/src/demo_stage.rb +1 -2
  60. data/lib/gamebox/templates/template_app/src/my_actor.rb +0 -3
  61. data/lib/gamebox/version.rb +2 -2
  62. data/lib/gamebox/viewport.rb +44 -8
  63. data/lib/gamebox/views/graphical_actor_view.rb +22 -16
  64. data/lib/gamebox/wrapped_screen.rb +9 -1
  65. data/script/perf_spatial_hash.rb +49 -58
  66. data/script/perf_struct_vs_array.rb +32 -0
  67. data/spec/actor_factory_spec.rb +61 -0
  68. data/spec/actor_spec.rb +24 -18
  69. data/spec/actor_view_spec.rb +51 -6
  70. data/spec/animated_spec.rb +27 -6
  71. data/spec/arbiter_spec.rb +12 -24
  72. data/spec/backstage_spec.rb +1 -1
  73. data/spec/behavior_spec.rb +3 -3
  74. data/spec/class_finder_spec.rb +13 -0
  75. data/spec/collidable_spec.rb +30 -10
  76. data/spec/emitter_spec.rb +20 -0
  77. data/spec/helper.rb +5 -21
  78. data/spec/input_manager_spec.rb +134 -0
  79. data/spec/label_spec.rb +0 -1
  80. data/spec/physical_spec.rb +114 -5
  81. data/spec/resource_manager_spec.rb +1 -2
  82. data/spec/spatial_hash_spec.rb +23 -7
  83. data/spec/spatial_stagehand_spec.rb +97 -0
  84. data/spec/stage_manager_spec.rb +0 -1
  85. data/spec/stage_spec.rb +2 -2
  86. data/spec/viewport_spec.rb +92 -48
  87. metadata +223 -119
  88. data/.gitignore +0 -11
  89. data/History.txt +0 -80
  90. data/VERSION +0 -1
  91. data/lib/gamebox/event_compat.rb +0 -285
  92. data/lib/gamebox/lib/diy.rb +0 -371
  93. data/lib/gamebox/lib/numbers_ext.rb +0 -3
  94. data/lib/gamebox/tasks/gamebox_tasks.rb +0 -61
  95. data/load_paths.rb +0 -20
@@ -1,4 +1,4 @@
1
- require 'linked_list'
1
+
2
2
 
3
3
  # Keeps a list of items sorted. Elements must be comparable
4
4
  class SortedList
@@ -0,0 +1,8 @@
1
+ unless Symbol.include? Comparable
2
+ class Symbol
3
+ include Comparable
4
+ def <=>(other)
5
+ self.to_i <=> other.to_i
6
+ end
7
+ end
8
+ end
@@ -1,4 +1,4 @@
1
- require 'director'
1
+
2
2
 
3
3
  class PhysicalDirector < Director
4
4
  def find_physical_obj(shape)
@@ -1,9 +1,9 @@
1
1
  # Stage represent on level of game play. Some games will likely have only one
2
2
  # stage. Stage is responsible for loading its background, props, and directors.
3
3
  # PhysicalStage adds a physics space to the Stage
4
- require 'stage'
5
- require 'physics'
6
- require 'physical_director'
4
+
5
+
6
+
7
7
  class PhysicalStage < Stage
8
8
 
9
9
  attr_accessor :space
@@ -1,6 +1,3 @@
1
- require 'chipmunk'
2
-
3
-
4
1
  def vec2(*args)
5
2
  CP::Vec2.new *args
6
3
  end
@@ -1,8 +1,5 @@
1
- #!/usr/bin/env ruby
2
1
  $: << "#{File.dirname(__FILE__)}/../config"
3
2
  require "fileutils"
4
- require 'inflector'
5
- require 'svg_document'
6
3
 
7
4
  class ResourceManager
8
5
 
@@ -92,7 +89,7 @@ class ResourceManager
92
89
  end
93
90
  cached_img = Image.new(@window, full_name)
94
91
  rescue Exception => ex
95
- puts "Cannot load image #{file_name}"
92
+ log "Cannot load image #{file_name}", :warn
96
93
  end
97
94
  @loaded_images[file_name] = cached_img
98
95
  end
@@ -103,8 +100,8 @@ class ResourceManager
103
100
  begin
104
101
  music = Song.new(@window, full_name)
105
102
  return music
106
- rescue Excpetion => ex
107
- puts "Cannot load music " + full_name + " : " + ex
103
+ rescue Exception => ex
104
+ log "Cannot load music " + full_name + " : " + ex, :warn
108
105
  end
109
106
  end
110
107
 
@@ -112,8 +109,8 @@ class ResourceManager
112
109
  begin
113
110
  sound = Sample.new(@window, full_name)
114
111
  return sound
115
- rescue Excpetion => ex
116
- puts "Cannot load sound " + full_name + " : " + ex
112
+ rescue Exception => ex
113
+ log "Cannot load sound " + full_name + " : " + ex.inspect, :warn
117
114
  end
118
115
  end
119
116
 
@@ -122,20 +119,23 @@ class ResourceManager
122
119
  @loaded_fonts[name] ||= {}
123
120
  return @loaded_fonts[name][size] if @loaded_fonts[name][size]
124
121
  begin
125
- #full_name = File.expand_path(FONTS_PATH + name)
126
- full_name = FONTS_PATH + name
127
- if File.exist? full_name
128
- font = Font.new(@window, full_name, size)
122
+ if name =~ /^\// and File.exists?(name)
123
+ font = Font.new(@window, name, size)
129
124
  @loaded_fonts[name][size] = font
130
125
  else
131
- #full_name = File.expand_path(GAMEBOX_FONTS_PATH + name)
132
- full_name = GAMEBOX_FONTS_PATH + name
133
- font = Font.new(@window, full_name, size)
134
- @loaded_fonts[name][size] = font
126
+ full_name = FONTS_PATH + name
127
+ if File.exist? full_name
128
+ font = Font.new(@window, full_name, size)
129
+ @loaded_fonts[name][size] = font
130
+ else
131
+ full_name = GAMEBOX_FONTS_PATH + name
132
+ font = Font.new(@window, full_name, size)
133
+ @loaded_fonts[name][size] = font
134
+ end
135
135
  end
136
136
  return font
137
137
  rescue Exception => ex
138
- puts "Cannot load font #{full_name}:#{ex}"
138
+ debug "Cannot load font #{full_name}:#{ex}"
139
139
  end
140
140
  return nil
141
141
  end
@@ -151,4 +151,9 @@ class ResourceManager
151
151
  cached_svg
152
152
  end
153
153
 
154
+ def load_tiles(filename, tile_width, tile_height)
155
+ Image.load_tiles @window, GFX_PATH+filename, tile_width, tile_height, true
156
+ end
157
+
158
+
154
159
  end
@@ -5,6 +5,7 @@ class SoundManager
5
5
  attr_accessor :sounds, :music
6
6
 
7
7
  constructor :resource_manager, :config_manager
8
+ SUPPORTED_AUDIO_EXTS = %w(wav ogg mp3 au aiff caf)
8
9
 
9
10
  # checks to see if sdl_mixer is availalbe and preloads the sounds and music directories.
10
11
  def setup
@@ -14,7 +15,7 @@ class SoundManager
14
15
 
15
16
  if @enabled
16
17
  @music = {}
17
- files = Dir.glob "#{MUSIC_PATH}**"
18
+ files = Dir.glob "#{MUSIC_PATH}**.{#{SUPPORTED_AUDIO_EXTS.join(',')}}"
18
19
  for f in files
19
20
  name = File.basename(f)
20
21
  begin
@@ -27,7 +28,7 @@ class SoundManager
27
28
  end if files
28
29
 
29
30
  @sounds = {}
30
- files = Dir.glob "#{SOUND_PATH}**"
31
+ files = Dir.glob "#{SOUND_PATH}**.{#{SUPPORTED_AUDIO_EXTS.join(',')}}"
31
32
  for f in files
32
33
  name = File.basename(f)
33
34
  begin
@@ -1,14 +1,19 @@
1
1
  class SpatialHash
2
2
 
3
- attr_reader :cell_size, :buckets
3
+ attr_reader :cell_size, :buckets, :items, :moved_items
4
4
  attr_accessor :auto_resize
5
5
 
6
6
  def initialize(cell_size, resize = false)
7
7
  @cell_size = cell_size.to_f
8
8
  @items = {}
9
- @total_w = 0
10
- @total_h = 0
11
9
  @auto_resize = resize
10
+
11
+ if @auto_resize
12
+ @total_w = 0
13
+ @total_h = 0
14
+ end
15
+ @items = {}
16
+ @buckets = {}
12
17
  rehash
13
18
  end
14
19
 
@@ -18,6 +23,8 @@ class SpatialHash
18
23
  end
19
24
 
20
25
  def rehash
26
+ @moved_items = {}
27
+ return
21
28
  items = @items
22
29
 
23
30
  if @auto_resize
@@ -28,10 +35,11 @@ class SpatialHash
28
35
 
29
36
  @cell_size = (avg_w+avg_h)
30
37
  end
38
+
39
+ @total_w = 0
40
+ @total_h = 0
31
41
  end
32
42
 
33
- @total_w = 0
34
- @total_h = 0
35
43
  @items = {}
36
44
  @buckets = {}
37
45
  items.values.each do |item|
@@ -39,24 +47,53 @@ class SpatialHash
39
47
  end
40
48
  end
41
49
 
50
+ # does not remove or add event handlers
51
+ def move(item)
52
+ @moved_items[item] = item
53
+ _remove item
54
+ _add item
55
+ end
56
+
42
57
  def add(item)
58
+ _add item
59
+ end
60
+
61
+ def _add(item)
43
62
  buckets = lookup item
63
+ @items[item] = buckets
44
64
  buckets.each do |bucket|
45
65
  x,y = *bucket
46
66
  @buckets[x] ||= {}
47
67
  @buckets[x][y] ||= []
48
68
  target_bucket = @buckets[x][y]
49
69
  target_bucket << item
50
- @items[item] = item
51
- w = item.width if item.respond_to? :width
52
- h = item.height if item.respond_to? :height
53
- w ||= 1
54
- h ||= 1
55
- @total_w += w
56
- @total_h += h
70
+
71
+ if @auto_resize
72
+ w = item.width if item.respond_to? :width
73
+ h = item.height if item.respond_to? :height
74
+ w ||= 1
75
+ h ||= 1
76
+ @total_w += w
77
+ @total_h += h
78
+ end
57
79
  end
58
80
  end
59
81
 
82
+ def remove(item)
83
+ @moved_items.delete item
84
+ _remove item
85
+ end
86
+
87
+ def _remove(item)
88
+ buckets = @items[item]
89
+ (buckets || []).each do |bucket|
90
+ x,y = *bucket
91
+ return if @buckets[x].nil? || @buckets[x][y].nil?
92
+ @buckets[x][y].delete item
93
+ end
94
+ @items.delete item
95
+ end
96
+
60
97
  def lookup(item)
61
98
  w = item.width if item.respond_to? :width
62
99
  h = item.height if item.respond_to? :height
@@ -90,16 +127,6 @@ class SpatialHash
90
127
  bucket_y = (y/cell_size).floor
91
128
  return [bucket_x, bucket_y]
92
129
  end
93
-
94
- def remove(item)
95
- buckets = lookup item
96
- buckets.each do |bucket|
97
- x,y = *bucket
98
- return if @buckets[x].nil? || @buckets[x][y].nil?
99
- @buckets[x][y].delete item
100
- end
101
- @items.delete item
102
- end
103
130
 
104
131
  def items_at(x,y)
105
132
  bucket_x = (x/@cell_size).floor
@@ -126,20 +153,22 @@ class SpatialHash
126
153
  end
127
154
 
128
155
  def items_in_bucket_range(min_x,min_y,max_x,max_y)
129
- items = []
130
- (max_x-min_x+1).times do |i|
131
- bucket_x = min_x + i
156
+ items = {}
157
+ (min_x+1..(max_x+1)).each do |bucket_x|
132
158
  x_bucket = @buckets[bucket_x]
133
- have_bucket_x = x_bucket.nil?
134
159
 
135
- (max_y-min_y+1).times do |j|
136
- bucket_y = min_y + j
137
- unless have_bucket_x || x_bucket[bucket_y].nil?
138
- items << x_bucket[bucket_y]
160
+ if x_bucket
161
+ (min_y+1..(max_y+1)).each do |bucket_y|
162
+ objects = x_bucket[bucket_y]
163
+ if objects
164
+ objects.each do |item|
165
+ items[item] = item
166
+ end
167
+ end
139
168
  end
140
169
  end
141
170
  end
142
- items.flatten.uniq
171
+ items.values
143
172
  end
144
173
 
145
174
  # will look dist number of cells around all the cells
@@ -1,4 +1,4 @@
1
- require 'spatial_hash'
1
+
2
2
 
3
3
  class SpatialStagehand < Stagehand
4
4
 
@@ -8,6 +8,8 @@ class SpatialStagehand < Stagehand
8
8
  def setup
9
9
  merged_opts = DEFAULT_PARAMS.merge opts
10
10
  @spatial_actors = SpatialHash.new merged_opts[:cell_size]
11
+ # TODO
12
+ # delegate :items, :cell_size, :to => @spatial_actors
11
13
  end
12
14
 
13
15
  def cell_size
@@ -22,23 +24,45 @@ class SpatialStagehand < Stagehand
22
24
  @spatial_actors.auto_resize = val
23
25
  end
24
26
 
27
+ def auto_resize
28
+ @spatial_actors.auto_resize
29
+ end
30
+
31
+ def moved_items
32
+ @spatial_actors.moved_items.values
33
+ end
34
+
35
+ def items
36
+ @spatial_actors.items.values
37
+ end
38
+
25
39
  def buckets
26
40
  @spatial_actors.buckets
27
41
  end
28
42
 
29
43
  def add(actor)
30
- @spatial_actors.add actor
31
- actor.when :remove_me do
32
- @spatial_actors.remove actor
44
+ # TODO change these to one event? position_changed?
45
+ # item.when :width_changed do |old_w, new_w|
46
+ # item.when :height_changed do |old_h, new_h|
47
+
48
+ actor.when :x_changed do |old_x, new_x|
49
+ move actor
50
+ end
51
+ actor.when :y_changed do |old_y, new_y|
52
+ move actor
33
53
  end
54
+ actor.when :remove_me do
55
+ remove actor
56
+ end
57
+ @spatial_actors.add actor
34
58
  end
35
59
 
36
60
  def remove(actor)
37
61
  @spatial_actors.remove actor
38
62
  end
39
63
 
40
- def update(time)
41
- @spatial_actors.rehash
64
+ def move(actor)
65
+ @spatial_actors.move actor
42
66
  end
43
67
 
44
68
  def items_at(x,y)
@@ -2,13 +2,13 @@
2
2
  module GameboxSpecHelpers
3
3
 
4
4
  def create_actor(type, args = {})
5
- InputManager.stub :setup
5
+ InputManager.any_instance.stubs :setup
6
6
  basic_opts = {
7
- :stage => @stage = stub.as_null_object,
8
- :input => @input_manager = InputManager.new(:config_manager => "config_manager"),
9
- :sound => @sound_manager = stub.as_null_object,
10
- :director => @director = stub.as_null_object,
11
- :resources => @resource_manager = stub.as_null_object
7
+ stage: @stage = stub_everything,
8
+ input: @input_manager = InputManager.new(wrapped_screen: 'wrapped_screen', config_manager: 'config_manager'),
9
+ sound: @sound_manager = stub_everything,
10
+ director: @director = stub_everything,
11
+ resources: @resource_manager = stub_everything,
12
12
  }.merge(args)
13
13
 
14
14
  klass = ClassFinder.find(type)
@@ -20,6 +20,6 @@ module GameboxSpecHelpers
20
20
 
21
21
  end
22
22
 
23
- Spec::Runner.configure do |configuration|
23
+ RSpec.configure do |configuration|
24
24
  configuration.include GameboxSpecHelpers
25
25
  end
data/lib/gamebox/stage.rb CHANGED
@@ -1,10 +1,3 @@
1
- require 'inflector'
2
- require 'publisher'
3
- require 'director'
4
- require 'viewport'
5
- require 'backstage'
6
- require 'arbiter'
7
-
8
1
  # Stage is a state that the game is in. (ie intro stage, multiplayer stage,
9
2
  # single player stage).
10
3
  class Stage
@@ -79,10 +72,9 @@ class Stage
79
72
  @director.update time
80
73
  @viewport.update time
81
74
  @stagehands.each do |name, stagehand|
82
- stagehand.update time if stagehand.respond_to? :update
75
+ stagehand.update time
83
76
  end
84
- # TODO can we change collisions to be a stagehand
85
- find_collisions unless @collidable_actors.nil?
77
+ find_collisions
86
78
  update_timers time
87
79
  end
88
80
 
@@ -119,6 +111,7 @@ class Stage
119
111
  rescue Exception => ex
120
112
  p drawable.class
121
113
  p ex
114
+ p ex.backtrace
122
115
  end
123
116
  end
124
117
  end
@@ -170,21 +163,27 @@ class Stage
170
163
  end
171
164
 
172
165
  # add block to be executed every interval_ms millis
166
+ # TODO make this hash based on object => name => block
167
+ # to clean up the timed behavior
173
168
  def add_timer(name, interval_ms, &block)
174
- @timers ||= {}
175
- @timers[name] = {:count => 0,
169
+ @new_timers ||= {}
170
+ @new_timers[name] = {:count => 0,
176
171
  :interval_ms => interval_ms, :callback => block}
177
172
  end
178
173
 
179
174
  # update each timers counts, call any blocks that are over their interval
180
175
  def update_timers(time_delta)
181
- unless @timers.nil?
182
- @timers.each do |name, timer_hash|
183
- timer_hash[:count] += time_delta
184
- if timer_hash[:count] > timer_hash[:interval_ms]
185
- timer_hash[:count] -= timer_hash[:interval_ms]
186
- timer_hash[:callback].call
187
- end
176
+ # TODO handle overwriting the same timer name...
177
+ @timers ||= {}
178
+ if @new_timers
179
+ @timers.merge!(@new_timers)
180
+ @new_timers = nil
181
+ end
182
+ @timers.each do |name, timer_hash|
183
+ timer_hash[:count] += time_delta
184
+ if timer_hash[:count] > timer_hash[:interval_ms]
185
+ timer_hash[:count] -= timer_hash[:interval_ms]
186
+ timer_hash[:callback].call
188
187
  end
189
188
  end
190
189
  end