fantasy 0.1.3 → 0.1.5

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