dxopal 1.0.0

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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.ignore +5 -0
  4. data/.nojekyll +4 -0
  5. data/CHANGELOG.md +90 -0
  6. data/DEVELOPMENT.md +57 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +39 -0
  9. data/README.md +22 -0
  10. data/Rakefile +66 -0
  11. data/TODO.md +278 -0
  12. data/build/dxopal.js +46590 -0
  13. data/build/dxopal.min.js +1 -0
  14. data/config.ru +39 -0
  15. data/doc/api/DXOpal.html +129 -0
  16. data/doc/api/DXOpal/Font.html +485 -0
  17. data/doc/api/DXOpal/Image.html +2533 -0
  18. data/doc/api/DXOpal/Input.html +1086 -0
  19. data/doc/api/DXOpal/Input/MouseCodes.html +146 -0
  20. data/doc/api/DXOpal/RemoteResource.html +641 -0
  21. data/doc/api/DXOpal/Sound.html +568 -0
  22. data/doc/api/DXOpal/SoundEffect.html +444 -0
  23. data/doc/api/DXOpal/SoundEffect/WaveTypes.html +130 -0
  24. data/doc/api/DXOpal/Sprite.html +1419 -0
  25. data/doc/api/DXOpal/Window.html +1915 -0
  26. data/doc/api/_index.html +228 -0
  27. data/doc/api/class_list.html +51 -0
  28. data/doc/api/css/common.css +1 -0
  29. data/doc/api/css/full_list.css +58 -0
  30. data/doc/api/css/style.css +492 -0
  31. data/doc/api/file.CHANGELOG.html +162 -0
  32. data/doc/api/file.README.html +124 -0
  33. data/doc/api/file_list.html +61 -0
  34. data/doc/api/frames.html +17 -0
  35. data/doc/api/index.html +124 -0
  36. data/doc/api/js/app.js +248 -0
  37. data/doc/api/js/full_list.js +216 -0
  38. data/doc/api/js/jquery.js +4 -0
  39. data/doc/api/method_list.html +939 -0
  40. data/doc/api/top-level-namespace.html +110 -0
  41. data/doc/en/index.html +93 -0
  42. data/doc/ja/index.html +92 -0
  43. data/dxopal.gemspec +29 -0
  44. data/exe/dxopal +44 -0
  45. data/index.html +56 -0
  46. data/opal/dxopal.rb +54 -0
  47. data/opal/dxopal/constants/colors.rb +16 -0
  48. data/opal/dxopal/font.rb +20 -0
  49. data/opal/dxopal/image.rb +301 -0
  50. data/opal/dxopal/input.rb +170 -0
  51. data/opal/dxopal/input/key_codes.rb +168 -0
  52. data/opal/dxopal/remote_resource.rb +65 -0
  53. data/opal/dxopal/sound.rb +53 -0
  54. data/opal/dxopal/sound_effect.rb +84 -0
  55. data/opal/dxopal/sprite.rb +94 -0
  56. data/opal/dxopal/sprite/collision_area.rb +288 -0
  57. data/opal/dxopal/sprite/collision_check.rb +106 -0
  58. data/opal/dxopal/sprite/collision_checker.rb +169 -0
  59. data/opal/dxopal/sprite/physics.rb +82 -0
  60. data/opal/dxopal/version.rb +3 -0
  61. data/opal/dxopal/window.rb +173 -0
  62. data/template/index.html +13 -0
  63. data/template/main.rb +9 -0
  64. metadata +191 -0
@@ -0,0 +1,16 @@
1
+ module DXOpal
2
+ module Constants
3
+ # Pre-defined ARGB colors
4
+ module Colors
5
+ C_BLACK = [255, 0, 0, 0]
6
+ C_RED = [255, 255, 0, 0]
7
+ C_GREEN = [255, 0, 255, 0]
8
+ C_BLUE = [255, 0, 0, 255]
9
+ C_YELLOW = [255, 255, 255, 0]
10
+ C_CYAN = [255, 0, 255, 255]
11
+ C_MAGENTA = [255, 255, 0, 255]
12
+ C_WHITE = [255, 255, 255, 255]
13
+ C_DEFAULT = [0, 0, 0, 0]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module DXOpal
2
+ class Font
3
+ def self.default; @@default ||= Font.new(24); end
4
+ def self.default=(f); @@default = f; end
5
+
6
+ def initialize(size, fontname=nil, option={})
7
+ @size = size
8
+ @orig_fontname = fontname
9
+ @fontname = fontname || "sans-serif"
10
+ end
11
+
12
+ def size; @size; end
13
+ def fontname; @orig_fontname; end
14
+
15
+ # Return a string like "48px serif"
16
+ def _spec_str
17
+ "#{@size}px #{@fontname}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,301 @@
1
+ require 'dxopal/remote_resource'
2
+
3
+ module DXOpal
4
+ class Image < RemoteResource
5
+ RemoteResource.add_class(Image)
6
+
7
+ # Load remote image (called via Window.load_resources)
8
+ def self._load(path_or_url)
9
+ raw_img = `new Image()`
10
+ img_promise = %x{
11
+ new Promise(function(resolve, reject) {
12
+ raw_img.onload = function() {
13
+ resolve(raw_img);
14
+ };
15
+ raw_img.src = path_or_url;
16
+ });
17
+ }
18
+
19
+ img = new(0, 0)
20
+ %x{
21
+ #{img_promise}.then(function(raw_img){
22
+ img.$_resize(raw_img.width, raw_img.height);
23
+ img.$_draw_raw_image(0, 0, raw_img);
24
+ });
25
+ }
26
+ return img, img_promise
27
+ end
28
+
29
+ # Create an instance of Image
30
+ def initialize(width, height, color=C_DEFAULT, canvas: nil)
31
+ @width, @height = width, height
32
+ @canvas = canvas || `document.createElement("canvas")`
33
+ @ctx = `#{@canvas}.getContext('2d')`
34
+ _resize(@width, @height)
35
+ box_fill(0, 0, @width, @height, color)
36
+ end
37
+ attr_reader :ctx, :canvas, :width, :height
38
+
39
+ # Set size of this image
40
+ def _resize(w, h)
41
+ @width, @height = w, h
42
+ %x{
43
+ #{@canvas}.width = w;
44
+ #{@canvas}.height = h;
45
+ }
46
+ end
47
+
48
+ # Draw an Image on this image
49
+ def draw(x, y, image)
50
+ %x{
51
+ #{@ctx}.drawImage(#{image.canvas}, x, y);
52
+ }
53
+ return self
54
+ end
55
+
56
+ # Draw an Image on this image with rotation
57
+ # - angle: Rotation angle (radian)
58
+ # - center_x, center_y: Rotation center in the `image` (default: center of the `image`)
59
+ def draw_rot(x, y, image, angle, center_x=nil, center_y=nil)
60
+ draw_ex(x, y, image, angle: angle, center_x: center_x, center_y: center_y)
61
+ end
62
+
63
+ def draw_ex(x, y, image, options={})
64
+ scale_x = options[:scale_x] || 1
65
+ scale_y = options[:scale_y] || 1
66
+ center_x = options[:center_x] || image.width/2
67
+ center_y = options[:center_y] || image.height/2
68
+ # TODO: alpha
69
+ # TODO: blend
70
+ angle = options[:angle] || 0
71
+
72
+ cx = x + center_x
73
+ cy = y + center_y
74
+ %x{
75
+ #{@ctx}.translate(cx, cy);
76
+ #{@ctx}.rotate(angle * Math.PI / 180.0);
77
+ #{@ctx}.scale(scale_x, scale_y);
78
+ #{@ctx}.drawImage(#{image.canvas}, x-cx, y-cy);
79
+ #{@ctx}.setTransform(1, 0, 0, 1, 0, 0); // reset
80
+ }
81
+ return self
82
+ end
83
+
84
+ # Draw some text on this image
85
+ def draw_font(x, y, string, font, color=[255,255,255])
86
+ ctx = @ctx
87
+ %x{
88
+ ctx.font = #{font._spec_str};
89
+ ctx.textBaseline = 'top';
90
+ ctx.fillStyle = #{_rgba(color)};
91
+ ctx.fillText(string, x, y);
92
+ }
93
+ return self
94
+ end
95
+
96
+ # Get a pixel as ARGB array
97
+ def [](x, y)
98
+ ctx = @ctx
99
+ ret = nil
100
+ %x{
101
+ var pixel = ctx.getImageData(x, y, 1, 1);
102
+ var rgba = pixel.data;
103
+ ret = [rgba[3], rgba[0], rgba[1], rgba[2]];
104
+ }
105
+ return ret
106
+ end
107
+
108
+ # Put a pixel on this image
109
+ def []=(x, y, color)
110
+ box_fill(x, y, x, y, color)
111
+ end
112
+
113
+ # Return true if the pixel at `(x, y)` has the `color`
114
+ def compare(x, y, color)
115
+ ctx = @ctx
116
+ rgba1 = _rgba_ary(color)
117
+ rgba2 = nil
118
+ ret = nil
119
+ %x{
120
+ var pixel = ctx.getImageData(x, y, 1, 1);
121
+ rgba2 = pixel.data;
122
+ // TODO: what is the right way to compare an Array and an Uint8ClampedArray?
123
+ ret = rgba1[0] == rgba2[0] &&
124
+ rgba1[1] == rgba2[1] &&
125
+ rgba1[2] == rgba2[2] &&
126
+ rgba1[3] == rgba2[3]
127
+ }
128
+ return ret
129
+ end
130
+
131
+ # Draw a line on this image
132
+ def line(x1, y1, x2, y2, color)
133
+ ctx = @ctx
134
+ %x{
135
+ ctx.beginPath();
136
+ ctx.strokeStyle = #{_rgba(color)};
137
+ ctx.moveTo(x1, y1);
138
+ ctx.lineTo(x2, y2);
139
+ ctx.stroke();
140
+ }
141
+ return self
142
+ end
143
+
144
+ # Draw a rectangle on this image
145
+ def box(x1, y1, x2, y2, color)
146
+ ctx = @ctx
147
+ %x{
148
+ ctx.beginPath();
149
+ ctx.strokeStyle = #{_rgba(color)};
150
+ ctx.rect(x1, y1, x2-x1+1, y2-y1+1);
151
+ ctx.stroke();
152
+ }
153
+ return self
154
+ end
155
+
156
+ # Draw a filled box on this image
157
+ def box_fill(x1, y1, x2, y2, color)
158
+ ctx = @ctx
159
+ %x{
160
+ ctx.beginPath();
161
+ ctx.fillStyle = #{_rgba(color)};
162
+ ctx.fillRect(x1, y1, x2-x1+1, y2-y1+1);
163
+ }
164
+ return self
165
+ end
166
+
167
+ # Draw a circle on this image
168
+ def circle(x, y, r, color)
169
+ ctx = @ctx
170
+ %x{
171
+ ctx.beginPath();
172
+ ctx.strokeStyle = #{_rgba(color)};
173
+ ctx.arc(x, y, r, 0, Math.PI*2, false)
174
+ ctx.stroke();
175
+ }
176
+ return self
177
+ end
178
+
179
+ # Draw a filled circle on this image
180
+ def circle_fill(x, y, r, color)
181
+ ctx = @ctx
182
+ %x{
183
+ ctx.beginPath();
184
+ ctx.fillStyle = #{_rgba(color)};
185
+ ctx.arc(x, y, r, 0, Math.PI*2, false)
186
+ ctx.fill();
187
+ }
188
+ return self
189
+ end
190
+
191
+ # Draw a triangle on this image
192
+ def triangle(x1, y1, x2, y2, x3, y3, color)
193
+ ctx = @ctx
194
+ %x{
195
+ ctx.beginPath();
196
+ ctx.strokeStyle = #{_rgba(color)};
197
+ ctx.moveTo(x1, y1);
198
+ ctx.lineTo(x2, y2);
199
+ ctx.lineTo(x3, y3);
200
+ ctx.lineTo(x1, y1);
201
+ ctx.stroke();
202
+ }
203
+ return self
204
+ end
205
+
206
+ # Draw a filled triangle on this image
207
+ def triangle_fill(x1, y1, x2, y2, x3, y3, color)
208
+ ctx = @ctx
209
+ %x{
210
+ ctx.beginPath();
211
+ ctx.fillStyle = #{_rgba(color)};
212
+ ctx.moveTo(x1, y1);
213
+ ctx.lineTo(x2, y2);
214
+ ctx.lineTo(x3, y3);
215
+ ctx.fill();
216
+ }
217
+ return self
218
+ end
219
+
220
+ # Fill this image with `color`
221
+ def fill(color)
222
+ box_fill(0, 0, @width-1, @height-1, color)
223
+ end
224
+
225
+ # Clear this image (i.e. fill with `[0,0,0,0]`)
226
+ def clear
227
+ fill([0, 0, 0, 0])
228
+ end
229
+
230
+ # Return an Image which is a copy of the specified area
231
+ def slice(x, y, width, height)
232
+ newimg = Image.new(width, height)
233
+ data = _image_data(x, y, width, height)
234
+ newimg._put_image_data(data, 0, 0)
235
+ return newimg
236
+ end
237
+
238
+ # Slice this image into xcount*ycount tiles
239
+ def slice_tiles(xcount, ycount)
240
+ tile_w = @width / xcount
241
+ tile_h = @height / ycount
242
+ return (0...ycount).flat_map{|v|
243
+ (0...xcount).map{|u|
244
+ slice(tile_w * u, tile_h * v, tile_w, tile_h)
245
+ }
246
+ }
247
+ end
248
+
249
+ # Copy an <img> onto this image
250
+ def _draw_raw_image(x, y, raw_img)
251
+ %x{
252
+ #{@ctx}.drawImage(#{raw_img}, x, y)
253
+ }
254
+ end
255
+
256
+ # Return .getImageData
257
+ def _image_data(x=0, y=0, w=@width, h=@height)
258
+ return `#{@ctx}.getImageData(x, y, w, h)`
259
+ end
260
+
261
+ # Call .putImageData
262
+ def _put_image_data(image_data, x, y)
263
+ `#{@ctx}.putImageData(image_data, x, y)`
264
+ end
265
+
266
+ # Return a string like 'rgb(255, 255, 255)'
267
+ # `color` is 3 or 4 numbers
268
+ def _rgb(color)
269
+ case color.length
270
+ when 4
271
+ # Just ignore alpha
272
+ rgb = color[1, 3]
273
+ when 3
274
+ rgb = color
275
+ else
276
+ raise "invalid color: #{color.inspect}"
277
+ end
278
+ return "rgb(" + rgb.join(', ') + ")";
279
+ end
280
+
281
+ # Return a string like 'rgba(255, 255, 255, 128)'
282
+ # `color` is 3 or 4 numbers
283
+ def _rgba(color)
284
+ return "rgba(" + _rgba_ary(color).join(', ') + ")"
285
+ end
286
+
287
+ # Return an array like `[255, 255, 255, 128]`
288
+ def _rgba_ary(color)
289
+ case color.length
290
+ when 4
291
+ # color is ARGB in DXRuby, so move A to the last
292
+ color[1, 3] + [color[0]/255.0]
293
+ when 3
294
+ # Complement 255 as alpha
295
+ color + [1.0]
296
+ else
297
+ raise "invalid color: #{color.inspect}"
298
+ end
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,170 @@
1
+ module DXOpal
2
+ module Input
3
+ module MouseCodes
4
+ M_LBUTTON = 1
5
+ M_RBUTTON = 2
6
+ M_MBUTTON = 4
7
+ # DXOpal extention
8
+ M_4TH_BUTTON = 8
9
+ M_5TH_BUTTON = 16
10
+ end
11
+
12
+ def self._pressing_keys; @@pressing_keys; end
13
+
14
+ # Internal setup for Input class
15
+ def self._init(canvas)
16
+ @@tick = 0
17
+ @@pressing_keys = `new Object()`
18
+ @@mouse_info = `{x: 0, y: 0}`
19
+ @@pressing_mouse_buttons = `new Object()`
20
+
21
+ rect = `canvas.getBoundingClientRect()`
22
+ @@canvas_x = `rect.left + window.pageXOffset`
23
+ @@canvas_y = `rect.top + window.pageYOffset`
24
+
25
+ self._init_mouse_events
26
+ self.keyevent_target = `window` unless Input.keyevent_target
27
+ end
28
+
29
+ # Called on every frame from Window
30
+ def self._on_tick
31
+ @@tick += 1
32
+ end
33
+
34
+ # Return 1 if 'right', -1 if 'left'
35
+ def self.x(pad_number=0)
36
+ ret = 0
37
+ ret += 1 if key_down?(K_RIGHT)
38
+ ret -= 1 if key_down?(K_LEFT)
39
+ ret
40
+ end
41
+
42
+ # Return 1 if 'down', -1 if 'up'
43
+ def self.y(pad_number=0)
44
+ ret = 0
45
+ ret += 1 if key_down?(K_DOWN)
46
+ ret -= 1 if key_down?(K_UP)
47
+ ret
48
+ end
49
+
50
+ #
51
+ # Keyboard
52
+ #
53
+
54
+ # Return true if the key is being pressed
55
+ def self.key_down?(code)
56
+ return `#{@@pressing_keys}[code] > 0`
57
+ end
58
+
59
+ # Return true if the key is just pressed
60
+ def self.key_push?(code)
61
+ return `#{@@pressing_keys}[code] == #{@@tick}-1`
62
+ end
63
+
64
+ # Return true if the key is just released
65
+ def self.key_release?(code)
66
+ return `#{@@pressing_keys}[code] == -(#{@@tick}-1)`
67
+ end
68
+
69
+ # (private) JS keydown event handler
70
+ ON_KEYDOWN_ = %x{
71
+ function(ev){
72
+ #{Input._pressing_keys}[ev.keyCode] = #{@@tick};
73
+ ev.preventDefault();
74
+ ev.stopPropagation();
75
+ }
76
+ }
77
+ # (private) JS keyup event handler
78
+ ON_KEYUP_ = %x{
79
+ function(ev){
80
+ #{Input._pressing_keys}[ev.keyCode] = -#{@@tick};
81
+ ev.preventDefault();
82
+ ev.stopPropagation();
83
+ }
84
+ }
85
+ # Set DOM element to receive keydown/keyup event
86
+ #
87
+ # By default, `window` is set to this (i.e. all key events are
88
+ # stolen by DXOpal.) If canvas element is set to this, only key events
89
+ # happend on canvas are processed by DXOpal.
90
+ def self.keyevent_target=(target)
91
+ if @@keyevent_target
92
+ %x{
93
+ #{@@keyevent_target}.removeEventListener('keydown', #{ON_KEYDOWN_});
94
+ #{@@keyevent_target}.removeEventListener('keyup', #{ON_KEYUP_});
95
+ }
96
+ end
97
+ @@keyevent_target = target
98
+ %x{
99
+ if (#{@@keyevent_target}.tagName == "CANVAS") {
100
+ #{@@keyevent_target}.setAttribute('tabindex', 0);
101
+ }
102
+ #{@@keyevent_target}.addEventListener('keydown', #{ON_KEYDOWN_});
103
+ #{@@keyevent_target}.addEventListener('keyup', #{ON_KEYUP_});
104
+ }
105
+ end
106
+
107
+ # Return DOM element set by `keyevent_target=`
108
+ def self.keyevent_target; @@keyevent_target; end
109
+
110
+ #
111
+ # Mouse
112
+ #
113
+
114
+ # (internal) initialize mouse events
115
+ def self._init_mouse_events
116
+ %x{
117
+ document.addEventListener('mousemove', function(ev){
118
+ #{@@mouse_info}.x = ev.pageX - #{@@canvas_x};
119
+ #{@@mouse_info}.y = ev.pageY - #{@@canvas_y};
120
+ });
121
+ document.addEventListener('mousedown', function(ev){
122
+ #{@@mouse_info}.x = ev.pageX - #{@@canvas_x};
123
+ #{@@mouse_info}.y = ev.pageY - #{@@canvas_y};
124
+ for (var k=1; k<=16; k<<=1) {
125
+ if (ev.buttons & k) {
126
+ #{@@pressing_mouse_buttons}[k] = #{@@tick};
127
+ }
128
+ }
129
+ });
130
+ document.addEventListener('mouseup', function(ev){
131
+ #{@@mouse_info}.x = ev.pageX - #{@@canvas_x};
132
+ #{@@mouse_info}.y = ev.pageY - #{@@canvas_y};
133
+ for (var k=1; k<=16; k<<=1) {
134
+ if ((ev.buttons & k) == 0 && #{@@pressing_mouse_buttons}[k]) {
135
+ #{@@pressing_mouse_buttons}[k] = -#{@@tick};
136
+ }
137
+ }
138
+ });
139
+ }
140
+ end
141
+
142
+ # Return position of mouse cursor
143
+ # (0, 0) is the top-left corner of the canvas
144
+ def self.mouse_x
145
+ return `#{@@mouse_info}.x`
146
+ end
147
+ def self.mouse_y
148
+ return `#{@@mouse_info}.y`
149
+ end
150
+ class << self
151
+ alias mouse_pos_x mouse_x
152
+ alias mouse_pos_y mouse_y
153
+ end
154
+
155
+ # Return true if the mouse button is being pressed
156
+ def self.mouse_down?(mouse_code)
157
+ return `#{@@pressing_mouse_buttons}[mouse_code] > 0`
158
+ end
159
+
160
+ # Return true if the mouse button is pressed in the last tick
161
+ def self.mouse_push?(mouse_code)
162
+ return `#{@@pressing_mouse_buttons}[mouse_code] == -(#{@@tick}-1)`
163
+ end
164
+
165
+ # Return true if the mouse button is released in the last tick
166
+ def self.mouse_release?(mouse_code)
167
+ return `#{@@pressing_mouse_buttons}[mouse_code] == -(#{@@tick}-1)`
168
+ end
169
+ end
170
+ end