metro 0.0.1 → 0.0.2

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