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.
- data/.gemtest +0 -0
- data/.yardopts +4 -0
- data/README.md +17 -21
- data/Rakefile +18 -139
- data/VERSION +1 -1
- data/ext/audio.cpp +723 -0
- data/ext/{color.c → color.cpp} +25 -13
- data/ext/drawable.cpp +91 -0
- data/ext/event.cpp +460 -0
- data/ext/extconf.rb +5 -104
- data/ext/font.cpp +190 -0
- data/ext/image.cpp +733 -0
- data/ext/input.cpp +74 -0
- data/ext/ray.cpp +168 -0
- data/ext/ray.hpp +356 -0
- data/ext/{rect.c → rect.cpp} +51 -37
- data/ext/shader.cpp +169 -0
- data/ext/shape.cpp +409 -0
- data/ext/sprite.cpp +306 -0
- data/ext/text.cpp +181 -0
- data/ext/vector.cpp +215 -0
- data/guide.md +619 -0
- data/lib/ray/audio.rb +0 -41
- data/lib/ray/color.rb +32 -10
- data/lib/ray/drawable.rb +16 -0
- data/lib/ray/dsl/event_listener.rb +25 -2
- data/lib/ray/dsl/event_runner.rb +33 -5
- data/lib/ray/dsl/event_translator.rb +66 -30
- data/lib/ray/dsl/handler.rb +3 -2
- data/lib/ray/dsl/matcher.rb +58 -14
- data/lib/ray/font.rb +38 -96
- data/lib/ray/font_set.rb +8 -8
- data/lib/ray/game.rb +87 -66
- data/lib/ray/helper.rb +105 -10
- data/lib/ray/image.rb +150 -24
- data/lib/ray/image_set.rb +3 -1
- data/lib/ray/input.rb +10 -0
- data/lib/ray/music_set.rb +5 -3
- data/lib/ray/ray.rb +21 -9
- data/lib/ray/rect.rb +48 -7
- data/lib/ray/rmagick.rb +41 -0
- data/lib/ray/scene.rb +99 -43
- data/lib/ray/scene_list.rb +67 -0
- data/lib/ray/shape.rb +132 -0
- data/lib/ray/sound_set.rb +4 -2
- data/lib/ray/sprite.rb +49 -111
- data/lib/ray/text.rb +101 -0
- data/lib/ray/text_helper.rb +37 -0
- data/lib/ray/turtle.rb +215 -0
- data/lib/ray/vector.rb +226 -0
- data/samples/audio/spacial.rb +44 -0
- data/samples/hello_world/hello.rb +9 -13
- data/samples/hello_world/hello_dsl.rb +8 -12
- data/samples/hello_world/text.rb +15 -0
- data/samples/opengl/binding.rb +38 -0
- data/samples/opengl/image.rb +32 -0
- data/samples/opengl/opengl.rb +34 -0
- data/samples/opengl/shader.rb +42 -0
- data/samples/pong/pong.rb +14 -10
- data/samples/run_scene.rb +53 -0
- data/samples/shaders/scene.rb +40 -0
- data/samples/shaders/shaders.rb +42 -0
- data/samples/shaders/shape.rb +34 -0
- data/samples/sokoban/sokoban.rb +18 -18
- data/samples/test/actual_scene.rb +41 -0
- data/samples/test/scene_riot.rb +39 -0
- data/samples/test/scene_spec.rb +32 -0
- data/samples/test/scene_test_unit.rb +25 -0
- data/samples/turtle/byzantium.rb +45 -0
- data/samples/turtle/hilbert.rb +48 -0
- data/samples/turtle/koch.rb +55 -0
- data/samples/turtle/mandala.rb +61 -0
- data/samples/turtle/tree.rb +57 -0
- data/test/audio_test.rb +69 -0
- data/test/color_test.rb +77 -0
- data/test/drawable_test.rb +19 -0
- data/test/dsl_test.rb +93 -0
- data/test/font_test.rb +57 -0
- data/test/helpers.rb +94 -0
- data/test/image_test.rb +82 -0
- data/test/ray_test.rb +25 -0
- data/test/rect_test.rb +121 -0
- data/{spec → test}/res/VeraMono.ttf +0 -0
- data/{spec → test}/res/aqua.bmp +0 -0
- data/{spec → test}/res/aqua.png +0 -0
- data/{spec → test}/res/aqua2.bmp +0 -0
- data/{spec → test}/res/not_a_jpeg.jpeg +0 -0
- data/{spec → test}/res/pop.wav +0 -0
- data/test/resource_set_test.rb +99 -0
- data/test/run_all.rb +7 -0
- data/test/shape_test.rb +101 -0
- data/test/sprite_test.rb +89 -0
- data/test/text_test.rb +78 -0
- data/test/turtle_test.rb +176 -0
- data/test/vector_test.rb +111 -0
- data/yard_ext.rb +0 -28
- metadata +95 -139
- data/.gitignore +0 -23
- data/.gitmodules +0 -3
- data/.rspec +0 -3
- data/ext/audio.c +0 -473
- data/ext/event.c +0 -557
- data/ext/font.c +0 -287
- data/ext/image.c +0 -933
- data/ext/joystick.c +0 -145
- data/ext/ray.c +0 -489
- data/ext/ray.h +0 -245
- data/ext/ray_osx.m +0 -161
- data/lib/ray/joystick.rb +0 -30
- data/psp/SDL_psp_main.c +0 -84
- data/psp/bigdecimal/README +0 -60
- data/psp/bigdecimal/bigdecimal.c +0 -4697
- data/psp/bigdecimal/bigdecimal.h +0 -216
- data/psp/bigdecimal/lib/bigdecimal/jacobian.rb +0 -85
- data/psp/bigdecimal/lib/bigdecimal/ludcmp.rb +0 -84
- data/psp/bigdecimal/lib/bigdecimal/math.rb +0 -235
- data/psp/bigdecimal/lib/bigdecimal/newton.rb +0 -77
- data/psp/bigdecimal/lib/bigdecimal/util.rb +0 -65
- data/psp/digest/bubblebabble/bubblebabble.c +0 -142
- data/psp/digest/defs.h +0 -20
- data/psp/digest/digest.c +0 -643
- data/psp/digest/digest.h +0 -32
- data/psp/digest/lib/digest.rb +0 -50
- data/psp/digest/lib/md5.rb +0 -27
- data/psp/digest/lib/sha1.rb +0 -27
- data/psp/digest/md5/md5.c +0 -420
- data/psp/digest/md5/md5.h +0 -80
- data/psp/digest/md5/md5init.c +0 -40
- data/psp/digest/rmd160/rmd160.c +0 -457
- data/psp/digest/rmd160/rmd160.h +0 -56
- data/psp/digest/rmd160/rmd160init.c +0 -40
- data/psp/digest/sha1/sha1.c +0 -269
- data/psp/digest/sha1/sha1.h +0 -39
- data/psp/digest/sha1/sha1init.c +0 -40
- data/psp/digest/sha2/lib/sha2.rb +0 -73
- data/psp/digest/sha2/sha2.c +0 -919
- data/psp/digest/sha2/sha2.h +0 -109
- data/psp/digest/sha2/sha2init.c +0 -52
- data/psp/enumerator/enumerator.c +0 -298
- data/psp/etc/etc.c +0 -559
- data/psp/ext.c +0 -289
- data/psp/fcntl/fcntl.c +0 -187
- data/psp/lib/rbconfig.rb +0 -178
- data/psp/nkf/lib/kconv.rb +0 -367
- data/psp/nkf/nkf-utf8/config.h +0 -88
- data/psp/nkf/nkf-utf8/nkf.c +0 -6040
- data/psp/nkf/nkf-utf8/utf8tbl.c +0 -8500
- data/psp/nkf/nkf-utf8/utf8tbl.h +0 -34
- data/psp/nkf/nkf.c +0 -654
- data/psp/socket/addrinfo.h +0 -173
- data/psp/socket/getaddrinfo.c +0 -676
- data/psp/socket/getnameinfo.c +0 -270
- data/psp/socket/pspsocket.c +0 -71
- data/psp/socket/pspsocket.h +0 -28
- data/psp/socket/socket.c +0 -4662
- data/psp/socket/sockport.h +0 -76
- data/psp/stringio/stringio.c +0 -1306
- data/psp/strscan/strscan.c +0 -1320
- data/psp/syck/bytecode.c +0 -1166
- data/psp/syck/emitter.c +0 -1242
- data/psp/syck/gram.c +0 -1894
- data/psp/syck/gram.h +0 -79
- data/psp/syck/handler.c +0 -174
- data/psp/syck/implicit.c +0 -2990
- data/psp/syck/node.c +0 -408
- data/psp/syck/rubyext.c +0 -2367
- data/psp/syck/syck.c +0 -504
- data/psp/syck/syck.h +0 -456
- data/psp/syck/token.c +0 -2725
- data/psp/syck/yaml2byte.c +0 -257
- data/psp/syck/yamlbyte.h +0 -170
- data/psp/thread/thread.c +0 -1175
- data/psp/zlib/zlib.c +0 -3547
- data/script.rb +0 -10
- data/spec/ray/audio_spec.rb +0 -146
- data/spec/ray/color_spec.rb +0 -57
- data/spec/ray/event_spec.rb +0 -80
- data/spec/ray/font_spec.rb +0 -93
- data/spec/ray/image_set_spec.rb +0 -48
- data/spec/ray/image_spec.rb +0 -162
- data/spec/ray/joystick_spec.rb +0 -21
- data/spec/ray/matcher_spec.rb +0 -50
- data/spec/ray/ray_spec.rb +0 -88
- data/spec/ray/rect_spec.rb +0 -154
- data/spec/ray/resource_set_spec.rb +0 -105
- data/spec/ray/sprite_spec.rb +0 -163
- data/spec/spec.opts +0 -4
- data/spec/spec_helper.rb +0 -8
data/ext/vector.cpp
ADDED
|
@@ -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
|
+
|
data/guide.md
ADDED
|
@@ -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.
|