ray 0.0.1 → 0.1.0.pre1

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 (188) hide show
  1. data/.gemtest +0 -0
  2. data/.yardopts +4 -0
  3. data/README.md +17 -21
  4. data/Rakefile +18 -139
  5. data/VERSION +1 -1
  6. data/ext/audio.cpp +723 -0
  7. data/ext/{color.c → color.cpp} +25 -13
  8. data/ext/drawable.cpp +91 -0
  9. data/ext/event.cpp +460 -0
  10. data/ext/extconf.rb +5 -104
  11. data/ext/font.cpp +190 -0
  12. data/ext/image.cpp +733 -0
  13. data/ext/input.cpp +74 -0
  14. data/ext/ray.cpp +168 -0
  15. data/ext/ray.hpp +356 -0
  16. data/ext/{rect.c → rect.cpp} +51 -37
  17. data/ext/shader.cpp +169 -0
  18. data/ext/shape.cpp +409 -0
  19. data/ext/sprite.cpp +306 -0
  20. data/ext/text.cpp +181 -0
  21. data/ext/vector.cpp +215 -0
  22. data/guide.md +619 -0
  23. data/lib/ray/audio.rb +0 -41
  24. data/lib/ray/color.rb +32 -10
  25. data/lib/ray/drawable.rb +16 -0
  26. data/lib/ray/dsl/event_listener.rb +25 -2
  27. data/lib/ray/dsl/event_runner.rb +33 -5
  28. data/lib/ray/dsl/event_translator.rb +66 -30
  29. data/lib/ray/dsl/handler.rb +3 -2
  30. data/lib/ray/dsl/matcher.rb +58 -14
  31. data/lib/ray/font.rb +38 -96
  32. data/lib/ray/font_set.rb +8 -8
  33. data/lib/ray/game.rb +87 -66
  34. data/lib/ray/helper.rb +105 -10
  35. data/lib/ray/image.rb +150 -24
  36. data/lib/ray/image_set.rb +3 -1
  37. data/lib/ray/input.rb +10 -0
  38. data/lib/ray/music_set.rb +5 -3
  39. data/lib/ray/ray.rb +21 -9
  40. data/lib/ray/rect.rb +48 -7
  41. data/lib/ray/rmagick.rb +41 -0
  42. data/lib/ray/scene.rb +99 -43
  43. data/lib/ray/scene_list.rb +67 -0
  44. data/lib/ray/shape.rb +132 -0
  45. data/lib/ray/sound_set.rb +4 -2
  46. data/lib/ray/sprite.rb +49 -111
  47. data/lib/ray/text.rb +101 -0
  48. data/lib/ray/text_helper.rb +37 -0
  49. data/lib/ray/turtle.rb +215 -0
  50. data/lib/ray/vector.rb +226 -0
  51. data/samples/audio/spacial.rb +44 -0
  52. data/samples/hello_world/hello.rb +9 -13
  53. data/samples/hello_world/hello_dsl.rb +8 -12
  54. data/samples/hello_world/text.rb +15 -0
  55. data/samples/opengl/binding.rb +38 -0
  56. data/samples/opengl/image.rb +32 -0
  57. data/samples/opengl/opengl.rb +34 -0
  58. data/samples/opengl/shader.rb +42 -0
  59. data/samples/pong/pong.rb +14 -10
  60. data/samples/run_scene.rb +53 -0
  61. data/samples/shaders/scene.rb +40 -0
  62. data/samples/shaders/shaders.rb +42 -0
  63. data/samples/shaders/shape.rb +34 -0
  64. data/samples/sokoban/sokoban.rb +18 -18
  65. data/samples/test/actual_scene.rb +41 -0
  66. data/samples/test/scene_riot.rb +39 -0
  67. data/samples/test/scene_spec.rb +32 -0
  68. data/samples/test/scene_test_unit.rb +25 -0
  69. data/samples/turtle/byzantium.rb +45 -0
  70. data/samples/turtle/hilbert.rb +48 -0
  71. data/samples/turtle/koch.rb +55 -0
  72. data/samples/turtle/mandala.rb +61 -0
  73. data/samples/turtle/tree.rb +57 -0
  74. data/test/audio_test.rb +69 -0
  75. data/test/color_test.rb +77 -0
  76. data/test/drawable_test.rb +19 -0
  77. data/test/dsl_test.rb +93 -0
  78. data/test/font_test.rb +57 -0
  79. data/test/helpers.rb +94 -0
  80. data/test/image_test.rb +82 -0
  81. data/test/ray_test.rb +25 -0
  82. data/test/rect_test.rb +121 -0
  83. data/{spec → test}/res/VeraMono.ttf +0 -0
  84. data/{spec → test}/res/aqua.bmp +0 -0
  85. data/{spec → test}/res/aqua.png +0 -0
  86. data/{spec → test}/res/aqua2.bmp +0 -0
  87. data/{spec → test}/res/not_a_jpeg.jpeg +0 -0
  88. data/{spec → test}/res/pop.wav +0 -0
  89. data/test/resource_set_test.rb +99 -0
  90. data/test/run_all.rb +7 -0
  91. data/test/shape_test.rb +101 -0
  92. data/test/sprite_test.rb +89 -0
  93. data/test/text_test.rb +78 -0
  94. data/test/turtle_test.rb +176 -0
  95. data/test/vector_test.rb +111 -0
  96. data/yard_ext.rb +0 -28
  97. metadata +95 -139
  98. data/.gitignore +0 -23
  99. data/.gitmodules +0 -3
  100. data/.rspec +0 -3
  101. data/ext/audio.c +0 -473
  102. data/ext/event.c +0 -557
  103. data/ext/font.c +0 -287
  104. data/ext/image.c +0 -933
  105. data/ext/joystick.c +0 -145
  106. data/ext/ray.c +0 -489
  107. data/ext/ray.h +0 -245
  108. data/ext/ray_osx.m +0 -161
  109. data/lib/ray/joystick.rb +0 -30
  110. data/psp/SDL_psp_main.c +0 -84
  111. data/psp/bigdecimal/README +0 -60
  112. data/psp/bigdecimal/bigdecimal.c +0 -4697
  113. data/psp/bigdecimal/bigdecimal.h +0 -216
  114. data/psp/bigdecimal/lib/bigdecimal/jacobian.rb +0 -85
  115. data/psp/bigdecimal/lib/bigdecimal/ludcmp.rb +0 -84
  116. data/psp/bigdecimal/lib/bigdecimal/math.rb +0 -235
  117. data/psp/bigdecimal/lib/bigdecimal/newton.rb +0 -77
  118. data/psp/bigdecimal/lib/bigdecimal/util.rb +0 -65
  119. data/psp/digest/bubblebabble/bubblebabble.c +0 -142
  120. data/psp/digest/defs.h +0 -20
  121. data/psp/digest/digest.c +0 -643
  122. data/psp/digest/digest.h +0 -32
  123. data/psp/digest/lib/digest.rb +0 -50
  124. data/psp/digest/lib/md5.rb +0 -27
  125. data/psp/digest/lib/sha1.rb +0 -27
  126. data/psp/digest/md5/md5.c +0 -420
  127. data/psp/digest/md5/md5.h +0 -80
  128. data/psp/digest/md5/md5init.c +0 -40
  129. data/psp/digest/rmd160/rmd160.c +0 -457
  130. data/psp/digest/rmd160/rmd160.h +0 -56
  131. data/psp/digest/rmd160/rmd160init.c +0 -40
  132. data/psp/digest/sha1/sha1.c +0 -269
  133. data/psp/digest/sha1/sha1.h +0 -39
  134. data/psp/digest/sha1/sha1init.c +0 -40
  135. data/psp/digest/sha2/lib/sha2.rb +0 -73
  136. data/psp/digest/sha2/sha2.c +0 -919
  137. data/psp/digest/sha2/sha2.h +0 -109
  138. data/psp/digest/sha2/sha2init.c +0 -52
  139. data/psp/enumerator/enumerator.c +0 -298
  140. data/psp/etc/etc.c +0 -559
  141. data/psp/ext.c +0 -289
  142. data/psp/fcntl/fcntl.c +0 -187
  143. data/psp/lib/rbconfig.rb +0 -178
  144. data/psp/nkf/lib/kconv.rb +0 -367
  145. data/psp/nkf/nkf-utf8/config.h +0 -88
  146. data/psp/nkf/nkf-utf8/nkf.c +0 -6040
  147. data/psp/nkf/nkf-utf8/utf8tbl.c +0 -8500
  148. data/psp/nkf/nkf-utf8/utf8tbl.h +0 -34
  149. data/psp/nkf/nkf.c +0 -654
  150. data/psp/socket/addrinfo.h +0 -173
  151. data/psp/socket/getaddrinfo.c +0 -676
  152. data/psp/socket/getnameinfo.c +0 -270
  153. data/psp/socket/pspsocket.c +0 -71
  154. data/psp/socket/pspsocket.h +0 -28
  155. data/psp/socket/socket.c +0 -4662
  156. data/psp/socket/sockport.h +0 -76
  157. data/psp/stringio/stringio.c +0 -1306
  158. data/psp/strscan/strscan.c +0 -1320
  159. data/psp/syck/bytecode.c +0 -1166
  160. data/psp/syck/emitter.c +0 -1242
  161. data/psp/syck/gram.c +0 -1894
  162. data/psp/syck/gram.h +0 -79
  163. data/psp/syck/handler.c +0 -174
  164. data/psp/syck/implicit.c +0 -2990
  165. data/psp/syck/node.c +0 -408
  166. data/psp/syck/rubyext.c +0 -2367
  167. data/psp/syck/syck.c +0 -504
  168. data/psp/syck/syck.h +0 -456
  169. data/psp/syck/token.c +0 -2725
  170. data/psp/syck/yaml2byte.c +0 -257
  171. data/psp/syck/yamlbyte.h +0 -170
  172. data/psp/thread/thread.c +0 -1175
  173. data/psp/zlib/zlib.c +0 -3547
  174. data/script.rb +0 -10
  175. data/spec/ray/audio_spec.rb +0 -146
  176. data/spec/ray/color_spec.rb +0 -57
  177. data/spec/ray/event_spec.rb +0 -80
  178. data/spec/ray/font_spec.rb +0 -93
  179. data/spec/ray/image_set_spec.rb +0 -48
  180. data/spec/ray/image_spec.rb +0 -162
  181. data/spec/ray/joystick_spec.rb +0 -21
  182. data/spec/ray/matcher_spec.rb +0 -50
  183. data/spec/ray/ray_spec.rb +0 -88
  184. data/spec/ray/rect_spec.rb +0 -154
  185. data/spec/ray/resource_set_spec.rb +0 -105
  186. data/spec/ray/sprite_spec.rb +0 -163
  187. data/spec/spec.opts +0 -4
  188. data/spec/spec_helper.rb +0 -8
@@ -0,0 +1,215 @@
1
+ #include "ray.hpp"
2
+
3
+ VALUE ray_cVector2 = Qnil;
4
+ VALUE ray_cVector3 = Qnil;
5
+
6
+ ray_vector2 *ray_rb2vector2_ptr(VALUE obj) {
7
+ if (!RAY_IS_A(obj, ray_cVector2)) {
8
+ rb_raise(rb_eTypeError, "Can't convert %s into Ray::Vector2",
9
+ RAY_OBJ_CLASSNAME(obj));
10
+ }
11
+
12
+ ray_vector2 *vector;
13
+ Data_Get_Struct(obj, ray_vector2, vector);
14
+
15
+ return vector;
16
+ }
17
+
18
+ ray_vector2 ray_convert_to_vector2(VALUE obj) {
19
+ obj = rb_funcall(obj, RAY_METH("to_vector2"), 0);
20
+ return *ray_rb2vector2_ptr(obj);
21
+ }
22
+
23
+ void ray_free_vector2(ray_vector2 *ptr) {
24
+ delete ptr;
25
+ }
26
+
27
+ VALUE ray_alloc_vector2(VALUE self) {
28
+ ray_vector2 *vector = new ray_vector2;
29
+ return Data_Wrap_Struct(self, 0, ray_free_vector2, vector);
30
+ }
31
+
32
+ VALUE ray_vector2_to_rb(const ray_vector2 &vector) {
33
+ ray_vector2 *copy = new ray_vector2(vector);
34
+ return Data_Wrap_Struct(ray_cVector2, 0, ray_free_vector2, copy);
35
+ }
36
+
37
+ /*
38
+ @overload initialize(x = 0.0, y = 0.0)
39
+ */
40
+ VALUE ray_init_vector2(int argc, VALUE *argv, VALUE self) {
41
+ ray_vector2 *vector = ray_rb2vector2_ptr(self);
42
+
43
+ VALUE x, y;
44
+ rb_scan_args(argc, argv, "02", &x, &y);
45
+
46
+ if (!NIL_P(x)) vector->x = NUM2DBL(x);
47
+ if (!NIL_P(y)) vector->y = NUM2DBL(y);
48
+
49
+ return self;
50
+ }
51
+
52
+ VALUE ray_init_vector2_copy(VALUE self, VALUE other) {
53
+ *ray_rb2vector2_ptr(self) = *ray_rb2vector2_ptr(other);
54
+ return self;
55
+ }
56
+
57
+ /* @return [Float] x position of the vector */
58
+ VALUE ray_vector2_x(VALUE self) {
59
+ return rb_float_new(ray_rb2vector2_ptr(self)->x);
60
+ }
61
+
62
+ /* @return [Float] y position of the vector */
63
+ VALUE ray_vector2_y(VALUE self) {
64
+ return rb_float_new(ray_rb2vector2_ptr(self)->y);
65
+ }
66
+
67
+ /* Sets the x position of the vector */
68
+ VALUE ray_vector2_set_x(VALUE self, VALUE x) {
69
+ rb_check_frozen(self);
70
+
71
+ ray_rb2vector2_ptr(self)->x = NUM2DBL(x);
72
+ return x;
73
+ }
74
+
75
+ /* Sets the y position of the vector */
76
+ VALUE ray_vector2_set_y(VALUE self, VALUE y) {
77
+ rb_check_frozen(self);
78
+
79
+ ray_rb2vector2_ptr(self)->y = NUM2DBL(y);
80
+ return y;
81
+ }
82
+
83
+ ray_vector3 *ray_rb2vector3_ptr(VALUE obj) {
84
+ if (!RAY_IS_A(obj, ray_cVector3)) {
85
+ rb_raise(rb_eTypeError, "Can't convert %s into Ray::Vector3",
86
+ RAY_OBJ_CLASSNAME(obj));
87
+ }
88
+
89
+ ray_vector3 *vector;
90
+ Data_Get_Struct(obj, ray_vector3, vector);
91
+
92
+ return vector;
93
+ }
94
+
95
+ ray_vector3 ray_convert_to_vector3(VALUE obj) {
96
+ obj = rb_funcall(obj, RAY_METH("to_vector3"), 0);
97
+ return *ray_rb2vector3_ptr(obj);
98
+ }
99
+
100
+ void ray_free_vector3(ray_vector3 *ptr) {
101
+ delete ptr;
102
+ }
103
+
104
+ VALUE ray_alloc_vector3(VALUE self) {
105
+ ray_vector3 *vector = new ray_vector3;
106
+ return Data_Wrap_Struct(self, 0, ray_free_vector3, vector);
107
+ }
108
+
109
+ VALUE ray_vector3_to_rb(const ray_vector3 &vector) {
110
+ ray_vector3 *copy = new ray_vector3(vector);
111
+ return Data_Wrap_Struct(ray_cVector3, 0, ray_free_vector3, copy);
112
+ }
113
+
114
+ /*
115
+ @overload initialize(x = 0.0, y = 0.0, z = 0.0)
116
+ */
117
+ VALUE ray_init_vector3(int argc, VALUE *argv, VALUE self) {
118
+ ray_vector3 *vector = ray_rb2vector3_ptr(self);
119
+
120
+ VALUE x, y, z;
121
+ rb_scan_args(argc, argv, "03", &x, &y, &z);
122
+
123
+ if (!NIL_P(x)) vector->x = NUM2DBL(x);
124
+ if (!NIL_P(y)) vector->y = NUM2DBL(y);
125
+ if (!NIL_P(z)) vector->z = NUM2DBL(z);
126
+
127
+ return self;
128
+ }
129
+
130
+ VALUE ray_init_vector3_copy(VALUE self, VALUE other) {
131
+ *ray_rb2vector3_ptr(self) = *ray_rb2vector3_ptr(other);
132
+ return self;
133
+ }
134
+
135
+ /* The x position of the vector */
136
+ VALUE ray_vector3_x(VALUE self) {
137
+ return rb_float_new(ray_rb2vector3_ptr(self)->x);
138
+ }
139
+
140
+ /* The y position of the vector */
141
+ VALUE ray_vector3_y(VALUE self) {
142
+ return rb_float_new(ray_rb2vector3_ptr(self)->y);
143
+ }
144
+
145
+ /* The z position of the vector */
146
+ VALUE ray_vector3_z(VALUE self) {
147
+ return rb_float_new(ray_rb2vector3_ptr(self)->z);
148
+ }
149
+
150
+ /* Sets the x position of the vector */
151
+ VALUE ray_vector3_set_x(VALUE self, VALUE x) {
152
+ rb_check_frozen(self);
153
+ ray_rb2vector3_ptr(self)->x = NUM2DBL(x);
154
+ return x;
155
+ }
156
+
157
+ /* Sets the y position of the vector */
158
+ VALUE ray_vector3_set_y(VALUE self, VALUE y) {
159
+ rb_check_frozen(self);
160
+ ray_rb2vector3_ptr(self)->y = NUM2DBL(y);
161
+ return y;
162
+ }
163
+
164
+ /* Sets the z position of the vector */
165
+ VALUE ray_vector3_set_z(VALUE self, VALUE z) {
166
+ rb_check_frozen(self);
167
+ ray_rb2vector3_ptr(self)->z = NUM2DBL(z);
168
+ return z;
169
+ }
170
+
171
+ /*
172
+ Document-class: Ray::Vector2
173
+
174
+ This class can be used to represent either a point (x, y) or a size WxH
175
+ (hence the aliases).
176
+ */
177
+
178
+ /*
179
+ Document-class: Ray::Vector3
180
+
181
+ Represents a point in a 3D space.
182
+ */
183
+
184
+ void Init_ray_vector() {
185
+ ray_cVector2 = rb_define_class_under(ray_mRay, "Vector2", rb_cObject);
186
+ rb_define_alloc_func(ray_cVector2, ray_alloc_vector2);
187
+ rb_define_method(ray_cVector2, "initialize", ray_init_vector2, -1);
188
+ rb_define_method(ray_cVector2, "initialize_copy", ray_init_vector2_copy, 1);
189
+
190
+ rb_define_method(ray_cVector2, "x", ray_vector2_x, 0);
191
+ rb_define_method(ray_cVector2, "y", ray_vector2_y, 0);
192
+
193
+ rb_define_method(ray_cVector2, "x=", ray_vector2_set_x, 1);
194
+ rb_define_method(ray_cVector2, "y=", ray_vector2_set_y, 1);
195
+
196
+ ray_cVector3 = rb_define_class_under(ray_mRay, "Vector3", rb_cObject);
197
+ rb_define_alloc_func(ray_cVector3, ray_alloc_vector3);
198
+ rb_define_method(ray_cVector3, "initialize", ray_init_vector3, -1);
199
+ rb_define_method(ray_cVector3, "initialize_copy", ray_init_vector3_copy, 1);
200
+
201
+ rb_define_method(ray_cVector3, "x", ray_vector3_x, 0);
202
+ rb_define_method(ray_cVector3, "y", ray_vector3_y, 0);
203
+ rb_define_method(ray_cVector3, "z", ray_vector3_z, 0);
204
+
205
+ rb_define_method(ray_cVector3, "x=", ray_vector3_set_x, 1);
206
+ rb_define_method(ray_cVector3, "y=", ray_vector3_set_y, 1);
207
+ rb_define_method(ray_cVector3, "z=", ray_vector3_set_z, 1);
208
+ }
209
+
210
+
211
+
212
+
213
+
214
+
215
+
@@ -0,0 +1,619 @@
1
+ # Installation
2
+ ## SFML
3
+
4
+ You can possibly install the SFML 2 using your packag manager. For instance,
5
+ with Archlinux:
6
+
7
+ yaourt -S sfml2-svn
8
+
9
+ Otherwise, download [the SFML](http://www.sfml-dev.org/download.php) and build
10
+ it, like:
11
+
12
+ cd sfml2
13
+ cmake .
14
+ make
15
+ make install # as root
16
+
17
+ ## Ray
18
+
19
+ The ray gem is currently an older version of Ray. You can get the latest
20
+ one from github:
21
+
22
+ git clone git://github.com/Mon-Ouie/ray.git
23
+ cd ray
24
+ rake install
25
+
26
+ Ray should work on at least Linux, Window, and Mac OS X, with Ruby 1.8.7,
27
+ 1.9.2 and Rubinius.
28
+
29
+ # Hello world!
30
+ Here's a simple "Hello world!" written with ray:
31
+
32
+ require 'ray'
33
+ screen = Ray.create_window(:w => 100, :h => 100).fill(Ray::Color.black)
34
+ Ray::Font.default.draw("Hello world!", :on => screen, :size => 12).update
35
+ sleep 5
36
+
37
+ It simply creates a window of size 100x100, fills it with black, then draws
38
+ "Hello world!" on it with size 12, with the upper-left corner of the text at (0, 0).
39
+ The screen must be updated so the modifications appear. Notice methods like #fill, #draw,
40
+ #update, ... return the object they drew on, which allows to chain methods call.
41
+
42
+ Unfortunately, we have to sleep for a few seconds, or the window will be closed
43
+ directly, and no one would see it. To exit when the user does something (say,
44
+ when he tries to close the window), we need to check for events. Here's the
45
+ lowest level way to do it:
46
+
47
+ require 'ray'
48
+
49
+ screen = Ray.create_window(:w => 100, :h => 100)
50
+ event = Ray::Event.new
51
+
52
+ while event.type != Ray::Event::TYPE_QUIT
53
+ event.poll!
54
+
55
+ screen.fill Ray::Color.black
56
+ Ray::Font.default.draw("Hello world!", :on => screen, :size => 12).update
57
+ end
58
+
59
+ The difference between the previous example is that we are using an event
60
+ object. As long as its type is not Ray::Event::TYPE_QUIT, we update the
61
+ event object (event.poll! does it without blocking the process) and
62
+ redraw the screen.
63
+
64
+ Ray has another way of doing this, though. It uses scenes and games objects.
65
+ A scene is an object checking events to draw something on screen, and a
66
+ game creates a window and handles a stack of scenes. Both kinds of
67
+ objects can register blocks or callable objects to some event.
68
+
69
+ require 'ray'
70
+
71
+ Ray.game "Hello world!", :size => [100, 100] do
72
+ register do
73
+ add_hook :quit, method(:exit!)
74
+ end
75
+
76
+ scene :hello do
77
+ @font = Ray::Font.default
78
+
79
+ render do |screen|
80
+ @font.draw("Hello world!", :on => screen, :size => 12)
81
+ end
82
+ end
83
+
84
+ push_scene :hello
85
+ end
86
+
87
+ Ray.game requires you to give the name of your game, which will be used as
88
+ the title of the window. You can then specify the size of the window,
89
+ 100x100 in this case.
90
+
91
+ In this block, self is the game object. After the end of the block, the game
92
+ will be automatically run.
93
+
94
+ The game object needs to register to some events. This must be done in a block,
95
+ which will be called whenever the game object needs to register for events again.
96
+ In this case, I used add_hook with a callable object. It is (almost) equivalent
97
+ to the following:
98
+
99
+ on(:quit) { exit! }
100
+
101
+ Then, we can specify the scenes used by the game, using #scene. In the block we
102
+ passed to it, self would be our scene object. We can specify how to render
103
+ our scene from there.
104
+
105
+ Lastly, it is required to add a scene to the game's stack. A game runs as long
106
+ as there are scenes in its stack.
107
+
108
+ # Images
109
+
110
+ Images are the first objects used, even in a Hello world. The screen itself
111
+ is represented as an image object. They can be drawn, and one can draw on
112
+ them.
113
+
114
+ You can generate them programatically...
115
+
116
+ img = Ray::Image.new(:w => 100, :h => 100)
117
+ img.fill Ray::Color.blue
118
+ img.draw_line [0, 0], [50, 50], Ray::Color.green
119
+ img.draw_filled_ellipse [20, 20], 10, 15, Ray::Color.white
120
+ img.update # Or you won't see anything.
121
+
122
+ ... or from a file...
123
+
124
+ img = Ray::Image.new("some_file.png")
125
+
126
+ ... or from some data stored in memory.
127
+
128
+ require 'open-uri'
129
+ img = open("https://github.com/favicon.png") { |io| Ray::Image.new(io) }
130
+
131
+ Then, the image can simply drawn on another one:
132
+
133
+ img.draw(:on => screen, :at => [30, 30])
134
+
135
+ ## Pixel access
136
+ You can read inidividual pixels of an image using #[]:
137
+
138
+ img[15, 15] # => RGBA(...)
139
+
140
+ It returns a Ray::Color object, on which you can call #r, #g, #b, and #a
141
+ or #red, #green, #blue, and #alpha (alpha is the opacity, 255 is opaque).
142
+
143
+ Similarily, you can use #[]= to change the color of a pixel /if/ the image
144
+ is locked. To lock it you can use #lock and #unlock or just #lock with a
145
+ block:
146
+ img.lock { img[15, 15] = Ray::Color.new(rand(256), rand(256), rand(256)) }
147
+
148
+ (You don't need to call #update after #unlock)
149
+
150
+ So, your images are basically collections of pixels. Therefore...
151
+
152
+ img.each do |color|
153
+ puts color
154
+ end
155
+
156
+ # map returns an image, not an array.
157
+ another_img = img.map do |color|
158
+ Ray::Color.new(0, 0, color.b)
159
+ end
160
+
161
+ img.map_with_pos! do |color, x, y|
162
+ Ray::Color.new(x, y, color.b)
163
+ end
164
+
165
+ # Sprites
166
+
167
+ Ray::Image#draw accepts many options: :on, :at, :rect, :shader, :color, :zoom,
168
+ :angle, ... and sometimes, you will just always draw the image with the same
169
+ parameters. Having an object storing those parameters is useful, and it's
170
+ the purpose of the sprite class. It is pretty simple to create one:
171
+
172
+ sprite = Ray::Sprite.new(image, :angle => 30)
173
+ sprite = Ray::Sprite.new("some_file.png")
174
+
175
+ There are also accessors for the attributes of the sprite:
176
+
177
+ sprite.color = Ray::Color.green
178
+ sprite.pos = [3, 4]
179
+ sprite.pos # => Vector2[3, 4]
180
+
181
+ Then you can draw an image using #draw_on:
182
+
183
+ sprite.draw_on some_image
184
+
185
+ ## Using sprite sheets
186
+
187
+ Often, big images containing several parts which are displayed
188
+ individually are used. You could just select the rect you want
189
+ to display manually:
190
+
191
+ sprite.from_rect = Ray::Rect.new(32, 64, 32, 32)
192
+
193
+ Ray also allows you to specify that there are, say, 3 cells
194
+ of equal width on each line, and 4 lines height in the image:
195
+
196
+ sprite.sheet_size = [3, 4]
197
+
198
+ Then, you could select the cell at (1, 2):
199
+
200
+ sprite.sheet_pos = [1, 2]
201
+
202
+ # Shapes
203
+
204
+ As shown, you can draw several shapes on an image using methods
205
+ like #draw\_circle or #draw\_filled_rect. You can also create more
206
+ complex polygons (NB: A circle is a polygon with many sides ; an
207
+ ellipse is a stretched circle) using the Shape class.
208
+
209
+ The shape itself has its own attributes:
210
+
211
+ * A scale factor
212
+ * The width of the outline
213
+ * Whether the shape is filled and the outline is visible
214
+
215
+ shape.scale = [3, 0.5]
216
+ shape.outline_width = 10
217
+ shape.filled = true
218
+ shape.outlined = false
219
+
220
+ And each point of a shape has a few attributes:
221
+
222
+ * Its position
223
+ * Its color
224
+ * The color of its outline
225
+
226
+ You can add a point with the required attributes using #add_point:
227
+
228
+ shape.add_point [0, 0]
229
+ shape.add_point [0, 0], Ray::Color.green
230
+ shape.add_point [0, 0], Ray::Color.green, Ray::Color.red
231
+
232
+ Or even use Shape.new's block form:
233
+
234
+ Ray::Shape.new(10) do |point|
235
+ p point.id # => Something in 0..9
236
+
237
+ point.pos = [3, 4]
238
+ point.color = Ray::Color.green
239
+ end
240
+
241
+ Draw the shape on an image is similar to drawing a sprite:
242
+
243
+ shape.draw_on image
244
+
245
+ # Text drawing
246
+
247
+ Fonts are used to draw text. Apart from the default font, you can create
248
+ a font from a file:
249
+
250
+ font = Ray::Font.new("file.ttf")
251
+ font = open("file.ttf") { |io| Ray::Font.new(io) }
252
+
253
+ Then you can just draw text using Font#draw:
254
+
255
+ some_font.draw(some_string, :on => some_image, :at => some_pos, :size => 12)
256
+
257
+ You can also specify the style with an array containing one or more of the
258
+ following symbols as the :style parameter:
259
+
260
+ * :bold
261
+ * :italic
262
+ * :underline
263
+
264
+ In Ruby 1.8, unless your string is UTF-8 encoded, you may want to specify
265
+ the actual encoding you're using with the :encoding parameter.
266
+
267
+ As you may need to compute the position of a text before drawing it, it is
268
+ possible to get the size it uses with a given set of options:
269
+
270
+ font.size_of("Hello", :size => 12)
271
+
272
+ ## Using text objects
273
+
274
+ Ray::Font#draw has, like Ray::Image#draw, many options. Simarily, a class
275
+ keeping those parameters to draw text exists: Ray::Text.
276
+
277
+ They can be created with a string, and some optional parameters:
278
+ text = Ray::Text.new "Hello world!", :at => [10, 10], :size => 30
279
+
280
+ Their attributes can then be changed:
281
+ text.angle += 60
282
+
283
+ And you can draw them on an image just like with sprite:
284
+ text.draw_on your_image
285
+
286
+ # Games & Scenes
287
+ ## Parts of a scene
288
+ The previous samples showed two parts in a scene: the setup code, and
289
+ the rendering code.
290
+
291
+ scene :name do
292
+ # setup
293
+
294
+ render do |win|
295
+ # render
296
+ end
297
+ end
298
+
299
+ Scenes are in fact more complex than that. A third important part is the
300
+ always-block: it is used to execute code during every frame. For instance,
301
+ the following scene draws an image moving to the right at every frame.
302
+
303
+ scene :moving_sprite do
304
+ self.frames_per_second = 30 # Framerate limitation
305
+
306
+ @sprite = sprite("image.png")
307
+
308
+ always do
309
+ @sprite.x += 1
310
+ end
311
+
312
+ render do |win|
313
+ @sprite.render_on win
314
+ end
315
+ end
316
+
317
+ Scenes also have a clean up step, where you can remove references to objects
318
+ that you don't need anymore so they can be garbage collected, or call methods
319
+ to clean them up (like IO#close).
320
+
321
+ scene :clean_up do
322
+ @some_big_object = load_some_big_object
323
+
324
+ # ...
325
+
326
+ clean_up do
327
+ @some_big_object = nil
328
+ end
329
+ end
330
+
331
+ ## Scene flow
332
+ It is likely an actual program will have to show more than a single scene. You
333
+ can simply define more than one scene and use the push_scene and pop_scene methods.
334
+
335
+ When a scene is popped, the previous scene is setup again. Sometimes, you may
336
+ want to run a scene, and then just come back to wherever you ran the scene from.
337
+ This is possible by using Scene#run_scene. Here's a more complex sample showing
338
+ how it behaves:
339
+
340
+ require 'ray'
341
+
342
+ $stdout = StringIO.new
343
+
344
+ Ray.game "run_scene" do
345
+ register do
346
+ add_hook :quit, method(:exit!)
347
+
348
+ on :key_press, key(:up) do
349
+ puts "#{self} knows you pressed up"
350
+ end
351
+ end
352
+
353
+ font = Ray::Font.default
354
+
355
+ scene :sec do
356
+ on :key_press, key(:down) do
357
+ puts "#{self} knows you pressed down"
358
+ end
359
+
360
+ on :key_press, key(:left) do
361
+ pop_scene
362
+ end
363
+
364
+ puts "In scene :sec"
365
+
366
+ render do |win|
367
+ font.draw($stdout.string, :on => win, :size => 12)
368
+ end
369
+ end
370
+
371
+ scene :first do
372
+ on :key_press, key(:left) do
373
+ puts "#{self} knows you pressed left"
374
+ end
375
+
376
+ on :key_press, key(:right) do
377
+ run_scene :sec
378
+ puts "Back to scene :first"
379
+ end
380
+
381
+ puts "In scene :first"
382
+
383
+ render do |win|
384
+ font.draw($stdout.string, :on => win, :size => 12)
385
+ end
386
+ end
387
+
388
+ push_scene :first
389
+ end
390
+
391
+ Another feature of scenes is their argument list. You could have a menu and using
392
+ it in several places with different elements, without creating two scenes, by using
393
+ scene arguments instead.
394
+
395
+ scene :menu do |elements|
396
+ # ...
397
+ end
398
+
399
+ push_scene :menu, %w[Hello world]
400
+ push_scene :menu, %w[This works too]
401
+
402
+ ## Handling input
403
+
404
+ Scenes raise several events related to what the user is doing, here are a few:
405
+ on :key_press, key(:left) do |key, mod_keys|
406
+ end
407
+
408
+ on :window_resize do |size|
409
+ end
410
+
411
+ on :joy_press, 0, more_than(3) do |joy_id, button_id|
412
+ end
413
+
414
+ You can see all the events raised in Ray::DSL::EventTranslator.
415
+
416
+ Aside from those events, it is always possible to know the position of the
417
+ mouse, and whether or not a key is being held.
418
+
419
+ holding? key(:left)
420
+ mouse_x
421
+ mouse_y
422
+
423
+ ## Event groups
424
+
425
+ Sometimes, in the same scene, you'll want to disable some event handlers. For instance,
426
+ in a game, when a menu is shown, you'd want the arrows to change the selected item
427
+ instead of moving the character. To accomplish this in Ray, one could use event
428
+ groups:
429
+
430
+ event_group :player do
431
+ %w[up down left right].each do |dir|
432
+ on(:key_press, key(dir)) { move_to(dir) }
433
+ end
434
+ end
435
+
436
+ event_group :menu do
437
+ on(:key_press, key(:up)) { select_prev }
438
+ on(:key_press, key(:down)) { select_next }
439
+ end
440
+
441
+ disable_event_group :menu
442
+
443
+ on :key_press, key(:enter) do
444
+ if menu.shown?
445
+ menu.hide
446
+
447
+ enable_event_group :player
448
+ disable_event_grroup :menu
449
+ else
450
+ menu.show
451
+
452
+ disable_event_group :player
453
+ enable_event_group :menu
454
+ end
455
+ end
456
+
457
+ Notice that, even though I used symbols to name event groups, this is not
458
+ required. In fact, using the menu object itself as the event group identifier
459
+ could sometimes be better than generating a string to identify each of them.
460
+
461
+ ## Subclassing Ray::Scene
462
+
463
+ In a bigger programming, having all the scenes in the same file would be
464
+ unmaintable. Using the DSL to define scenes is thus not requirde:
465
+
466
+ class YourScene < Ray::Scene
467
+ scene_name :your_scene
468
+
469
+ def setup(*arguments)
470
+ # load stuff
471
+ end
472
+
473
+ def register
474
+ # register for events
475
+ end
476
+
477
+ def render(win)
478
+ # render stuff on an image
479
+ end
480
+
481
+ def clean_up
482
+ # clean the scene
483
+ end
484
+ end
485
+
486
+ Then, instead of calling the scene method you'd use YourScene.bind, as in:
487
+ Ray.game "some game" do
488
+ YourScene.bind(self)
489
+ push_scene :your_scene
490
+ end
491
+
492
+ # Turtle Graphics
493
+
494
+ Ray implements turtle graphics. You can just start drawing on an image using
495
+ Ray::Image#turtle:
496
+
497
+ image.turtle do
498
+ # self is a turtle!
499
+ end
500
+
501
+ Then you can move forward or backward, turn left or right, change the color
502
+ the pen, ...
503
+
504
+ image.turtle do
505
+ backward 10
506
+ right 30
507
+ forward 10
508
+
509
+ self.pen_width = 10
510
+ self.color = Ray::Color.red
511
+
512
+ forward 10
513
+ end
514
+
515
+ # Shaders
516
+
517
+ Ray has methods which allow you to use pixel shaders. Those are written in
518
+ the GLSL language, which you need to know to use them.
519
+
520
+ They can be created from files quite simply:
521
+
522
+ shader = Ray::Shader.new("file.c")
523
+
524
+ You can also load them from an IO object, possible a StringIO:
525
+
526
+ shader = Ray::Shader.new StringIO.new(<<-EOF)
527
+ void main() {
528
+ /* Your shader! */
529
+ }
530
+ EOF
531
+
532
+ They can be used to draw images, sprites, or shapes:
533
+
534
+ image.draw :on => other, :shader => your_shader
535
+ sprite.shader = your_shader
536
+ shape.draw :on => other, :shader => your_shader
537
+
538
+ If you've enabled lazy rendering for scenes, you can also set a shader to be used
539
+ whenever you're drawing your image to the screen:
540
+
541
+ self.lazy_rendering = true
542
+ self.shader = Ray::Shader.new("some_file")
543
+
544
+ # OpenGL
545
+
546
+ It is possible to use plain OpenGL to draw stuff when using Ray. To do so,
547
+ you would of course need an OpenGL binding. Then you can use the Ray::Drawable
548
+ class to draw stuff:
549
+
550
+ drawable = Ray::Drawable.new do |img|
551
+ # img is the image you are drawing on
552
+
553
+ # OpenGL rendering code here
554
+ end
555
+
556
+ drawable.draw_on image
557
+
558
+ Ray automatically changes the projection matrix so (0, 0, 0) is the upper left
559
+ corner, and (width, height, 0) the bottom down corner. Of course, you can change
560
+ the matrix to whatever you want.
561
+
562
+ ## Using Ray's resources
563
+
564
+ Ray can load images and shaders. If you want to draw a 3D cube, you can still use
565
+ Ray's images. To do so, just call Ray::Image#bind. Similarily, you can use a shader
566
+ by passing a block to Ray::Shader#bind.
567
+
568
+ drawable = Ray::Drawable.new do |img|
569
+ some_other_image.bind
570
+
571
+ some_shader.bind do
572
+ # Draw something using the previous image as a texture
573
+ end
574
+ end
575
+
576
+ # Audio
577
+ ## Playing sound
578
+
579
+ Ray uses two classes to play sounds: Ray::Sound and Ray::Music. The former is used
580
+ for short sounds, and the latter to stream musics. They both have a very similar
581
+ API. You can create them from files or IO objects:
582
+
583
+ sound = Ray::Sound.new("file.wav")
584
+ sound = open("file.wav") { |io| Ray::Sound.new(io) }
585
+
586
+ music = Ray::Music.new("file.wav")
587
+ music = open("file.wav") { |io| Ray::Music.new(io) }
588
+
589
+ Then they can all be played, paused, or stopped:
590
+
591
+ sound.play
592
+ sound.pause
593
+ sound.stop
594
+
595
+ They can be configured to play in a loop:
596
+
597
+ sound.loop = true
598
+
599
+ Their volume can be changed:
600
+
601
+ sound.volume = 30 # (value between 0 and 100)
602
+
603
+ ## 3D sound effects
604
+
605
+ It is possible to change several paremeters to give 3D sound effects.
606
+ You can move the listener however you want:
607
+
608
+ Ray::Audio.position = [3, -4, 2.5]
609
+ Ray::Audio.direction = [1, -2, 3]
610
+
611
+ Same for the sounds which are played:
612
+
613
+ sound.position = [1, -3, 4]
614
+
615
+ # More
616
+ Ray's API is documented too. Check the documentation to know more about
617
+ how to use it.
618
+
619
+ There are also a few samples which you can find in the git repository.