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,5 +1,3 @@
1
- require 'actor'
2
- require 'graphical_actor_view'
3
1
 
4
2
  # ActorFactory is responsible for loading all Actors. It passes along required params such as
5
3
  # stage, input_manager, director, resource_manager. It also creates the ActorView
@@ -16,6 +14,8 @@ class ActorFactory
16
14
  return cached_actor if cached_actor
17
15
 
18
16
  model_klass = ClassFinder.find(actor)
17
+ raise "#{actor} not found" unless model_klass
18
+
19
19
  view_klass = ClassFinder.find("#{actor}_view")
20
20
 
21
21
  actor_def = {
@@ -30,16 +30,15 @@ class ActorFactory
30
30
  def build(actor, stage, opts={})
31
31
  actor_def = cached_actor_def actor
32
32
 
33
- actor_type = actor_def[:actor_type]
34
33
  basic_opts = {
35
34
  :stage => stage,
36
35
  :input => @input_manager,
37
36
  :director => @director,
38
37
  :resources => stage.resource_manager,
39
- :actor_type => actor
38
+ :actor_type => actor,
39
+ :wrapped_screen => @wrapped_screen
40
40
  }
41
41
  merged_opts = basic_opts.merge(opts)
42
-
43
42
  model = actor_def[:model_klass].new merged_opts
44
43
 
45
44
  view_klass = opts[:view]
@@ -32,5 +32,13 @@ class ActorView
32
32
  def setup
33
33
  end
34
34
 
35
+ def screen_width
36
+ @screen_width ||= @wrapped_screen.width
37
+ end
38
+
39
+ def screen_height
40
+ @screen_height ||= @wrapped_screen.height
41
+ end
42
+
35
43
  end
36
44
 
@@ -1,5 +1,5 @@
1
- require 'actor'
2
- require 'actor_view'
1
+
2
+
3
3
 
4
4
  class CollidableDebugger < Actor
5
5
 
@@ -1,10 +1,10 @@
1
- require 'actor'
1
+
2
2
  require 'publisher'
3
- require 'actor_view'
3
+
4
4
 
5
5
  class CurtainView < ActorView
6
6
  def draw(target,x_off,y_off,z)
7
- target.fill 0,0,1024,800, [0,0,0,@actor.height]
7
+ target.fill 0,0,1024,800, [0,0,0,@actor.height], z
8
8
  end
9
9
  end
10
10
 
@@ -0,0 +1,51 @@
1
+ # class used for particle emissions
2
+ class Emitter < Actor
3
+
4
+ # options:
5
+ # delay: ms to wait between creating more particles
6
+ # particle_actor: actor to spawn as particle
7
+ # particle_opts: opts to be passed to the spawned particle
8
+ # ttl: ms to live (optional, will live forever if omitted)
9
+ # spawn_variance: dist in pixels to spawn away from emitter (default -10..10)
10
+ # location_tween: tween object that will be used to move the emitter (optional)
11
+ # follow: actor to follow (optional)
12
+ def setup
13
+ @variance = opts[:spawn_variance] || (-10..10)
14
+
15
+ spawn_timer = "#{self.object_id}_spawn"
16
+ self.when :remove_me do
17
+ stage.remove_timer spawn_timer
18
+ end
19
+ stage.add_timer spawn_timer, opts[:delay] do
20
+ spawn_particle
21
+ end
22
+ ttl = opts[:ttl]
23
+ if ttl
24
+ suicide_timer = "#{self.object_id}_ttl"
25
+ stage.add_timer suicide_timer, ttl do
26
+ stage.remove_timer suicide_timer
27
+ remove_self
28
+ end
29
+ end
30
+
31
+ target = opts[:follow]
32
+ if target
33
+ self.x = target.x
34
+ self.y = target.y
35
+ target.when :x_changed do
36
+ self.x = target.x
37
+ end
38
+ target.when :y_changed do
39
+ self.y = target.y
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ def spawn_particle
46
+ spawn_x = self.x + @variance.sample
47
+ spawn_y = self.y + @variance.sample
48
+
49
+ spawn opts[:particle_actor], {x: spawn_x, y: spawn_y}.merge(opts[:particle_opts])
50
+ end
51
+ end
@@ -1,23 +1,43 @@
1
1
  class LabelView < ActorView
2
- def draw(target,x_off,y_off)
3
- actor.text_image.blit target.screen, [actor.x, actor.y]
2
+ def draw(target,x_off,y_off,z)
3
+ @converted_color ||= target.convert_color(actor.color)
4
+ actor.font.draw actor.text, actor.x, actor.y, z,
5
+ 1,1, # x factor, y factor
6
+ @converted_color
4
7
  end
5
8
  end
9
+
6
10
  class Label < Actor
7
- attr_accessor :text, :text_image
11
+ has_behavior layered: {layer: 1}
12
+ attr_accessor :text, :font, :color
8
13
 
9
14
  def setup
10
15
  @text = @opts[:text]
11
16
  @size = @opts[:size]
12
- @font = @opts[:font]
17
+ font_name = @opts[:font]
13
18
  @color = @opts[:color]
14
19
 
15
20
  @text ||= ""
16
21
  @size ||= 30
17
- @font ||= "Asimov.ttf"
22
+ font_name ||= "Asimov.ttf"
18
23
  @color ||= [250,250,250,255]
24
+ layer = opts[:layer]
25
+ layered.layer = layer
26
+
27
+ @font = resource_manager.load_font font_name, @size
28
+ end
29
+
30
+ def width
31
+ font.text_width(text)
32
+ end
33
+
34
+ def height
35
+ [font.text_width(text),font.height]
36
+ end
19
37
 
20
- font = resource_manager.load_font @font, @size
21
- @text_image = font.render @text.to_s, true, @color
38
+ def resize(size)
39
+ @size = size
40
+ resource_manager.load_font @font, @size
22
41
  end
42
+
23
43
  end
@@ -1,4 +1,4 @@
1
- require 'actor'
1
+
2
2
  class Logo < Actor
3
3
  has_behaviors :graphical,
4
4
  :layered => {:layer => 999}
@@ -1,5 +1,5 @@
1
- require 'actor'
2
- require 'actor_view'
1
+
2
+
3
3
 
4
4
  class SpatialDebugger < Actor
5
5
 
@@ -24,24 +24,39 @@ end
24
24
 
25
25
  class SpatialDebuggerView < ActorView
26
26
  def setup
27
- size = 30
28
- font = "Asimov.ttf"
29
- @font = @actor.resource_manager.load_font font, size
27
+ # size = 30
28
+ # font = "Asimov.ttf"
29
+ # @font = @actor.resource_manager.load_font font, size
30
30
  end
31
31
 
32
- def draw(target,x_off,y_off)
32
+ def draw(target,x_off,y_off, z)
33
+ vp = stage.viewport
33
34
  cell_size = @actor.spatial.cell_size
34
35
  max = 0
35
36
  @actor.spatial.buckets.each do |x_bucket,stuff|
36
37
  stuff.each do |y_bucket,items|
37
- max = items.size if items.size > max
38
+ # max = items.size if items.size > max
38
39
  x = x_bucket * cell_size + x_off
39
40
  y = y_bucket * cell_size + y_off
40
- target.draw_box [x,y], [x+cell_size,y+cell_size], Color[:white]
41
+ target.draw_box x, y, x+cell_size, y+cell_size, [255,25,25], z
42
+
43
+ # Yes I know I may be drawing this many times for an actor
44
+ items.each do |item|
45
+ # puts "#{item.class}: [#{item.center_x}, #{item.center_y}] #{item.collidable_shape}"
46
+ parallax = 1
47
+ ix = item.center_x + x_off
48
+ iy = item.center_y + y_off
49
+
50
+ target.draw_box ix, iy, ix + 2, iy + 2, [255,255,255], 10
51
+
52
+ # TODO make work for non circles
53
+ r = item.radius
54
+ target.draw_box ix-r, iy-r, ix + r, iy + r, [255,255,255], 10
55
+ end
41
56
  end
42
57
  end
43
58
 
44
- @text_image = @font.render "#{@actor.ratio} [#{max}]", true, Color[:white]
45
- @text_image.blit target.screen, [@actor.x, @actor.y]
59
+ # @text_image = @font.render "#{@actor.ratio} [#{max}]", true, Color[:white]
60
+ # @text_image.blit target.screen, [@actor.x, @actor.y]
46
61
  end
47
62
  end
@@ -3,21 +3,11 @@ module Arbiter
3
3
  attr_reader :checks, :collisions
4
4
 
5
5
  def register_collidable(actor)
6
- @spatial_hash = stagehand(:spatial)
7
- @collidable_actors ||= []
8
- unless @collidable_actors.include? actor
9
- actor.when :remove_me do
10
- unregister_collidable actor
11
- end
12
- @collidable_actors << actor
13
- @spatial_hash.add(actor)
14
- end
6
+ stagehand(:spatial).add(actor)
15
7
  end
16
8
 
17
9
  def unregister_collidable(actor)
18
- @collidable_actors ||= []
19
- @collidable_actors.delete actor
20
- @spatial_hash.remove(actor)
10
+ stagehand(:spatial).remove(actor)
21
11
  end
22
12
 
23
13
  def on_collision_of(first_objs, second_objs, &block)
@@ -28,7 +18,7 @@ module Arbiter
28
18
 
29
19
  first_objs.each do |fobj|
30
20
  second_objs.each do |sobj|
31
- if fobj.to_i < sobj.to_i
21
+ if fobj < sobj
32
22
  @collision_handlers[fobj] ||= {}
33
23
  @collision_handlers[fobj][sobj] = [false,block]
34
24
  else
@@ -44,7 +34,7 @@ module Arbiter
44
34
  collisions.each do |collision|
45
35
  first = collision.first
46
36
  second = collision.last
47
- unless first.actor_type.to_i < second.actor_type.to_i
37
+ unless first.actor_type < second.actor_type
48
38
  tmp = first
49
39
  first = second
50
40
  second = tmp
@@ -63,29 +53,33 @@ module Arbiter
63
53
  end
64
54
 
65
55
  def find_collisions
66
- @collidable_actors ||= []
56
+ spatial_hash = stagehand(:spatial)
57
+ collidable_actors = spatial_hash.moved_items
67
58
  @checks = 0
68
59
  @collisions = 0
69
- tmp_collidable_actors = @collidable_actors.dup
70
60
  collisions = {}
71
61
 
72
- @collidable_actors.each do |first|
73
- x = first.x - @spatial_hash.cell_size
74
- y = first.y - @spatial_hash.cell_size
62
+ collidable_actors.each do |first|
63
+ x = first.x - spatial_hash.cell_size
64
+ y = first.y - spatial_hash.cell_size
75
65
  # TODO base this on size of object
76
- w = @spatial_hash.cell_size * 3
66
+ w = spatial_hash.cell_size * 3
77
67
  h = w
78
68
 
79
- tmp_collidable_actors = @spatial_hash.neighbors_of(first)
80
-
81
- tmp_collidable_actors.each do |second|
82
- @checks += 1
83
- if first != second && collide?(first, second)
84
- collisions[second] ||= []
85
- if !collisions[second].include?(first)
86
- @collisions += 1
87
- collisions[first] ||= []
88
- collisions[first] << second
69
+ tmp_collidable_actors = spatial_hash.neighbors_of(first)
70
+
71
+ if first.is? :collidable
72
+ tmp_collidable_actors.each do |second|
73
+ @checks += 1
74
+ if second.is? :collidable
75
+ if first != second && collide?(first, second)
76
+ collisions[second] ||= []
77
+ if !collisions[second].include?(first)
78
+ @collisions += 1
79
+ collisions[first] ||= []
80
+ collisions[first] << second
81
+ end
82
+ end
89
83
  end
90
84
  end
91
85
  end
@@ -96,12 +90,43 @@ module Arbiter
96
90
  unique_collisions << [first,second]
97
91
  end
98
92
  end
93
+
99
94
  run_callbacks unique_collisions
100
95
  end
101
96
 
102
97
  def collide?(object, other)
103
- # TODO perf analysis of this
104
- self.send "collide_#{object.collidable_shape}_#{other.collidable_shape}?", object, other
98
+ if !other.is? :collidable
99
+ binding.pry
100
+ end
101
+ case object.collidable_shape
102
+ when :circle
103
+ case other.collidable_shape
104
+ when :circle
105
+ collide_circle_circle? object, other
106
+ when :aabb
107
+ collide_circle_aabb? object, other
108
+ when :polygon
109
+ collide_circle_polygon? object, other
110
+ end
111
+ when :aabb
112
+ case other.collidable_shape
113
+ when :circle
114
+ collide_aabb_circle? object, other
115
+ when :aabb
116
+ collide_aabb_aabb? object, other
117
+ when :polygon
118
+ collide_aabb_polygon? object, other
119
+ end
120
+ when :polygon
121
+ case other.collidable_shape
122
+ when :circle
123
+ collide_polygon_circle? object, other
124
+ when :aabb
125
+ collide_polygon_aabb? object, other
126
+ when :polygon
127
+ collide_polygon_polygon? object, other
128
+ end
129
+ end
105
130
  end
106
131
 
107
132
  def collide_circle_circle?(object, other)
@@ -110,8 +135,10 @@ module Arbiter
110
135
  x_prime = other.center_x
111
136
  y_prime = other.center_y
112
137
 
113
- x_dist = (x_prime - x) * (x_prime - x)
114
- y_dist = (y_prime - y) * (y_prime - y)
138
+ x_delta = x_prime - x
139
+ x_dist = x_delta * x_delta
140
+ y_delta = y_prime - y
141
+ y_dist = y_delta * y_delta
115
142
 
116
143
  total_radius = object.radius + other.radius
117
144
  x_dist + y_dist <= (total_radius * total_radius)
@@ -56,11 +56,11 @@ class Behavior
56
56
  @actor.instance_eval do
57
57
  (class << self; self; end).class_eval do
58
58
  methods.each do |meth|
59
- log("redefining #{meth} for #{@actor.class}") if @actor.respond_to? meth
59
+ # log("redefining #{meth} for #{@actor.class}") if @actor.respond_to? meth
60
60
  target.relegated_methods << meth
61
61
 
62
- define_method meth do |*args|
63
- target.send meth, *args
62
+ define_method meth do |*args, &block|
63
+ target.send meth, *args, &block
64
64
  end
65
65
  end
66
66
  end
@@ -1,4 +1,4 @@
1
- require 'behavior'
1
+
2
2
  # keeps track of an image for you based on the actor's class, and
3
3
  # current action, and frame num
4
4
  # by default it expects images to be:
@@ -1,4 +1,4 @@
1
- require 'behavior'
1
+
2
2
 
3
3
  class Audible < Behavior
4
4
 
@@ -1,7 +1,7 @@
1
- require 'behavior'
2
- require 'collidable/circle_collidable'
3
- require 'collidable/aabb_collidable'
4
- require 'collidable/polygon_collidable'
1
+
2
+
3
+
4
+
5
5
 
6
6
  # available collidable_shapes are :circle, :polygon, :aabb
7
7
  class Collidable < Behavior
@@ -22,6 +22,11 @@ class Collidable < Behavior
22
22
  @actor.stage.register_collidable @actor
23
23
  end
24
24
 
25
+ def removed
26
+ @actor.stage.unregister_collidable @actor
27
+ super
28
+ end
29
+
25
30
  def build_shape
26
31
  shape = nil
27
32
  @collidable_shape = opts[:shape]
@@ -1,8 +1,33 @@
1
- require 'collidable/collidable_shape'
1
+
2
2
 
3
3
  class AaBbCollidable < CollidableShape
4
4
  attr_accessor :cw_local_points
5
5
 
6
+ def setup
7
+ @collidable_shape = opts[:shape]
8
+
9
+ @cw_local_points = opts[:cw_local_points]
10
+ @cw_local_points ||= opts[:points]
11
+ @cw_world_points ||= build_aabb
12
+
13
+ @radius = opts[:radius]
14
+ @radius ||= calculate_radius
15
+
16
+ @old_x = actor_x
17
+ @old_y = actor_y
18
+ end
19
+
20
+ def build_aabb
21
+ w = @actor.width
22
+ h = @actor.height
23
+ [
24
+ [0,0],
25
+ [w,0],
26
+ [w,h],
27
+ [0,h]
28
+ ]
29
+ end
30
+
6
31
  # TODO infinite loop if actor hasn't defined width and it gets relegated to us
7
32
  def calculate_radius
8
33
  w = @actor.width