metro 0.2.1 → 0.2.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 (45) hide show
  1. data/README.md +2 -0
  2. data/changelog.md +10 -0
  3. data/lib/assets/missing.mp3 +0 -0
  4. data/lib/assets/missing.wav +0 -0
  5. data/lib/gosu_ext/image.rb +4 -0
  6. data/lib/gosu_ext/sample.rb +18 -0
  7. data/lib/gosu_ext/song.rb +18 -0
  8. data/lib/metro.rb +2 -0
  9. data/lib/metro/animation/easing/ease_in.rb +15 -0
  10. data/lib/metro/animation/easing/easing.rb +51 -0
  11. data/lib/metro/animation/easing/linear.rb +15 -0
  12. data/lib/metro/animation/has_animations.rb +17 -0
  13. data/lib/metro/animation/implicit_animation.rb +5 -24
  14. data/lib/metro/models/draws.rb +10 -1
  15. data/lib/metro/models/model.rb +117 -89
  16. data/lib/metro/models/models/image.rb +8 -9
  17. data/lib/metro/models/models/label.rb +4 -8
  18. data/lib/metro/models/models/menu.rb +7 -9
  19. data/lib/metro/models/models/rectangle.rb +7 -12
  20. data/lib/metro/models/models/song.rb +33 -0
  21. data/lib/metro/models/properties/dimensions_property.rb +18 -1
  22. data/lib/metro/models/properties/image_property.rb +2 -2
  23. data/lib/metro/models/properties/position_property.rb +6 -0
  24. data/lib/metro/models/properties/property.rb +129 -23
  25. data/lib/metro/models/properties/sample_property.rb +97 -0
  26. data/lib/metro/models/properties/song_property.rb +113 -0
  27. data/lib/metro/scene.rb +2 -11
  28. data/lib/metro/scenes.rb +14 -7
  29. data/lib/metro/transitions/fade_transition_scene.rb +8 -1
  30. data/lib/metro/units/bounds.rb +8 -0
  31. data/lib/metro/units/dimensions.rb +23 -5
  32. data/lib/metro/units/point.rb +26 -1
  33. data/lib/metro/units/rectangle_bounds.rb +52 -0
  34. data/lib/metro/units/scale.rb +16 -0
  35. data/lib/metro/units/units.rb +3 -1
  36. data/lib/metro/version.rb +1 -1
  37. data/lib/metro/window.rb +7 -0
  38. data/lib/templates/game/README.md.tt +20 -1
  39. data/spec/metro/models/models/label_spec.rb +4 -4
  40. data/spec/metro/models/properties/dimensions_spec.rb +29 -0
  41. data/spec/metro/models/properties/position_property_spec.rb +5 -5
  42. data/spec/spec_helper.rb +3 -1
  43. metadata +22 -9
  44. data/lib/metro/animation/easing.rb +0 -31
  45. data/lib/metro/models/rectangle_bounds.rb +0 -28
data/README.md CHANGED
@@ -12,6 +12,8 @@ Metro is a framework built around [gosu](https://github.com/jlnr/gosu) (the 2D g
12
12
 
13
13
  > NOTE: This project is very early in development and at this point mostly a prototype to explore more of theses concepts to gain an understanding of core tools necessary to make games.
14
14
 
15
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/burtlo/metro)
16
+
15
17
  ## Installation
16
18
 
17
19
  $ gem install metro
data/changelog.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Metro
2
2
 
3
+ ## 0.2.2 / 2012-11-10
4
+
5
+ * Song support added (scene methods and model properties)
6
+ * Sample support added (model properties)
7
+ * Added a missing sample/song
8
+ * Implicit Animation easings can now be more easily created and registered.
9
+ * Properties can now be defined with a block
10
+ * FIX Dimensions parse creation called wrong method
11
+ * Removed support for specifying a color in animation
12
+
3
13
  ## 0.2.1 / 2012-11-08
4
14
 
5
15
  * FIX Scene fade transition color changing and implicit animations
Binary file
Binary file
@@ -34,5 +34,9 @@ module Metro
34
34
  # The tileability of the image
35
35
  attr_reader :tileable
36
36
 
37
+ def dimensions
38
+ Metro::Units::Dimensions.of width, height
39
+ end
40
+
37
41
  end
38
42
  end
@@ -0,0 +1,18 @@
1
+ module Metro
2
+
3
+ #
4
+ # Sample is a wrapper class for a Gosu Sample. This allows for additional data to be stored
5
+ # without relying on monkey-patching on functionality.
6
+ #
7
+ class Sample < SimpleDelegator
8
+
9
+ attr_accessor :sample, :path
10
+
11
+ def initialize(sample,path)
12
+ super(sample)
13
+ @sample = sample
14
+ @path = path
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Metro
2
+
3
+ #
4
+ # Song is a wrapper class for a Gosu Song. This allows for additional data to be stored
5
+ # without relying on monkey-patching on functionality.
6
+ #
7
+ class Song < SimpleDelegator
8
+
9
+ attr_accessor :song, :path
10
+
11
+ def initialize(song,path)
12
+ super(song)
13
+ @song = song
14
+ @path = path
15
+ end
16
+
17
+ end
18
+ end
data/lib/metro.rb CHANGED
@@ -13,6 +13,8 @@ require 'active_support/hash_with_indifferent_access'
13
13
  require 'gosu_ext/color'
14
14
  require 'gosu_ext/image'
15
15
  require 'gosu_ext/animation'
16
+ require 'gosu_ext/song'
17
+ require 'gosu_ext/sample'
16
18
  require 'gosu_ext/gosu_constants'
17
19
  require 'core_ext/numeric'
18
20
 
@@ -0,0 +1,15 @@
1
+ module Metro
2
+ class Easing
3
+
4
+ #
5
+ # Perform a ease-in motion between the start position and the final position.
6
+ #
7
+ class EaseIn < Easing
8
+ def self.calculation(moment,start,change,interval)
9
+ change * (moment = moment / interval) * moment + start
10
+ end
11
+ end
12
+
13
+ register :ease_in, EaseIn
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ module Metro
2
+
3
+ #
4
+ # An easing is a means to calculate the steps between the start and the final
5
+ # over an interval.
6
+ #
7
+ class Easing
8
+
9
+ #
10
+ # The calculate method is called within the ImplicitAnimation and will create
11
+ # an array of all the states between the start and the final value.
12
+ #
13
+ def self.calculate(start,final,interval)
14
+ change = final - start
15
+ (1..interval).map { |time| calculation(time.to_f,start,change,interval) }
16
+ end
17
+
18
+ #
19
+ # The calculation method is to be overriden in the Easing subclasses.
20
+ # This calculation figures out the value at the current moemnt.
21
+ #
22
+ def self.calculation(moment,start,change,interval) ; 0 ; end
23
+
24
+ #
25
+ # Register an easing within the game system.
26
+ #
27
+ def self.register(name,easing_class)
28
+ easings[name] = easing_class.to_s
29
+ end
30
+
31
+ #
32
+ # @return the easing class based on the specified easing name.
33
+ #
34
+ def self.easing_for(easing)
35
+ easing_classname = easings[easing]
36
+ easing_classname.constantize
37
+ end
38
+
39
+ #
40
+ # A hash of all the supported easings within metro or game.
41
+ #
42
+ def self.easings
43
+ @easings_hash ||= HashWithIndifferentAccess.new("Metro::Easing::Linear")
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ require_relative 'linear'
51
+ require_relative 'ease_in'
@@ -0,0 +1,15 @@
1
+ module Metro
2
+ class Easing
3
+
4
+ #
5
+ # Perform a linear motion between the start position and the final position.
6
+ #
7
+ class Linear < Easing
8
+ def self.calculation(moment,start,change,interval)
9
+ change * moment / interval + start
10
+ end
11
+ end
12
+
13
+ register :linear, Linear
14
+ end
15
+ end
@@ -7,6 +7,20 @@ module Metro
7
7
  base.extend ClassMethods
8
8
  end
9
9
 
10
+ #
11
+ # A simplier syntax to enqueue an animation. At the moment this animation is going
12
+ # to be an implicit animation.
13
+ #
14
+ def animate(actor_or_actor_name,options,&block)
15
+ options[:actor] = actor(actor_or_actor_name)
16
+ options[:context] = self
17
+ animation_group = SceneAnimation.build options, &block
18
+ enqueue animation_group
19
+ end
20
+
21
+ # An alternative to stating `animate` syntax.
22
+ alias_method :change, :animate
23
+
10
24
  module ClassMethods
11
25
 
12
26
  #
@@ -24,6 +38,9 @@ module Metro
24
38
  animations.push scene_animation
25
39
  end
26
40
 
41
+ # Provide an alternative to the `animate` syntax.
42
+ alias_method :change, :animate
43
+
27
44
  #
28
45
  # All the animations that are defined for the scene to be run the scene starts.
29
46
  #
@@ -1,4 +1,4 @@
1
- require_relative 'easing'
1
+ require_relative 'easing/easing'
2
2
 
3
3
  module Metro
4
4
 
@@ -52,21 +52,7 @@ module Metro
52
52
  def after_initialize
53
53
  to.each do |attribute,final|
54
54
  start = actor.send(attribute)
55
-
56
- if attribute == :color
57
- final = Gosu::Color.new final
58
-
59
- # @TODO: This is not going to work when the color uses a non-standard
60
- # name for the color property.
61
-
62
- animations.push build_animation_step(:red,start.red,final.red)
63
- animations.push build_animation_step(:green,start.green,final.green)
64
- animations.push build_animation_step(:blue,start.blue,final.blue)
65
- animations.push build_animation_step(:alpha,start.alpha,final.alpha)
66
- else
67
- animations.push build_animation_step(attribute,start,final)
68
- end
69
-
55
+ animations.push build_animation_step(attribute,start,final)
70
56
  end
71
57
  end
72
58
 
@@ -74,7 +60,7 @@ module Metro
74
60
  step = AnimationStep.new
75
61
  step.actor = actor
76
62
  step.attribute = attribute
77
- step.deltas = stepping(easing).calculate(start.to_f,final.to_f,interval.to_f)
63
+ step.deltas = easing_for(easing).calculate(start.to_f,final.to_f,interval.to_f)
78
64
  step
79
65
  end
80
66
 
@@ -90,13 +76,8 @@ module Metro
90
76
  # @return the correct easing based on the specified name. When the name
91
77
  # provided does not match anything then default to linear easing.
92
78
  #
93
- def stepping(stepping)
94
- @steppings ||= begin
95
- hash = HashWithIndifferentAccess.new("Metro::Easing::Linear")
96
- hash.merge! linear: "Metro::Easing::Linear",
97
- ease_in: "Metro::Easing::EaseIn"
98
- end
99
- @steppings[stepping].constantize
79
+ def easing_for(name)
80
+ Metro::Easing.easing_for(name)
100
81
  end
101
82
 
102
83
  #
@@ -12,7 +12,7 @@ module Metro
12
12
  #
13
13
  # Define an actor with the given name and options.
14
14
  #
15
- # As a convience the draw method will define `getter` and `setter`
15
+ # As a convenience the draw method will define `getter` and `setter`
16
16
  # methods for the specified actor.
17
17
  #
18
18
  # @example Defining a title label within a scene
@@ -48,6 +48,15 @@ module Metro
48
48
  drawings.push scene_actor
49
49
  end
50
50
 
51
+ #
52
+ # Define a sound actor with the given anem and options.
53
+ #
54
+ # @see #draw
55
+ #
56
+ def play(song_name,options={})
57
+ draw song_name, options.merge(model: "metro::models::song")
58
+ end
59
+
51
60
  #
52
61
  # Define several actors to be drawn.
53
62
  #
@@ -1,5 +1,4 @@
1
1
  require_relative 'key_value_coding'
2
- require_relative 'rectangle_bounds'
3
2
  require_relative 'properties/property'
4
3
 
5
4
  module Metro
@@ -16,6 +15,31 @@ module Metro
16
15
  class Model
17
16
  include Units
18
17
 
18
+ #
19
+ # This is an entry point for customization. As the model's {#initialize}
20
+ # method performs may perform some initialization that may be necessary.
21
+ #
22
+ # At this point the model has been created. However, the window and scene
23
+ # of the model will not have been defined and defined properties rely on
24
+ # the window or scene will return nil values. Other properties also will
25
+ # likely not be set.
26
+ #
27
+ # @note This method should be implemented in the Model subclass.
28
+ #
29
+ def after_initialize ; end
30
+
31
+
32
+ #
33
+ # This is an entry point for customization. After the model's properties
34
+ # have been set and the model has been assigned to the window and scene
35
+ # this method is called. Here is where customization of properties or
36
+ # final positioning can be performed.
37
+ #
38
+ # @note This method may be implemented in the Model subclass.
39
+ #
40
+ def show ; end
41
+
42
+
19
43
  #
20
44
  # This is called every update interval while the actor is in the scene
21
45
  #
@@ -23,6 +47,7 @@ module Metro
23
47
  #
24
48
  def update ; end
25
49
 
50
+
26
51
  #
27
52
  # This is called after an update. A model normally is not removed after
28
53
  # an update, however if the model responds true to #completed? then it
@@ -34,7 +59,41 @@ module Metro
34
59
  def completed? ; false ; end
35
60
 
36
61
 
37
- def self.property(name,options={})
62
+ #
63
+ # This is called after every {#update} and when the OS wants the window to
64
+ # repaint itself.
65
+ #
66
+ # @note This method should be implemented in the Model subclass.
67
+ #
68
+ def draw ; end
69
+
70
+
71
+
72
+ #
73
+ # Define a property for the model. A property has a name and then can optionally specify
74
+ # a property type which will receive additional options.
75
+ #
76
+ # @example Defining various propertys for a model
77
+ #
78
+ # class Player
79
+ # property :position
80
+ # property :angle, default: 0.0
81
+ # property :turn_amount, default: 4.5
82
+ # property :image, path: "player.png"
83
+ # property :motto, type: :text, default: 'Hometown Heroes!'
84
+ # end
85
+ #
86
+ # When the property name matches a property definition with that name they will be used. This is what
87
+ # happens for the 'position' and 'image' properties defined above. Both of those map to respective
88
+ # properties with matching names.
89
+ #
90
+ # Properties by default are assumed to be numeric properties so the types does not have to be stated.
91
+ # This is the case for 'angle' and 'turn_amount' properties.
92
+ #
93
+ # You may use any particular name for your properties as long as you specify the type. This is the case
94
+ # for the 'motto' property.
95
+ #
96
+ def self.property(name,options={},&block)
38
97
 
39
98
  # Use the name as the property type if one has not been provided.
40
99
 
@@ -42,6 +101,18 @@ module Metro
42
101
 
43
102
  property_class = Property.property(property_type)
44
103
 
104
+ define_method name do
105
+ raw_value = properties[name]
106
+ property_class.new(self,options,&block).get raw_value
107
+ end
108
+
109
+ define_method "#{name}=" do |value|
110
+ prepared_value = property_class.new(self,options).set(value)
111
+ properties[name] = prepared_value
112
+ end
113
+
114
+ # Define any sub-properties defined on this property
115
+
45
116
  # When the name does not match the property type then we want to force
46
117
  # the prefixing to be on for our sub-properties. This is to make sure
47
118
  # that when people define multiple fonts and colors that they do not
@@ -49,72 +120,60 @@ module Metro
49
120
 
50
121
  override_prefix = !(name == property_type)
51
122
 
52
- # Define any properties defined on this property
53
-
54
123
  property_class.defined_properties.each do |subproperty|
55
124
  sub_options = { prefix: override_prefix }.merge(subproperty.options)
56
125
  sub_options = sub_options.merge(parents: (Array(sub_options[:parents]) + [name]))
57
-
58
- property subproperty.name, sub_options
126
+ _sub_property subproperty.name, sub_options
59
127
  end
60
128
 
61
- # To ensure that our sub-properties are aware of the of their
62
- # parent property for which they are dependent on we will need
63
- # to know this information because we need to behave appropriately
64
- # within the getter and setter blocks below.
65
-
66
- is_a_dependency = true if options[:parents]
67
-
68
- if is_a_dependency
129
+ end
69
130
 
70
- parents = Array(options[:parents])
131
+ #
132
+ # Defines the sub-properties defined within the property. This is to be used internally
133
+ # by the #property method.
134
+ #
135
+ def self._sub_property(name,options={},&block)
71
136
 
72
- method_name = name
137
+ # Use the name as the property type if one has not been provided.
73
138
 
74
- if options[:prefix]
75
- method_name = (parents + [name]).join("_")
76
- end
139
+ property_type = options[:type] || name
77
140
 
78
- # Define a getter for the sub-property that will traverse the
79
- # parent properties, finally returning the filtered value
141
+ property_class = Property.property(property_type)
80
142
 
81
- define_method method_name do
82
- raw_value = (parents + [name]).inject(self) {|current,method| current.send(method) }
83
- property_class.new(self,options).get raw_value
84
- end
143
+ parents = Array(options[:parents])
85
144
 
86
- # Define a setter for the sub-property that will find the parent
87
- # value and set itself on that with the filtered value. The parent
88
- # is then set.
89
- #
90
- # @TODO: If getters return dups and not instances of the original object then a very
91
- # deep setter will not be valid.
92
- #
93
- define_method "#{method_name}=" do |value|
94
- parent_value = parents.inject(self) {|current,method| current.send(method) }
145
+ method_name = name
95
146
 
96
- prepared_value = property_class.new(self,options).set(value)
97
- parent_value.send("#{name}=",prepared_value)
147
+ if options[:prefix]
148
+ method_name = (parents + [name]).join("_")
149
+ end
98
150
 
99
- send("#{parents.last}=",parent_value)
100
- end
151
+ # Define a getter for the sub-property that will traverse the
152
+ # parent properties, finally returning the filtered value
101
153
 
102
- else
154
+ define_method method_name do
155
+ raw_value = (parents + [name]).inject(self) {|current,method| current.send(method) }
156
+ property_class.new(self,options).get raw_value
157
+ end
103
158
 
104
- define_method name do
105
- raw_value = properties[name]
106
- property_class.new(self,options).get raw_value
107
- end
159
+ # Define a setter for the sub-property that will find the parent
160
+ # value and set itself on that with the filtered value. The parent
161
+ # is then set.
162
+ #
163
+ # @TODO: If getters return dups and not instances of the original object then a very
164
+ # deep setter will not be valid.
165
+ #
166
+ define_method "#{method_name}=" do |value|
167
+ parent_value = parents.inject(self) {|current,method| current.send(method) }
108
168
 
109
- define_method "#{name}=" do |value|
110
- prepared_value = property_class.new(self,options).set(value)
111
- properties[name] = prepared_value
112
- end
169
+ prepared_value = property_class.new(self,options,&block).set(value)
170
+ parent_value.send("#{name}=",prepared_value)
113
171
 
172
+ send("#{parents.last}=",parent_value)
114
173
  end
115
-
116
174
  end
117
175
 
176
+
118
177
  def properties
119
178
  @properties ||= {}
120
179
  end
@@ -122,14 +181,6 @@ module Metro
122
181
  property :model, type: :text
123
182
  property :name, type: :text
124
183
 
125
- #
126
- # This is called after every {#update} and when the OS wants the window to
127
- # repaint itself.
128
- #
129
- # @note This method should be implemented in the Model subclass.
130
- #
131
- def draw ; end
132
-
133
184
  #
134
185
  # The window that this model that this window is currently being
135
186
  # displayed.
@@ -153,14 +204,6 @@ module Metro
153
204
 
154
205
  include KeyValueCoding
155
206
 
156
- #
157
- # This is an entry point for customization. As the model's {#initialize}
158
- # method performs may perform some initialization that may be necessary.
159
- #
160
- # @note This method should be implemented in the Model subclass.
161
- #
162
- def after_initialize ; end
163
-
164
207
  #
165
208
  # Generate a custom notification event with the given name.
166
209
  #
@@ -253,19 +296,12 @@ module Metro
253
296
  #
254
297
  # Captures all classes that subclass Model.
255
298
  #
256
- # @see #self.scenes
299
+ # @see #self.models_hash
257
300
  #
258
- def self.inherited(base)
259
- models << base.to_s
260
- end
261
-
262
- #
263
- # All subclasses of Model, this should be all the defined model within the game.
264
- #
265
- # @return an Array of Scene subclasses
266
- #
267
- def self.models
268
- @models ||= []
301
+ def self.inherited(model)
302
+ models_hash[model.to_s] = model.to_s
303
+ models_hash[model.to_s.downcase] = model.to_s
304
+ models_hash[model.to_s.underscore] = model.to_s
269
305
  end
270
306
 
271
307
  #
@@ -273,20 +309,11 @@ module Metro
273
309
  #
274
310
  # @return the Model class given the specified model name.
275
311
  def self.model(name)
276
- @models_hash ||= begin
277
-
278
- hash = HashWithIndifferentAccess.new("Metro::Models::Generic")
279
-
280
- models.each do |model|
281
- hash[model.to_s] = model
282
- hash[model.downcase] = model
283
- hash[model.to_s.underscore] = model
284
- end
285
-
286
- hash
287
- end
312
+ models_hash[name]
313
+ end
288
314
 
289
- @models_hash[name]
315
+ def self.models_hash
316
+ @models_hash ||= HashWithIndifferentAccess.new("Metro::Models::Generic")
290
317
  end
291
318
 
292
319
  end
@@ -298,3 +325,4 @@ require_relative 'models/menu'
298
325
  require_relative 'models/image'
299
326
  require_relative 'models/rectangle'
300
327
  require_relative 'models/grid_drawer'
328
+ require_relative 'models/song'