metro 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +1 -0
- data/bin/metro +11 -0
- data/lib/metro.rb +51 -0
- data/lib/metro/event_relay.rb +56 -0
- data/lib/metro/game.rb +37 -0
- data/lib/metro/game/dsl.rb +29 -0
- data/lib/metro/model.rb +12 -0
- data/lib/metro/scene.rb +192 -0
- data/lib/metro/scene_view/components/drawer.rb +44 -0
- data/lib/metro/scene_view/components/label.rb +23 -0
- data/lib/metro/scene_view/components/select.rb +62 -0
- data/lib/metro/scene_view/components/unsupported_component.rb +9 -0
- data/lib/metro/scene_view/json_view.rb +21 -0
- data/lib/metro/scene_view/no_view.rb +15 -0
- data/lib/metro/scene_view/scene_view.rb +52 -0
- data/lib/metro/scene_view/yaml_view.rb +21 -0
- data/lib/metro/scenes.rb +23 -0
- data/lib/metro/version.rb +3 -0
- data/lib/metro/window.rb +39 -0
- data/metro.gemspec +21 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Franklin Webber
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# metro
|
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.
|
4
|
+
|
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
|
+
|
7
|
+
Please take a look at the [example game project](https://github.com/burtlo/starry-knight) that I am building alongside of 'metro' to see how it can be used to develop games.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'metro'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install metro
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```
|
26
|
+
bundle exec metro [gamefilename]
|
27
|
+
```
|
28
|
+
|
29
|
+
By default Metro will look for a file named 'metro' within the current working directory if no *gamefilename* has been provided.
|
30
|
+
|
31
|
+
Please take a look at the [example game project](https://github.com/burtlo/starry-knight) that I am building alongside of 'metro' to see how it can be used to develop games.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/metro
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
path = __FILE__
|
4
|
+
while File.symlink?(path)
|
5
|
+
path = File.expand_path(File.readlink(__FILE__), File.dirname(__FILE__))
|
6
|
+
end
|
7
|
+
$:.unshift(File.join(File.dirname(File.expand_path(path)), '..', 'lib'))
|
8
|
+
|
9
|
+
require 'metro'
|
10
|
+
|
11
|
+
Metro.run(*ARGV)
|
data/lib/metro.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'metro/version'
|
5
|
+
require 'metro/window'
|
6
|
+
require 'metro/game'
|
7
|
+
require 'metro/model'
|
8
|
+
require 'metro/scene'
|
9
|
+
|
10
|
+
def asset_path(name)
|
11
|
+
File.join Dir.pwd, "assets", name
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def log
|
16
|
+
@log ||= begin
|
17
|
+
logger = Logger.new(STDOUT)
|
18
|
+
logger.level = Logger::DEBUG
|
19
|
+
logger
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Metro
|
24
|
+
extend self
|
25
|
+
|
26
|
+
def run(filename="metro")
|
27
|
+
load_game_files
|
28
|
+
load_game_configuration(filename)
|
29
|
+
start_game
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_game_files
|
33
|
+
$LOAD_PATH.unshift(Dir.pwd) unless $LOAD_PATH.include?(Dir.pwd)
|
34
|
+
Dir['models/*.rb'].each {|model| require model }
|
35
|
+
Dir['scenes/*.rb'].each {|scene| require scene }
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_game_configuration(filename)
|
39
|
+
game_contents = File.read(filename)
|
40
|
+
game_block = lambda {|instance| eval(game_contents) }
|
41
|
+
game = Game::DSL.parse(&game_block)
|
42
|
+
Game.setup game
|
43
|
+
end
|
44
|
+
|
45
|
+
def start_game
|
46
|
+
window = Window.new Game.width, Game.height, false
|
47
|
+
window.scene = Game.first_scene.new(window)
|
48
|
+
window.show
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Metro
|
2
|
+
class EventRelay
|
3
|
+
|
4
|
+
attr_reader :target, :window
|
5
|
+
|
6
|
+
attr_reader :up_actions, :down_actions, :held_actions
|
7
|
+
|
8
|
+
def initialize(target,window)
|
9
|
+
@target = target
|
10
|
+
@window = window
|
11
|
+
@up_actions ||= Hash.new(:_no_action)
|
12
|
+
@down_actions ||= Hash.new(:_no_action)
|
13
|
+
@held_actions ||= Hash.new(:_no_action)
|
14
|
+
end
|
15
|
+
|
16
|
+
def on(hash,args,block)
|
17
|
+
options = (args.last.is_a?(Hash) ? args.pop : {})
|
18
|
+
|
19
|
+
args.each do |keystroke|
|
20
|
+
hash[keystroke] = block || lambda { |instance| send(options[:do]) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_up(*args,&block)
|
25
|
+
on(@up_actions,args,block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_down(*args,&block)
|
29
|
+
on(@down_actions,args,block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_hold(*args,&block)
|
33
|
+
on(@held_actions,args,block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def button_up(id)
|
37
|
+
action = up_actions[id]
|
38
|
+
target.instance_eval(&action)
|
39
|
+
end
|
40
|
+
|
41
|
+
def trigger_held_buttons
|
42
|
+
held_actions.each do |key,action|
|
43
|
+
target.instance_eval(&action) if window.button_down?(key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
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
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/lib/metro/game.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'metro/game/dsl'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
module Game
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def setup(game_configuration)
|
8
|
+
@config = game_configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :config
|
12
|
+
|
13
|
+
def first_scene
|
14
|
+
config.first_scene
|
15
|
+
end
|
16
|
+
|
17
|
+
def width
|
18
|
+
config.width || 640
|
19
|
+
end
|
20
|
+
|
21
|
+
def height
|
22
|
+
config.height || 480
|
23
|
+
end
|
24
|
+
|
25
|
+
def dimensions
|
26
|
+
[ width, height ]
|
27
|
+
end
|
28
|
+
|
29
|
+
def center
|
30
|
+
[ width / 2 , height / 2 ]
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO: ZOrder related constants that belong to Starry Knight
|
34
|
+
Background, Stars, Players, UI = *0..3
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Metro
|
2
|
+
module Game
|
3
|
+
class DSL
|
4
|
+
|
5
|
+
def self.parse(&block)
|
6
|
+
config = new
|
7
|
+
config.instance_eval(&block)
|
8
|
+
config
|
9
|
+
end
|
10
|
+
|
11
|
+
def first_scene(scene_name = nil)
|
12
|
+
scene_name ? @first_scene = Scenes.find(scene_name) : @first_scene
|
13
|
+
end
|
14
|
+
|
15
|
+
def width(game_width = nil)
|
16
|
+
game_width ? @width = game_width : @width
|
17
|
+
end
|
18
|
+
|
19
|
+
def height(game_height = nil)
|
20
|
+
game_height ? @height = game_height : @height
|
21
|
+
end
|
22
|
+
|
23
|
+
def resolution(w,h)
|
24
|
+
[ width(w), height(h) ]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/metro/model.rb
ADDED
data/lib/metro/scene.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
require_relative 'scene_view/scene_view'
|
2
|
+
require_relative 'scene_view/components/drawer'
|
3
|
+
require_relative 'event_relay'
|
4
|
+
|
5
|
+
module Metro
|
6
|
+
|
7
|
+
#
|
8
|
+
# A scene is a basic unit of a game. Within a scene you define a number of methods
|
9
|
+
# that handle the initial setup, event configuration, logic updating, and drawing.
|
10
|
+
#
|
11
|
+
# @see #show
|
12
|
+
# @see #update
|
13
|
+
# @see #draw
|
14
|
+
# @see #events
|
15
|
+
#
|
16
|
+
# A fair number of private methods within Scene are prefaced with an underscore.
|
17
|
+
# These methods often call non-underscored methods within those methods. This allows
|
18
|
+
# for scene to configure or perform some functionality, while providing an interface
|
19
|
+
# so that every subclass does not have to constantly call `super`.
|
20
|
+
#
|
21
|
+
class Scene
|
22
|
+
|
23
|
+
#
|
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.
|
27
|
+
#
|
28
|
+
# @see Metro::Window
|
29
|
+
# @see Gosu::Window
|
30
|
+
#
|
31
|
+
attr_reader :window
|
32
|
+
|
33
|
+
#
|
34
|
+
# The events object that is configured through the {#events} method, which stores
|
35
|
+
# all the gamepad and keyboard events defined.
|
36
|
+
#
|
37
|
+
# @see Events
|
38
|
+
#
|
39
|
+
attr_reader :event_relays
|
40
|
+
|
41
|
+
#
|
42
|
+
# Customized views that contain elements to be drawn will be handled by the
|
43
|
+
# view_drawer.
|
44
|
+
#
|
45
|
+
# @see SceneView::Drawer
|
46
|
+
#
|
47
|
+
attr_reader :view_drawer
|
48
|
+
|
49
|
+
#
|
50
|
+
# Captures all classes that subclass Scene.
|
51
|
+
#
|
52
|
+
def self.inherited(base)
|
53
|
+
scenes << base
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# All subclasses of Scene, this should be all the defined scenes
|
58
|
+
# within the game.
|
59
|
+
#
|
60
|
+
# @return an Array of Scene subclasses
|
61
|
+
#
|
62
|
+
def self.scenes
|
63
|
+
@scenes ||= []
|
64
|
+
end
|
65
|
+
|
66
|
+
# This provides the functionality for view handling.
|
67
|
+
include SceneView
|
68
|
+
|
69
|
+
#
|
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.
|
74
|
+
#
|
75
|
+
def initialize(window)
|
76
|
+
@window = window
|
77
|
+
|
78
|
+
@event_relays ||= []
|
79
|
+
|
80
|
+
@scene_events = EventRelay.new(self,window)
|
81
|
+
events(@scene_events)
|
82
|
+
|
83
|
+
@event_relays << @scene_events
|
84
|
+
|
85
|
+
@view_drawer = SceneView::Drawer.new(self)
|
86
|
+
|
87
|
+
show
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# This method should be defined in the Scene subclass.
|
92
|
+
#
|
93
|
+
# @param [Events] e is the object that you can register button presses
|
94
|
+
#
|
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
|
111
|
+
end
|
112
|
+
|
113
|
+
def button_down(id)
|
114
|
+
event_relays.each do |er|
|
115
|
+
er.button_down(id)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# This method is solely a non-action method for when events are triggered
|
121
|
+
# for button up and button down
|
122
|
+
#
|
123
|
+
def _no_action ; end
|
124
|
+
|
125
|
+
# This method is called right after initialization
|
126
|
+
def show ; end
|
127
|
+
|
128
|
+
#
|
129
|
+
# This method handles the logic or game loop for the scene.
|
130
|
+
#
|
131
|
+
# @note This method should be implemented in the subclassed Scene
|
132
|
+
#
|
133
|
+
def update ; end
|
134
|
+
|
135
|
+
#
|
136
|
+
# The `_draw` method is called by the Game Window to allow for any view related
|
137
|
+
# drawing needs to be handled before calling the traditional `draw` method defined
|
138
|
+
# in the subclassed Scenes.
|
139
|
+
#
|
140
|
+
def _draw
|
141
|
+
view_drawer.draw(view)
|
142
|
+
draw
|
143
|
+
end
|
144
|
+
|
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
|
151
|
+
|
152
|
+
#
|
153
|
+
# `transition_to` performs the work of transitioning this scene
|
154
|
+
# to another scene.
|
155
|
+
#
|
156
|
+
# @param [String,Symbol,Class] scene_name the name of the Scene which can be either
|
157
|
+
# the class or a string/symbol representation of the shortened scene name.
|
158
|
+
#
|
159
|
+
def transition_to(scene_name)
|
160
|
+
new_scene = Scenes.create(scene_name,window)
|
161
|
+
_prepare_transition(new_scene)
|
162
|
+
window.scene = new_scene
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
#
|
167
|
+
# Before a scene is transitioned away from to a new scene, this private method is
|
168
|
+
# here to allow for any housekeeping or other work that needs to be done before
|
169
|
+
# calling the subclasses implementation of `prepare_transition`.
|
170
|
+
#
|
171
|
+
# @param [Scene] new_scene this is the instance of the scene that is about to replace
|
172
|
+
# the current scene.
|
173
|
+
#
|
174
|
+
def _prepare_transition(new_scene)
|
175
|
+
log.debug "Preparing to transition from scene #{self} to #{new_scene}"
|
176
|
+
prepare_transition(new_scene)
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
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.
|
183
|
+
#
|
184
|
+
# @note This method should be implemented in the subclassed Scene
|
185
|
+
#
|
186
|
+
# @param [Scene] new_scene this is the instance of the scene that is about to replace
|
187
|
+
# the current scene.
|
188
|
+
#
|
189
|
+
def prepare_transition(new_scene) ; end
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Metro
|
2
|
+
module SceneView
|
3
|
+
class Drawer
|
4
|
+
|
5
|
+
# The window is necessary as all drawing elements created require
|
6
|
+
# an access to this instance.
|
7
|
+
attr_reader :window
|
8
|
+
|
9
|
+
# The scene is necessary to ensure that any fields which contain
|
10
|
+
# variables within the application are properly interpolated.
|
11
|
+
attr_reader :scene
|
12
|
+
|
13
|
+
def initialize(scene)
|
14
|
+
@scene = scene
|
15
|
+
@window = scene.window
|
16
|
+
after_initialize
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_initialize ; end
|
20
|
+
|
21
|
+
def components
|
22
|
+
@components ||= begin
|
23
|
+
Hash.new(UnsupportedComponent).merge label: Label.new(scene),
|
24
|
+
select: Select.new(scene)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Render all the view elements defined that are supported by this drawer.
|
30
|
+
#
|
31
|
+
def draw(view)
|
32
|
+
view.each do |name,content|
|
33
|
+
component = content['type'].to_sym
|
34
|
+
components[component].draw(content)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
require_relative 'unsupported_component'
|
43
|
+
require_relative 'label'
|
44
|
+
require_relative 'select'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Metro
|
2
|
+
module SceneView
|
3
|
+
class Label < Drawer
|
4
|
+
|
5
|
+
def font
|
6
|
+
@font ||= Gosu::Font.new(window, Gosu::default_font_name, 20)
|
7
|
+
end
|
8
|
+
|
9
|
+
def draw(view)
|
10
|
+
label_text = scene.instance_eval( "\"#{view['text']}\"" )
|
11
|
+
|
12
|
+
label_color = view['color'] || 0xffffffff
|
13
|
+
label_color = label_color.to_i(16) if label_color.is_a? String
|
14
|
+
|
15
|
+
font.draw label_text,
|
16
|
+
view['x'], view['y'], view['z-order'],
|
17
|
+
view['x-factor'] || 1.0, view['y-factor'] || 1.0,
|
18
|
+
label_color
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Metro
|
2
|
+
module SceneView
|
3
|
+
class Select < Drawer
|
4
|
+
|
5
|
+
attr_accessor :selected_index, :options
|
6
|
+
|
7
|
+
def after_initialize
|
8
|
+
name, content = scene.view.find { |name,content| content['type'].to_sym == :select }
|
9
|
+
|
10
|
+
if content
|
11
|
+
@selected_index = 0
|
12
|
+
@options = content['options']
|
13
|
+
events = EventRelay.new(self,window)
|
14
|
+
|
15
|
+
events.on_up Gosu::KbLeft, Gosu::GpLeft, Gosu::KbUp, Gosu::GpUp, do: :previous_option
|
16
|
+
events.on_up Gosu::KbRight, Gosu::GpRight, Gosu::KbDown, Gosu::GpDown, do: :next_option
|
17
|
+
events.on_up Gosu::KbEnter, Gosu::KbReturn, Gosu::GpButton0, do: :selection
|
18
|
+
|
19
|
+
scene.add_event_relay events
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def selection
|
24
|
+
scene_method = options[selected_index].downcase.gsub(/\s/,'_')
|
25
|
+
scene.send scene_method
|
26
|
+
end
|
27
|
+
|
28
|
+
def previous_option
|
29
|
+
@selected_index = @selected_index - 1
|
30
|
+
@selected_index = options.length - 1 if @selected_index <= -1
|
31
|
+
end
|
32
|
+
|
33
|
+
def next_option
|
34
|
+
@selected_index = @selected_index + 1
|
35
|
+
@selected_index = 0 if @selected_index >= options.length
|
36
|
+
end
|
37
|
+
|
38
|
+
def _no_action ; end
|
39
|
+
|
40
|
+
def font
|
41
|
+
@font ||= Gosu::Font.new(window, Gosu::default_font_name, 20)
|
42
|
+
end
|
43
|
+
|
44
|
+
def draw(view)
|
45
|
+
view['options'].each_with_index do |option,index|
|
46
|
+
|
47
|
+
color = view["color"] || 0xffffffff
|
48
|
+
|
49
|
+
if index == selected_index
|
50
|
+
color = view["highlight-color"] || 0xffffff00
|
51
|
+
end
|
52
|
+
|
53
|
+
color = color.to_i(16) if color.is_a? String
|
54
|
+
|
55
|
+
y_position = view["y"] + view["padding"] * index
|
56
|
+
font.draw option, view["x"], y_position, Metro::Game::UI, 1.0, 1.0, color
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
module SceneView
|
5
|
+
|
6
|
+
class JSONView
|
7
|
+
def self.find(view_name)
|
8
|
+
File.exists? json_view_name(view_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.parse(view_name)
|
12
|
+
JSON.parse File.read json_view_name(view_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.json_view_name(view_name)
|
16
|
+
File.extname(view_name) == "" ? "#{view_name}.json" : view_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'yaml_view'
|
2
|
+
require_relative 'json_view'
|
3
|
+
require_relative 'no_view'
|
4
|
+
|
5
|
+
module Metro
|
6
|
+
module SceneView
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Supported view formats
|
14
|
+
#
|
15
|
+
def _view_parsers
|
16
|
+
[ YAMLView, JSONView, NoView ]
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Loads the view based on the view parsers.
|
21
|
+
#
|
22
|
+
def view
|
23
|
+
@view ||= begin
|
24
|
+
parser = _view_parsers.find { |parser| parser.find self.class.view_name }
|
25
|
+
parser.parse self.class.view_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
|
31
|
+
#
|
32
|
+
# A Scene by default uses the name of the Scene to find it's associated
|
33
|
+
# view.
|
34
|
+
#
|
35
|
+
# @example Custom View Name
|
36
|
+
#
|
37
|
+
# class ClosingScene < Metro::Scene
|
38
|
+
# view_name :alternate
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
def view_name(filename = nil)
|
42
|
+
if filename
|
43
|
+
@view_name = File.join "views", filename.to_s
|
44
|
+
else
|
45
|
+
@view_name ||= File.join "views", "#{self.to_s[/^(.+)Scene$/,1].downcase}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
module SceneView
|
5
|
+
|
6
|
+
class YAMLView
|
7
|
+
def self.find(view_name)
|
8
|
+
File.exists? yaml_view_name(view_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.parse(view_name)
|
12
|
+
YAML.load File.read yaml_view_name(view_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.yaml_view_name(view_name)
|
16
|
+
File.extname(view_name) == "" ? "#{view_name}.yaml" : view_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/metro/scenes.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Metro
|
2
|
+
module Scenes
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def scenes
|
6
|
+
@scenes ||= Scene.scenes.inject({}) do |dict,scene|
|
7
|
+
name = scene.to_s.gsub(/Scene$/,'').downcase.to_sym
|
8
|
+
dict[name] = scene
|
9
|
+
dict[scene] = scene
|
10
|
+
dict
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(scene_name)
|
15
|
+
scenes[scene_name.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
def create(scene_name,window)
|
19
|
+
find(scene_name).new(window)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/metro/window.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'scenes'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
|
5
|
+
#
|
6
|
+
# A subclass of the Gosu::Window which simply acts as system
|
7
|
+
# to shuffle in and out scenes and transfer events.
|
8
|
+
#
|
9
|
+
class Window < Gosu::Window
|
10
|
+
|
11
|
+
attr_reader :scene
|
12
|
+
|
13
|
+
def initialize(width,height,something)
|
14
|
+
super width, height, something
|
15
|
+
end
|
16
|
+
|
17
|
+
def scene=(new_scene)
|
18
|
+
@scene = new_scene
|
19
|
+
end
|
20
|
+
|
21
|
+
def update
|
22
|
+
scene.trigger_held_buttons
|
23
|
+
scene.update
|
24
|
+
end
|
25
|
+
|
26
|
+
def draw
|
27
|
+
scene._draw
|
28
|
+
end
|
29
|
+
|
30
|
+
def button_up(id)
|
31
|
+
scene.button_up(id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def button_down(id)
|
35
|
+
scene.button_down(id)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/metro.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'metro/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "metro"
|
8
|
+
gem.version = Metro::VERSION
|
9
|
+
gem.authors = ["Franklin Webber"]
|
10
|
+
gem.email = ["franklin.webber@gmail.com"]
|
11
|
+
gem.description = %q{A framework around Gosu to make game creation less tedious}
|
12
|
+
gem.summary = %q{A framework around Gosu to make game creation less tedious}
|
13
|
+
gem.homepage = "https://github.com/burtlo/metro"
|
14
|
+
|
15
|
+
gem.add_dependency 'gosu', '~> 0.7'
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: metro
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Franklin Webber
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: gosu
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.7'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.7'
|
30
|
+
description: A framework around Gosu to make game creation less tedious
|
31
|
+
email:
|
32
|
+
- franklin.webber@gmail.com
|
33
|
+
executables:
|
34
|
+
- metro
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- Gemfile
|
40
|
+
- LICENSE.txt
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- bin/metro
|
44
|
+
- lib/metro.rb
|
45
|
+
- lib/metro/event_relay.rb
|
46
|
+
- lib/metro/game.rb
|
47
|
+
- lib/metro/game/dsl.rb
|
48
|
+
- lib/metro/model.rb
|
49
|
+
- lib/metro/scene.rb
|
50
|
+
- lib/metro/scene_view/components/drawer.rb
|
51
|
+
- lib/metro/scene_view/components/label.rb
|
52
|
+
- lib/metro/scene_view/components/select.rb
|
53
|
+
- lib/metro/scene_view/components/unsupported_component.rb
|
54
|
+
- lib/metro/scene_view/json_view.rb
|
55
|
+
- lib/metro/scene_view/no_view.rb
|
56
|
+
- lib/metro/scene_view/scene_view.rb
|
57
|
+
- lib/metro/scene_view/yaml_view.rb
|
58
|
+
- lib/metro/scenes.rb
|
59
|
+
- lib/metro/version.rb
|
60
|
+
- lib/metro/window.rb
|
61
|
+
- metro.gemspec
|
62
|
+
homepage: https://github.com/burtlo/metro
|
63
|
+
licenses: []
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 1.8.24
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: A framework around Gosu to make game creation less tedious
|
86
|
+
test_files: []
|