dxopal 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|