author_engine 0.5.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,66 +17,88 @@ class AuthorEngine
17
17
  @save_file = AuthorEngine::SaveFile.new(nil)
18
18
  @save_file.load(false, project_string)
19
19
 
20
- size = 16
21
-
22
- @levels = @save_file.levels
23
- @levels.each {|level| level.each {|sprite| sprite.x = sprite.x * size; sprite.y = sprite.y * size}}
24
-
25
20
  @sprites = []
26
21
  @spritesheet = nil
27
22
  @spritesheet_width = @save_file.sprites.columns
28
23
  @spritesheet_height = @save_file.sprites.rows
29
24
  @sprite_size = 16
30
25
 
31
- @fps = 0
32
- @counted_frames = 0
33
- @frame_count_stated_at = 0
34
-
35
26
  @game = Game.new(code: @save_file.code)
36
- build_spritesheet_and_sprites_list
37
27
  resize_canvas
38
28
 
39
- @collision_detection = AuthorEngine::CollisionDetection.new(@sprites, @levels)
40
- @game.collision_detection = @collision_detection
41
-
42
- @levels.each {|level| @collision_detection.add_level(level) }
43
-
44
- @game.init
29
+ @fps = 0
30
+ @counted_frames = 0
31
+ @frame_count_stated_at = 0
45
32
 
46
33
  @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
34
+
35
+ @game_loaded = false
36
+
37
+ @loader_tasks = [
38
+ [
39
+ "Loading levels",
40
+ proc {
41
+ @levels = @save_file.levels
42
+ @levels.each {|level| level.each {|sprite| sprite.x = sprite.x * @sprite_size; sprite.y = sprite.y * @sprite_size}}
43
+ }
44
+ ],
45
+
46
+ [
47
+ "Evaluating game",
48
+ proc {
49
+ @game.authorengine_eval_code
50
+ },
51
+ ],
52
+
53
+ [
54
+ "Loading sprites",
55
+ proc {
56
+ build_spritesheet_and_sprites_list
57
+ },
58
+ ],
59
+
60
+ [
61
+ "Setting up collision detection",
62
+ proc {
63
+ @collision_detection = AuthorEngine::CollisionDetection.new(@sprites, @levels, @save_file.sprites)
64
+ @game.authorengine_collision_detection = @collision_detection
65
+ },
66
+ ],
67
+
68
+ [
69
+ "Initializing game",
70
+ proc {
71
+ @game.init
72
+ },
73
+ ],
74
+
75
+ [
76
+ "Setting up touch controls",
77
+ proc {
78
+ @touch_joystick = TouchJoystick.new(radius: 50)
79
+ @touch_buttons = []
80
+ @touch_buttons.push(
81
+ TouchButton.new(
82
+ label: "X", color: @game.red, width: 50, height: 50, for_key: "x"
83
+ ),
84
+ TouchButton.new(
85
+ label: "Y", color: @game.yellow, width: 50, height: 50, for_key: "y"
86
+ )
87
+ )
88
+
89
+ @fullscreen_button = TouchButton.new(label: "Fullscreen", color: @game.black, width: 100, height: 50)
90
+ touch_handler_setup
91
+ reposition_touch_controls
92
+ },
93
+ ],
94
+
95
+ [
96
+ "Loading done",
97
+ proc {
98
+ @game_loaded = true
99
+ },
100
+ ],
101
+ ]
80
102
 
81
103
  return self
82
104
  end
@@ -94,7 +116,14 @@ class AuthorEngine
94
116
 
95
117
  def run_game
96
118
  `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)`
119
+ width = `window.innerWidth`
120
+ height = `window.innerHeight`
121
+ game_width = 128 * @game.authorengine_scale
122
+ game_height = 128 * @game.authorengine_scale
123
+
124
+ area_width = (`window.innerWidth` - game_width) / 2
125
+
126
+ `#{@game.authorengine_canvas_context}.clearRect(#{area_width},0, #{game_width}, #{game_height})`
98
127
 
99
128
  @counted_frames+=1
100
129
 
@@ -104,25 +133,22 @@ class AuthorEngine
104
133
  @counted_frames = 0
105
134
  end
106
135
 
136
+ `#{@game.authorengine_canvas_context}.save()`
137
+ `#{@game.authorengine_canvas_context}.translate(window.innerWidth/2 - #{game_height/2}, 0)`
138
+ `#{@game.authorengine_canvas_context}.scale(#{@game.authorengine_scale}, #{@game.authorengine_scale})`
139
+ `#{@game.authorengine_canvas_context}.save()`
107
140
 
108
- if @sprites.size == (@spritesheet_width/@sprite_size)*(@spritesheet_height/@sprite_size)
109
- width = 128 * @game.scale
141
+ region = `new Path2D()`
142
+ `#{region}.rect(0, 0, 128, 128)`
143
+ `#{@game.authorengine_canvas_context}.clip(#{region})`
144
+ `#{@game.authorengine_canvas_context}.save()`
110
145
 
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
146
 
117
- region = `new Path2D()`
118
- `#{region}.rect(0, 0, 128, 128)`
119
- `#{@game.canvas_context}.clip(#{region})`
120
- `#{@game.canvas_context}.save()`
147
+ if @game_loaded or @loader_tasks.empty?
121
148
  draw
122
-
123
- `#{@game.canvas_context}.restore()`
124
- `#{@game.canvas_context}.restore()`
125
- `#{@game.canvas_context}.restore()`
149
+ `#{@game.authorengine_canvas_context}.restore()`
150
+ `#{@game.authorengine_canvas_context}.restore()`
151
+ `#{@game.authorengine_canvas_context}.restore()`
126
152
 
127
153
  update
128
154
 
@@ -130,31 +156,89 @@ class AuthorEngine
130
156
  draw_touch_controls
131
157
  update_touch_controls
132
158
  end
159
+
133
160
  else
134
- @game.draw_background
135
- @game.text("Loading sprite #{@sprites.size}/#{(@spritesheet_width/@sprite_size)*(@spritesheet_height/@sprite_size)}.", 0, @game.height/2, 8)
161
+ task = @loader_tasks.shift
162
+ @game.rect(0, 0, @game.width, @game.height, @game.dark_purple)
163
+ @game.text("AuthorEngine v#{AuthorEngine::VERSION}", 2, @game.height / 2 - 20, 10)
164
+ @game.text("#{task[0]}...", 6, @game.height / 2 - 4, 8, 0, @game.light_gray)
165
+ @game.text("Empowered by Opal v#{Opal::VERSION}, a Ruby interpeter.", 4, @game.height - 6, 4, 0, @game.indigo)
166
+
167
+ `#{@game.authorengine_canvas_context}.restore()`
168
+ `#{@game.authorengine_canvas_context}.restore()`
169
+ `#{@game.authorengine_canvas_context}.restore()`
170
+
171
+ task[1].call
136
172
  end
137
173
 
138
174
  return nil
139
175
  end
140
176
 
141
177
  def draw_touch_controls
178
+ @fullscreen_button.draw
142
179
  @touch_buttons.each(&:draw)
180
+ @touch_joystick.draw
143
181
  end
144
182
 
145
183
  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
184
+ @touch_buttons.each { |button| button.trigger?(@current_touches) }
185
+ @touch_joystick.update(@current_touches)
186
+ end
187
+
188
+ def reposition_touch_controls
189
+ return nil unless @touch_joystick
190
+
191
+ width = `window.innerWidth`
192
+ height = `window.innerHeight`
193
+ game_width = 128 * @game.authorengine_scale
194
+ game_height = 128 * @game.authorengine_scale
195
+
196
+ # place controls under game
197
+ if width < height
198
+ area_width = width
199
+ area_height = height - game_height
200
+
201
+ puts "space: width #{area_width} x height #{area_height}"
202
+
203
+ @touch_joystick.x = @touch_joystick.radius + @touch_joystick.radius
204
+ @touch_joystick.y = game_height + area_height / 2
205
+
206
+ padding = 10
207
+ last_x = 20
208
+ @touch_buttons.reverse.each do |button|
209
+ button.x = width - (last_x + button.width)
210
+ button.y = (height - area_height) + area_height / 2 - button.height / 2
211
+
212
+ last_x += button.width + padding
154
213
  end
214
+
215
+ @fullscreen_button.x = width - (width / 2 + @fullscreen_button.width / 2)
216
+ @fullscreen_button.y = height - @fullscreen_button.height
217
+
218
+ # place controls beside game
219
+ else
220
+ area_width = (`window.innerWidth` - game_width) / 2
221
+ area_height = game_height
222
+
223
+ puts "space: width #{area_width} x height #{area_height}"
224
+
225
+ @touch_joystick.x = @touch_joystick.radius + @touch_joystick.radius
226
+ @touch_joystick.y = game_height / 2
227
+
228
+ padding = 10
229
+ last_x = 50
230
+ @touch_buttons.reverse.each do |button|
231
+ button.x = width - (last_x + button.width)
232
+ button.y = game_height / 2 - button.height / 2
233
+
234
+ last_x += button.width + padding
235
+ end
236
+
237
+ @fullscreen_button.x = width - @fullscreen_button.width
238
+ @fullscreen_button.y = 0
155
239
  end
156
240
 
157
- (@touch_buttons - active_buttons).each(&:inactive)
241
+ return nil
158
242
  end
159
243
 
160
244
  def resize_canvas
@@ -162,17 +246,20 @@ class AuthorEngine
162
246
  height = `window.innerHeight`
163
247
 
164
248
  if width < height
165
- @game.scale = `window.innerWidth / 128.0`
249
+ @game.authorengine_scale = `#{width} / 128.0`
166
250
  else
167
- @game.scale = `window.innerHeight / 128.0`
251
+ @game.authorengine_scale = `#{height} / 128.0`
168
252
  end
169
253
 
170
- `#{@game.canvas}.width = #{width}`
171
- `#{@game.canvas}.height = #{height}`
172
- `#{@game.canvas}.style.width = #{width}`
173
- `#{@game.canvas}.style.height = #{height}`
254
+ `#{@game.authorengine_canvas}.width = #{width}`
255
+ `#{@game.authorengine_canvas}.height = #{height}`
256
+ `#{@game.authorengine_canvas}.style.width = #{width}`
257
+ `#{@game.authorengine_canvas}.style.height = #{height}`
258
+
259
+ `#{@game.authorengine_canvas_context}.imageSmoothingEnabled = false`
174
260
 
175
- `#{@game.canvas_context}.imageSmoothingEnabled = false`
261
+ reposition_touch_controls
262
+ `#{@game.authorengine_canvas_context}.clearRect(0, 0, window.innerWidth, window.innerHeight)`
176
263
  return nil
177
264
  end
178
265
 
@@ -212,7 +299,6 @@ class AuthorEngine
212
299
  (width/size).times do |x|
213
300
  `#{temp_canvas_context}.clearRect(0,0, #{size}, #{size})`
214
301
  `#{temp_canvas_context}.drawImage(#{@spritesheet}, #{x * size}, #{y * size}, #{size}, #{size}, 0, 0, #{size}, #{size})`
215
- @collision_detection.add_sprite(`#{temp_canvas_context}.getImageData(0,0, #{size}, #{size}).data`)
216
302
 
217
303
  `createImageBitmap(#{@spritesheet}, #{x * size}, #{y * size}, #{size}, #{size}).then(sprite => { #{@sprites.push(`sprite`)} })`
218
304
  end
@@ -221,19 +307,26 @@ class AuthorEngine
221
307
  return nil
222
308
  end
223
309
 
310
+ def fullscreen_changed
311
+ resize_canvas
312
+ end
313
+
224
314
  def show(update_interval = (1000.0 / 60))
225
315
  return unless RUBY_ENGINE == "opal"
226
316
 
227
317
  `window.addEventListener('resize', () => { #{resize_canvas} })`
228
- `document.addEventListener('keydown', (event) => { #{AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = true} })`
318
+ `document.addEventListener('keydown', (event) => { #{@show_touch_controls = false; AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = true} })`
229
319
  `document.addEventListener('keyup', (event) => { #{AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = false} })`
230
320
 
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."} })`
321
+ `#{@game.authorengine_canvas}.addEventListener('touchstart', (event) => { #{@show_touch_controls = true; handle_touch_start(`event`)} })`
322
+ `#{@game.authorengine_canvas}.addEventListener('touchmove', (event) => { #{handle_touch_move(`event`)} })`
323
+ `#{@game.authorengine_canvas}.addEventListener('touchcancel', (event) => { #{handle_touch_cancel(`event`)} })`
324
+ `#{@game.authorengine_canvas}.addEventListener('touchend', (event) => { #{handle_touch_end(`event`)} })`
325
+
326
+ `#{@game.authorengine_canvas}.addEventListener('fullscreenchange', () => { #{fullscreen_changed} })`
235
327
 
236
328
  `document.getElementById('loading').style.display = "none"`
329
+
237
330
  `window.requestAnimationFrame(function() {#{run_game}})`
238
331
  return nil
239
332
  end
@@ -2,8 +2,8 @@ class AuthorEngine
2
2
  class Part
3
3
  module OpalGraphics
4
4
  def rect(x = 0, y = 0, width = 1, height = 1, color = "white", z = 0)
5
- `#{@canvas_context}.fillStyle = #{color}`
6
- `#{@canvas_context}.fillRect(#{x}, #{y}, #{width}, #{height})`
5
+ `#{@authorengine_canvas_context}.fillStyle = #{color}`
6
+ `#{@authorengine_canvas_context}.fillRect(#{x}, #{y}, #{width}, #{height})`
7
7
  return nil
8
8
  end
9
9
 
@@ -11,25 +11,25 @@ class AuthorEngine
11
11
  size = 16 # sprite size
12
12
  sprites = AuthorEngine::GameRunner.instance.sprites
13
13
 
14
- `#{@canvas_context}.save()`
14
+ `#{@authorengine_canvas_context}.save()`
15
15
  if alpha <= 0
16
16
  alpha = 0
17
17
  else
18
18
  alpha = (alpha / 255.0)
19
19
  end
20
- `#{@canvas_context}.globalAlpha = #{alpha}`
20
+ `#{@authorengine_canvas_context}.globalAlpha = #{alpha}`
21
21
 
22
- `#{@canvas_context}.drawImage(#{sprites[sprite_sheet_index]}, #{x}, #{y})`
22
+ `#{@authorengine_canvas_context}.drawImage(#{sprites[sprite_sheet_index]}, #{x}, #{y})`
23
23
 
24
- `#{@canvas_context}.restore()`
24
+ `#{@authorengine_canvas_context}.restore()`
25
25
  end
26
26
 
27
27
  def text(string, x = 0, y = 0, size = 4, z = 0, color = "white")
28
28
  font = "#{size}px Connection, Consolas"
29
- `#{@canvas_context}.font = #{font}`
30
- `#{@canvas_context}.fillStyle = #{color}`
31
- `#{@canvas_context}.textBaseline = "top"`
32
- `#{@canvas_context}.fillText(#{string}, #{x}, #{y})`
29
+ `#{@authorengine_canvas_context}.font = #{font}`
30
+ `#{@authorengine_canvas_context}.fillStyle = #{color}`
31
+ `#{@authorengine_canvas_context}.textBaseline = "top"`
32
+ `#{@authorengine_canvas_context}.fillText(#{string}, #{x}, #{y})`
33
33
  end
34
34
 
35
35
  def level(index, z = 0)
@@ -45,24 +45,24 @@ class AuthorEngine
45
45
  _level = AuthorEngine::GameRunner.instance.levels[level]
46
46
  raise "No level at '#{index}'!" unless _level
47
47
  raise "No sprite at '#{current_sprite}'!" unless AuthorEngine::GameRunner.instance.sprites[current_sprite]
48
- raise "No sprite at '#{current_sprite}'!" unless AuthorEngine::GameRunner.instance.sprites[replacement_sprite]
48
+ raise "No sprite at '#{replacement_sprite}'!" unless AuthorEngine::GameRunner.instance.sprites[replacement_sprite]
49
49
 
50
50
  _level.each {|sprite| sprite.sprite = replacement_sprite if sprite.sprite == current_sprite}
51
51
  end
52
52
 
53
53
  def translate(x, y, &block)
54
- `#{@canvas_context}.save()`
55
- `#{@canvas_context}.translate(#{x}, #{y})`
54
+ `#{@authorengine_canvas_context}.save()`
55
+ `#{@authorengine_canvas_context}.translate(#{x}, #{y})`
56
56
  block.call if block
57
- `#{@canvas_context}.restore()`
57
+ `#{@authorengine_canvas_context}.restore()`
58
58
  end
59
59
 
60
60
  def rotate(angle, x = 0, y = 0, &block)
61
- `#{@canvas_context}.save()`
62
- `#{@canvas_context}.translate(#{x}, #{y})`
63
- `#{@canvas_context}.rotate(#{angle})`
61
+ `#{@authorengine_canvas_context}.save()`
62
+ `#{@authorengine_canvas_context}.translate(#{x}, #{y})`
63
+ `#{@authorengine_canvas_context}.rotate(#{angle})`
64
64
  block.call if block
65
- `#{@canvas_context}.restore()`
65
+ `#{@authorengine_canvas_context}.restore()`
66
66
  end
67
67
  end
68
68
  end
@@ -1,36 +1,57 @@
1
1
  class AuthorEngine
2
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:)
3
+ attr_accessor :x, :y
4
+ attr_reader :width, :height
5
+ def initialize(label:, color:, x: 0, y: 0, width:, height:, for_key: nil, &block)
5
6
  @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
+ @for_key = for_key
8
+ @block = block
9
+
10
+ @buttons = AuthorEngine::Part::OpalInput::BUTTONS
11
+ @key_states = AuthorEngine::Part::OpalInput::KEY_STATES
7
12
 
8
13
  @game = AuthorEngine::GameRunner.instance.game
9
- @game_width = 128 * @game.scale
14
+ @game_width = 128 * @game.authorengine_scale
10
15
  @game_x = `window.innerWidth/2 - #{@game_width/2}`
16
+ end
11
17
 
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
18
+ def draw
19
+ `#{@game.authorengine_canvas_context}.fillStyle = #{@color}`
20
+ `#{@game.authorengine_canvas_context}.fillRect(#{@x}, #{@y}, #{@width}, #{@height})`
19
21
 
20
- @y = `window.innerHeight/2 - #{height}` unless @y.is_a?(Numeric)
22
+ font = "#{@height}px Connection, Consolas"
23
+ `#{@game.authorengine_canvas_context}.font = #{font}`
24
+ `#{@game.authorengine_canvas_context}.fillStyle = "white"`
25
+ `#{@game.authorengine_canvas_context}.textBaseline = "top"`
26
+ `#{@game.authorengine_canvas_context}.fillText(#{@label}, #{@x}, #{@y}, #{@width})`
21
27
  end
22
28
 
23
- def draw
24
- `#{@game.canvas_context}.fillStyle = #{@color}`
25
- `#{@game.canvas_context}.fillRect(#{@x}, #{@y}, #{width}, #{width})`
29
+ def trigger?(touches)
30
+ triggered = false
31
+
32
+ touches.detect do |id, touch|
33
+ if touch.x.between?(@x, @x+@width) && touch.y.between?(@y, @y+@height)
34
+ triggered = true
35
+ end
36
+ end
37
+
38
+
39
+ if @for_key
40
+ active if triggered
41
+ inactive unless triggered
42
+ else
43
+ @block.call if @block && triggered
44
+ end
45
+
46
+ return triggered
26
47
  end
27
48
 
28
49
  def active
29
- @contact_proc.call
50
+ @key_states[@buttons[@for_key]] = true
30
51
  end
31
52
 
32
53
  def inactive
33
- @no_contact_proc.call
54
+ @key_states[@buttons[@for_key]] = false
34
55
  end
35
56
  end
36
57
  end