fantasy 0.1.3 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe5958bf098b2f7a0190eecbd5a9914c5a232e38863b4e9a19280b44653d771a
4
- data.tar.gz: fa8899b7f6f6c04cda1e3ee6d470e940e377395b403d6dd3cddcdbf39583cc30
3
+ metadata.gz: df4ba5be3b9ac4fbe5530172717fc75bda3687980d3b495c025fa7165e582254
4
+ data.tar.gz: a57ad439621fa4b7e531047350e5703812bad73338829cbab29e49e857c5dde8
5
5
  SHA512:
6
- metadata.gz: 7c7830aca33b0a07482782f644301c854f9017ed10bc4602435945a70901e60fdd01adae8ed757f4ecb3a07a51e0b4279a273dbe149c665472e75e7c32e80f28
7
- data.tar.gz: 5fee0000ec447edd1849592ab3f5145214d4a84dcf983bf1fff9c05a5ac3655d99bf519d4760ebaba2fadba6194dbf697468cd2483d0b3a77e8ab1611210eb65
6
+ metadata.gz: 32cd9949324305bfbca173ef312797cd0a6ad7ca71c1bdf508220f10b02430501441acc537d02b5409d6c91f2865fb715315da65416932930069c87510369795
7
+ data.tar.gz: 797984f3d327e193e30665d633373afb45e9f34c90e84ad801379378c742f2d19eec69dc6aba779aba2be156f030bf322bb8863105bc7526564118fd647f2cd0
data/README.md CHANGED
@@ -1,11 +1,83 @@
1
1
  # Ruby in Fantasy
2
2
 
3
+ Simple toolbox library and lean API to build great mini games in Ruby.
4
+
3
5
  An upper layer over Gosu library to offer a more friendly API for an easy and lean game development.
4
6
 
5
7
  Specially intended to use Ruby as a learning language to introduce children into programming.
6
8
 
7
9
  **Attention**: This project is in early development phase, right now it is just an experiment
8
10
 
11
+ ## Use
12
+
13
+ ### Hello World
14
+
15
+ ```ruby
16
+ require "fantasy"
17
+
18
+ SCREEN_WIDTH = 320
19
+ SCREEN_HEIGHT = 240
20
+
21
+ on_game do
22
+ message = HudText.new(position: Coordinates.new(10, 10))
23
+ message.text = "Hello, World!"
24
+ message.size = "big"
25
+ end
26
+
27
+ start!
28
+ ```
29
+
30
+ ### Action game
31
+
32
+ ```ruby
33
+ SCREEN_WIDTH = 640
34
+ SCREEN_HEIGHT = 360
35
+
36
+ on_game do
37
+ player = Actor.new("warrior")
38
+ player.position = Coordinates.new(100, 100)
39
+ points = HudText.new(position: Coordinates.new(10, 20))
40
+ points.text = 0
41
+
42
+ on_space_bar do
43
+ Sound.play("shoot")
44
+ bullet = Actor.new("bullet")
45
+ bullet.position = player.position
46
+ bullet.speed = 100
47
+ bullet.direction = Coordinates.up
48
+ bullet.on_collision do |other|
49
+ if other.name == "enemy"
50
+ Sound.play("impact")
51
+ other.destroy
52
+ bullet.destroy
53
+ points.text += 1
54
+ end
55
+ end
56
+ end
57
+
58
+ player.on_collision do |other|
59
+ if other.name == "enemy"
60
+ Sound.play("game_over")
61
+ Global.go_to_end
62
+ end
63
+ end
64
+ end
65
+
66
+ on_end do
67
+ HudText.new(position: Coordinates.new(10, 100), text: "You are dead. Press space to re-tart")
68
+
69
+ on_space_bar do
70
+ Global.go_to_game
71
+ end
72
+ end
73
+
74
+ start!
75
+ ```
76
+
77
+ ## Examples
78
+
79
+ See the [Ruby in Fantasy Games Collection](https://github.com/fguillen/RubyInFantasyGames).
80
+
9
81
  ## Installation
10
82
 
11
83
  Add this line to your application's Gemfile:
@@ -24,6 +96,19 @@ Or install it yourself as:
24
96
 
25
97
  ## Features
26
98
 
99
+ ### Game Scene transitions
100
+
101
+ Easy to configure 3 basic game states:
102
+
103
+ - Game presentation scene
104
+ - Game game scene
105
+ - Game end scene
106
+ - Other scenes, like levels or such (TODO)
107
+
108
+ Each state should be independent and unique Actors and other elements can be created and configured for each state
109
+
110
+ Built-in mechanism to move from one state to another.
111
+
27
112
  ### Actor
28
113
 
29
114
  Managing game elements which have (optionally) image, movement and collision
@@ -36,6 +121,7 @@ Managing game elements which have (optionally) image, movement and collision
36
121
  - Gravity (TODO)
37
122
  - Animations (TODO)
38
123
  - Possibility to extend Actor class or instantiate it directly for simple characters
124
+ - Allowing magic instance properties (Like in OpenStruct). So programmer can do `actor.stuff = 1` and it is valid (TODO)
39
125
 
40
126
  ### Clock
41
127
 
@@ -79,7 +165,7 @@ For easy creation of head-up display components.
79
165
 
80
166
  ### Debug Mode
81
167
 
82
- When active different attributes from all the Actors and HUD elements will be visible in the screen.
168
+ Press `d` to activate it. When active debug visuals are shown:
83
169
 
84
170
  - Position
85
171
  - Collider
@@ -99,18 +185,6 @@ Actors in the game will be rendered in the relative position to this camera.
99
185
 
100
186
  - Not deal with RGB or anything, just a list of colors (TODO)
101
187
 
102
- ### Game Scene transitions
103
-
104
- Easy to configure 3 basic game states:
105
-
106
- - Game presentation scene
107
- - Game game scene
108
- - Game end scene
109
- - Other scenes, like levels or such (TODO)
110
-
111
- Each state should be independent and unique Actors and other elements can be created and configured for each state
112
-
113
- Built-in mechanism to move from one state to another.
114
188
 
115
189
  ### Pause Game (TODO)
116
190
 
@@ -129,8 +203,8 @@ Direct and easy way to play a sound
129
203
  Simple way to set up:
130
204
 
131
205
  - Background color
132
- - Image background (TODO)
133
- - Repeatable image background (TODO)
206
+ - Image background
207
+ - Repeatable image background
134
208
 
135
209
  ### Data Persistance (TODO)
136
210
 
@@ -143,20 +217,224 @@ Easy access to keyboard and mouse inputs on any part of the code. Specially in t
143
217
  - Allow "on_space_bar" and each Actor (TODO)
144
218
  - Allow multiple "on_space_bar" (TODO)
145
219
  - Remove "on_space_bar" when changing scene (TODO)
220
+ - Detect when key/mouse button is pressed in the actual frame in any part of the code (TODO)
221
+
222
+ ### Tile Map (TODO)
223
+
224
+ For easy creation of:
225
+
226
+ - Top-down map levels (TODO)
227
+ - Platformer map levels (TODO)
228
+
229
+ ### Tweens (TODO)
230
+
231
+ Multiple movement animation effects like in [DoTween](http://dotween.demigiant.com/documentation.php) (TODO)
146
232
 
147
233
  ## API
148
234
 
235
+ ### Game Scene transitions
236
+
237
+ Configure your game elements on each Scene:
238
+
239
+ ```ruby
240
+ SCREEN_WIDTH = 640
241
+ SCREEN_HEIGHT = 360
242
+
243
+ # (Optional)
244
+ on_presentation do
245
+ # Game elements running when the game loads
246
+ end
247
+
248
+ on_game do
249
+ # Game elements running when in game Scene
250
+ end
251
+
252
+ # (Optional)
253
+ on_end do
254
+ # Game elements running when game is ended
255
+ end
256
+ ```
257
+
258
+ How to go from Scene to Scene:
259
+
260
+ ```ruby
261
+ Global.go_to_presentation
262
+ Global.go_to_game
263
+ Global.go_to_end
264
+ ```
265
+
266
+ #### Example
267
+
268
+ ```ruby
269
+ on_presentation do
270
+ HudText.new(position: Coordinates.new(10, 100), text: "Press space to start")
271
+
272
+ on_space_bar do
273
+ Global.go_to_game
274
+ end
275
+ end
276
+
277
+ on_game do
278
+ # [...]
279
+ if player.dead
280
+ Global.go_to_end
281
+ end
282
+ end
283
+
284
+ on_end do
285
+ HudText.new(position: Coordinates.new(10, 100), text: "You are dead. Press space to re-tart")
286
+
287
+ on_space_bar do
288
+ Global.go_to_presentation
289
+ end
290
+ end
291
+ ```
292
+
293
+ ### Actor
294
+
295
+ Actor can be used directly:
296
+
297
+ ```ruby
298
+ player = Actor.new("warrior") # ./images/warrior.png
299
+ player.position = Coordinates.new(100, 100)
300
+ player.solid = true
301
+ player.speed = 200
302
+ player.layer = 1
303
+ player.move_with_cursors
304
+
305
+ player.on_collision do |other|
306
+ if other.name == "enemy"
307
+ player.destroy
308
+ end
309
+ end
310
+
311
+ player.on_after_move do
312
+ if player.position.x > SCREEN_WIDTH
313
+ player.position.x = SCREEN_WIDTH
314
+ end
315
+
316
+ if player.position.x < 0
317
+ player.position.x = 0
318
+ end
319
+ end
320
+ ```
321
+
322
+ Or in a subclass:
323
+
324
+ ```ruby
325
+ class Player < Actor
326
+ def initialize
327
+ super("warrior") # ./images/warrior.png
328
+ @position = Coordinates.new(100, 100)
329
+ @solid = true
330
+ @speed = 200
331
+ @layer = 1
332
+ @direction = Coordinates.zero
333
+ move_with_cursors
334
+ end
335
+
336
+ on_collision do |other|
337
+ if other.name == "enemy"
338
+ destroy
339
+ end
340
+ end
341
+
342
+ on_after_move do
343
+ if @position.x > SCREEN_WIDTH
344
+ @position.x = SCREEN_WIDTH
345
+ end
346
+
347
+ if @position.x < 0
348
+ @position.x = 0
349
+ end
350
+ end
351
+ end
352
+ ```
353
+
354
+ ### Clock
355
+
356
+ ```ruby
357
+ clock =
358
+ Clock.new do
359
+ enemy.attack
360
+ sleep(1)
361
+ enemy.defend
362
+ end
363
+
364
+ clock.run_now
365
+ clock.run_on(seconds: 2)
366
+ clock.repeat(seconds: 2, times: 10)
367
+ clock.stop
368
+ ```
369
+
370
+ ### Background
371
+
372
+ ```ruby
373
+ # Simple color
374
+ on_presentation do
375
+ Global.background = Color.new(r: 34, g: 35, b: 35)
376
+ end
377
+
378
+ # Replicable (by default) Image
379
+ # position is relative to Global.camera
380
+ on_game do
381
+ background = Background.new(image_name: "beach")
382
+ # background.replicable = false # if you don't want the image to replicate
383
+ background.scale = 6
384
+ end
385
+ ```
386
+
387
+ ### Camera
388
+
389
+ ```ruby
390
+ on_game do
391
+ on_loop do
392
+ # Camera follows player
393
+ Global.camera.position.y = player.position.y - (SCREEN_HEIGHT / 2)
394
+ end
395
+ end
396
+ ```
397
+
398
+ ### Sound
399
+
400
+ ```ruby
401
+ Sound.play("shoot") # ./sounds/shoot.wav
402
+ ```
403
+
404
+
405
+ ### UI
406
+
407
+ #### HUD Text
408
+
409
+ ```ruby
410
+ timer = HudText.new(position: Coordinates.new(20, 10))
411
+ timer.text = 0
412
+ timer.size = "big"
413
+
414
+ Clock.new { timer.text += 1 }.repeat(seconds: 1)
415
+ ```
416
+
417
+ #### HUD Image
418
+
419
+ ```ruby
420
+ icon = HudImage.new(position: Coordinates.new(SCREEN_WIDTH - 220, 8), image_name: "ring")
421
+ icon.scale = 4
422
+ icon.visible = true
423
+
424
+ Clock.new { icon.visible = !icon.visible }.repeat(seconds: 1)
425
+ ```
149
426
 
150
427
 
151
428
  ## Credits for assets
152
429
 
153
- - Sprites: www.kenney.nl
154
430
  - Font: VT323 Project Authors (peter.hull@oikoi.com)
155
431
 
156
432
  ## Bugs
157
433
 
158
434
  - When dragging in debug mode new elements are being added to the drag (TODO)
159
435
  - Rubocop is not passing
436
+ - Tests are missing
437
+ - Allow Ruby 2.5+
160
438
 
161
439
  ## Development
162
440
 
data/fantasy.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Fernando Guillen"]
9
9
  spec.email = ["fguillen.mail@gmail.com"]
10
10
 
11
- spec.summary = "Simple toolbox library and lean API to build great mini games"
12
- spec.description = "Simple toolbox library and lean API to build great mini games"
11
+ spec.summary = "Simple toolbox library and lean API to build great mini games in Ruby"
12
+ spec.description = "Simple toolbox library and lean API to build great mini games in Ruby"
13
13
  spec.homepage = "https://github.com/fguillen/fantasy"
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = ">= 3.0.0"
data/lib/fantasy/actor.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  class Actor
2
+ include MoveByCursor
3
+
2
4
  attr_reader :image, :moving_with_cursors
3
- attr_accessor :position, :direction, :speed, :solid, :scale, :name, :layer
5
+ attr_accessor :image_name, :position, :direction, :speed, :solid, :scale, :name, :layer
4
6
 
5
7
  def initialize(image_name)
8
+ @image_name = image_name
6
9
  @image = Image.new(image_name)
7
10
  @name = image_name
8
11
  @position = Coordinates.new(0, 0)
@@ -113,20 +116,6 @@ class Actor
113
116
  @on_collision_callback.call(actor) unless @on_collision_callback.nil?
114
117
  end
115
118
 
116
- def calculate_direction_by_cursors
117
- if Gosu.button_down?(Gosu::KB_DOWN)
118
- @direction = Coordinates.down
119
- elsif Gosu.button_down?(Gosu::KB_UP)
120
- @direction = Coordinates.up
121
- elsif Gosu.button_down?(Gosu::KB_RIGHT)
122
- @direction = Coordinates.right
123
- elsif Gosu.button_down?(Gosu::KB_LEFT)
124
- @direction = Coordinates.left
125
- else
126
- @direction = Coordinates.zero
127
- end
128
- end
129
-
130
119
  def collisions
131
120
  Global.actors.reject { |e| e == self }.select { |e| e.solid? }.select do |actor|
132
121
  Utils.collision? self, actor
@@ -137,4 +126,37 @@ class Actor
137
126
  @on_destroy_callback.call unless @on_destroy_callback.nil?
138
127
  Global.actors.delete(self)
139
128
  end
129
+
130
+ def clone
131
+ actor = self.class.new(@image_name)
132
+ actor.image_name = @image_name
133
+ actor.name = @name
134
+ actor.position = @position.clone
135
+ actor.direction = @direction.clone
136
+ actor.speed = @speed
137
+ actor.scale = @scale
138
+ actor.moving_with_cursors if @moving_with_cursors
139
+ actor.solid = @solid
140
+ actor.layer = @layer
141
+
142
+ actor.on_after_move_callback = @on_after_move_callback
143
+ actor.on_collision_callback = @on_collision_callback
144
+ actor.on_destroy_callback = @on_destroy_callback
145
+
146
+ actor
147
+ end
148
+
149
+ protected
150
+
151
+ def on_after_move_callback=(block)
152
+ @on_after_move_callback = block
153
+ end
154
+
155
+ def on_collision_callback=(block)
156
+ @on_collision_callback = block
157
+ end
158
+
159
+ def on_destroy_callback=(block)
160
+ @on_destroy_callback = block
161
+ end
140
162
  end
@@ -10,6 +10,7 @@ class Background
10
10
  @draggable_on_debug = true
11
11
  @dragging = false
12
12
  @layer = -100
13
+ @replicable = true
13
14
 
14
15
  Global.backgrounds.push(self)
15
16
  end
@@ -22,9 +23,36 @@ class Background
22
23
  @image.height() * @scale
23
24
  end
24
25
 
26
+ def position_in_camera
27
+ @position - Global.camera.position
28
+ end
29
+
25
30
  def draw
26
- if visible
27
- @image.draw(x: @position.x, y: @position.y, scale: @scale)
31
+ if @visible
32
+ if @replicable
33
+ draw_replicable
34
+ else
35
+ draw_normal
36
+ end
37
+ end
38
+ end
39
+
40
+ def draw_normal
41
+ @image.draw(x: position_in_camera.x, y: position_in_camera.y, scale: @scale)
42
+ end
43
+
44
+ # Camera relative Tiles
45
+ def draw_replicable
46
+ tiles_delta_x = (position_in_camera.x % width) - width
47
+ tiles_delta_y = (position_in_camera.y % height) - height
48
+
49
+ tiles_needed_horizontal = ((SCREEN_WIDTH - (tiles_delta_x + width)) / width.to_f).ceil + 1
50
+ tiles_needed_vertical = ((SCREEN_HEIGHT - (tiles_delta_y + height)) / height.to_f).ceil + 1
51
+
52
+ tiles_needed_horizontal.times do |index_horizontal|
53
+ tiles_needed_vertical.times do |index_vertical|
54
+ @image.draw(x: tiles_delta_x + (width * index_horizontal), y: tiles_delta_y + (height * index_vertical), scale: @scale)
55
+ end
28
56
  end
29
57
  end
30
58
 
data/lib/fantasy/base.rb CHANGED
@@ -54,6 +54,7 @@ def on_mouse_button_right(&block)
54
54
  end
55
55
 
56
56
  def start!
57
+ Global.setup
57
58
  Global.game = Game.new
58
59
  Global.game.show
59
60
  end
@@ -1,7 +1,31 @@
1
1
  class Camera
2
- attr_accessor :position
2
+ include MoveByCursor
3
+
4
+ attr_accessor :position, :direction, :speed
3
5
 
4
6
  def initialize(position: Coordinates.zero)
5
7
  @position = position
8
+ @direction = Coordinates.zero
9
+ @speed = 0
10
+ @moving_with_cursors = false
11
+ @on_after_move_callback = nil
12
+ end
13
+
14
+ def move_with_cursors
15
+ @moving_with_cursors = true
16
+ end
17
+
18
+ def move
19
+ calculate_direction_by_cursors if @moving_with_cursors
20
+
21
+ if @direction != Coordinates.zero && !@speed.zero?
22
+ @position = @position + (@direction * @speed * Global.frame_time)
23
+ end
24
+
25
+ @on_after_move_callback.call unless @on_after_move_callback.nil?
26
+ end
27
+
28
+ def on_after_move(&block)
29
+ @on_after_move_callback = block
6
30
  end
7
31
  end
@@ -28,4 +28,8 @@ class Coordinates < Vector2d
28
28
  def y=(value)
29
29
  @y = value
30
30
  end
31
+
32
+ def clone
33
+ Coordinates.new(@x, @y)
34
+ end
31
35
  end
@@ -2,7 +2,7 @@ require "ostruct"
2
2
 
3
3
  module Global
4
4
  class << self
5
- attr_accessor :actors, :hud_texts, :hud_images, :backgrounds, :clocks
5
+ attr_accessor :actors, :hud_texts, :hud_images, :backgrounds, :tile_maps, :clocks
6
6
  attr_accessor :debug
7
7
  attr_accessor :setup_proc, :loop_proc, :button_proc
8
8
  attr_accessor :presentation_proc, :game_proc, :end_proc
@@ -26,6 +26,7 @@ module Global
26
26
  @hud_texts = []
27
27
  @hud_images = []
28
28
  @backgrounds = []
29
+ @tile_maps = []
29
30
  @clocks = []
30
31
  @last_frame_at = Time.now
31
32
  @debug = false
@@ -34,6 +35,7 @@ module Global
34
35
  @references = OpenStruct.new
35
36
  @camera = Camera.new(position: Coordinates.zero)
36
37
  @game_state = Global.presentation_proc.nil? ? "game" : "presentation"
38
+ @game_started_at = Time.now
37
39
 
38
40
  if @presentation_proc.nil?
39
41
  on_presentation { Global.default_on_presentation }
@@ -104,6 +106,7 @@ module Global
104
106
  @hud_texts.clear
105
107
  @hud_images.clear
106
108
  @backgrounds.clear
109
+ @tile_maps.clear
107
110
  @clocks.reject(&:persistent?).each(&:stop)
108
111
  @background = Color.new(r: 0, g: 0, b: 0)
109
112
  end
@@ -111,5 +114,14 @@ module Global
111
114
  def mouse_position
112
115
  Coordinates.new(Global.game.mouse_x, Global.game.mouse_y)
113
116
  end
117
+
118
+ def setup
119
+ Sound.preload_sounds
120
+ Image.preload_images
121
+ end
122
+
123
+ def seconds_in_game
124
+ Time.now - @game_started_at
125
+ end
114
126
  end
115
127
  end
data/lib/fantasy/image.rb CHANGED
@@ -22,6 +22,12 @@ class Image
22
22
  locate_image(image_name)
23
23
  end
24
24
 
25
+ def preload_images
26
+ Dir.each_child(base_path) do |file_name|
27
+ locate_image(file_name) unless file_name.start_with?(".")
28
+ end
29
+ end
30
+
25
31
  private
26
32
 
27
33
  def locate_image(image_name)
@@ -29,14 +35,17 @@ class Image
29
35
 
30
36
  puts "Initialize image: '#{image_name}'"
31
37
 
32
- base_path = "#{Dir.pwd}/images"
33
- file_name = Dir.entries(base_path).find { |e| e.start_with?("#{image_name}.") }
38
+ file_name = Dir.entries(base_path).find { |e| e =~ /^#{image_name}($|\.)/ }
34
39
 
35
- raise "Image file not found with name '#{image_name}'" if file_name.nil?
40
+ raise "Image file not found with name '#{image_name}' in #{base_path}" if file_name.nil?
36
41
 
37
42
  @@images[image_name] = Gosu::Image.new("#{base_path}/#{file_name}", { retro: true })
38
43
 
39
44
  return @@images[image_name]
40
45
  end
46
+
47
+ def base_path
48
+ "#{Dir.pwd}/images"
49
+ end
41
50
  end
42
51
  end
@@ -0,0 +1,15 @@
1
+ module MoveByCursor
2
+ def calculate_direction_by_cursors
3
+ if Gosu.button_down?(Gosu::KB_DOWN)
4
+ @direction = Coordinates.down
5
+ elsif Gosu.button_down?(Gosu::KB_UP)
6
+ @direction = Coordinates.up
7
+ elsif Gosu.button_down?(Gosu::KB_RIGHT)
8
+ @direction = Coordinates.right
9
+ elsif Gosu.button_down?(Gosu::KB_LEFT)
10
+ @direction = Coordinates.left
11
+ else
12
+ @direction = Coordinates.zero
13
+ end
14
+ end
15
+ end
data/lib/fantasy/loop.rb CHANGED
@@ -38,6 +38,8 @@ class Game < Gosu::Window
38
38
  e.move
39
39
  end
40
40
 
41
+ Global.camera.move
42
+
41
43
  Global.loop_proc.call() unless Global.loop_proc.nil?
42
44
  end
43
45
 
@@ -46,6 +48,7 @@ class Game < Gosu::Window
46
48
 
47
49
  (
48
50
  Global.backgrounds +
51
+ Global.tile_maps +
49
52
  Global.actors +
50
53
  Global.hud_texts +
51
54
  Global.hud_images
data/lib/fantasy/sound.rb CHANGED
@@ -11,8 +11,7 @@ module Sound
11
11
 
12
12
  puts "Initialize Sound: '#{sound_name}'"
13
13
 
14
- base_path = "#{Dir.pwd}/sounds"
15
- file_name = Dir.entries(base_path).find { |e| e.start_with?("#{sound_name}.") }
14
+ file_name = Dir.entries(base_path).find { |e| e =~ /^#{sound_name}($|\.)/ }
16
15
 
17
16
  raise "Sound file not found with name '#{sound_name}'" if file_name.nil?
18
17
 
@@ -20,5 +19,15 @@ module Sound
20
19
 
21
20
  return @@sounds[sound_name]
22
21
  end
22
+
23
+ def preload_sounds
24
+ Dir.each_child(base_path) do |file_name|
25
+ locate_sound(file_name) unless file_name.start_with?(".")
26
+ end
27
+ end
28
+
29
+ def base_path
30
+ "#{Dir.pwd}/sounds"
31
+ end
23
32
  end
24
33
  end
@@ -0,0 +1,78 @@
1
+ class TileMap
2
+ attr_accessor :position
3
+
4
+ def initialize(map_name:, tiles:, tile_size: nil, tile_width: nil, tile_height: nil)
5
+ @tile_width = tile_width || tile_size
6
+ @tile_height = tile_height || tile_size
7
+
8
+ if(@tile_height.nil? or @tile_width.nil?)
9
+ raise("Tile size is not properly defined. Either you set a `tile_size` or a `tile_width` and `tile_height`")
10
+ end
11
+
12
+ @map_name = map_name
13
+ @tiles = tiles
14
+ @position = Coordinates.zero
15
+
16
+ @grid = TileMap.load_grid(@map_name)
17
+ end
18
+
19
+ def width
20
+ @grid.max(&:length) * @tile_width
21
+ end
22
+
23
+ def height
24
+ @grid.length * @tile_height
25
+ end
26
+
27
+ def spawn
28
+ tile_position = Coordinates.zero
29
+
30
+ @grid.each do |line|
31
+ tile_position.x = 0
32
+
33
+ line.each do |tile_index|
34
+ if !tile_index.nil?
35
+ actor = @tiles[tile_index].clone
36
+ actor.position.x = @position.x + (tile_position.x * @tile_width)
37
+ actor.position.y = @position.y + (tile_position.y * @tile_height)
38
+ end
39
+
40
+ tile_position.x += 1
41
+ end
42
+
43
+ tile_position.y += 1
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ class << self
50
+ @@maps = {}
51
+
52
+ def load_grid(map_name)
53
+ File.readlines(TileMap.locate_map(map_name), chomp: true).map do |line|
54
+ line.each_char.map do |char|
55
+ char == " " ? nil : char.to_i
56
+ end
57
+ end
58
+ end
59
+
60
+ def locate_map(map_name)
61
+ return @@maps[map_name] if @@maps[map_name]
62
+
63
+ puts "Initialize map: '#{map_name}'"
64
+
65
+ file_name = Dir.entries(base_path).find { |e| e =~ /^#{map_name}($|\.)/ }
66
+
67
+ raise "Map file not found with name '#{map_name}' in #{base_path}" if file_name.nil?
68
+
69
+ @@maps[map_name] = "#{base_path}/#{file_name}"
70
+
71
+ return @@maps[map_name]
72
+ end
73
+
74
+ def base_path
75
+ "#{Dir.pwd}/maps"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,11 @@
1
+ module Tween
2
+ def self.move_towards(from:, to:, speed:)
3
+ direction = to - from
4
+ step = [direction.length, speed * Global.frame_time].min
5
+
6
+ return to if step.zero?
7
+
8
+ direction = direction.normalize
9
+ return from + (direction * step)
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fantasy
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.5"
5
5
  end
data/lib/fantasy.rb CHANGED
@@ -2,6 +2,8 @@
2
2
  require "gosu"
3
3
 
4
4
  require_relative "fantasy/version"
5
+ require_relative "fantasy/includes/move_by_cursors"
6
+ require_relative "fantasy/tween"
5
7
  require_relative "fantasy/draggable"
6
8
  require_relative "fantasy/color"
7
9
  require_relative "fantasy/actor"
@@ -16,4 +18,5 @@ require_relative "fantasy/background"
16
18
  require_relative "fantasy/sound"
17
19
  require_relative "fantasy/camera"
18
20
  require_relative "fantasy/image"
21
+ require_relative "fantasy/tile_map"
19
22
  require_relative "fantasy/base"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fantasy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fernando Guillen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-12 00:00:00.000000000 Z
11
+ date: 2022-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gosu
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 2.2.3
41
- description: Simple toolbox library and lean API to build great mini games
41
+ description: Simple toolbox library and lean API to build great mini games in Ruby
42
42
  email:
43
43
  - fguillen.mail@gmail.com
44
44
  executables: []
@@ -69,8 +69,11 @@ files:
69
69
  - lib/fantasy/hud_image.rb
70
70
  - lib/fantasy/hud_text.rb
71
71
  - lib/fantasy/image.rb
72
+ - lib/fantasy/includes/move_by_cursors.rb
72
73
  - lib/fantasy/loop.rb
73
74
  - lib/fantasy/sound.rb
75
+ - lib/fantasy/tile_map.rb
76
+ - lib/fantasy/tween.rb
74
77
  - lib/fantasy/utils.rb
75
78
  - lib/fantasy/version.rb
76
79
  homepage: https://github.com/fguillen/fantasy
@@ -99,5 +102,5 @@ requirements: []
99
102
  rubygems_version: 3.2.22
100
103
  signing_key:
101
104
  specification_version: 4
102
- summary: Simple toolbox library and lean API to build great mini games
105
+ summary: Simple toolbox library and lean API to build great mini games in Ruby
103
106
  test_files: []