ray 0.0.0.pre2 → 0.0.1

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 (76) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +3 -0
  3. data/README.md +62 -0
  4. data/Rakefile +33 -23
  5. data/VERSION +1 -1
  6. data/ext/audio.c +473 -0
  7. data/ext/color.c +4 -4
  8. data/ext/event.c +25 -3
  9. data/ext/extconf.rb +35 -22
  10. data/ext/font.c +287 -0
  11. data/ext/image.c +682 -33
  12. data/ext/joystick.c +9 -9
  13. data/ext/ray.c +166 -55
  14. data/ext/ray.h +120 -9
  15. data/ext/ray_osx.m +161 -0
  16. data/ext/rect.c +31 -4
  17. data/lib/ray/audio.rb +52 -0
  18. data/lib/ray/color.rb +16 -0
  19. data/lib/ray/dsl.rb +1 -3
  20. data/lib/ray/dsl/event.rb +1 -39
  21. data/lib/ray/dsl/event_listener.rb +38 -0
  22. data/lib/ray/dsl/event_runner.rb +3 -1
  23. data/lib/ray/dsl/event_translator.rb +74 -8
  24. data/lib/ray/dsl/handler.rb +3 -33
  25. data/lib/ray/dsl/matcher.rb +129 -23
  26. data/lib/ray/font.rb +108 -0
  27. data/lib/ray/font_set.rb +37 -0
  28. data/lib/ray/game.rb +171 -34
  29. data/lib/ray/helper.rb +43 -5
  30. data/lib/ray/image.rb +90 -3
  31. data/lib/ray/image_set.rb +35 -0
  32. data/lib/ray/joystick.rb +30 -0
  33. data/lib/ray/music_set.rb +35 -0
  34. data/lib/ray/ray.rb +17 -9
  35. data/lib/ray/rect.rb +51 -0
  36. data/lib/ray/resource_set.rb +92 -0
  37. data/lib/ray/scene.rb +220 -51
  38. data/lib/ray/sound_set.rb +35 -0
  39. data/lib/ray/sprite.rb +184 -0
  40. data/psp/ext.c +4 -0
  41. data/samples/hello_world/hello.rb +35 -0
  42. data/samples/hello_world/hello_dsl.rb +24 -0
  43. data/samples/pong/pong.rb +128 -0
  44. data/samples/sokoban/level_1 +7 -0
  45. data/samples/sokoban/sokoban.rb +370 -0
  46. data/spec/ray/audio_spec.rb +146 -0
  47. data/spec/ray/color_spec.rb +13 -0
  48. data/spec/ray/event_spec.rb +57 -168
  49. data/spec/ray/font_spec.rb +93 -0
  50. data/spec/ray/image_set_spec.rb +48 -0
  51. data/spec/ray/image_spec.rb +130 -44
  52. data/spec/ray/joystick_spec.rb +13 -9
  53. data/spec/ray/matcher_spec.rb +32 -55
  54. data/spec/ray/ray_spec.rb +33 -31
  55. data/spec/ray/rect_spec.rb +80 -0
  56. data/spec/ray/resource_set_spec.rb +105 -0
  57. data/spec/ray/sprite_spec.rb +163 -0
  58. data/spec/res/VeraMono.ttf +0 -0
  59. data/spec/res/aqua2.bmp +0 -0
  60. data/spec/res/pop.wav +0 -0
  61. data/spec/spec.opts +4 -0
  62. data/spec/spec_helper.rb +8 -0
  63. data/yard_ext.rb +91 -0
  64. metadata +104 -38
  65. data/bin/ray +0 -5
  66. data/bin/ray_irb +0 -4
  67. data/ext/SDLMain.h +0 -17
  68. data/ext/SDLMain.m +0 -381
  69. data/lib/ray/config.rb +0 -84
  70. data/lib/ray/dsl/converter.rb +0 -65
  71. data/lib/ray/dsl/listener.rb +0 -30
  72. data/lib/ray/dsl/type.rb +0 -58
  73. data/spec/ray/config_spec.rb +0 -90
  74. data/spec/ray/conversion_spec.rb +0 -43
  75. data/spec/ray/type_spec.rb +0 -17
  76. data/spec_runner.rb +0 -27
data/lib/ray/scene.rb CHANGED
@@ -1,102 +1,271 @@
1
1
  module Ray
2
+ # Scenes contain the main logic of a game.
3
+ #
4
+ # You can define a new scene using a block, which will be called every time
5
+ # your scene is about to be used. However, you may also want to subclass
6
+ # Ray::Scene. When doing this, you'll probably want to override the register
7
+ # method:
8
+ # def register
9
+ # on :some_event do some_stuff end
10
+ # end
11
+ #
12
+ # Another method is called before register: setup. Putting code in register or
13
+ # in setup doesn't matter, but setting the scene up inside register method
14
+ # seems (and, indeed, is) inappropriate. You can override it:
15
+ # def setup
16
+ # @sprite = sprite("image.png")
17
+ # end
18
+ #
19
+ # You can indicate how your scene should be rendered there:
20
+ # render do |win|
21
+ # # Do drawing here
22
+ # end
23
+ #
24
+ # Or you can override render:
25
+ # def render(win)
26
+ # # Do drawing here
27
+ # end
28
+ #
29
+ # Notice win is not filled with an empty color when render is called, i.e.
30
+ # it still contains the frame which appears to the user.
31
+ #
32
+ # Also, scenes are rendered lazily: only once when the scene is created,
33
+ # and then every time need_render! is called.
34
+ #
35
+ # Once your scene is loaded, you'll probably want to clean it up (set some
36
+ # instance variables to nil so they can be garbaged collected for instance).
37
+ # You can do that by passing a block to clean_up:
38
+ # clean_up do
39
+ # @some_big_resource = nil
40
+ # end
41
+ #
42
+ # Or by overriding it:
43
+ # def clean_up
44
+ # @some_big_resource = nil
45
+ # end
46
+ #
47
+ # == Managing the stack of scenes
48
+ # exit is called when you want to stop running the scene, but not to remove
49
+ # the last scene from the stack. It is useful if you want to push a new
50
+ # scene. Hence Ray::Scene#push_scene will call exit.
51
+ #
52
+ # exit! (or pop_scene), on the other hand, is used to go back to the previous
53
+ # scene in the hierarchy
54
+ #
55
+ # == Sending informations to a scene
56
+ # Scenes may need some arguments to work. You can pass those in push_scene:
57
+ # push_scene(:polygon, 6, Ray::Color.red)
58
+ # Then you can use them with scene_arguments:
59
+ # scene :polygon do
60
+ # sides, color = scene_arguments
61
+ # # ...
62
+ # end
63
+ #
64
+ # They are also passed to #setup:
65
+ # def setup(sides, color)
66
+ # # ...
67
+ # end
68
+ #
69
+ # == Limiting the loop rate
70
+ # You can prevent a scene from always running by using #loops_per_second=:
71
+ # self.loops_per_second = 30 # will sleep some time after each loop
72
+ #
73
+ # @see Ray::DSL::EventTranslator
2
74
  class Scene
3
75
  include Ray::Helper
4
76
 
5
77
  class << self
6
- # If you want to subclass Scene, this is the method you need to call.
7
- # It will return a subclass, responding to bind, which allows you to
8
- # register it.
9
- #
10
- # @example
11
- # Klass = Scene.create(:foo) do ... end
12
- # Klass.bind(game)
13
- # game.push_scene(:foo)
14
- def create(scene_name, &block)
15
- klass = Class.new(self)
16
- klass.instance_variable_set("@block", block)
17
-
18
- (class << klass; self; end).class_eval do
19
- define_method(:bind) do |game|
20
- game.scene(scene_name, self, &@block)
21
- end
22
-
23
- define_method(:inspect) { "Scene:#{scene_name}" }
24
- define_method(:block) { @block }
25
- end
26
-
27
- klass.class_eval do
28
- define_method(:initialize) do
29
- super(&self.class.block)
30
- end
31
- end
78
+ # Registers a scene to a game object, used for subclasses.
79
+ def bind(game)
80
+ game.scene(scene_name, self)
81
+ end
32
82
 
33
- return klass
83
+ # @overload scene_name
84
+ # @return [Symbol] the name of the scene
85
+ # @overload scene_name(value)
86
+ # Sets the name of the scene
87
+ def scene_name(val = nil)
88
+ @scene_name = val || @scene_name
34
89
  end
35
90
  end
36
91
 
37
- # Creates a new scene. block will be instance evaluated
38
- # every time the current scene changes.
92
+ scene_name :scene
93
+
94
+ # Creates a new scene. block will be instance evaluated when
95
+ # this scene becomes the current one.
39
96
  def initialize(&block)
40
- @exit = false
41
- @block = block
97
+ @scene_register_block = block
42
98
  end
43
99
 
44
100
  def register_events
45
- instance_eval(&@block)
101
+ @scene_held_keys = []
102
+
103
+ on :key_press do |key, mod|
104
+ @scene_held_keys << key
105
+ end
106
+
107
+ on :key_release do |key, mod|
108
+ @scene_held_keys.reject! { |i| i == key }
109
+ end
110
+
111
+ if @scene_register_block
112
+ instance_eval(&@scene_register_block)
113
+ else
114
+ register
115
+ end
116
+
117
+ @scene_exit = false
118
+ end
119
+
120
+ # Override this method in subclasses to setup the initial state
121
+ # of your scene.
122
+ def setup(*args)
123
+ end
124
+
125
+ # Override this method in subclasses to register your own events
126
+ def register
127
+ end
128
+
129
+ # @param [Symbol, Integer] val A symbol to find the key (its name)
130
+ # or an integer (Ray::Event::KEY_*)
131
+ #
132
+ # @return [true, false] True if the user is holding key.
133
+ def holding?(val)
134
+ if val.is_a? Symbol
135
+ val = key(val)
136
+ @scene_held_keys.any? { |o| val === o }
137
+ elsif val.is_a? DSL::Matcher
138
+ @scene_held_keys.any? { |o| val === o }
139
+ else
140
+ @scene_held_keys.include? val
141
+ end
46
142
  end
47
143
 
48
144
  # Runs until you exit the scene.
49
145
  # This will also raise events if the mouse moves, ... allowing you
50
146
  # to directly listen to a such event.
51
147
  def run
52
- until @exit
53
- ev = DSL::EventTranslator.translate_event(Ray::Event.new)
54
- raise_event(*ev) if ev
148
+ until @scene_exit
149
+ loop_start = Time.now
55
150
 
56
- @always.call if @always
151
+ DSL::EventTranslator.translate_event(Ray::Event.new).each do |args|
152
+ raise_event(*args)
153
+ end
154
+
155
+ @scene_always_block.call if @scene_always_block
57
156
 
58
157
  listener_runner.run
59
158
 
60
- if @need_render
61
- @need_render = false
159
+ if @scene_need_render
160
+ @scene_need_render = false
161
+
162
+ render(@scene_window)
163
+ @scene_window.flip
164
+ end
165
+
166
+ if @scene_loops_per_second
167
+ ellapsed_time = Time.now - loop_start
168
+ time_per_loop = 1.0 / @scene_loops_per_second
62
169
 
63
- @render.call(@window)
64
- @window.flip
170
+ sleep(time_per_loop - ellapsed_time) if ellapsed_time < time_per_loop
65
171
  end
66
172
  end
173
+
174
+ clean_up
67
175
  end
68
176
 
69
- # Exits the scene, but does not pops the scene (the next scene
70
- # will be the same one)
177
+ # Exits the scene, but does not pop the scene.
178
+ #
179
+ # You may want to call this if you pushed a new scene, to switch to
180
+ # the new scene.
71
181
  def exit
72
- @exit = true
182
+ @scene_exit = true
73
183
  end
74
184
 
75
- # Exits the scene and pops it.
185
+ # Exits the scene and pops it (may not work as expected if the current
186
+ # scene is not the last one)
76
187
  def exit!
77
188
  exit
78
189
  game.pop_scene
79
190
  end
80
191
 
81
- # Register a block to be excuted as often as possible.
192
+ # Registers a block to be excuted as often as possible.
82
193
  def always(&block)
83
- @always = block
194
+ @scene_always_block = block
84
195
  end
85
196
 
86
197
  # Marks the scene should be redrawn.
87
198
  def need_render!
88
- @need_render = true
199
+ @scene_need_render = true
89
200
  end
90
201
 
91
202
  # Registers the block to draw the scene.
92
203
  #
204
+ # Does nothing if no block is given, this method being called if you
205
+ # didn't register any render block. You can thus override it in subclasses
206
+ # instead of providing a block to it.
207
+ #
93
208
  # @yield [window] Block to render this scene.
94
209
  # @yieldparam [Ray::Image] window The window you should draw on
95
- def render(&block)
96
- @render = block
210
+ def render(win = nil, &block)
211
+ if block_given?
212
+ @scene_render_block = block
213
+ else
214
+ @scene_render_block.call(win) if @scene_render_block
215
+ end
216
+ end
217
+
218
+ # Pushes a scene in the stack, and exits that one
219
+ def push_scene(scene, *args)
220
+ game.push_scene(scene, *args)
221
+ exit
222
+ end
223
+
224
+ # @see Ray::Game#resize_window
225
+ def resize_window(w, h)
226
+ game.resize_window(w, h)
227
+ end
228
+
229
+ # Cleans the scene or registers a block to clean it.
230
+ def clean_up(&block)
231
+ if block_given?
232
+ @scene_clean_block = block
233
+ else
234
+ @scene_clean_block.call if @scene_clean_block
235
+ end
236
+ end
237
+
238
+ def inspect
239
+ "#<#{self.class} game=#{self.game.inspect}>"
240
+ end
241
+
242
+ alias :pop_scene :exit!
243
+
244
+ def game
245
+ @scene_game
246
+ end
247
+
248
+ def game=(val)
249
+ @scene_game = val
250
+ end
251
+
252
+ def window
253
+ @scene_window
254
+ end
255
+
256
+ def window=(val)
257
+ @scene_window = val
258
+ end
259
+
260
+ def loops_per_second
261
+ @scene_loops_per_second
262
+ end
263
+
264
+ def loops_per_second=(val)
265
+ @scene_loops_per_second = val
97
266
  end
98
267
 
99
- attr_accessor :game
100
- attr_accessor :window
268
+ # The arguments passed to the scene with push_scene
269
+ attr_accessor :scene_arguments
101
270
  end
102
271
  end
@@ -0,0 +1,35 @@
1
+ module Ray
2
+ module SoundSet
3
+ extend Ray::ResourceSet
4
+
5
+ class << self
6
+ def missing_pattern(string)
7
+ Ray::Sound[string]
8
+ end
9
+
10
+ def select!(&block)
11
+ super(&block)
12
+ Ray::Sound.select!(&block)
13
+ end
14
+ end
15
+ end
16
+
17
+ # Creates a new sound set.
18
+ #
19
+ # @param [Regexp] regex Regular expression used to match file
20
+ # @yield [*args] Block returning the sound
21
+ #
22
+ # @yieldparam args Regex captures
23
+ def self.sound_set(regex, &block)
24
+ Ray::SoundSet.add_set(regex, &block)
25
+ end
26
+ end
27
+
28
+ begin
29
+ require 'open-uri'
30
+
31
+ Ray.sound_set(/^(http|ftp):\/\/(\S+)$/) do |protocol, address|
32
+ open("#{protocol}://#{address}") { |io| Ray::Sound.new(io) }
33
+ end
34
+ rescue LoadError
35
+ end
data/lib/ray/sprite.rb ADDED
@@ -0,0 +1,184 @@
1
+ module Ray
2
+ class Sprite
3
+ # Creates a sprite.
4
+ #
5
+ # @param [String, Ray::Image] The image this object will wrap;
6
+ # @option opts [Ray::Rect, Array<Integer>] :at Position of the sprite
7
+ # (defaults to 0,0)
8
+ # @option opts [Ray::Rect, Array<Integer>] :rect Rect of the image which will
9
+ # be drawn (defaults to the
10
+ # whole image)
11
+ # @option opts [Float] :angle The angle which will be used to draw the image
12
+ # in degrees. Defaults to 0.
13
+ # @option opts [Float] :zoom The zoom level which will be used to draw the image.
14
+ # Defaults to 1.
15
+ def initialize(image, opts = {})
16
+ opts = {
17
+ :rect => Ray::Rect.new(0, 0, 0, 0),
18
+ :at => [0, 0],
19
+ :angle => 0.0,
20
+ :zoom => 1
21
+ }.merge(opts)
22
+
23
+ @from_rect = opts[:rect].to_rect
24
+
25
+ rect = opts[:at].to_rect
26
+ @x, @y = rect.x, rect.y
27
+
28
+ self.angle = opts[:angle]
29
+ self.zoom = opts[:zoom]
30
+
31
+ @image = image.to_image
32
+ end
33
+
34
+ # Sets the size of the sprite sheet. For instance,
35
+ # sprite.sheet_size = [3, 4]
36
+ # would mean there are 4 rows and 3 columns in the sprite (and each cell
37
+ # has the same size).
38
+ def sheet_size=(ary)
39
+ w, h = ary
40
+
41
+ @uses_sprite_sheet = true
42
+
43
+ @sprite_sheet_w = w
44
+ @sprite_sheet_h = h
45
+
46
+ self.sheet_pos = [0, 0]
47
+ end
48
+
49
+ # Sets which cell of the sprite sheet should be displayed.
50
+ # sprite.sheet_pos = [0, 1] # Uses the first cell of the second line.
51
+ def sheet_pos=(ary)
52
+ x, y = ary
53
+
54
+ self.from_rect = Ray::Rect.new(x * sprite_width, y * sprite_height,
55
+ sprite_width, sprite_height)
56
+
57
+ @sheet_pos_x = x
58
+ @sheet_pos_y = y
59
+ end
60
+
61
+ # Returns the position of the cell which is being used.
62
+ def sheet_pos
63
+ [@sheet_pos_x, @sheet_pos_y]
64
+ end
65
+
66
+ # @return [Integer, nil] The width of each cell in the sprite sheet
67
+ def sprite_width
68
+ if uses_sprite_sheet?
69
+ @image.w / @sprite_sheet_w
70
+ end
71
+ end
72
+
73
+ # @return [Integer, nil] The height of each cell in the sprite sheet
74
+ def sprite_height
75
+ if uses_sprite_sheet?
76
+ @image.h / @sprite_sheet_h
77
+ end
78
+ end
79
+
80
+ # Disables the sprite sheet
81
+ def disable_sprite_sheet
82
+ self.from_rect = Ray::Rect.new(0, 0, @image.w, @image.h)
83
+
84
+ @sprite_sheet_w = nil
85
+ @sprite_sheet_h = nil
86
+
87
+ @uses_sprite_sheet = false
88
+ end
89
+
90
+ def uses_sprite_sheet?
91
+ @uses_sprite_sheet
92
+ end
93
+
94
+ # Draws the sprite on an image.
95
+ # @param [Ray::Image] screen The image on which the sprite will be drawn
96
+ def draw_on(screen)
97
+ @image.blit(:rect => @from_rect, :on => screen, :at => [@x, @y],
98
+ :angle => @angle, :zoom => @zoom)
99
+ end
100
+
101
+ # Draws the sprite on the screen or on another image
102
+ # @option opts [Ray::Image] :on The image we should draw on. Defaults to
103
+ # Ray.screen.
104
+ def draw(opts = {})
105
+ draw_on(opts[:on] || Ray.screen)
106
+ end
107
+
108
+ # @return [true, false] True if the sprite is located at (x, y)
109
+ def is_at?(x, y)
110
+ @x == x && @y == y
111
+ end
112
+
113
+ # @return [Ray::Rect] The rect where this sprite will be drawn.
114
+ def rect
115
+ Ray::Rect.new(@x, @y, @from_rect.w == 0 ? @image.w : @from_rect.w,
116
+ @from_rect.h == 0 ? @image.h : @from_rect.h)
117
+ end
118
+
119
+ # @param [Ray::Rect, #rect] An object with which the receiver may collide
120
+ # @return [true, false]
121
+ def collide?(obj)
122
+ rect.collide?(obj.to_rect)
123
+ end
124
+
125
+ # @param [Ray::Rect, #rect] (See #collide?)
126
+ # @return [true, false]
127
+ def inside?(obj)
128
+ rect.inside?(obj.to_rect)
129
+ end
130
+
131
+ # @param [Ray::Rect, #rect] (See #collide?)
132
+ # @return [true, false]
133
+ def outside?(obj)
134
+ rect.outside?(obj.to_rect)
135
+ end
136
+
137
+ def to_rect
138
+ rect
139
+ end
140
+
141
+ # @return [Ray::Image]
142
+ attr_reader :image
143
+
144
+ # @return [Integer] position of the sprite
145
+ attr_accessor :x, :y
146
+
147
+ # @return [Ray::Rect] the part of the image which will be drawn. An empty
148
+ # rect means the whole image.
149
+ attr_accessor :from_rect
150
+
151
+ # @return [Ray::Rect] the position of the sprite
152
+ def pos
153
+ [x, y]
154
+ end
155
+
156
+ # Sets the position of the sprite
157
+ def pos=(ary)
158
+ rect = ary.to_rect
159
+
160
+ self.x = rect.x
161
+ self.y = rect.y
162
+ end
163
+
164
+ # @return [Float] The angle used when the image is drawn.
165
+ def angle
166
+ @angle ? @angle : 0
167
+ end
168
+
169
+ # Sets the angle.
170
+ def angle=(val)
171
+ @angle = (val % 360).zero? ? nil : val
172
+ end
173
+
174
+ # @return [Float] the zoom applied to the image when it is drawn.
175
+ def zoom
176
+ @zoom ? @zoom : 1
177
+ end
178
+
179
+ # Sets the zoom level.
180
+ def zoom=(val)
181
+ @zoom = val == 1 ? nil : val
182
+ end
183
+ end
184
+ end