gamebox 0.3.4 → 0.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. data/README.rdoc +38 -0
  2. data/Rakefile +1 -10
  3. data/TODO.txt +6 -6
  4. data/app_generators/gamebox_generator.rb +95 -0
  5. data/{lib/gamebox/templates/template_app → app_generators/templates}/.gitignore +0 -0
  6. data/app_generators/templates/Gemfile +7 -0
  7. data/app_generators/templates/NEXT_STEPS.txt +1 -0
  8. data/{lib/gamebox/templates/template_app/README → app_generators/templates/README.rdoc} +0 -0
  9. data/{lib/gamebox/templates/template_app → app_generators/templates}/Rakefile +0 -0
  10. data/{lib/gamebox/templates/template_app → app_generators/templates}/config/boot.rb +0 -0
  11. data/app_generators/templates/config/environment.rb +30 -0
  12. data/{lib/gamebox/templates/template_app → app_generators/templates}/config/game.yml +0 -0
  13. data/{lib/gamebox/templates/template_app → app_generators/templates}/data/fonts/FONTS_GO_HERE +0 -0
  14. data/{lib/gamebox/templates/template_app → app_generators/templates}/data/graphics/GRAPHICS_GO_HERE +0 -0
  15. data/{lib/gamebox/templates/template_app → app_generators/templates}/data/music/MUSIC_GOES_HERE +0 -0
  16. data/{lib/gamebox/templates/template_app → app_generators/templates}/data/sounds/SOUND_FX_GO_HERE +0 -0
  17. data/{lib/gamebox/templates → app_generators/templates/script}/actor.erb +0 -0
  18. data/{lib/gamebox/templates → app_generators/templates/script}/actor_spec.erb +0 -0
  19. data/{lib/gamebox/templates → app_generators/templates/script}/actor_view.erb +0 -0
  20. data/{lib/gamebox/templates → app_generators/templates/script}/actor_view_spec.erb +0 -0
  21. data/app_generators/templates/script/generate +12 -0
  22. data/{lib/gamebox/templates/template_app → app_generators/templates}/spec/helper.rb +0 -0
  23. data/app_generators/templates/src/actors/player.rb +8 -0
  24. data/{lib/gamebox/templates/template_app → app_generators/templates}/src/app.rb +0 -0
  25. data/app_generators/templates/src/demo_stage.rb +7 -0
  26. data/bin/gamebox +8 -70
  27. data/component_generators/actor_generator.rb +17 -0
  28. data/docs/CODE_REVIEW +1 -1
  29. data/docs/REFACTOR_NOTES.txt +25 -0
  30. data/docs/getting_started.rdoc +1 -1
  31. data/gamebox.gemspec +7 -4
  32. data/lib/gamebox.rb +6 -3
  33. data/lib/gamebox/actors/collidable_debugger.rb +13 -15
  34. data/lib/gamebox/actors/curtain.rb +44 -43
  35. data/lib/gamebox/actors/emitter.rb +3 -42
  36. data/lib/gamebox/actors/fps.rb +13 -6
  37. data/lib/gamebox/actors/label.rb +42 -34
  38. data/lib/gamebox/actors/logo.rb +2 -4
  39. data/lib/gamebox/actors/score.rb +37 -27
  40. data/lib/gamebox/actors/svg_actor.rb +45 -32
  41. data/lib/gamebox/behaviors/animated.rb +39 -59
  42. data/lib/gamebox/behaviors/audible.rb +14 -14
  43. data/lib/gamebox/behaviors/collidable.rb +65 -36
  44. data/lib/gamebox/behaviors/collidable/aabb_collidable.rb +2 -3
  45. data/lib/gamebox/behaviors/collidable/collidable_shape.rb +6 -4
  46. data/lib/gamebox/behaviors/collidable/polygon_collidable.rb +1 -1
  47. data/lib/gamebox/behaviors/emitting.rb +48 -0
  48. data/lib/gamebox/behaviors/graphical.rb +22 -56
  49. data/lib/gamebox/behaviors/layered.rb +8 -21
  50. data/lib/gamebox/behaviors/physical.rb +202 -213
  51. data/lib/gamebox/behaviors/positioned.rb +16 -0
  52. data/lib/gamebox/behaviors/projectile.rb +15 -0
  53. data/lib/gamebox/behaviors/visible.rb +16 -0
  54. data/lib/gamebox/core/aabb_helpers.rb +61 -0
  55. data/lib/gamebox/core/aabb_node.rb +118 -0
  56. data/lib/gamebox/core/aabb_tree.rb +137 -0
  57. data/lib/gamebox/core/actor.rb +102 -0
  58. data/lib/gamebox/core/actor_factory.rb +56 -0
  59. data/lib/gamebox/core/actor_view.rb +63 -0
  60. data/lib/gamebox/core/actor_view_factory.rb +40 -0
  61. data/lib/gamebox/{arbiter.rb → core/arbiter.rb} +31 -34
  62. data/lib/gamebox/{backstage.rb → core/backstage.rb} +0 -0
  63. data/lib/gamebox/core/behavior.rb +64 -0
  64. data/lib/gamebox/core/behavior_factory.rb +56 -0
  65. data/lib/gamebox/{class_finder.rb → core/class_finder.rb} +0 -0
  66. data/lib/gamebox/{config_manager.rb → core/config_manager.rb} +1 -1
  67. data/lib/gamebox/core/configuration.rb +39 -0
  68. data/lib/gamebox/core/core.rb +30 -0
  69. data/lib/gamebox/core/deprecated.rb +15 -0
  70. data/lib/gamebox/{director.rb → core/director.rb} +6 -11
  71. data/lib/gamebox/core/font_style.rb +26 -0
  72. data/lib/gamebox/core/font_style_factory.rb +11 -0
  73. data/lib/gamebox/core/game.rb +19 -0
  74. data/lib/gamebox/{hooked_gosu_window.rb → core/hooked_gosu_window.rb} +12 -6
  75. data/lib/gamebox/{input_manager.rb → core/input_manager.rb} +106 -99
  76. data/lib/gamebox/core/physics.rb +22 -0
  77. data/lib/gamebox/{physical_stage.rb → core/physics_manager.rb} +36 -30
  78. data/lib/gamebox/{resource_manager.rb → core/resource_manager.rb} +19 -18
  79. data/lib/gamebox/{sound_manager.rb → core/sound_manager.rb} +9 -7
  80. data/lib/gamebox/{stage.rb → core/stage.rb} +42 -80
  81. data/lib/gamebox/{stage_manager.rb → core/stage_manager.rb} +46 -53
  82. data/lib/gamebox/{stagehand.rb → core/stagehand.rb} +0 -0
  83. data/lib/gamebox/{svg_document.rb → core/svg_document.rb} +0 -0
  84. data/lib/gamebox/core/timer_manager.rb +50 -0
  85. data/lib/gamebox/{viewport.rb → core/viewport.rb} +2 -3
  86. data/lib/gamebox/{wrapped_screen.rb → core/wrapped_screen.rb} +12 -19
  87. data/lib/gamebox/gamebox_application.rb +7 -15
  88. data/lib/gamebox/lib/evented_attributes.rb +51 -0
  89. data/lib/gamebox/{ftor.rb → lib/ftor.rb} +0 -0
  90. data/lib/gamebox/lib/min_max_helpers.rb +10 -0
  91. data/lib/gamebox/lib/rect.rb +112 -54
  92. data/lib/gamebox/lib/yoda.rb +46 -0
  93. data/lib/gamebox/spec/helper.rb +317 -12
  94. data/lib/gamebox/stagehands/spatial_tree_stagehand.rb +61 -0
  95. data/lib/gamebox/version.rb +8 -3
  96. data/lib/gamebox/views/graphical_actor_view.rb +22 -29
  97. data/script/perf_aabb.rb +56 -0
  98. data/script/perf_array_access.rb +16 -0
  99. data/script/perf_collisions.rb +37 -18
  100. data/script/perf_struct_vs_array.rb +7 -7
  101. data/spec/acceptance/animation_spec.rb +65 -0
  102. data/spec/acceptance/basic_actor_lifecycle_spec.rb +92 -0
  103. data/spec/acceptance/built_in_collision_handling_spec.rb +55 -0
  104. data/spec/acceptance/chipmunk_collision_handling_spec.rb +83 -0
  105. data/spec/acceptance/fps_actor_spec.rb +40 -0
  106. data/spec/acceptance/pausing_spec.rb +61 -0
  107. data/spec/acceptance/timer_usage_spec.rb +53 -0
  108. data/spec/actors/emitter_spec.rb +5 -0
  109. data/spec/{label_spec.rb → actors/label_spec.rb} +1 -1
  110. data/spec/behaviors/animated_spec.rb +85 -0
  111. data/spec/behaviors/collidable_spec.rb +134 -0
  112. data/spec/{physical_spec.rb → behaviors/physical_spec.rb} +2 -1
  113. data/spec/behaviors/positioned_spec.rb +6 -0
  114. data/spec/behaviors/projectile_spec.rb +6 -0
  115. data/spec/core/aabb_tree_spec.rb +109 -0
  116. data/spec/core/actor_factory_spec.rb +44 -0
  117. data/spec/core/actor_spec.rb +78 -0
  118. data/spec/core/actor_view_spec.rb +53 -0
  119. data/spec/{arbiter_spec.rb → core/arbiter_spec.rb} +29 -30
  120. data/spec/core/backstage_spec.rb +37 -0
  121. data/spec/core/behavior_factory_spec.rb +50 -0
  122. data/spec/core/behavior_spec.rb +8 -0
  123. data/spec/core/configuration_spec.rb +8 -0
  124. data/spec/core/core_spec.rb +13 -0
  125. data/spec/core/font_style_factory_spec.rb +17 -0
  126. data/spec/core/font_style_spec.rb +41 -0
  127. data/spec/core/hooked_gosu_window_spec.rb +75 -0
  128. data/spec/core/input_manager_spec.rb +285 -0
  129. data/spec/core/physics_manager_spec.rb +11 -0
  130. data/spec/core/resource_manager_spec.rb +12 -0
  131. data/spec/core/stage_manager_spec.rb +140 -0
  132. data/spec/core/stage_spec.rb +73 -0
  133. data/spec/core/timer_manager_spec.rb +89 -0
  134. data/spec/{viewport_spec.rb → core/viewport_spec.rb} +6 -3
  135. data/spec/core/wrapped_screen_spec.rb +26 -0
  136. data/spec/fixtures/game.yml +7 -0
  137. data/spec/fixtures/snelpling/idle/1.png +0 -0
  138. data/spec/fixtures/snelpling/jump/1.png +0 -0
  139. data/spec/fixtures/snelpling/jump/2.png +0 -0
  140. data/spec/fixtures/snelpling/jump/3.png +0 -0
  141. data/spec/helper.rb +8 -0
  142. data/spec/{class_finder_spec.rb → lib/class_finder_spec.rb} +2 -1
  143. data/spec/stagehands/spatial_tree_stagehand_spec.rb +19 -0
  144. data/spec/views/graphical_actor_view_spec.rb +116 -0
  145. metadata +343 -144
  146. data/README.txt +0 -34
  147. data/lib/gamebox/actor.rb +0 -179
  148. data/lib/gamebox/actor_factory.rb +0 -57
  149. data/lib/gamebox/actor_view.rb +0 -44
  150. data/lib/gamebox/actors/spatial_debugger.rb +0 -62
  151. data/lib/gamebox/behavior.rb +0 -70
  152. data/lib/gamebox/behaviors/timed.rb +0 -33
  153. data/lib/gamebox/behaviors/updatable.rb +0 -12
  154. data/lib/gamebox/console_app.rb +0 -41
  155. data/lib/gamebox/gamebox_generator.rb +0 -32
  156. data/lib/gamebox/generators/actor_generator.rb +0 -43
  157. data/lib/gamebox/generators/view_generator.rb +0 -42
  158. data/lib/gamebox/physical_director.rb +0 -17
  159. data/lib/gamebox/physics.rb +0 -32
  160. data/lib/gamebox/spatial_bucket.rb +0 -9
  161. data/lib/gamebox/spatial_hash.rb +0 -194
  162. data/lib/gamebox/spatial_stagehand.rb +0 -80
  163. data/lib/gamebox/templates/template_app/Gemfile +0 -6
  164. data/lib/gamebox/templates/template_app/config/environment.rb +0 -23
  165. data/lib/gamebox/templates/template_app/config/stage_config.yml +0 -2
  166. data/lib/gamebox/templates/template_app/script/generate +0 -7
  167. data/lib/gamebox/templates/template_app/src/demo_stage.rb +0 -11
  168. data/lib/gamebox/templates/template_app/src/game.rb +0 -19
  169. data/lib/gamebox/templates/template_app/src/my_actor.rb +0 -14
  170. data/script/perf_spatial_hash.rb +0 -64
  171. data/spec/actor_factory_spec.rb +0 -61
  172. data/spec/actor_spec.rb +0 -71
  173. data/spec/actor_view_spec.rb +0 -61
  174. data/spec/animated_spec.rb +0 -83
  175. data/spec/backstage_spec.rb +0 -45
  176. data/spec/behavior_spec.rb +0 -28
  177. data/spec/collidable_spec.rb +0 -135
  178. data/spec/emitter_spec.rb +0 -20
  179. data/spec/input_manager_spec.rb +0 -134
  180. data/spec/resource_manager_spec.rb +0 -13
  181. data/spec/spatial_hash_spec.rb +0 -119
  182. data/spec/spatial_stagehand_spec.rb +0 -93
  183. data/spec/stage_manager_spec.rb +0 -25
  184. data/spec/stage_spec.rb +0 -65
@@ -0,0 +1,16 @@
1
+ define_behavior :positioned do
2
+ requires :director
3
+ setup do
4
+ actor.has_attributes x: 0, y: 0
5
+ director.when :update do |time|
6
+ if @x_dirty || @y_dirty
7
+ actor.react_to :position_changed
8
+ actor.emit :position_changed
9
+ end
10
+ @x_dirty = false
11
+ @y_dirty = false
12
+ end
13
+ actor.when :x_changed do @x_dirty = true end
14
+ actor.when :y_changed do @y_dirty = true end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ define_behavior :projectile do
2
+
3
+ requires :director
4
+ requires_behaviors :positioned
5
+ setup do
6
+ actor.has_attributes vel_x: 0,
7
+ vel_y: 0
8
+
9
+ director.when :update do |time, secs|
10
+ actor.x += (actor.vel_x * secs)
11
+ actor.y += (actor.vel_y * secs)
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,16 @@
1
+ # in charge of registering / showing / hiding of actor views
2
+ define_behavior :visible do
3
+ requires :stage
4
+ setup do
5
+ actor.has_attribute :visible
6
+ end
7
+
8
+ react_to do |message, *args|
9
+ if message == :show
10
+ stage.register_drawable opts[:view] unless actor.visible
11
+ elsif message == :hide
12
+ stage.unregister_drawable opts[:view] if actor.visible
13
+ end
14
+ actor.visible = message == :show
15
+ end
16
+ end
@@ -0,0 +1,61 @@
1
+ module AABBTreeDebugHelpers
2
+ def valid?
3
+ return true unless @root
4
+ @root.contains_children?
5
+ end
6
+
7
+ def each(&blk)
8
+ return unless @root
9
+ query @root.bb, &blk
10
+ end
11
+
12
+ def each_node(&blk)
13
+ return unless @root
14
+ @root.each_node &blk
15
+ end
16
+
17
+ def each_leaf(&blk)
18
+ return unless @root
19
+ @root.each_leaf &blk
20
+ end
21
+ end
22
+
23
+ module AABBNodeDebugHelpers
24
+
25
+ def contains_children?
26
+ if leaf?
27
+ true
28
+ else
29
+ @bb.contain?(a.bb) &&
30
+ @bb.contain?(b.bb) &&
31
+ @a.contains_children? &&
32
+ @b.contains_children?
33
+ end
34
+ end
35
+
36
+ def each_node(&blk)
37
+ blk.call self
38
+ unless leaf?
39
+ @a.each_node &blk
40
+ @b.each_node &blk
41
+ end
42
+ end
43
+
44
+ def each_leaf(&blk)
45
+ if leaf?
46
+ blk.call self
47
+ else leaf?
48
+ @a.each_leaf &blk
49
+ @b.each_leaf &blk
50
+ end
51
+ end
52
+
53
+ def root
54
+ node = self
55
+ while node.parent
56
+ node = node.parent
57
+ end
58
+ node
59
+ end
60
+
61
+ end
@@ -0,0 +1,118 @@
1
+ # This is the primary element in an AABBTree.
2
+ # It acts as both containing elements and leaf elements. Leaves have an @object,
3
+ # containers have @a and @b nodes. All leaf nodes have a cached list of
4
+ # collisions that contain references to other nodes in the tree.
5
+ class AABBNode
6
+ include AABBNodeDebugHelpers
7
+ attr_accessor :bb, :a, :b, :parent, :object, :cached_collisions
8
+
9
+ def initialize(parent, object, bb)
10
+ @parent = parent
11
+ @a = nil
12
+ @b = nil
13
+
14
+ @object = object
15
+ @bb = bb
16
+ end
17
+
18
+ def insert_subtree(leaf)
19
+ if leaf?
20
+ new_node = AABBNode.new nil, nil, @bb.union_fast(leaf.bb)
21
+ new_node.a = self
22
+ new_node.b = leaf
23
+ return new_node
24
+ else
25
+ cost_a = @b.bb.area + @a.bb.union_area(leaf.bb)
26
+ cost_b = @a.bb.area + @b.bb.union_area(leaf.bb)
27
+
28
+ if cost_a == cost_b
29
+ cost_a = @a.proximity(leaf)
30
+ cost_b = @b.proximity(leaf)
31
+ end
32
+
33
+ if cost_b < cost_a
34
+ self.b = @b.insert_subtree leaf
35
+ else
36
+ self.a = @a.insert_subtree leaf
37
+ end
38
+
39
+ @bb.expand_to_include! leaf.bb
40
+ return self
41
+ end
42
+ end
43
+
44
+ def remove_subtree(leaf)
45
+ if leaf == self
46
+ return nil
47
+ else
48
+ if leaf.parent == self
49
+ other_child = other(leaf)
50
+ other_child.parent = @parent
51
+ return other_child
52
+ else
53
+ leaf.parent.disown_child leaf
54
+ return self
55
+ end
56
+ end
57
+ end
58
+
59
+ def query_subtree(search_bb, &blk)
60
+ if @bb.collide_rect? search_bb
61
+ if leaf?
62
+ blk.call @object
63
+ else
64
+ @a.query_subtree search_bb, &blk
65
+ @b.query_subtree search_bb, &blk
66
+ end
67
+ end
68
+ end
69
+
70
+ def leaf?
71
+ @object
72
+ end
73
+
74
+ def a=(new_a)
75
+ @a = new_a
76
+ @a.parent = self
77
+ end
78
+
79
+ def b=(new_b)
80
+ @b = new_b
81
+ @b.parent = self
82
+ end
83
+
84
+ def other(child)
85
+ @a == child ? @b : @a
86
+ end
87
+
88
+ def disown_child(leaf)
89
+ value = other(leaf)
90
+ raise "Internal Error: Cannot replace child of a leaf." if @parent.leaf?
91
+ raise "Internal Error: AABBNode is not a child of parent." unless self == @parent.a || self == @parent.b
92
+
93
+ if @parent.a == self
94
+ @parent.a = value
95
+ else
96
+ @parent.b = value
97
+ end
98
+
99
+ @parent.update_bb
100
+ end
101
+
102
+ def update_bb
103
+ node = self
104
+ unless node.leaf?
105
+ node.bb.refit_for! node.a.bb, node.b.bb
106
+ while node = node.parent
107
+ node.bb.refit_for! node.a.bb, node.b.bb
108
+ end
109
+ end
110
+ end
111
+
112
+ def proximity(other_node)
113
+ other_bb = other_node.bb
114
+ (@bb.left + @bb.right - other_bb.left - other_bb.right).abs +
115
+ (@bb.bottom + @bb.top - other_bb.bottom - other_bb.top).abs
116
+ end
117
+ end
118
+
@@ -0,0 +1,137 @@
1
+ # AABBTree is a binary tree where each node has a bounding box that contains
2
+ # its children's bounding boxes. It extrapolates and expands the node bounding
3
+ # boxes before inserting to lower the amount of updates to the tree structure.
4
+ # It caches all leaf node collisions for quick lookup.
5
+ class AABBTree
6
+ include AABBTreeDebugHelpers
7
+ DEFAULT_BB_SCALE = 1
8
+ VELOCITY_SCALE = 3
9
+
10
+ attr_reader :items
11
+ extend Forwardable
12
+ def_delegators :@items, :size, :include?
13
+
14
+ def initialize
15
+ @items = {}
16
+ @root = nil
17
+ end
18
+
19
+ def query(search_bb, &callback)
20
+ return unless @root
21
+ @root.query_subtree search_bb, &callback
22
+ end
23
+
24
+ def collisions(item, &blk)
25
+ leaf = @items[item]
26
+ return unless leaf && leaf.cached_collisions
27
+ leaf.cached_collisions.each do |collider|
28
+ blk.call collider.object
29
+ end
30
+ end
31
+
32
+ def insert(item)
33
+ raise "Adding an existing item, please use update" if @items[item]
34
+ leaf = AABBNode.new nil, item, calculate_bb(item)
35
+ @items[item] = leaf
36
+ insert_leaf leaf
37
+ end
38
+
39
+ def remove(item)
40
+ leaf = @items.delete item
41
+ @root = @root.remove_subtree leaf if leaf
42
+ clear_cached_collisions leaf
43
+ end
44
+
45
+ def update(item)
46
+ node = @items[item]
47
+ if node && node.leaf?
48
+ new_bb = calculate_bb(item)
49
+ unless node.bb.contain? item.bb
50
+ node.bb = new_bb
51
+ clear_cached_collisions node
52
+ @root = @root.remove_subtree node
53
+ insert_leaf node
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def clear_cached_collisions(leaf)
61
+ cached_collisions = leaf.cached_collisions
62
+
63
+ if cached_collisions
64
+ leaf.cached_collisions = nil
65
+ cached_collisions.each do |other|
66
+ other.cached_collisions.delete leaf
67
+ end
68
+ end
69
+ end
70
+
71
+ def build_cached_collisions(leaf)
72
+ each_leaf do |other|
73
+ if leaf != other && leaf.bb.collide_rect?(other.bb)
74
+ leaf.cached_collisions ||= []
75
+ other.cached_collisions ||= []
76
+ leaf.cached_collisions << other
77
+ other.cached_collisions << leaf
78
+ end
79
+ end
80
+ end
81
+
82
+ def insert_leaf(leaf)
83
+ if @root
84
+ @root = @root.insert_subtree leaf
85
+ else
86
+ @root = leaf
87
+ end
88
+ build_cached_collisions leaf
89
+ end
90
+
91
+ def expand_bb_by!(bb, percent)
92
+ new_w = bb.w * percent
93
+ new_h = bb.h * percent
94
+ hw = new_w / 2.0
95
+ hh = new_h / 2.0
96
+
97
+ bb.x = (bb.x - hw).ceil
98
+ bb.y = (bb.y - hh).ceil
99
+ bb.w = (bb.w + new_w).ceil
100
+ bb.h = (bb.h + new_h).ceil
101
+ bb
102
+ end
103
+
104
+ def project_bb_by_velocity!(bb, velocity_vector)
105
+ future_vector = velocity_vector * VELOCITY_SCALE
106
+ projected_bb = [bb.x + velocity_vector.x,
107
+ bb.y + velocity_vector.y,
108
+ bb.w, bb.h ]
109
+
110
+ u_bb = bb.union_fast(projected_bb)
111
+ bb.x = u_bb.x
112
+ bb.y = u_bb.y
113
+ bb.w = u_bb.w
114
+ bb.h = u_bb.y
115
+ end
116
+
117
+ def calculate_bb(item)
118
+ if item.respond_to? :bb
119
+ bb = item.bb.dup
120
+ project_bb_by_velocity!(bb, item.velocity) if item.respond_to? :velocity
121
+ expand_bb_by!(bb, DEFAULT_BB_SCALE)
122
+ else
123
+ if item.respond_to? :width
124
+ w = item.width
125
+ h = item.height
126
+ elsif item.respond_to? :radius
127
+ w = item.radius * 2
128
+ h = item.radius * 2
129
+ end
130
+ w ||= 2
131
+ h ||= 2
132
+
133
+ expand_bb_by!(Rect.new item.x, item.y, w, h, DEFAULT_BB_SCALE)
134
+ end
135
+ end
136
+ end
137
+
@@ -0,0 +1,102 @@
1
+ # Actor represent a game object.
2
+ # Actors can have behaviors added and removed from them. Such as :physical or :animated.
3
+ # They are created and hooked up to their optional View class in Stage#create_actor.
4
+ class Actor
5
+ extend Publisher
6
+ include EventedAttributes
7
+ include Gamebox::Extensions::Object::Yoda
8
+ can_fire_anything
9
+ construct_with :this_object_context
10
+ public :this_object_context
11
+
12
+ has_attribute :alive
13
+
14
+ def initialize
15
+ @behaviors = {}
16
+ end
17
+
18
+ def configure(opts={}) # :nodoc:
19
+ has_attributes opts
20
+ self.actor_type = opts[:actor_type]
21
+ self.alive = true
22
+ end
23
+
24
+ def add_behavior(name, behavior)
25
+ @behaviors[name] = behavior
26
+ end
27
+
28
+ def react_to(message, *opts)
29
+ # TODO cache the values array?
30
+ @behaviors.values.each do |behavior|
31
+ behavior.react_to(message, *opts)
32
+ end
33
+ end
34
+
35
+ def has_behavior?(name)
36
+ @behaviors[name]
37
+ end
38
+
39
+ def emit(event, *args)
40
+ fire event, *args
41
+ end
42
+
43
+ # Tells the actor's Director that he wants to be removed; and unsubscribes
44
+ # the actor from all input events.
45
+ def remove
46
+ self.alive = false
47
+ react_to :remove
48
+ fire :remove_me
49
+ end
50
+
51
+ def to_s
52
+ atts = methods.sort - Actor.instance_methods
53
+ atts_hash = {}
54
+ atts.each do |att|
55
+ atts_hash[att] = send(att) unless att.to_s.end_with? "="
56
+ end
57
+ "#{self.actor_type}:#{self.object_id} with attributes\n#{atts_hash.inspect}"
58
+ end
59
+
60
+ # TODO should this live somewhere else?
61
+ # TODO should we support "inheritance" of components?
62
+ class << self
63
+
64
+ def define(actor_type, opts={}, &blk)
65
+ @definitions ||= {}
66
+ definition = ActorDefinition.new
67
+ definition.instance_eval &blk if block_given?
68
+ @definitions[actor_type] = definition
69
+
70
+ view_blk = definition.view_blk
71
+ if view_blk
72
+ ActorView.define "#{actor_type}_view".to_sym, &view_blk
73
+ end
74
+ end
75
+
76
+ def definitions
77
+ @definitions ||= {}
78
+ end
79
+
80
+ end
81
+
82
+ class ActorDefinition
83
+ attr_accessor :behaviors, :attributes, :view_blk
84
+ def initialize
85
+ @behaviors = []
86
+ @attributes = []
87
+ end
88
+
89
+ def has_behaviors(*behaviors)
90
+ behaviors.each do |beh|
91
+ @behaviors << beh
92
+ end
93
+ end
94
+ alias has_behavior has_behaviors
95
+
96
+ # TODO spec view helper on actor def
97
+ def view(&blk)
98
+ @view_blk = blk
99
+ end
100
+ end
101
+
102
+ end