metro 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +2 -0
- data/README.md +18 -19
- data/changelog.md +5 -1
- data/lib/core_ext/numeric.rb +17 -0
- data/lib/metro.rb +8 -3
- data/lib/metro/animation/on_update_operation.rb +7 -7
- data/lib/metro/events/event_relay.rb +43 -1
- data/lib/metro/events/event_state_manager.rb +7 -0
- data/lib/metro/image.rb +13 -0
- data/lib/metro/models/model.rb +12 -2
- data/lib/metro/models/properties/options_property/options.rb +4 -0
- data/lib/metro/models/ui/physics_sprite.rb +105 -0
- data/lib/metro/models/ui/space.rb +152 -0
- data/lib/metro/models/ui/sprite.rb +2 -2
- data/lib/metro/models/ui/tile_map.rb +133 -0
- data/lib/metro/models/ui/tmx/isometric_position.rb +43 -0
- data/lib/metro/models/ui/tmx/orthogonal_position.rb +15 -0
- data/lib/metro/models/ui/tmx/tile_layer.rb +78 -0
- data/lib/metro/models/ui/ui.rb +3 -0
- data/lib/metro/scene.rb +2 -1
- data/lib/metro/units/rectangle_bounds.rb +75 -5
- data/lib/metro/version.rb +4 -4
- data/lib/metro/window.rb +1 -0
- data/lib/templates/game/assets/missing.png +0 -0
- data/lib/tmx_ext/object.rb +61 -0
- data/lib/tmx_ext/object_shape.rb +93 -0
- data/lib/tmx_ext/tile_set.rb +41 -0
- data/metro.gemspec +3 -1
- data/metro.png +0 -0
- data/spec/core_ext/numeric_spec.rb +28 -0
- data/spec/metro/image_spec.rb +33 -0
- data/spec/metro/units/rectangle_bounds_spec.rb +56 -0
- data/spec/tmx_ext/object_spec.rb +49 -0
- data/spec/tmx_ext/tile_set_spec.rb +24 -0
- metadata +56 -27
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZmI2NDBlMjQ5MzRjZGM4ZTMwZmRkMGJjMDM3NDg2YzUwNGM4NmFmYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
N2IwMTFlODhlYzg1ODZkMmU1MzJiNDRkMTQ3Y2Q2ZTc2MWYyMzM5MQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NzI4ZDMyOTBhZTdlOTcwYjgwMWQ3NDJiNWVlZTllNmNlYTY0YWI3ODc5YWNj
|
10
|
+
NGMzMmEwODY2YjBlYjkzMTkxZjUyMWJmNDYxYTMxZDlhOWQ2NjQ4OGIxMDBm
|
11
|
+
OTEwYTM0OTA3ODExNDVhNGFkYTkxNjFjOWU5MjViZWVkMTYyZmY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NjI1ZDY5MDMxNjNkZWU3NzQ5MjBhOTVmMTY2YzM2NWM3MzRmYzY4MWIzYTFj
|
14
|
+
NmRmYTBkOGE0OGI5ZWVlZTFhMmE0ZjljYjFhNzNjZjI4N2NlNTI1MGM2MjJl
|
15
|
+
OTEwODhiMTI5Y2UyMDYyZDg2NmI1MjYyNmU4NjJkZTdkMzg4N2E=
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
|
2
|
-
______ ___ _____
|
3
|
-
___ |/ /_____ __ /_______________
|
4
|
-
__ /|_/ / _ _ \_ __/__ ___/_ __ \
|
5
|
-
_ / / / / __// /_ _ / / /_/ /
|
6
|
-
/_/ /_/ \___/ \__/ /_/ \____/
|
7
|
-
|
8
|
-
```
|
1
|
+
![Metro Image](metro.png)
|
9
2
|
|
10
3
|
Metro is a framework built around [gosu](https://github.com/jlnr/gosu) (the 2D
|
11
4
|
game development library in Ruby). The goal of Metro is to enforce common
|
@@ -133,19 +126,25 @@ Creating a Game can be done with a single command.
|
|
133
126
|
$ metro new GAMENAME
|
134
127
|
```
|
135
128
|
|
136
|
-
This should generate for you a starting game with a branding scene
|
137
|
-
scene. The game allows the player to start the game.
|
129
|
+
This should generate for you a starting game with a branding scene, title
|
130
|
+
scene and first game scene. The game allows the player to start the game.
|
138
131
|
|
139
|
-
|
140
|
-
scene generator:
|
132
|
+
## Resources
|
141
133
|
|
142
|
-
|
143
|
-
$ metro generate scene first
|
144
|
-
```
|
134
|
+
### Metro Documentation
|
145
135
|
|
146
|
-
|
136
|
+
The Metro [wiki](../../wiki) contains lots of documentation:
|
147
137
|
|
148
|
-
|
138
|
+
* [Game Configuration](wiki/game-configuration)
|
139
|
+
* [Scenes](../../wiki/scenes)
|
140
|
+
* [Views](../../wiki/views)
|
141
|
+
* [Animations](../../wiki/animations)
|
142
|
+
* [Scene Transitions](../../wiki/transitions)
|
143
|
+
* [Models](../../wiki/models)
|
144
|
+
* [Metro Models](../../wiki/metro-models)
|
145
|
+
* [Model Properties](../../wiki/properties)
|
146
|
+
* [Events](../../wiki/events)
|
147
|
+
* [Units](../../wiki/units)
|
149
148
|
|
150
149
|
### Programming
|
151
150
|
|
@@ -155,7 +154,7 @@ This should generate a scene in the scenes directory. The scene file contains a
|
|
155
154
|
### Art
|
156
155
|
|
157
156
|
* [Lost Garden](http://www.lostgarden.com/2007/05/dancs-miraculously-flexible-game.html)
|
158
|
-
* [
|
157
|
+
* [TimelineFX](http://www.rigzsoft.co.uk/) particle editor allows you to export animations.
|
159
158
|
* [Text to ASCII Art Generator](http://patorjk.com/software/taag)
|
160
159
|
* [Icons](http://css-tricks.com/flat-icons-icon-fonts/)
|
161
160
|
* [Subtle Patterns](http://subtlepatterns.com/) various backgrounds and textures.
|
@@ -175,4 +174,4 @@ This should generate a scene in the scenes directory. The scene file contains a
|
|
175
174
|
* [Challenges For Game Designers](http://www.amazon.com/dp/158450580X)
|
176
175
|
* [The Art of Game Design: A book of lenses](http://www.amazon.com/dp/0123694965)
|
177
176
|
* [A Theory of Fun](http://www.theoryoffun.com)
|
178
|
-
* [Andrew Rollings and Ernest Adams on Game Design](http://www.amazon.com/dp/1592730019)
|
177
|
+
* [Andrew Rollings and Ernest Adams on Game Design](http://www.amazon.com/dp/1592730019)
|
data/changelog.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# Metro
|
2
2
|
|
3
|
+
## 0.3.5 / 2014-08-11
|
4
|
+
|
5
|
+
* FIX active_support is now activesupport
|
6
|
+
|
3
7
|
## 0.3.4 / 2012-12-14
|
4
8
|
|
5
9
|
* `metro::ui::sprite` and `metro::ui::animated_sprite` model classes
|
6
10
|
to make it easier to take care of all the basic model attributes.
|
7
11
|
* Event Management changed in the background. The API remains the
|
8
12
|
same.
|
9
|
-
|
13
|
+
|
10
14
|
## 0.3.3 / 2012-11-28
|
11
15
|
|
12
16
|
* Edit Mode - actors within a scene can have their position edited
|
data/lib/core_ext/numeric.rb
CHANGED
@@ -56,4 +56,21 @@ class Numeric
|
|
56
56
|
|
57
57
|
alias_method :ticks, :tick
|
58
58
|
|
59
|
+
def radians
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def degrees
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Convert the specified numeric value in radians to degrees
|
68
|
+
def to_degrees
|
69
|
+
self * 180 / Math::PI
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convert the specified numeric value in degrees to radians
|
73
|
+
def to_radians
|
74
|
+
self * Math::PI / 180
|
75
|
+
end
|
59
76
|
end
|
data/lib/metro.rb
CHANGED
@@ -4,6 +4,9 @@ require 'erb'
|
|
4
4
|
require 'open3'
|
5
5
|
|
6
6
|
require 'gosu'
|
7
|
+
require 'chipmunk'
|
8
|
+
require 'texplay'
|
9
|
+
require 'tmx'
|
7
10
|
require 'i18n'
|
8
11
|
require 'listen'
|
9
12
|
require 'active_support'
|
@@ -12,10 +15,12 @@ require 'active_support/inflector'
|
|
12
15
|
require 'active_support/core_ext/hash'
|
13
16
|
require 'active_support/hash_with_indifferent_access'
|
14
17
|
|
15
|
-
require 'gosu_ext/color'
|
16
|
-
require 'gosu_ext/gosu_constants'
|
17
18
|
require 'core_ext/numeric'
|
18
19
|
require 'core_ext/class'
|
20
|
+
require 'gosu_ext/color'
|
21
|
+
require 'gosu_ext/gosu_constants'
|
22
|
+
require 'tmx_ext/tile_set'
|
23
|
+
require 'tmx_ext/object'
|
19
24
|
|
20
25
|
require 'locale/locale'
|
21
26
|
|
@@ -132,7 +137,7 @@ module Metro
|
|
132
137
|
end
|
133
138
|
|
134
139
|
require 'setup_handlers/move_to_game_directory'
|
135
|
-
require 'setup_handlers/load_game_files'
|
136
140
|
require 'setup_handlers/load_game_configuration'
|
141
|
+
require 'setup_handlers/load_game_files'
|
137
142
|
require 'setup_handlers/exit_if_dry_run'
|
138
143
|
require 'setup_handlers/reload_game_on_game_file_changes'
|
@@ -39,14 +39,14 @@ module Metro
|
|
39
39
|
# Perform a step of an animation, if it hasn't already been completed.
|
40
40
|
#
|
41
41
|
def update
|
42
|
-
return if
|
42
|
+
return if update_completed?
|
43
43
|
|
44
44
|
execute_step
|
45
45
|
next_step
|
46
46
|
|
47
|
-
complete! if
|
47
|
+
complete! if update_completed?
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
#
|
51
51
|
# @return the current step of the animation.
|
52
52
|
#
|
@@ -67,12 +67,12 @@ module Metro
|
|
67
67
|
def step_interval
|
68
68
|
1
|
69
69
|
end
|
70
|
-
|
71
|
-
#
|
70
|
+
|
71
|
+
#
|
72
72
|
# @return true if the animation has completed all the actions, false
|
73
73
|
# if there are remaining actions.
|
74
|
-
#
|
75
|
-
def
|
74
|
+
#
|
75
|
+
def update_completed?
|
76
76
|
current_step >= interval
|
77
77
|
end
|
78
78
|
|
@@ -66,6 +66,7 @@ module Metro
|
|
66
66
|
@up_actions ||= {}
|
67
67
|
@down_actions ||= {}
|
68
68
|
@held_actions ||= {}
|
69
|
+
@mouse_movement_actions ||= []
|
69
70
|
@custom_notifications ||= HashWithIndifferentAccess.new([])
|
70
71
|
end
|
71
72
|
|
@@ -179,6 +180,33 @@ module Metro
|
|
179
180
|
alias_method :button_hold, :on_hold
|
180
181
|
alias_method :button_held, :on_hold
|
181
182
|
|
183
|
+
|
184
|
+
#
|
185
|
+
# Register for mouse movements events. These events are fired each update
|
186
|
+
# providing an event which contains the current position of the mouse.
|
187
|
+
#
|
188
|
+
# @note mouse movement events fire with each update so it is up to the
|
189
|
+
# receiving object of the event to determine if the new mouse movement
|
190
|
+
# is a delta.
|
191
|
+
#
|
192
|
+
# @note mouse movement events require that the window be specified during initialization.
|
193
|
+
#
|
194
|
+
# @example Registering for button held events
|
195
|
+
#
|
196
|
+
# class ExampleScene
|
197
|
+
#
|
198
|
+
# draws :player
|
199
|
+
#
|
200
|
+
# event :on_mouse_movement do |event|
|
201
|
+
# player.position = event.mouse_point
|
202
|
+
# end
|
203
|
+
# end
|
204
|
+
#
|
205
|
+
def on_mouse_movement(*args,&block)
|
206
|
+
options = (args.last.is_a?(Hash) ? args.pop : {})
|
207
|
+
@mouse_movement_actions << ( block || lambda { |instance| send(options[:do]) } )
|
208
|
+
end
|
209
|
+
|
182
210
|
#
|
183
211
|
# Register for a custom notification event. These events are fired when
|
184
212
|
# another object within the game posts a notification with matching criteria.
|
@@ -217,7 +245,11 @@ module Metro
|
|
217
245
|
custom_notifications[param.to_sym] = custom_notifications[param.to_sym] + [ block ]
|
218
246
|
end
|
219
247
|
|
220
|
-
attr_reader :up_actions, :down_actions, :held_actions
|
248
|
+
attr_reader :up_actions, :down_actions, :held_actions
|
249
|
+
|
250
|
+
attr_reader :mouse_movement_actions
|
251
|
+
|
252
|
+
attr_reader :custom_notifications
|
221
253
|
|
222
254
|
def _on(hash,args,block)
|
223
255
|
options = (args.last.is_a?(Hash) ? args.pop : {})
|
@@ -254,6 +286,16 @@ module Metro
|
|
254
286
|
end
|
255
287
|
end
|
256
288
|
|
289
|
+
#
|
290
|
+
# Fire events for all the registered actions that are suppose to receive
|
291
|
+
# the mouse movement events.
|
292
|
+
#
|
293
|
+
def fire_events_for_mouse_movement
|
294
|
+
mouse_movement_actions.each do |action|
|
295
|
+
execute_block_for_target(&action)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
257
299
|
def execute_block_for_target(&block)
|
258
300
|
event_data = EventData.new(window)
|
259
301
|
target.instance_exec(event_data,&block)
|
@@ -25,6 +25,13 @@ module Metro
|
|
25
25
|
current_state.each {|cs| cs.fire_events_for_held_buttons }
|
26
26
|
end
|
27
27
|
|
28
|
+
#
|
29
|
+
# Fire events for mouse events within the current game state
|
30
|
+
#
|
31
|
+
def fire_events_for_mouse_movement
|
32
|
+
current_state.each {|cs| cs.fire_events_for_mouse_movement }
|
33
|
+
end
|
34
|
+
|
28
35
|
#
|
29
36
|
# Fire events for button up for the current game state
|
30
37
|
#
|
data/lib/metro/image.rb
CHANGED
@@ -32,6 +32,12 @@ module Metro
|
|
32
32
|
# path: "asset_path", tileable: tileable
|
33
33
|
#
|
34
34
|
def self.find_or_create(options)
|
35
|
+
begin
|
36
|
+
File.open(File.join("assets", options[:path]), "r")
|
37
|
+
rescue Exception
|
38
|
+
puts $! # <- make this prettier
|
39
|
+
options[:path] = "missing.png" # <- make this file installed by default into the assets folder of the game
|
40
|
+
end
|
35
41
|
path = AssetPath.with(options[:path])
|
36
42
|
images[path.to_s] or (images[path.to_s] = create(options))
|
37
43
|
end
|
@@ -50,6 +56,13 @@ module Metro
|
|
50
56
|
new gosu_image, asset_path.path, tileable
|
51
57
|
end
|
52
58
|
|
59
|
+
def self.crop(window,image,bounds)
|
60
|
+
cropped_image = TexPlay.create_image(window,bounds.width,bounds.height)
|
61
|
+
cropped_image.refresh_cache
|
62
|
+
cropped_image.splice image, 0, 0, crop: [ bounds.left, bounds.top, bounds.right, bounds.bottom ]
|
63
|
+
cropped_image
|
64
|
+
end
|
65
|
+
|
53
66
|
private
|
54
67
|
|
55
68
|
def self.create_params(options)
|
data/lib/metro/models/model.rb
CHANGED
@@ -49,13 +49,13 @@ module Metro
|
|
49
49
|
|
50
50
|
#
|
51
51
|
# This is called after an update. A model normally is not removed after
|
52
|
-
# an update, however if the model responds true to #
|
52
|
+
# an update, however if the model responds true to #update_completed? then it
|
53
53
|
# will be removed.
|
54
54
|
#
|
55
55
|
# @note This method should be implemented in the Model sublclass if you
|
56
56
|
# are interested in having the model be removed from the scene.
|
57
57
|
#
|
58
|
-
def
|
58
|
+
def update_completed? ; false ; end
|
59
59
|
|
60
60
|
#
|
61
61
|
# This is called after every {#update} and when the OS wants the window to
|
@@ -65,6 +65,16 @@ module Metro
|
|
65
65
|
#
|
66
66
|
def draw ; end
|
67
67
|
|
68
|
+
#
|
69
|
+
# This is called after a draw. A model normally is not removed after
|
70
|
+
# a draw, however if the model responds true to #draw_completed? then it
|
71
|
+
# will be removed.
|
72
|
+
#
|
73
|
+
# @note This method should be implemented in the Model sublclass if you
|
74
|
+
# are interested in having the model be removed from the scene.
|
75
|
+
#
|
76
|
+
def draw_completed? ; false ; end
|
77
|
+
|
68
78
|
def self.model_name(model_name=nil)
|
69
79
|
@model_name ||= to_s.underscore
|
70
80
|
model_name ? @model_name = model_name.to_s : @model_name
|
@@ -40,7 +40,11 @@ module Metro
|
|
40
40
|
# count of options will reset to the beginning of the list of options.
|
41
41
|
# Values that proceed the start of of the list of options will fallback to the last option.
|
42
42
|
#
|
43
|
+
# @param [Fixnum,Object] value is the integer position within the menu
|
44
|
+
# to select or the item in the options to select.
|
45
|
+
#
|
43
46
|
def current_selected_index=(value)
|
47
|
+
value = index(value) unless value.is_a?(Fixnum)
|
44
48
|
@current_selected_index = value || 0
|
45
49
|
@current_selected_index = 0 if @current_selected_index >= count
|
46
50
|
@current_selected_index = count - 1 if @current_selected_index <= -1
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Metro
|
2
|
+
module UI
|
3
|
+
|
4
|
+
#
|
5
|
+
# A physics sprite is a Metro model that is specially designed to draw and
|
6
|
+
# manage an image. A sprite maintains an image, location information, and
|
7
|
+
# rotation. It also has a physics body and shape to assist with being
|
8
|
+
# placed within a `Metro::UI::Space`.
|
9
|
+
#
|
10
|
+
class PhysicsSprite < Sprite
|
11
|
+
|
12
|
+
# @attribute
|
13
|
+
# The mass specified here is given to the body.
|
14
|
+
property :mass, default: 10
|
15
|
+
|
16
|
+
# @attribute
|
17
|
+
# The moment of inertia determines how the sprite will react to the forces
|
18
|
+
# applied to it.
|
19
|
+
property :moment_of_interia, default: 1000000
|
20
|
+
|
21
|
+
# @attribute
|
22
|
+
# A physics sprite has by default a collision shape that is a square.
|
23
|
+
# So this property defines the length of one side which is used to create
|
24
|
+
# the appropriately sized shape for the sprite.
|
25
|
+
property :shape_size, default: 48.0
|
26
|
+
|
27
|
+
# @attribute
|
28
|
+
# The name of the shape. This name is important when the space defines
|
29
|
+
# actions based on the collision of particular objects. By default all
|
30
|
+
# physics sprites are named 'object'.
|
31
|
+
property :shape_name, type: :text, default: "object"
|
32
|
+
|
33
|
+
# @attribute
|
34
|
+
# When this value is true the bounding box will be placed around the
|
35
|
+
# sprite. This is useful in determining correct sizes and collisions.
|
36
|
+
property :debug, type: :boolean, default: false
|
37
|
+
|
38
|
+
# @return the body of the physics sprite that is created with the mass
|
39
|
+
# and moment of interia specified in the other properties.
|
40
|
+
def body
|
41
|
+
@body ||= begin
|
42
|
+
body = CP::Body.new(mass,moment_of_interia)
|
43
|
+
body.p = CP::Vec2::ZERO
|
44
|
+
body.v = CP::Vec2::ZERO
|
45
|
+
body.a = 0
|
46
|
+
body
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return a polygon shape for the physics sprite with the size based on
|
51
|
+
# the `shape_size` property value.
|
52
|
+
def shape
|
53
|
+
@shape ||= begin
|
54
|
+
poly_array = [ [ -1 * shape_size, -1 * shape_size ],
|
55
|
+
[ -1 * shape_size, shape_size ],
|
56
|
+
[ shape_size, shape_size ],
|
57
|
+
[ shape_size, -1 * shape_size ] ].map do |x,y|
|
58
|
+
CP::Vec2.new(x,y)
|
59
|
+
end
|
60
|
+
|
61
|
+
new_shape = CP::Shape::Poly.new(body,poly_array, CP::Vec2::ZERO)
|
62
|
+
new_shape.collision_type = shape_name.to_sym
|
63
|
+
new_shape.e = 0.0
|
64
|
+
new_shape
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# An helper method that makes it easy to apply an impulse to the body at
|
70
|
+
# the center of the body. The impulse provided is in two parameters, the
|
71
|
+
# x and y component.
|
72
|
+
def push(x_amount,y_amount)
|
73
|
+
body.apply_impulse(CP::Vec2.new(x_amount,y_amount),CP::Vec2.new(0.0, 0.0))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Upon the scene start the body is assigned the x and y position. If this
|
77
|
+
# method is overriden the position will need to be set manually.
|
78
|
+
def show
|
79
|
+
body.p = CP::Vec2.new(x,y)
|
80
|
+
end
|
81
|
+
|
82
|
+
# On update track the position of the sprite based on the position of the
|
83
|
+
# physics body.
|
84
|
+
def update
|
85
|
+
self.x = body.p.x
|
86
|
+
self.y = body.p.y
|
87
|
+
end
|
88
|
+
|
89
|
+
# On draw, draw the specified sprite.
|
90
|
+
def draw
|
91
|
+
angle_in_degrees = body.a.radians.to_degrees
|
92
|
+
image.draw_rot(x,y,z_order,angle_in_degrees)
|
93
|
+
|
94
|
+
draw_bounding_box if debug
|
95
|
+
end
|
96
|
+
|
97
|
+
def draw_bounding_box
|
98
|
+
@bounding_box_border ||= create "metro::ui::border"
|
99
|
+
@bounding_box_border.position = Point.at(position.x - shape_size,shape.bb.t)
|
100
|
+
@bounding_box_border.dimensions = Dimensions.of(shape.bb.r - shape.bb.l,shape.bb.b - shape.bb.t)
|
101
|
+
@bounding_box_border.draw
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|