gamebox 0.4.1 → 0.5.0

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