author_engine 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/author_engine/game/opal/exporter.rb +82 -20
- data/lib/author_engine/game/opal/game_runner.rb +88 -5
- data/lib/author_engine/game/opal/parts/input.rb +1 -1
- data/lib/author_engine/game/opal/touch_button.rb +36 -0
- data/lib/author_engine/game/opal/touch_handler.rb +49 -0
- data/lib/author_engine/opal.rb +2 -0
- data/lib/author_engine/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 240a86f7ca8210f55b1000684999a28d178bb8aee05816924c09987279930acb
|
4
|
+
data.tar.gz: b020c530b4e217ff3b2c6fa8f956eae3a7faef8c3e07de8226fdbd239a0adec9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 979bd0fc59a1767dede59ca38f1ac63965beb8c538c95c25fd824d7d5c0b4449508d83f4dec6bc2e4b0d8c046f37f35c1995da4f2460e4d9562534d59b4e41fd
|
7
|
+
data.tar.gz: d105fe0793a73d82bb36c69623a031ad0a45ffd0dfbae4027a55e4a77e05bcd75be360fc9fa8427080a2ab1dc4f993f87065a63b0fdbb7e1ec89c99dbc2649d6
|
data/Gemfile.lock
CHANGED
@@ -14,6 +14,21 @@ class AuthorEngine
|
|
14
14
|
return name.split("_").map {|n| n.capitalize}.join(" ")
|
15
15
|
end
|
16
16
|
|
17
|
+
# Rebuild opal runtime of it doesn't exist or if its out of date
|
18
|
+
def build_opal?
|
19
|
+
opal_runtime = "#{export_directory}/js/runtime.js"
|
20
|
+
|
21
|
+
if File.exists?(opal_runtime)
|
22
|
+
file = File.open(opal_runtime)
|
23
|
+
version = file.first.gsub("/", "").strip
|
24
|
+
file.close
|
25
|
+
|
26
|
+
Opal::VERSION != version
|
27
|
+
else
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
17
32
|
def stylesheet
|
18
33
|
%{
|
19
34
|
@font-face { font-family: Connection; src: url('fonts/Connection.otf'); }
|
@@ -27,7 +42,7 @@ body {
|
|
27
42
|
#canvas {
|
28
43
|
display: block;
|
29
44
|
margin: 0 auto;
|
30
|
-
cursor: none;
|
45
|
+
// cursor: none;
|
31
46
|
}
|
32
47
|
#loading {
|
33
48
|
font-family: Connection, sans serif;
|
@@ -54,8 +69,8 @@ var projectString = `#{File.open(@project_file).read}`;
|
|
54
69
|
|
55
70
|
def game_runtime
|
56
71
|
program = %{
|
57
|
-
require "opal"
|
58
|
-
require "opal-parser"
|
72
|
+
# require "opal"
|
73
|
+
# require "opal-parser"
|
59
74
|
require "author_engine/opal"
|
60
75
|
|
61
76
|
`var callback = function(){
|
@@ -72,18 +87,34 @@ if (
|
|
72
87
|
}`
|
73
88
|
}
|
74
89
|
|
75
|
-
|
90
|
+
puts "Transpiling to JavaScript using Opal..."
|
91
|
+
|
92
|
+
opal_builder = nil
|
93
|
+
if build_opal?
|
94
|
+
puts " Building Opal runtime..."
|
95
|
+
|
96
|
+
opal_builder = Opal::Builder.new
|
97
|
+
opal_builder.build("opal")
|
98
|
+
opal_builder.build("opal-parser")
|
99
|
+
else
|
100
|
+
puts " Skipping Opal runtime. Already exists as v#{Opal::VERSION}..."
|
101
|
+
end
|
102
|
+
|
103
|
+
puts " Building AuthorEngine runtime with project..."
|
104
|
+
game_builder = Opal::Builder.new
|
76
105
|
base_path = File.expand_path("../../../..", __FILE__)
|
77
|
-
|
106
|
+
game_builder.append_paths("#{base_path}")
|
78
107
|
|
79
|
-
|
108
|
+
game_builder.build_require("author_engine/opal")
|
80
109
|
|
110
|
+
opal_builder_js = nil
|
111
|
+
if opal_builder
|
112
|
+
opal_runtime_js = opal_builder.build_str("", "(inline)").to_s
|
113
|
+
end
|
81
114
|
|
82
|
-
|
83
|
-
builder.build("opal-parser")
|
84
|
-
builder.build_require("author_engine/opal")
|
115
|
+
author_engine_js = game_builder.build_str(program, "(inline)").to_s
|
85
116
|
|
86
|
-
|
117
|
+
return {opal_runtime: opal_runtime_js, author_engine_runtime: author_engine_js}
|
87
118
|
end
|
88
119
|
|
89
120
|
def template
|
@@ -103,13 +134,23 @@ if (
|
|
103
134
|
<h1>Your Browser Does Not Support HTML5 Canvas!</h1>
|
104
135
|
</canvas>
|
105
136
|
|
137
|
+
<script src="game.js"></script>
|
106
138
|
<script>
|
107
139
|
// Add a small delay before loading application in order to finish loading page and show "Loading..."
|
108
140
|
window.setTimeout(function() {
|
109
|
-
|
110
|
-
|
141
|
+
console.log("Loading Opal runtime...");
|
142
|
+
|
143
|
+
var opal_runtime = document.createElement('script');
|
144
|
+
opal_runtime.onload = function() {
|
145
|
+
console.log("Loading AuthorEngine runtime...");
|
111
146
|
|
112
|
-
|
147
|
+
var author_engine_runtime = document.createElement('script');
|
148
|
+
author_engine_runtime.src = "js/author_engine.js";
|
149
|
+
document.head.appendChild(author_engine_runtime);
|
150
|
+
}
|
151
|
+
opal_runtime.src = "js/runtime.js";
|
152
|
+
|
153
|
+
document.head.appendChild(opal_runtime);
|
113
154
|
}, 500);
|
114
155
|
</script>
|
115
156
|
</body>
|
@@ -121,17 +162,28 @@ if (
|
|
121
162
|
template
|
122
163
|
end
|
123
164
|
|
124
|
-
def
|
165
|
+
def export_directory
|
125
166
|
filename = File.basename(@project_file)
|
126
167
|
directory = File.expand_path(@project_file.sub(filename, ''))
|
127
168
|
name = filename.sub(".authorengine", "")
|
128
169
|
|
170
|
+
return "#{directory}/#{name}"
|
171
|
+
end
|
172
|
+
|
173
|
+
def save(string)
|
174
|
+
filename = File.basename(@project_file)
|
175
|
+
directory = File.expand_path(@project_file.sub(filename, ''))
|
176
|
+
name = filename.sub(".authorengine", "")
|
129
177
|
export_path = "#{directory}/#{name}"
|
178
|
+
|
130
179
|
unless File.exists?(export_path)
|
131
180
|
Dir.mkdir(export_path)
|
132
|
-
|
133
|
-
|
134
|
-
|
181
|
+
end
|
182
|
+
unless File.exists?("#{export_path}/fonts")
|
183
|
+
Dir.mkdir("#{export_path}/fonts")
|
184
|
+
end
|
185
|
+
unless File.exists?("#{export_path}/js")
|
186
|
+
Dir.mkdir("#{export_path}/js")
|
135
187
|
end
|
136
188
|
|
137
189
|
puts "Saving to \"#{export_path}\""
|
@@ -139,10 +191,20 @@ if (
|
|
139
191
|
file.write(string)
|
140
192
|
end
|
141
193
|
|
142
|
-
|
194
|
+
hash = game_runtime
|
195
|
+
if hash[:opal_runtime]
|
196
|
+
File.open("#{export_path}/js/runtime.js", "w") do |file|
|
197
|
+
file.write("// #{Opal::VERSION}\n")
|
198
|
+
file.write(hash[:opal_runtime])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
File.open("#{export_path}/game.js", "w") do |file|
|
143
203
|
file.write(project)
|
144
|
-
|
145
|
-
|
204
|
+
end
|
205
|
+
|
206
|
+
File.open("#{export_path}/js/author_engine.js", "w") do |file|
|
207
|
+
file.write(hash[:author_engine_runtime])
|
146
208
|
end
|
147
209
|
|
148
210
|
fonts_path = "#{File.expand_path("../../../../../", __FILE__)}/assets/fonts"
|
@@ -7,6 +7,8 @@ class AuthorEngine
|
|
7
7
|
@instance = klass
|
8
8
|
end
|
9
9
|
|
10
|
+
include TouchHandler
|
11
|
+
|
10
12
|
attr_reader :save_file, :spritesheet, :sprites, :levels, :fps
|
11
13
|
attr_reader :game
|
12
14
|
def initialize(project_string)
|
@@ -41,6 +43,41 @@ class AuthorEngine
|
|
41
43
|
|
42
44
|
@game.init
|
43
45
|
|
46
|
+
@show_touch_controls = false
|
47
|
+
@touch_buttons = []
|
48
|
+
buttons = AuthorEngine::Part::OpalInput::BUTTONS
|
49
|
+
key_states = AuthorEngine::Part::OpalInput::KEY_STATES
|
50
|
+
@touch_buttons.push(
|
51
|
+
TouchButton.new(
|
52
|
+
label: "Up", color: @game.dark_gray, x: 137, y: `window.innerHeight/2 - #{125}`, width: 50, height: 50, side: :left,
|
53
|
+
contact_proc: proc { key_states[buttons["up"]] = true}, no_contact_proc: proc { key_states[buttons["up"]] = false}
|
54
|
+
),
|
55
|
+
TouchButton.new(
|
56
|
+
label: "Down", color: @game.dark_gray, x: 137, y: `window.innerHeight/2 + 25`, width: 50, height: 50, side: :left,
|
57
|
+
contact_proc: proc { key_states[buttons["down"]] = true}, no_contact_proc: proc { key_states[buttons["down"]] = false}
|
58
|
+
),
|
59
|
+
|
60
|
+
TouchButton.new(
|
61
|
+
label: "Left", color: @game.black, x: 175, width: 50, height: 50, side: :left,
|
62
|
+
contact_proc: proc { key_states[buttons["left"]] = true}, no_contact_proc: proc { key_states[buttons["left"]] = false}
|
63
|
+
),
|
64
|
+
TouchButton.new(
|
65
|
+
label: "Right", color: @game.black, x: 100, width: 50, height: 50, side: :left,
|
66
|
+
contact_proc: proc { key_states[buttons["right"]] = true}, no_contact_proc: proc { key_states[buttons["right"]] = false}
|
67
|
+
),
|
68
|
+
|
69
|
+
|
70
|
+
TouchButton.new(
|
71
|
+
label: "X", color: @game.red, x: 50, width: 50, height: 50, side: :right,
|
72
|
+
contact_proc: proc { key_states[buttons["x"]] = true}, no_contact_proc: proc { key_states[buttons["x"]] = false}
|
73
|
+
),
|
74
|
+
TouchButton.new(
|
75
|
+
label: "Y", color: @game.yellow, x: 125, width: 50, height: 50, side: :right,
|
76
|
+
contact_proc: proc { key_states[buttons["y"]] = true}, no_contact_proc: proc { key_states[buttons["y"]] = false}
|
77
|
+
)
|
78
|
+
)
|
79
|
+
touch_handler_setup
|
80
|
+
|
44
81
|
return self
|
45
82
|
end
|
46
83
|
|
@@ -57,6 +94,7 @@ class AuthorEngine
|
|
57
94
|
|
58
95
|
def run_game
|
59
96
|
`window.requestAnimationFrame(function() {#{run_game}})` # placed here to ensure next frame is called even if draw or update throw an error
|
97
|
+
`#{@game.canvas_context}.clearRect(0,0, window.innerWidth, window.innerHeight)`
|
60
98
|
|
61
99
|
@counted_frames+=1
|
62
100
|
|
@@ -68,8 +106,30 @@ class AuthorEngine
|
|
68
106
|
|
69
107
|
|
70
108
|
if @sprites.size == (@spritesheet_width/@sprite_size)*(@spritesheet_height/@sprite_size)
|
109
|
+
width = 128 * @game.scale
|
110
|
+
|
111
|
+
# `#{@canvas_context}.setTransform(1, 0, 0, 1, 0, 0)`
|
112
|
+
`#{@game.canvas_context}.save()`
|
113
|
+
`#{@game.canvas_context}.translate(window.innerWidth/2 - #{width/2}, 0)`
|
114
|
+
`#{@game.canvas_context}.scale(#{@game.scale}, #{@game.scale})`
|
115
|
+
`#{@game.canvas_context}.save()`
|
116
|
+
|
117
|
+
region = `new Path2D()`
|
118
|
+
`#{region}.rect(0, 0, 128, 128)`
|
119
|
+
`#{@game.canvas_context}.clip(#{region})`
|
120
|
+
`#{@game.canvas_context}.save()`
|
71
121
|
draw
|
122
|
+
|
123
|
+
`#{@game.canvas_context}.restore()`
|
124
|
+
`#{@game.canvas_context}.restore()`
|
125
|
+
`#{@game.canvas_context}.restore()`
|
126
|
+
|
72
127
|
update
|
128
|
+
|
129
|
+
if @show_touch_controls
|
130
|
+
draw_touch_controls
|
131
|
+
update_touch_controls
|
132
|
+
end
|
73
133
|
else
|
74
134
|
@game.draw_background
|
75
135
|
@game.text("Loading sprite #{@sprites.size}/#{(@spritesheet_width/@sprite_size)*(@spritesheet_height/@sprite_size)}.", 0, @game.height/2, 8)
|
@@ -78,6 +138,25 @@ class AuthorEngine
|
|
78
138
|
return nil
|
79
139
|
end
|
80
140
|
|
141
|
+
def draw_touch_controls
|
142
|
+
@touch_buttons.each(&:draw)
|
143
|
+
end
|
144
|
+
|
145
|
+
def update_touch_controls
|
146
|
+
active_buttons = []
|
147
|
+
|
148
|
+
@touch_buttons.each do |button|
|
149
|
+
@current_touches.each do |id, touch|
|
150
|
+
if touch.x.between?(button.x, button.x+button.width) && touch.y.between?(button.y, button.y+button.height)
|
151
|
+
active_buttons << button
|
152
|
+
button.active
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
(@touch_buttons - active_buttons).each(&:inactive)
|
158
|
+
end
|
159
|
+
|
81
160
|
def resize_canvas
|
82
161
|
width = `window.innerWidth`
|
83
162
|
height = `window.innerHeight`
|
@@ -88,12 +167,11 @@ class AuthorEngine
|
|
88
167
|
@game.scale = `window.innerHeight / 128.0`
|
89
168
|
end
|
90
169
|
|
91
|
-
`#{@game.canvas}.width =
|
92
|
-
`#{@game.canvas}.height =
|
93
|
-
`#{@game.canvas}.style.width =
|
94
|
-
`#{@game.canvas}.style.height =
|
170
|
+
`#{@game.canvas}.width = #{width}`
|
171
|
+
`#{@game.canvas}.height = #{height}`
|
172
|
+
`#{@game.canvas}.style.width = #{width}`
|
173
|
+
`#{@game.canvas}.style.height = #{height}`
|
95
174
|
|
96
|
-
`#{@game.canvas_context}.scale(#{@game.scale}, #{@game.scale})`
|
97
175
|
`#{@game.canvas_context}.imageSmoothingEnabled = false`
|
98
176
|
return nil
|
99
177
|
end
|
@@ -150,6 +228,11 @@ class AuthorEngine
|
|
150
228
|
`document.addEventListener('keydown', (event) => { #{AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = true} })`
|
151
229
|
`document.addEventListener('keyup', (event) => { #{AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = false} })`
|
152
230
|
|
231
|
+
`#{@game.canvas}.addEventListener('touchstart', (event) => { #{@show_touch_controls = true; handle_touch_start(`event`); puts "Touch started..."} })`
|
232
|
+
`#{@game.canvas}.addEventListener('touchmove', (event) => { #{handle_touch_move(`event`); puts "Touch moved..."} })`
|
233
|
+
`#{@game.canvas}.addEventListener('touchcancel', (event) => { #{handle_touch_cancel(`event`); puts "Touch canceled."} })`
|
234
|
+
`#{@game.canvas}.addEventListener('touchend', (event) => { #{handle_touch_end(`event`); puts "Touch Ended."} })`
|
235
|
+
|
153
236
|
`document.getElementById('loading').style.display = "none"`
|
154
237
|
`window.requestAnimationFrame(function() {#{run_game}})`
|
155
238
|
return nil
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class AuthorEngine
|
2
|
+
class TouchButton
|
3
|
+
attr_reader :x, :y, :width, :height
|
4
|
+
def initialize(label:, color:, x:, y: nil, width:, height:, side:, contact_proc:, no_contact_proc:)
|
5
|
+
@label, @color, @x, @y, @width, @height = label, color, x, y, width, height
|
6
|
+
@side, @contact_proc, @no_contact_proc = side, contact_proc, no_contact_proc
|
7
|
+
|
8
|
+
@game = AuthorEngine::GameRunner.instance.game
|
9
|
+
@game_width = 128 * @game.scale
|
10
|
+
@game_x = `window.innerWidth/2 - #{@game_width/2}`
|
11
|
+
|
12
|
+
if @side == :left
|
13
|
+
@x = @game_x-@x
|
14
|
+
elsif @side == :right
|
15
|
+
@x = @game_x+@game_width+@x
|
16
|
+
else
|
17
|
+
raise "side must be :left or :right"
|
18
|
+
end
|
19
|
+
|
20
|
+
@y = `window.innerHeight/2 - #{height}` unless @y.is_a?(Numeric)
|
21
|
+
end
|
22
|
+
|
23
|
+
def draw
|
24
|
+
`#{@game.canvas_context}.fillStyle = #{@color}`
|
25
|
+
`#{@game.canvas_context}.fillRect(#{@x}, #{@y}, #{width}, #{width})`
|
26
|
+
end
|
27
|
+
|
28
|
+
def active
|
29
|
+
@contact_proc.call
|
30
|
+
end
|
31
|
+
|
32
|
+
def inactive
|
33
|
+
@no_contact_proc.call
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class AuthorEngine
|
2
|
+
module TouchHandler
|
3
|
+
Touch = Struct.new(:x, :y)
|
4
|
+
def touch_handler_setup
|
5
|
+
@current_touches = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def copy_touch(touch)
|
9
|
+
Touch.new(`touch.pageX`, `touch.pageY`)
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle_touch_start(event)
|
13
|
+
`#{event}.preventDefault()`
|
14
|
+
|
15
|
+
touches = `#{event}.changedTouches`
|
16
|
+
`for (var i = 0; i < #{touches}.length; i++) {
|
17
|
+
#{@current_touches[`touches[i].identifier`] = copy_touch(`touches[i]`)}
|
18
|
+
}`
|
19
|
+
|
20
|
+
return nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_touch_move(event)
|
24
|
+
`#{event}.preventDefault()`
|
25
|
+
|
26
|
+
touches = `#{event}.changedTouches`
|
27
|
+
`for (var i = 0; i < #{touches}.length; i++) {
|
28
|
+
#{@current_touches[`touches[i].identifier`] = copy_touch(`touches[i]`)}
|
29
|
+
}`
|
30
|
+
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def handle_touch_cancel(event)
|
35
|
+
handle_touch_end(event)
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_touch_end(event)
|
39
|
+
`#{event}.preventDefault()`
|
40
|
+
|
41
|
+
touches = `#{event}.changedTouches`
|
42
|
+
`for (var i = 0; i < #{touches}.length; i++) {
|
43
|
+
#{@current_touches.delete(`touches[i].identifier`)}
|
44
|
+
}`
|
45
|
+
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/author_engine/opal.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: author_engine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyberarm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gosu
|
@@ -146,6 +146,8 @@ files:
|
|
146
146
|
- lib/author_engine/game/opal/game_runner.rb
|
147
147
|
- lib/author_engine/game/opal/parts/graphics.rb
|
148
148
|
- lib/author_engine/game/opal/parts/input.rb
|
149
|
+
- lib/author_engine/game/opal/touch_button.rb
|
150
|
+
- lib/author_engine/game/opal/touch_handler.rb
|
149
151
|
- lib/author_engine/image.rb
|
150
152
|
- lib/author_engine/level_picker.rb
|
151
153
|
- lib/author_engine/opal.rb
|