metro 0.0.1 → 0.0.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.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # metro
2
2
 
3
- Metro is a framework built around [gosu](https://github.com/jlnr/gosu) the 2D game development library in Ruby. The goal of Metro is to enforce common conceptual structures and conventions making it easier to quickly generate a game.
3
+ Metro is a framework built around [gosu](https://github.com/jlnr/gosu) (the 2D game development library in Ruby). The goal of Metro is to enforce common conceptual structures and conventions making it easier to quickly generate a game.
4
4
 
5
5
  > 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.
6
6
 
@@ -1,56 +1,225 @@
1
1
  module Metro
2
- class EventRelay
3
-
4
- attr_reader :target, :window
5
2
 
6
- attr_reader :up_actions, :down_actions, :held_actions
3
+ #
4
+ # An EventRelay represents a target's willingness to respond to events
5
+ # generate from the window. An event relay is generate for every scene
6
+ # but additional relays can be generated to also listen for events.
7
+ #
8
+ # An event relay can register a target to listen for the following window
9
+ # events: 'button_down'; 'button_up'; and 'button_held'.
10
+ #
11
+ # @note registering for 'button_held' events require that the window be
12
+ # speicfied. As that is the only way to ask if the button is currently
13
+ # pressed.
14
+ #
15
+ # @see #on_up
16
+ # @see #on_down
17
+ # @see #on_hold
18
+ #
19
+ # A target can also receive events when 'button_up' and 'button_down' events
20
+ # have been fired but did not map to any specified actions. This is similar to
21
+ # Ruby's {#method_missing}.
22
+ #
23
+ # To receive unampped 'button_up' events define a method
24
+ # named `up_action_missing(id)` within your target.
25
+ #
26
+ # To receive unampped 'button_down' events define a method
27
+ # named `down_action_missing(id)` within your target.
28
+ #
29
+ # @example Scene that receives all the 'button_up' and 'button_down' events that
30
+ # are not mapped to actions.
31
+ #
32
+ # class ExampleScene
33
+ # def up_action_missing(id)
34
+ # puts "No up action found for #{id}"
35
+ # end
36
+ #
37
+ # def down_action_missing(id)
38
+ # puts "No down action found for #{id}"
39
+ # end
40
+ # end
41
+ #
42
+ class EventRelay
7
43
 
8
- def initialize(target,window)
44
+ #
45
+ # An event relay is created a with a target and optionally a window.
46
+ #
47
+ # @param [Object] target the object that will execute the code when
48
+ # the button events have fired have been triggered.
49
+ # @param [Window] window the window of the game. This parameter is
50
+ # optional and only required if the events are interested in buttons
51
+ # being held.
52
+ #
53
+ def initialize(target,window = nil)
9
54
  @target = target
10
55
  @window = window
11
- @up_actions ||= Hash.new(:_no_action)
12
- @down_actions ||= Hash.new(:_no_action)
13
- @held_actions ||= Hash.new(:_no_action)
56
+ @up_actions ||= {}
57
+ @down_actions ||= {}
58
+ @held_actions ||= {}
14
59
  end
15
60
 
16
- def on(hash,args,block)
17
- options = (args.last.is_a?(Hash) ? args.pop : {})
61
+ attr_reader :target, :window
18
62
 
19
- args.each do |keystroke|
20
- hash[keystroke] = block || lambda { |instance| send(options[:do]) }
21
- end
63
+ #
64
+ # Register for a button_down event. These events are fired when
65
+ # the button is pressed down. This event only fires once when the
66
+ # button moves from the not pressed to the down state.
67
+ #
68
+ # @example Registering for a button down event to call a method named 'previous_option'
69
+ #
70
+ # class ExampleScene
71
+ # def events(e)
72
+ # e.on_down Gosu::GpLeft, Gosu::GpUp, do: :previous_option
73
+ # end
74
+ #
75
+ # def previous_option
76
+ # @selected_index = @selected_index - 1
77
+ # @selected_index = options.length - 1 if @selected_index <= -1
78
+ # end
79
+ # end
80
+ #
81
+ # Here in this scene if the GpLeft or GpUp buttons are pressed down the method
82
+ # `previous_options` will be executed.
83
+ #
84
+ #
85
+ # @example Registering for a button down event with a block of code to execute
86
+ #
87
+ # class ExampleScene
88
+ # def events(e)
89
+ # e.on_down Gosu::GpLeft, Gosu::GpUp do
90
+ # @selected_index = @selected_index - 1
91
+ # @selected_index = options.length - 1 if @selected_index <= -1
92
+ # end
93
+ # end
94
+ # end
95
+ #
96
+ # This example uses a block instead of a method name but it is absolultey the same
97
+ # as the last example.
98
+ #
99
+ def on_down(*args,&block)
100
+ _on(@down_actions,args,block)
22
101
  end
23
102
 
103
+ #
104
+ # Register for a button_up event. These events are fired when
105
+ # the button is released (from being pressed down). This event only fires
106
+ # once when the button moves from the pressed state to the up state.
107
+ #
108
+ # @example Registering for a button down event to call a method named 'next_option'
109
+ #
110
+ # class ExampleScene
111
+ # def events(e)
112
+ # e.on_up Gosu::KbEscape, do: :leave_scene
113
+ # end
114
+ #
115
+ # def leave_scene
116
+ # transition_to :title
117
+ # end
118
+ # end
119
+ #
120
+ # Here in this scene if the Escape Key is pressed and released the example scene
121
+ # will transition to the title scene.
122
+ #
123
+ # @example Registering for a button up event with a block of code to execute
124
+ #
125
+ # class ExampleScene
126
+ # def events(e)
127
+ # e.on_up Gosu::KbEscape do
128
+ # transition_to :title
129
+ # end
130
+ # end
131
+ # end
132
+ #
133
+ # This example uses a block instead of a method name but it is absolultey the same
134
+ # as the last example.
135
+ #
24
136
  def on_up(*args,&block)
25
- on(@up_actions,args,block)
137
+ _on(@up_actions,args,block)
26
138
  end
27
139
 
28
- def on_down(*args,&block)
29
- on(@down_actions,args,block)
140
+ #
141
+ # Register for a button_held event. These events are fired when
142
+ # the button is currently in the downstate. This event continues to fire at the
143
+ # beginning of every update of a scene until the button is released.
144
+ #
145
+ # @note button_held events require that the window be specified during initialization.
146
+ #
147
+ # @example Registering for button held events
148
+ #
149
+ # class ExampleScene
150
+ # def events(e)
151
+ # e.on_hold Gosu::KbLeft, Gosu::GpLeft do
152
+ # player.turn_left
153
+ # end
154
+ #
155
+ # e.on_hold Gosu::KbRight, Gosu::GpRight do
156
+ # player.turn_right
157
+ # end
158
+ #
159
+ # e.on_hold Gosu::KbUp, Gosu::GpButton0, do: :calculate_accleration
160
+ # end
161
+ #
162
+ # def calculate_acceleration
163
+ # long_complicated_calculated_result = 0
164
+ # # ... multi-line calculations to determine the player acceleration ...
165
+ # player.accelerate = long_complicated_calculated_result
166
+ # end
167
+ # end
168
+ #
169
+ def on_hold(*args,&block)
170
+ log.warn "Registering for a on_hold event requires that a window be provided." unless window
171
+ _on(@held_actions,args,block)
30
172
  end
31
173
 
32
- def on_hold(*args,&block)
33
- on(@held_actions,args,block)
174
+ attr_reader :up_actions, :down_actions, :held_actions
175
+
176
+ def _on(hash,args,block)
177
+ options = (args.last.is_a?(Hash) ? args.pop : {})
178
+
179
+ args.each do |keystroke|
180
+ hash[keystroke] = block || lambda { |instance| send(options[:do]) }
181
+ end
34
182
  end
35
183
 
184
+ #
185
+ # This is called by external or parent source of events, usually a Scene, when a button up event
186
+ # has been triggered.
187
+ #
36
188
  def button_up(id)
37
- action = up_actions[id]
38
- target.instance_eval(&action)
189
+ target.instance_eval( &up_action(id) )
190
+ end
191
+
192
+ #
193
+ # This is called by external or parent source of events, usually a Scene, when a button down
194
+ # event has been triggered.
195
+ #
196
+ def button_down(id)
197
+ target.instance_eval( &down_action(id) )
39
198
  end
40
199
 
41
- def trigger_held_buttons
200
+ #
201
+ # Fire the events mapped to the held buttons within the context
202
+ # of the specified target. This method is differently formatted because held buttons are not
203
+ # events but polling to see if the button is still being held.
204
+ #
205
+ def fire_events_for_held_buttons
42
206
  held_actions.each do |key,action|
43
- target.instance_eval(&action) if window.button_down?(key)
207
+ target.instance_eval(&action) if window and window.button_down?(key)
44
208
  end
45
209
  end
46
210
 
47
- def button_down(id)
48
- down_actions.each do |key,action|
49
- if window.button_down?(key)
50
- target.instance_eval(&action)
51
- end
52
- end
211
+ # @return a block of code that is mapped for the 'button_up' id or a block that will attempt to call out
212
+ # to the action missing method.
213
+ def up_action(id)
214
+ up_actions[id] || lambda {|instance| send(:up_action_missing,id) if respond_to?(:up_action_missing) }
53
215
  end
54
216
 
217
+ # @return a block of code that is mapped for the 'button_down' id or a block that will attempt to call out
218
+ # to the action missing method.
219
+ def down_action(id)
220
+ down_actions[id] || lambda {|instance| send(:down_action_missing,id) if respond_to?(:down_action_missing) }
221
+ end
222
+
223
+
55
224
  end
56
225
  end
@@ -9,7 +9,7 @@ module Metro
9
9
  end
10
10
 
11
11
  def first_scene(scene_name = nil)
12
- scene_name ? @first_scene = Scenes.find(scene_name) : @first_scene
12
+ scene_name ? @first_scene = scene_name : @first_scene
13
13
  end
14
14
 
15
15
  def width(game_width = nil)
@@ -24,6 +24,10 @@ module Metro
24
24
  [ width(w), height(h) ]
25
25
  end
26
26
 
27
+ def fullscreen(set_fullscreen = nil)
28
+ set_fullscreen.nil? ? @fullscreen : @fullscreen = set_fullscreen
29
+ end
30
+
27
31
  end
28
32
  end
29
33
  end
data/lib/metro/game.rb CHANGED
@@ -30,6 +30,10 @@ module Metro
30
30
  [ width / 2 , height / 2 ]
31
31
  end
32
32
 
33
+ def fullscreen?
34
+ !!config.fullscreen
35
+ end
36
+
33
37
  # TODO: ZOrder related constants that belong to Starry Knight
34
38
  Background, Stars, Players, UI = *0..3
35
39
 
data/lib/metro/scene.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require_relative 'scene_view/scene_view'
2
- require_relative 'scene_view/components/drawer'
3
2
  require_relative 'event_relay'
4
3
 
5
4
  module Metro
@@ -21,133 +20,148 @@ module Metro
21
20
  class Scene
22
21
 
23
22
  #
24
- # The window is the main instance of the game. Using window can access a lot of
25
- # underlying Metro::Window, a subclass of Gosu::Window, that the Scene class is
26
- # obfuscating.
23
+ # The events method is where a scene has access to configure the events that it
24
+ # would like to listen for during the scene.
27
25
  #
28
- # @see Metro::Window
29
- # @see Gosu::Window
26
+ # @note This method should be implemented in the Scene subclass.
30
27
  #
31
- attr_reader :window
28
+ # @param [EventRelay] e is the EventRelay that you can register for button up,
29
+ # button down, or button held events.
30
+ #
31
+ # @see EventRelay
32
+ #
33
+ def events(e) ; end
32
34
 
33
35
  #
34
- # The events object that is configured through the {#events} method, which stores
35
- # all the gamepad and keyboard events defined.
36
+ # This method is called right after the scene has been adopted by the window
36
37
  #
37
- # @see Events
38
+ # @note This method should be implemented in the Scene subclass.
38
39
  #
39
- attr_reader :event_relays
40
+ def show ; end
40
41
 
41
42
  #
42
- # Customized views that contain elements to be drawn will be handled by the
43
- # view_drawer.
43
+ # This is called every update interval while the window is being shown.
44
44
  #
45
- # @see SceneView::Drawer
45
+ # @note This method should be implemented in the Scene subclass.
46
46
  #
47
- attr_reader :view_drawer
47
+ def update ; end
48
48
 
49
49
  #
50
- # Captures all classes that subclass Scene.
50
+ # This is called after every {#update} and when the OS wants the window to
51
+ # repaint itself.
51
52
  #
52
- def self.inherited(base)
53
- scenes << base
54
- end
53
+ # @note This method should be implemented in the Scene subclass.
54
+ #
55
+ def draw ; end
55
56
 
56
57
  #
57
- # All subclasses of Scene, this should be all the defined scenes
58
- # within the game.
58
+ # Before a scene is transisitioned away from to a new scene, this method is called
59
+ # to allow for the scene to complete any taskss, stop any actions, or pass any
60
+ # information from the existing scene to the scene that is about to replace it.
59
61
  #
60
- # @return an Array of Scene subclasses
62
+ # @note This method should be implemented in the Scene subclass.
61
63
  #
62
- def self.scenes
63
- @scenes ||= []
64
- end
64
+ # @param [Scene] new_scene this is the instance of the scene that is about to replace
65
+ # the current scene.
66
+ #
67
+ def prepare_transition(new_scene) ; end
65
68
 
66
- # This provides the functionality for view handling.
67
- include SceneView
69
+ #
70
+ # The window is the main instance of the game. Using window can access a lot of
71
+ # underlying Metro::Window, a subclass of Gosu::Window, that the Scene class is
72
+ # obfuscating.
73
+ #
74
+ # @see Metro::Window
75
+ # @see Gosu::Window
76
+ #
77
+ attr_reader :window
68
78
 
69
79
  #
70
- # A scene is created with a window instance. When subclassing a Scene, you should
71
- # hopefully not need to create an {#initialize} method or call `super` but instead
72
- # implement the {#show} method which is the point of incision in the subclasses
73
- # that allow for the subclasses of Scene to be setup correctly.
80
+ # Setting the window places the scene within in the specified window. Which
81
+ # will cause a number of variables and settings to be set up. The {#show}
82
+ # method is called after the window has been set.
74
83
  #
75
- def initialize(window)
84
+ def window=(window)
76
85
  @window = window
77
86
 
78
- @event_relays ||= []
87
+ @event_relays = []
79
88
 
80
89
  @scene_events = EventRelay.new(self,window)
81
90
  events(@scene_events)
82
91
 
83
92
  @event_relays << @scene_events
84
93
 
85
- @view_drawer = SceneView::Drawer.new(self)
86
-
87
94
  show
88
95
  end
89
96
 
90
97
  #
91
- # This method should be defined in the Scene subclass.
98
+ # Allows you to set or retrieve the scene name for the Scene.
92
99
  #
93
- # @param [Events] e is the object that you can register button presses
100
+ # @example Retrieving the default scene name
94
101
  #
95
- def events(e) ; end
96
-
97
- def add_event_relay(event_relay)
98
- @event_relays << event_relay
99
- end
100
-
101
- def trigger_held_buttons
102
- event_relays.each do |er|
103
- er.trigger_held_buttons
104
- end
105
- end
106
-
107
- def button_up(id)
108
- event_relays.each do |er|
109
- er.button_up(id)
110
- end
102
+ # class ExampleScene
103
+ # def show
104
+ # puts "Showing Scene: #{self.class.scene_name}"
105
+ # end
106
+ # end
107
+ #
108
+ # ExampleScene.scene_name
109
+ #
110
+ # @example Setting a custom name for the Scene
111
+ #
112
+ # class RollingCreditsScene
113
+ # scene_name "credits"
114
+ # end
115
+ #
116
+ # @param [String] scene_name when specified it will set the scene name for the class
117
+ # to the value specified.
118
+ #
119
+ # @return the String name of the scene which it can be used as a reference for transitioning
120
+ # or for generating the appropriate view information.
121
+ #
122
+ def self.scene_name(scene_name=nil)
123
+ @scene_name ||= to_s[/^(.+)Scene$/,1].downcase
124
+ scene_name ? @scene_name = scene_name.to_s : @scene_name
111
125
  end
112
126
 
113
- def button_down(id)
114
- event_relays.each do |er|
115
- er.button_down(id)
116
- end
127
+ #
128
+ # @return the string representation of a scene, this is used for debugging.
129
+ #
130
+ def to_s
131
+ "[SCENE: #{self.class.scene_name}(#{self.class})]"
117
132
  end
118
133
 
119
134
  #
120
- # This method is solely a non-action method for when events are triggered
121
- # for button up and button down
135
+ # Captures all classes that subclass Scene.
122
136
  #
123
- def _no_action ; end
124
-
125
- # This method is called right after initialization
126
- def show ; end
137
+ # @see #self.scenes
138
+ #
139
+ def self.inherited(base)
140
+ scenes << base
141
+ end
127
142
 
128
143
  #
129
- # This method handles the logic or game loop for the scene.
144
+ # All subclasses of Scene, this should be all the defined scenes within the game.
130
145
  #
131
- # @note This method should be implemented in the subclassed Scene
146
+ # @return an Array of Scene subclasses
132
147
  #
133
- def update ; end
148
+ def self.scenes
149
+ @scenes ||= []
150
+ end
134
151
 
135
152
  #
136
- # The `_draw` method is called by the Game Window to allow for any view related
153
+ # The `base_draw` method is called by the Game Window. This is allow for any special
137
154
  # drawing needs to be handled before calling the traditional `draw` method defined
138
- # in the subclassed Scenes.
155
+ # in the subclassed Scene.
139
156
  #
140
- def _draw
141
- view_drawer.draw(view)
157
+ def base_draw
142
158
  draw
143
159
  end
144
160
 
145
- #
146
- # This method handles all the visual rendering of the scene.
147
- #
148
- # @note This method should be implemented in the subclassed Scene
149
- #
150
- def draw ; end
161
+ # This provides the functionality for view handling.
162
+ # @note the inclusion of view functionality redefines {#base_draw} so it must proceed
163
+ # the declaration of that method.
164
+ include SceneView
151
165
 
152
166
  #
153
167
  # `transition_to` performs the work of transitioning this scene
@@ -157,7 +171,7 @@ module Metro
157
171
  # the class or a string/symbol representation of the shortened scene name.
158
172
  #
159
173
  def transition_to(scene_name)
160
- new_scene = Scenes.create(scene_name,window)
174
+ new_scene = Scenes.generate(scene_name)
161
175
  _prepare_transition(new_scene)
162
176
  window.scene = new_scene
163
177
  end
@@ -177,16 +191,55 @@ module Metro
177
191
  end
178
192
 
179
193
  #
180
- # Before a scene is transisitioned away from to a new scene, this method is called
181
- # to allow for the scene to complete any taskss, stop any actions, or pass any
182
- # information from the existing scene to the scene that is about to replace it.
194
+ # The events object that is configured through the {#events} method, which stores
195
+ # all the gamepad and keyboard events defined. By default a scene has an event
196
+ # relay defined. Additional relays can be defined based on the components added.
183
197
  #
184
- # @note This method should be implemented in the subclassed Scene
198
+ # @see Events
199
+ # @see #add_event_relay
185
200
  #
186
- # @param [Scene] new_scene this is the instance of the scene that is about to replace
187
- # the current scene.
201
+ attr_reader :event_relays
202
+
188
203
  #
189
- def prepare_transition(new_scene) ; end
204
+ # Add an additional event relay to the list of event relays. It is appended
205
+ # to the end of the list of relays.
206
+ #
207
+ # @param [EventRelay] event_relay an event relay instance that will now
208
+ # receive events generated from this scene.
209
+ #
210
+ def add_event_relay(event_relay)
211
+ @event_relays << event_relay
212
+ end
213
+
214
+ #
215
+ # This method is called during a scene update and will fire all the events
216
+ # that have been defined for all held buttons for all defined event relays.
217
+ #
218
+ def fire_events_for_held_buttons
219
+ event_relays.each do |relay|
220
+ relay.fire_events_for_held_buttons
221
+ end
222
+ end
223
+
224
+ #
225
+ # This method is called before a scene update and passes the button up events
226
+ # to each of the defined event relays.
227
+ #
228
+ def button_up(id)
229
+ event_relays.each do |relay|
230
+ relay.button_up(id)
231
+ end
232
+ end
233
+
234
+ #
235
+ # This method is called before a scene update and passes the button down events
236
+ # to each of the defined event relays.
237
+ #
238
+ def button_down(id)
239
+ event_relays.each do |relay|
240
+ relay.button_down(id)
241
+ end
242
+ end
190
243
 
191
244
  end
192
245
  end
@@ -0,0 +1,17 @@
1
+ module Metro
2
+ module SceneView
3
+
4
+ #
5
+ # This Drawer draws nothing. This is a nod to the age old struggle that
6
+ # as artists we struggle with the impetus and ability to convey a message
7
+ # but simply lack the message.
8
+ #
9
+ # It is the fallback drawer when no suitable drawer can be found.
10
+ #
11
+ class ArtistsBlock < Drawer
12
+ def self.draw(view)
13
+ # log.warn "The component #{view['type']} does not have a supported drawer."
14
+ end
15
+ end
16
+ end
17
+ end