gamebox 0.4.1 → 0.5.0

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 (39) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -2
  3. data/gamebox.gemspec +1 -1
  4. data/lib/gamebox/actors/collidable_debugger.rb +3 -4
  5. data/lib/gamebox/actors/label.rb +2 -0
  6. data/lib/gamebox/actors/score.rb +4 -25
  7. data/lib/gamebox/behaviors/animated_with_spritemap.rb +97 -0
  8. data/lib/gamebox/behaviors/collidable.rb +6 -14
  9. data/lib/gamebox/behaviors/collidable/aabb_collidable.rb +2 -3
  10. data/lib/gamebox/behaviors/graphical.rb +2 -1
  11. data/lib/gamebox/behaviors/physical.rb +1 -1
  12. data/lib/gamebox/behaviors/positioned.rb +10 -4
  13. data/lib/gamebox/core/actor.rb +1 -1
  14. data/lib/gamebox/core/actor_factory.rb +2 -0
  15. data/lib/gamebox/core/arbiter.rb +2 -0
  16. data/lib/gamebox/core/font_style.rb +2 -2
  17. data/lib/gamebox/core/input_mapper.rb +4 -4
  18. data/lib/gamebox/core/resource_manager.rb +2 -2
  19. data/lib/gamebox/core/viewport.rb +78 -54
  20. data/lib/gamebox/spec/helper.rb +2 -3
  21. data/lib/gamebox/tasks/gamebox_tasks.rake +1 -90
  22. data/lib/gamebox/version.rb +2 -2
  23. data/lib/gamebox/views/graphical_actor_view.rb +11 -6
  24. data/spec/acceptance/input_mapper_spec.rb +14 -14
  25. data/spec/actors/label_spec.rb +13 -4
  26. data/spec/behaviors/collidable_spec.rb +2 -2
  27. data/spec/behaviors/positioned_spec.rb +27 -30
  28. data/spec/core/actor_spec.rb +2 -2
  29. data/spec/core/input_mapper_spec.rb +1 -1
  30. data/spec/core/resource_manager_spec.rb +20 -4
  31. data/spec/core/viewport_spec.rb +202 -108
  32. data/spec/views/graphical_actor_view_spec.rb +4 -1
  33. data/templates/app/Gemfile.tt +3 -1
  34. data/templates/app/Rakefile +51 -0
  35. data/templates/app/config/environment.rb +2 -1
  36. data/templates/app/config/game.yml +0 -1
  37. data/templates/app/spec/helper.rb +1 -1
  38. data/templates/app/src/views/.gitkeep +0 -0
  39. metadata +51 -97
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 29d17db2fdbdf1732b5e68f6b8584d12270e8c3d
4
+ data.tar.gz: b955424dab5554402acf08f8dd425c1af86649c3
5
+ SHA512:
6
+ metadata.gz: 17cbdefcebc2f904c9ccfb4a4d44477b5ffc07a0a5eac840933fc7ff2e68da7fe7e343835eb3a69a98c52ec1fdd5d63d84a228dcebe55676690b883571c7c133
7
+ data.tar.gz: 1e3ac16757af09d3404decd43298a5509fbb0461fff5bfe1cbbcca7af86562ef6f6d5e6c60a88bdd82d8603bf40ae3b86e070400980445be80f5f9a6ecf1457f
data/README.md CHANGED
@@ -3,12 +3,13 @@
3
3
  [![Gamebox Build Status](https://secure.travis-ci.org/shawn42/gamebox.png)](http://travis-ci.org/shawn42/gamebox)
4
4
  [![Gamebox Deps Status](https://gemnasium.com/shawn42/gamebox.png)](https://gemnasium.com/shawn42/gamebox)
5
5
  [![Code Climate](https://codeclimate.com/github/shawn42/gamebox.png)](https://codeclimate.com/github/shawn42/gamebox)
6
+ [![Stories in Ready](https://badge.waffle.io/shawn42/gamebox.png)](http://waffle.io/shawn42/gamebox)
6
7
 
7
8
  * A **game template** for building and distributing Gosu games.
8
9
  * Quickly **generate a game** and have it **up and running**.
9
10
  * Provide **conventions and DSL** for building your game.
10
11
  * Facilitate quickly building **distributable artifacts**.
11
- * http://shawn42.github.com/gamebox/
12
+ * http://gamebox.io/
12
13
  * see [gamebox on rubygems.org](https://rubygems.org/gems/gamebox) for the list of requirements
13
14
 
14
15
  ## Getting started with Gamebox
@@ -102,7 +103,7 @@ end
102
103
 
103
104
  ### Behaviors
104
105
 
105
- Behaviors are what bring life to actors. They interact interact with the actor's internal data, input, audio, etc.
106
+ Behaviors are what bring life to actors. They interact with the actor's internal data, input, audio, etc.
106
107
 
107
108
  ```ruby
108
109
  define_behavior :projectile do
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.add_dependency "thor"#, ">= 0.14.6"
27
27
  s.add_dependency "require_all"
28
28
  s.add_dependency "kvo", ">= 0.1.0"
29
- s.add_dependency "listen", ">= 0.5.3"
29
+ s.add_dependency "listen", ">= 2.4.0"
30
30
 
31
31
  s.add_development_dependency "pry", '~>0.9.7'
32
32
  s.add_development_dependency "pry-remote"
@@ -4,7 +4,7 @@ end
4
4
 
5
5
  define_behavior :collider_container do
6
6
  setup do
7
- actor.has_attributes collider: actor.opts[:collider]
7
+ raise "collider required" unless actor.do_or_do_not(:collider)
8
8
  actor.collider.when :remove do
9
9
  actor.remove
10
10
  end
@@ -14,13 +14,12 @@ end
14
14
  define_actor_view :collidable_debugger_view do
15
15
 
16
16
  setup do
17
- @color = Color::WHITE
17
+ @color = actor.do_or_do_not(:color) || Color::WHITE
18
18
  end
19
19
 
20
20
  draw do |target,x_off,y_off,z|
21
21
  collider = actor.collider
22
- case collider.shape_type
23
- when :circle
22
+ if collider.shape_type == :circle
24
23
  target.draw_circle x_off+collider.center_x, y_off+collider.center_y, collider.radius, @color, z
25
24
  else
26
25
  collider.cw_world_lines.each do |line|
@@ -35,6 +35,8 @@ define_actor :label do
35
35
  actor.font_style.color = actor.color
36
36
  end
37
37
 
38
+ recalculate_size
39
+
38
40
  end
39
41
 
40
42
  helpers do
@@ -2,42 +2,21 @@ define_behavior :score_keeper do
2
2
  requires :backstage, :stage
3
3
 
4
4
  setup do
5
- clear if score.nil?
6
5
  # TODO helper for "attached subactor"
7
6
  label = stage.create_actor(:label, actor.attributes)
8
- actor.has_attributes label: label
7
+ actor.has_attributes label: label, score: 0
9
8
  actor.when :position_changed do
10
9
  actor.label.x = actor.x
11
10
  actor.label.y = actor.y
12
11
  end
13
- actor.when :remove_me do
14
- label.remove
15
- end
12
+ actor.when(:remove_me) { label.remove }
13
+ actor.when(:score_changed) { update_text }
16
14
  update_text
17
- reacts_with :subtract, :add
18
15
  end
19
16
 
20
17
  helpers do
21
- def score
22
- backstage[:score]
23
- end
24
-
25
- def clear
26
- backstage[:score] = 0
27
- end
28
-
29
- def add(amount)
30
- backstage[:score] += amount
31
- update_text
32
- end
33
-
34
- def subtract(amount)
35
- backstage[:score] -= amount
36
- update_text
37
- end
38
-
39
18
  def update_text
40
- actor.label.text = score
19
+ actor.label.text = actor.score
41
20
  end
42
21
  end
43
22
  end
@@ -0,0 +1,97 @@
1
+ define_behavior :animated_with_spritemap do
2
+ requires :resource_manager, :director
3
+ setup do
4
+
5
+ actor.has_attributes animation_file: opts[:file],
6
+ action: :idle,
7
+ animating: true,
8
+ image: nil,
9
+ width: 1,
10
+ height: 1
11
+
12
+ setup_animation if actor.animation_file?
13
+
14
+ actor.when :animation_file_changed do
15
+ setup_animation
16
+ end
17
+
18
+ end
19
+
20
+ remove do
21
+ actor.unsubscribe_all self
22
+ end
23
+
24
+ helpers do
25
+ def setup_animation
26
+ @frame_update_time = opts[:interval] || 60
27
+ @frame_time = 0
28
+
29
+ @frame_num = 0
30
+
31
+ file = actor.animation_file
32
+ rows, cols, actions = opts[:rows], opts[:cols], opts[:actions]
33
+ # @spritemap = resource_manager.load_image file
34
+
35
+ # negatives means rows/cols instead of w/h
36
+ # http://www.libgosu.org/rdoc/Gosu/Image.html#load_tiles-class_method
37
+ @sprites = resource_manager.load_tiles file, -cols, -rows
38
+
39
+ @frames = {}
40
+ actions.each do |action, frames|
41
+ frames = frames.to_a if frames.is_a?(Range)
42
+ images = Array.wrap(frames).map { |f| @sprites[f] }
43
+ @frames[action] = images
44
+ end
45
+
46
+ actor.when :action_changed do |old_action, new_action|
47
+ unless old_action == new_action
48
+ action_changed old_action, new_action
49
+ actor.animating = @frames[new_action].size > 1
50
+ end
51
+ end
52
+
53
+ action_changed nil, actor.action
54
+
55
+ director.when :update do |time|
56
+ if actor.animating
57
+ @frame_time += time
58
+ if @frame_time > @frame_update_time
59
+ next_frame
60
+ @frame_time = @frame_time-@frame_update_time
61
+ end
62
+ set_frame
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ def next_frame
69
+ action_set = @frames[actor.action]
70
+ return unless action_set
71
+ @frame_num = @frame_num + 1
72
+ if @frame_num >= action_set.size
73
+ actor.emit :action_loop_complete
74
+ @frame_num = 0
75
+ end
76
+ # @frame_num = (@frame_num + 1) % action_set.size unless action_set.nil?
77
+ end
78
+
79
+ def action_changed(old_action, new_action)
80
+ @frame_num = 0
81
+ set_frame
82
+ end
83
+
84
+ def set_frame
85
+ action_set = @frames[actor.action]
86
+ raise "unknown action set #{actor.action} for #{actor}" if action_set.nil?
87
+
88
+ image = action_set[@frame_num]
89
+ actor.image = image
90
+ actor.width = image.width
91
+ actor.height = image.height
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
@@ -15,24 +15,16 @@ Behavior.define :collidable do
15
15
  y = (actor.do_or_do_not(:y) || 0) - hh
16
16
 
17
17
  actor.has_attributes( shape_type: shape_type,
18
- width: w,
19
- height: h,
20
18
  x: x,
21
- y: y )
19
+ y: y,
20
+ width: w,
21
+ height: h
22
+ )
22
23
 
23
- shape =
24
- case shape_type
25
- when :circle
26
- CircleCollidable.new actor, opts
27
- when :aabb
28
- AaBbCollidable.new actor, opts
29
- when :polygon
30
- PolygonCollidable.new actor, opts
31
- end
24
+ shape_klass = shape_type.to_s.capitalize + "Collidable"
25
+ shape = Object.const_get(shape_klass).new actor, opts
32
26
  shape.setup
33
27
 
34
- actor.width = shape.width
35
- actor.height = shape.height
36
28
  bb = Rect.new
37
29
  bb.x = actor.x
38
30
  bb.y = actor.y
@@ -1,14 +1,13 @@
1
1
 
2
2
 
3
- class AaBbCollidable < CollidableShape
3
+ class AabbCollidable < CollidableShape
4
4
  attr_accessor :cw_local_points
5
5
 
6
6
  def setup
7
7
  @shape_type = opts[:shape]
8
8
 
9
9
  @cw_local_points = opts[:cw_local_points]
10
- @cw_local_points ||= opts[:points] || []
11
- @cw_world_points ||= build_aabb
10
+ @cw_local_points ||= opts[:points] || build_aabb
12
11
 
13
12
  @radius = opts[:radius]
14
13
  @radius ||= calculate_radius
@@ -4,7 +4,7 @@
4
4
  # data/graphics/classname.png
5
5
  Behavior.define :graphical do
6
6
 
7
- requires_behaviors :layered
7
+ requires_behaviors :layered, :positioned
8
8
  requires :resource_manager
9
9
  setup do
10
10
  image = actor.do_or_do_not(:image) || resource_manager.load_actor_image(actor)
@@ -23,6 +23,7 @@ Behavior.define :graphical do
23
23
  scale: scale,
24
24
  x_scale: @opts[:x_scale] || scale,
25
25
  y_scale: @opts[:y_scale] || scale,
26
+ anchor: @opts[:anchor] || :center,
26
27
  rotation: 0.0 )
27
28
 
28
29
  actor.when :image_changed do |old, new|
@@ -188,7 +188,7 @@ Behavior.define :physical do
188
188
 
189
189
  def warp(new_p)
190
190
  actor.body.p = new_p
191
- physics_manager.space.rehash_static if opts[:fixed]
191
+ physics_manager.space.reindex_static if opts[:fixed]
192
192
  end
193
193
 
194
194
  # TODO use react_to to handle these requests?
@@ -1,11 +1,17 @@
1
1
  define_behavior :positioned do
2
2
  setup do
3
- x = opts[:x] || 0
4
- y = opts[:y] || 0
5
- actor.has_attributes x: x, y: y
6
- actor.has_attributes position: vec2(actor.x, actor.y)
3
+ x = opts[:x] || actor.do_or_do_not(:x) || 0
4
+ y = opts[:y] || actor.do_or_do_not(:y) || 0
5
+ actor.has_attributes position: vec2(x, y), x: x, y: y
7
6
  actor.when(:x_changed) { actor.position = vec2(actor.x, actor.y) }
8
7
  actor.when(:y_changed) { actor.position = vec2(actor.x, actor.y) }
8
+
9
+ actor.when(:position_changed) do
10
+ actor.update_attributes(
11
+ x: actor.position.x,
12
+ y: actor.position.y
13
+ )
14
+ end
9
15
  end
10
16
 
11
17
  end
@@ -70,7 +70,7 @@ class Actor
70
70
  emit :remove_me
71
71
  end
72
72
 
73
- def input
73
+ def controller
74
74
  # TODO conject should have a lazily loaded dependency mechanism
75
75
  @input_mapper ||= this_object_context[:input_mapper]
76
76
  end
@@ -22,6 +22,8 @@ class ActorFactory
22
22
  model.has_attributes attr
23
23
  end
24
24
 
25
+ model.has_attributes(view: "#{actor}_view") if actor_definition.view_blk
26
+
25
27
  actor_definition.behaviors.each do |behavior|
26
28
  beh_opts = {}
27
29
  beh_key = behavior
@@ -97,6 +97,8 @@ module Arbiter
97
97
 
98
98
 
99
99
 
100
+ # This reads terribly and violates open-closed principle,
101
+ # but does perform better than any dynamic lookup based on shape_types
100
102
  def collide?(object, other)
101
103
  case object.shape_type
102
104
  when :circle
@@ -13,11 +13,11 @@ class FontStyle
13
13
  end
14
14
 
15
15
  def calc_width(text)
16
- @font.text_width text
16
+ @font.text_width text if @font
17
17
  end
18
18
 
19
19
  def height
20
- @font.height
20
+ @font.height if @font
21
21
  end
22
22
 
23
23
  def reload
@@ -7,10 +7,10 @@ class InputMapper
7
7
  @action_ids = {}
8
8
  end
9
9
 
10
- # map_input is to be setup outside of an actor and passed in at construction
10
+ # map_controls is to be setup outside of an actor and passed in at construction
11
11
  # time ie:
12
12
  #
13
- # mapper.map_input {
13
+ # mapper.map_controls {
14
14
  # '+a' => :jump, # emit jump when 'a' is _pressed_
15
15
  # '-b' => :duck, # emit duck when 'b' is _released_
16
16
  # 'c' => :block # emit block when 'c' is _pressed_ AND _released_
@@ -20,7 +20,7 @@ class InputMapper
20
20
  # create_actor :fighter, input: mapper
21
21
  #
22
22
  # all of the actor's behaviors can / should use the input mapper instead of raw key bindings
23
- def map_input(input_hash)
23
+ def map_controls(input_hash)
24
24
  input_hash.each do |input, actions|
25
25
  if input.start_with? '-'
26
26
  register_key_released(input[1..-1], actions)
@@ -35,7 +35,7 @@ class InputMapper
35
35
 
36
36
  # unsubscribes for all input
37
37
  def clear
38
- input_manager.unsubscribe_all
38
+ input_manager.unsubscribe_all self
39
39
  end
40
40
 
41
41
  def method_missing(name, *args)
@@ -14,8 +14,8 @@ class ResourceManager
14
14
 
15
15
  def load_actor_image(actor)
16
16
  # use pngs only for now
17
- # actor_name = Inflector.underscore(actor.class)
18
- return load_image("#{actor.actor_type}.png")
17
+ image_name = actor.do_or_do_not(:image_name) || actor.actor_type
18
+ return load_image("#{image_name}.png")
19
19
  end
20
20
 
21
21
  def load_animation_set(actor, action)
@@ -10,6 +10,7 @@ class Viewport
10
10
  :height, :x_offset_range, :y_offset_range, :boundary, :rotation
11
11
 
12
12
  attr_reader :speed
13
+ alias :follow_target? :follow_target
13
14
 
14
15
  def debug
15
16
  "xoff:#{@x_offset} yoff:#{@y_offset}"
@@ -59,64 +60,29 @@ class Viewport
59
60
  end
60
61
 
61
62
  def update(time)
62
- scrolled = false
63
- if @follow_target
64
- x = @follow_target.x
65
- y = @follow_target.y
66
- if @x_offset_range
67
- x = @x_offset_range.min if @x_offset_range.min > x
68
- x = @x_offset_range.max if @x_offset_range.max < x
69
- end
70
- if @y_offset_range
71
- y = @y_offset_range.min if @y_offset_range.min > y
72
- y = @y_offset_range.max if @y_offset_range.max < y
73
- end
74
- x_diff = @width/2 + @follow_offset_x - x - @x_offset
75
- if x_diff.abs > @buffer_x
76
- # move screen
77
- if x_diff > 0
78
- @x_offset += (x_diff - @buffer_x) * @speed
79
- else
80
- @x_offset += (x_diff + @buffer_x) * @speed
81
- end
82
-
83
- scrolled = true
84
- end
85
-
86
- y_diff = @height/2 + @follow_offset_y - y - @y_offset
87
- if y_diff.abs > @buffer_y
88
- # move screen
89
- if y_diff > 0
90
- @y_offset += (y_diff - @buffer_y) * @speed
91
- else
92
- @y_offset += (y_diff + @buffer_y) * @speed
93
- end
94
- scrolled = true
95
- end
96
-
97
- # constrain_x_offset
98
- if @boundary
99
- if @x_offset > 0 - @boundary[0] # Left-wall bump
100
- @x_offset = @boundary[0]
101
- elsif @x_offset < @width - @boundary[2] # right-wall bump
102
- @x_offset = @width - @boundary[2]
103
- end
104
- end
105
-
106
- # constrain_y_offset
107
- if @boundary
108
- if @y_offset > 0 - @boundary[1]
109
- @y_offset = @boundary[1]
110
- elsif @y_offset < @height - @boundary[3]
111
- @y_offset = @height - @boundary[3]
112
- end
113
- end
114
-
63
+ if follow_target?
64
+ scrolled = move_towards_target
65
+ clamp_to_boundary
115
66
  fire :scrolled if scrolled
116
67
  end
117
68
  end
118
69
 
70
+ # Viewport will stay centered on the targets x,y
71
+ #
72
+ # args hash:
73
+ # :x_offset - keep the viewport centered on x + x_offset
74
+ # :y_offset - keep the viewport centered on y + y_offset
75
+ # :x_chain_length - allow this much x slack when following
76
+ # :y_chain_length - allow this much y slack when following
77
+ def stay_centered_on(target, args={})
78
+ offset_x = args[:x_offset] || 0
79
+ offset_y = args[:y_offset] || 0
80
+ x_chain_length = args[:x_chain_length] || 0
81
+ y_chain_length = args[:y_chain_length] || 0
82
+ follow target, [-offset_x, -offset_y], [x_chain_length, y_chain_length]
83
+ end
119
84
 
85
+ # deprecated; use #stay_centered_on
120
86
  def follow(target, off=[0,0], buff=[0,0])
121
87
  @follow_target = target
122
88
  @follow_offset_x = off[0]
@@ -133,7 +99,65 @@ class Viewport
133
99
  def bounds
134
100
  left = -@x_offset
135
101
  top = -@y_offset
136
- Rect.new left, top, left + @width, top + @height
102
+ # Rect.new left, top, left + @width, top + @height
103
+ Rect.new left, top, @width, @height
104
+ end
105
+
106
+ private
107
+ def move_towards_target
108
+ scrolled = false
109
+
110
+ x = @follow_target.x
111
+ y = @follow_target.y
112
+
113
+ if @x_offset_range
114
+ x = @x_offset_range.min if @x_offset_range.min > x
115
+ x = @x_offset_range.max if @x_offset_range.max < x
116
+ end
117
+ if @y_offset_range
118
+ y = @y_offset_range.min if @y_offset_range.min > y
119
+ y = @y_offset_range.max if @y_offset_range.max < y
120
+ end
121
+ x_diff = @width/2 + @follow_offset_x - x - @x_offset
122
+ if x_diff.abs > @buffer_x
123
+ # move screen
124
+ if x_diff > 0
125
+ @x_offset += (x_diff - @buffer_x) * @speed
126
+ else
127
+ @x_offset += (x_diff + @buffer_x) * @speed
128
+ end
129
+
130
+ scrolled = true
131
+ end
132
+
133
+ y_diff = @height/2 + @follow_offset_y - y - @y_offset
134
+ if y_diff.abs > @buffer_y
135
+ # move screen
136
+ if y_diff > 0
137
+ @y_offset += (y_diff - @buffer_y) * @speed
138
+ else
139
+ @y_offset += (y_diff + @buffer_y) * @speed
140
+ end
141
+ scrolled = true
142
+ end
143
+
144
+ scrolled
145
+ end
146
+
147
+ def clamp_to_boundary
148
+ if @boundary
149
+ if @x_offset > 0 - @boundary[0] # Left-wall bump
150
+ @x_offset = @boundary[0]
151
+ elsif @x_offset < @width - @boundary[2] # right-wall bump
152
+ @x_offset = @width - @boundary[2]
153
+ end
154
+
155
+ if @y_offset > 0 - @boundary[1]
156
+ @y_offset = @boundary[1]
157
+ elsif @y_offset < @height - @boundary[3]
158
+ @y_offset = @height - @boundary[3]
159
+ end
160
+ end
137
161
  end
138
162
 
139
163
  end