gamebox 0.2.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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