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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.ignore +5 -0
- data/.nojekyll +4 -0
- data/CHANGELOG.md +90 -0
- data/DEVELOPMENT.md +57 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +39 -0
- data/README.md +22 -0
- data/Rakefile +66 -0
- data/TODO.md +278 -0
- data/build/dxopal.js +46590 -0
- data/build/dxopal.min.js +1 -0
- data/config.ru +39 -0
- data/doc/api/DXOpal.html +129 -0
- data/doc/api/DXOpal/Font.html +485 -0
- data/doc/api/DXOpal/Image.html +2533 -0
- data/doc/api/DXOpal/Input.html +1086 -0
- data/doc/api/DXOpal/Input/MouseCodes.html +146 -0
- data/doc/api/DXOpal/RemoteResource.html +641 -0
- data/doc/api/DXOpal/Sound.html +568 -0
- data/doc/api/DXOpal/SoundEffect.html +444 -0
- data/doc/api/DXOpal/SoundEffect/WaveTypes.html +130 -0
- data/doc/api/DXOpal/Sprite.html +1419 -0
- data/doc/api/DXOpal/Window.html +1915 -0
- data/doc/api/_index.html +228 -0
- data/doc/api/class_list.html +51 -0
- data/doc/api/css/common.css +1 -0
- data/doc/api/css/full_list.css +58 -0
- data/doc/api/css/style.css +492 -0
- data/doc/api/file.CHANGELOG.html +162 -0
- data/doc/api/file.README.html +124 -0
- data/doc/api/file_list.html +61 -0
- data/doc/api/frames.html +17 -0
- data/doc/api/index.html +124 -0
- data/doc/api/js/app.js +248 -0
- data/doc/api/js/full_list.js +216 -0
- data/doc/api/js/jquery.js +4 -0
- data/doc/api/method_list.html +939 -0
- data/doc/api/top-level-namespace.html +110 -0
- data/doc/en/index.html +93 -0
- data/doc/ja/index.html +92 -0
- data/dxopal.gemspec +29 -0
- data/exe/dxopal +44 -0
- data/index.html +56 -0
- data/opal/dxopal.rb +54 -0
- data/opal/dxopal/constants/colors.rb +16 -0
- data/opal/dxopal/font.rb +20 -0
- data/opal/dxopal/image.rb +301 -0
- data/opal/dxopal/input.rb +170 -0
- data/opal/dxopal/input/key_codes.rb +168 -0
- data/opal/dxopal/remote_resource.rb +65 -0
- data/opal/dxopal/sound.rb +53 -0
- data/opal/dxopal/sound_effect.rb +84 -0
- data/opal/dxopal/sprite.rb +94 -0
- data/opal/dxopal/sprite/collision_area.rb +288 -0
- data/opal/dxopal/sprite/collision_check.rb +106 -0
- data/opal/dxopal/sprite/collision_checker.rb +169 -0
- data/opal/dxopal/sprite/physics.rb +82 -0
- data/opal/dxopal/version.rb +3 -0
- data/opal/dxopal/window.rb +173 -0
- data/template/index.html +13 -0
- data/template/main.rb +9 -0
- metadata +191 -0
@@ -0,0 +1,168 @@
|
|
1
|
+
module DXOpal
|
2
|
+
module Input
|
3
|
+
module KeyCodes
|
4
|
+
K_SPACE = " "
|
5
|
+
K_X = "x"
|
6
|
+
|
7
|
+
K_ESCAPE = 27
|
8
|
+
# K_1 1
|
9
|
+
# K_2 2
|
10
|
+
# K_3 3
|
11
|
+
# K_4 4
|
12
|
+
# K_5 5
|
13
|
+
# K_6 6
|
14
|
+
# K_7 7
|
15
|
+
# K_8 8
|
16
|
+
# K_9 9
|
17
|
+
# K_0 0
|
18
|
+
K_MINUS = 189
|
19
|
+
#K_EQUALS = "="
|
20
|
+
K_BACK = 8 # Backspace
|
21
|
+
K_TAB = 9
|
22
|
+
K_Q = 81
|
23
|
+
K_W = 87
|
24
|
+
K_E = 69
|
25
|
+
K_R = 82
|
26
|
+
K_T = 84
|
27
|
+
K_Y = 89
|
28
|
+
K_U = 85
|
29
|
+
K_I = 73
|
30
|
+
K_O = 79
|
31
|
+
K_P = 80
|
32
|
+
K_LBRACKET = 219
|
33
|
+
K_RBRACKET = 221
|
34
|
+
K_RETURN = 13
|
35
|
+
#K_LCONTROL Ctrl
|
36
|
+
K_A = 65
|
37
|
+
K_S = 83
|
38
|
+
K_D = 68
|
39
|
+
K_F = 70
|
40
|
+
K_G = 71
|
41
|
+
K_H = 72
|
42
|
+
K_J = 74
|
43
|
+
K_K = 75
|
44
|
+
K_L = 76
|
45
|
+
K_SEMICOLON = 186
|
46
|
+
#K_APOSTROPHE = "'"
|
47
|
+
#K_GRAVE = "`"
|
48
|
+
#K_LSHIFT Shift
|
49
|
+
K_BACKSLASH = 0
|
50
|
+
K_Z = 90
|
51
|
+
K_X = 88
|
52
|
+
K_C = 67
|
53
|
+
K_V = 86
|
54
|
+
K_B = 66
|
55
|
+
K_N = 78
|
56
|
+
K_M = 77
|
57
|
+
K_COMMA = 188
|
58
|
+
K_PERIOD = 190
|
59
|
+
K_SLASH = 191
|
60
|
+
#K_RSHIFT Shift
|
61
|
+
#K_MULTIPLY = "*"
|
62
|
+
#K_LMENU Alt
|
63
|
+
K_SPACE = 32
|
64
|
+
#K_CAPITAL Caps Lock
|
65
|
+
K_F1 = 112
|
66
|
+
K_F2 = 113
|
67
|
+
K_F3 = 114
|
68
|
+
K_F4 = 115
|
69
|
+
K_F5 = 116
|
70
|
+
K_F6 = 117
|
71
|
+
K_F7 = 118
|
72
|
+
K_F8 = 119
|
73
|
+
K_F9 = 120
|
74
|
+
K_F10 = 121
|
75
|
+
#K_NUMLOCK = "NumLock"
|
76
|
+
#K_SCROLL = "ScrollLock"
|
77
|
+
#K_NUMPAD7 7 *3
|
78
|
+
#K_NUMPAD8 8 *3
|
79
|
+
#K_NUMPAD9 9 *3
|
80
|
+
#K_SUBTRACT - *3
|
81
|
+
#K_NUMPAD4 4 *3
|
82
|
+
#K_NUMPAD5 5 *3
|
83
|
+
#K_NUMPAD6 6 *3
|
84
|
+
#K_ADD + *3
|
85
|
+
#K_NUMPAD1 1 *3
|
86
|
+
#K_NUMPAD2 2 *3
|
87
|
+
#K_NUMPAD3 3 *3
|
88
|
+
#K_NUMPAD0 0 *3
|
89
|
+
#K_DECIMAL . *3
|
90
|
+
#K_OEM_102
|
91
|
+
K_F11 = 122
|
92
|
+
K_F12 = 123
|
93
|
+
K_F13 = 124
|
94
|
+
K_F14 = 125
|
95
|
+
K_F15 = 126
|
96
|
+
#K_KANA
|
97
|
+
#K_ABNT_C1
|
98
|
+
#K_CONVERT
|
99
|
+
#K_NOCONVERT
|
100
|
+
#K_YEN
|
101
|
+
#K_ABNT_C2
|
102
|
+
#K_NUMPADEQUALS = *3 *1
|
103
|
+
K_PREVTRACK = 187 # ^
|
104
|
+
K_AT = 219 # @
|
105
|
+
K_COLON = 186
|
106
|
+
K_UNDERLINE = 189 # _
|
107
|
+
#K_KANJI
|
108
|
+
#K_STOP
|
109
|
+
#K_AX
|
110
|
+
#K_UNLABELED
|
111
|
+
#K_NEXTTRACK
|
112
|
+
#K_NUMPADENTER Enter *3
|
113
|
+
#K_RCONTROL Ctrl
|
114
|
+
#K_MUTE
|
115
|
+
#K_CALCULATOR
|
116
|
+
#K_PLAYPAUSE
|
117
|
+
#K_MEDIASTOP
|
118
|
+
#K_VOLUMEDOWN
|
119
|
+
#K_VOLUMEUP
|
120
|
+
#K_WEBHOME
|
121
|
+
#K_NUMPADCOMMA , *3 *1
|
122
|
+
#K_DIVIDE / *3
|
123
|
+
#K_SYSRQ
|
124
|
+
#K_RMENU Alt
|
125
|
+
#K_PAUSE = "Pause"
|
126
|
+
#K_HOME "Home"
|
127
|
+
K_UP = 38
|
128
|
+
#K_PRIOR
|
129
|
+
K_LEFT = 37
|
130
|
+
K_RIGHT = 39
|
131
|
+
#K_END = "End"
|
132
|
+
K_DOWN = 40
|
133
|
+
#K_NEXT
|
134
|
+
#K_INSERT = "Insert"
|
135
|
+
#K_DELETE = "Delete"
|
136
|
+
#K_LWIN Windows
|
137
|
+
#K_RWIN Windows
|
138
|
+
#K_APPS
|
139
|
+
#K_POWER
|
140
|
+
#K_SLEEP
|
141
|
+
#K_WAKE
|
142
|
+
#K_WEBSEARCH
|
143
|
+
#K_WEBFAVORITES
|
144
|
+
#K_WEBREFRESH
|
145
|
+
#K_WEBSTOP
|
146
|
+
#K_WEBFORWARD
|
147
|
+
#K_WEBBACK
|
148
|
+
#K_MYCOMPUTER
|
149
|
+
#K_MAIL
|
150
|
+
#K_MEDIASELECT
|
151
|
+
K_BACKSPACE = 8
|
152
|
+
#K_NUMPADSTAR * *3
|
153
|
+
#K_LALT Alt
|
154
|
+
#K_CAPSLOCK CapsLock
|
155
|
+
#K_NUMPADMINUS - *3
|
156
|
+
#K_NUMPADPLUS + *3
|
157
|
+
#K_NUMPADPERIOD . *3
|
158
|
+
#K_NUMPADSLASH / *3
|
159
|
+
#K_RALT Alt
|
160
|
+
K_UPARROW = 38
|
161
|
+
#K_PGUP = "PageUp"
|
162
|
+
K_LEFTARROW = 37
|
163
|
+
K_RIGHTARROW = 39
|
164
|
+
K_DOWNARROW = 40
|
165
|
+
#K_PGDN = "PageDown"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module DXOpal
|
2
|
+
# A base class for resources acquired through JS Promises.
|
3
|
+
# Provides `<klass>.register` and `<klass>[name]`.
|
4
|
+
# A subclass must impelment `<klass>._load`.
|
5
|
+
class RemoteResource
|
6
|
+
# List of registered resources (Contains path_or_url)
|
7
|
+
@@resources = Hash.new{|h,k| h[k] = {}}
|
8
|
+
# Contains promises
|
9
|
+
@@promises = Hash.new{|h,k| h[k] = {}}
|
10
|
+
# Contains instances of Image, Sound
|
11
|
+
@@instances = Hash.new{|h,k| h[k] = {}}
|
12
|
+
|
13
|
+
# Subclasses of RemoteResource
|
14
|
+
@@klasses = {}
|
15
|
+
def self.add_class(subklass)
|
16
|
+
@@klasses[subklass._klass_name] = subklass
|
17
|
+
end
|
18
|
+
|
19
|
+
# Reserve instance generation
|
20
|
+
def self.register(name, *args, &block)
|
21
|
+
@@resources[_klass_name] ||= {}
|
22
|
+
@@resources[_klass_name][name] = [block, args]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return instance of loaded resource (call on subclasses)
|
26
|
+
def self.[](name)
|
27
|
+
if (ret = @@instances[_klass_name][name])
|
28
|
+
return ret
|
29
|
+
else
|
30
|
+
raise "#{_klass_name} #{name.inspect} is not registered"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Called from Window.load_resources
|
35
|
+
def self._load_resources(&block)
|
36
|
+
@@resources.each do |klass_name, items|
|
37
|
+
klass = @@klasses[klass_name]
|
38
|
+
items.each do |name, (block, args)|
|
39
|
+
if !@@promises[klass_name][name]
|
40
|
+
instance, promise = klass._load(*args, &block)
|
41
|
+
@@instances[klass_name][name] = instance
|
42
|
+
@@promises[klass_name][name] = promise
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
promises = @@promises.values.flat_map(&:values)
|
47
|
+
%x{
|
48
|
+
Promise.all(#{promises}).then(function() {
|
49
|
+
#{block.call}
|
50
|
+
});
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Load actual content (defined on subclasses)
|
55
|
+
# Return `[instance, promise]`
|
56
|
+
def self._load(*args)
|
57
|
+
raise "override me"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return a string like "Image" or "Sound"
|
61
|
+
def self._klass_name
|
62
|
+
return self.name.split(/::/).last
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'dxopal/remote_resource'
|
2
|
+
|
3
|
+
module DXOpal
|
4
|
+
class Sound < RemoteResource
|
5
|
+
RemoteResource.add_class(Sound)
|
6
|
+
|
7
|
+
# Return AudioContext
|
8
|
+
def self.audio_context
|
9
|
+
@@audio_context ||= %x{
|
10
|
+
new (window.AudioContext||window.webkitAudioContext)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Load remote sound (called via Window.load_resources)
|
15
|
+
def self._load(path_or_url)
|
16
|
+
snd = new(path_or_url)
|
17
|
+
snd_promise = %x{
|
18
|
+
new Promise(function(resolve, reject) {
|
19
|
+
var request = new XMLHttpRequest();
|
20
|
+
request.open('GET', #{path_or_url}, true);
|
21
|
+
request.responseType = 'arraybuffer';
|
22
|
+
request.onload = function() {
|
23
|
+
var audioData = request.response;
|
24
|
+
var context = #{Sound.audio_context};
|
25
|
+
context.decodeAudioData(audioData, function(decoded) {
|
26
|
+
snd['$decoded='](decoded);
|
27
|
+
resolve();
|
28
|
+
});
|
29
|
+
};
|
30
|
+
request.send();
|
31
|
+
});
|
32
|
+
}
|
33
|
+
return snd, snd_promise
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(path_or_url)
|
37
|
+
@path_or_url = path_or_url # Used in error message
|
38
|
+
end
|
39
|
+
attr_accessor :decoded
|
40
|
+
|
41
|
+
# Play this sound once
|
42
|
+
def play
|
43
|
+
raise "Sound #{path_or_url} is not loaded yet" unless @decoded
|
44
|
+
%x{
|
45
|
+
var context = #{Sound.audio_context};
|
46
|
+
var source = context.createBufferSource();
|
47
|
+
source.buffer = #{@decoded};
|
48
|
+
source.connect(context.destination);
|
49
|
+
source.start(0);
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module DXOpal
|
2
|
+
# User-generated sound
|
3
|
+
#
|
4
|
+
# Example:
|
5
|
+
# v = 80
|
6
|
+
# SoundEffect.register(:sound1, 4000, WAVE_RECT, 5000) do
|
7
|
+
# v = v - 0.03
|
8
|
+
# [rand(300), v]
|
9
|
+
# end
|
10
|
+
class SoundEffect < Sound
|
11
|
+
RemoteResource.add_class(SoundEffect)
|
12
|
+
|
13
|
+
module WaveTypes
|
14
|
+
WAVE_SIN = :sine
|
15
|
+
WAVE_SAW = :sawtooth
|
16
|
+
WAVE_TRI = :triangle
|
17
|
+
WAVE_RECT = :square
|
18
|
+
end
|
19
|
+
|
20
|
+
# time : Total number of ticks
|
21
|
+
# When resolution=1000(default), `time` is equivalent to the
|
22
|
+
# total length of the sound in milliseconds.
|
23
|
+
# wave_type : Type of wave form
|
24
|
+
# resolution : Number of ticks per second
|
25
|
+
# block : Should return [freq(0~44100), volume(0~255)]
|
26
|
+
def self._load(time, wave_type=WAVE_RECT, resolution=1000, &block)
|
27
|
+
snd = new("(soundeffect)")
|
28
|
+
snd_promise = %x{
|
29
|
+
new Promise(function(resolve, reject){
|
30
|
+
var n_channels = 1;
|
31
|
+
var context = #{Sound.audio_context};
|
32
|
+
var n_ticks = #{time};
|
33
|
+
var totalSeconds = #{time / resolution};
|
34
|
+
var valuesPerSecond = context.sampleRate;
|
35
|
+
var n_values = totalSeconds * valuesPerSecond;
|
36
|
+
var myArrayBuffer = context.createBuffer(n_channels, n_values, valuesPerSecond);
|
37
|
+
var values = myArrayBuffer.getChannelData(0);
|
38
|
+
var n = 0;
|
39
|
+
for (var i = 0; i < n_ticks; i++) {
|
40
|
+
var ret = #{block.call};
|
41
|
+
var freq = ret[0], volume = ret[1];
|
42
|
+
if (freq < 0) freq = 0;
|
43
|
+
if (freq > 44100) freq = 44100;
|
44
|
+
if (volume < 0) volume = 0;
|
45
|
+
if (volume > 255) volume = 255;
|
46
|
+
var vol = volume / 255; // 0.0~1.0
|
47
|
+
|
48
|
+
var period = valuesPerSecond * 1 / freq;
|
49
|
+
for (; n < ((i+1) / n_ticks * n_values); n++) {
|
50
|
+
var phase = (n % period) / period; // 0.0~1.0
|
51
|
+
var value; // -1.0~1.0
|
52
|
+
switch(#{wave_type}) {
|
53
|
+
case "sine":
|
54
|
+
value = Math.sin(2 * Math.PI * phase) * 2 - 1;
|
55
|
+
break;
|
56
|
+
case "sawtooth":
|
57
|
+
value = phase * 2 - 1;
|
58
|
+
break;
|
59
|
+
case "triangle":
|
60
|
+
value = phase < 0.25 ? 0+phase*4 :
|
61
|
+
phase < 0.5 ? 1-(phase-0.25)*4 :
|
62
|
+
phase < 0.75 ? 0-(phase-0.5)*4 :
|
63
|
+
-1+(phase-0.75)*4;
|
64
|
+
break;
|
65
|
+
case "square":
|
66
|
+
value = (phase < 0.5 ? 1 : -1);
|
67
|
+
break;
|
68
|
+
default:
|
69
|
+
#{raise "unknown wave_type: " + wave_type.inspect};
|
70
|
+
}
|
71
|
+
values[n] = value * vol;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
snd['$decoded='](myArrayBuffer);
|
75
|
+
resolve();
|
76
|
+
});
|
77
|
+
}
|
78
|
+
return snd, snd_promise
|
79
|
+
end
|
80
|
+
|
81
|
+
def add(wave_type=WAVE_RECT, resolution=1000)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'dxopal/sprite/collision_check'
|
2
|
+
require 'dxopal/sprite/physics'
|
3
|
+
|
4
|
+
module DXOpal
|
5
|
+
class Sprite
|
6
|
+
extend DXOpal::Sprite::CollisionCheck::ClassMethods
|
7
|
+
include DXOpal::Sprite::CollisionCheck
|
8
|
+
include DXOpal::Sprite::Physics
|
9
|
+
|
10
|
+
# Call #update on each sprite (unless it is vanished or do not have #update)
|
11
|
+
def self.update(sprites)
|
12
|
+
sprites.each do |sprite|
|
13
|
+
next if !sprite.respond_to?(:update)
|
14
|
+
next if sprite.respond_to?(:vanished?) && sprite.vanished?
|
15
|
+
sprite.update
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Remove vanished sprites (and nils) from the array, destructively
|
20
|
+
def self.clean(sprites)
|
21
|
+
sprites.reject!{|sprite|
|
22
|
+
sprite.nil? || sprite.vanished?
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Draw each of the given sprites (unless it is vanished)
|
27
|
+
def self.draw(sprites)
|
28
|
+
sprites.flatten.sort_by(&:z).each do |sprite|
|
29
|
+
next if sprite.respond_to?(:vanished?) && sprite.vanished?
|
30
|
+
sprite.draw
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(x=0, y=0, image=nil)
|
35
|
+
@x, @y, @image = x, y, image
|
36
|
+
@z = 0
|
37
|
+
@angle = 0
|
38
|
+
@scale_x = @scale_y = 1.0
|
39
|
+
if image
|
40
|
+
@center_x = image.width / 2
|
41
|
+
@center_y = image.height / 2
|
42
|
+
end
|
43
|
+
|
44
|
+
@visible = true
|
45
|
+
@vanished = false
|
46
|
+
_init_collision_info(@image)
|
47
|
+
end
|
48
|
+
attr_accessor :z, :visible
|
49
|
+
|
50
|
+
# Set angle (0~360, default: 0)
|
51
|
+
attr_accessor :angle
|
52
|
+
# Set horizontal/vertical scale (default: 1.0)
|
53
|
+
attr_accessor :scale_x, :scale_y
|
54
|
+
# Set rotation center (default: center of `image`)
|
55
|
+
attr_accessor :center_x, :center_y
|
56
|
+
|
57
|
+
attr_reader :x, :y
|
58
|
+
|
59
|
+
def x=(newx)
|
60
|
+
@x = newx
|
61
|
+
_move_matter_body if @_matter_body
|
62
|
+
end
|
63
|
+
|
64
|
+
def y=(newy)
|
65
|
+
@y = newy
|
66
|
+
_move_matter_body if @_matter_body
|
67
|
+
end
|
68
|
+
|
69
|
+
def image; @image; end
|
70
|
+
def image=(img)
|
71
|
+
@image = img
|
72
|
+
if @collision.nil?
|
73
|
+
self.collision = [0, 0, img.width-1, img.height-1]
|
74
|
+
end
|
75
|
+
if @center_x.nil?
|
76
|
+
@center_x = img.width / 2
|
77
|
+
@center_y = img.height / 2
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def vanish; @vanished = true; end
|
82
|
+
def vanished?; @vanished; end
|
83
|
+
|
84
|
+
# Draw this sprite to Window
|
85
|
+
def draw
|
86
|
+
raise "image not set to Sprite" if @image.nil?
|
87
|
+
return if !@visible
|
88
|
+
|
89
|
+
Window.draw_ex(@x, @y, @image,
|
90
|
+
scale_x: @scale_x, scale_y: @scale_y,
|
91
|
+
angle: @angle, center_x: @center_x, center_y: @center_y)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|