fantasy 0.1.13 → 0.1.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +22 -1
- data/.yardopts +4 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +55 -0
- data/README.md +14 -6
- data/docs/Actor.html +2737 -0
- data/docs/Background.html +961 -0
- data/docs/Camera.html +791 -0
- data/docs/Clock.html +753 -0
- data/docs/Color.html +776 -0
- data/docs/Coordinates.html +730 -0
- data/docs/Cursor.html +752 -0
- data/docs/Disk.html +236 -0
- data/docs/Draggable.html +198 -0
- data/docs/Fantasy.html +121 -0
- data/docs/Game.html +904 -0
- data/docs/Global.html +2791 -0
- data/docs/Gravitier.html +179 -0
- data/docs/HudImage.html +979 -0
- data/docs/HudText.html +1151 -0
- data/docs/Image.html +506 -0
- data/docs/Jumper.html +189 -0
- data/docs/Mouse.html +226 -0
- data/docs/MoveByCursor.html +374 -0
- data/docs/MoveByDirection.html +179 -0
- data/docs/Mover.html +305 -0
- data/docs/Music.html +524 -0
- data/docs/Shape.html +1057 -0
- data/docs/Sound.html +374 -0
- data/docs/Tilemap.html +491 -0
- data/docs/Tween.html +186 -0
- data/docs/UserInputs.html +879 -0
- data/docs/Utils.html +345 -0
- data/docs/_index.html +346 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +497 -0
- data/docs/file.CHANGELOG.html +121 -0
- data/docs/file.README.html +599 -0
- data/docs/file_list.html +61 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +599 -0
- data/docs/js/app.js +314 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +1931 -0
- data/docs/top-level-namespace.html +978 -0
- data/lib/fantasy/actor.rb +455 -123
- data/lib/fantasy/background.rb +109 -13
- data/lib/fantasy/base.rb +113 -1
- data/lib/fantasy/camera.rb +95 -11
- data/lib/fantasy/clock.rb +4 -2
- data/lib/fantasy/color.rb +158 -153
- data/lib/fantasy/coordinates.rb +5 -9
- data/lib/fantasy/cursor.rb +2 -1
- data/lib/fantasy/disk.rb +12 -8
- data/lib/fantasy/draggable.rb +22 -1
- data/lib/fantasy/global.rb +59 -31
- data/lib/fantasy/hud_image.rb +5 -3
- data/lib/fantasy/hud_text.rb +6 -2
- data/lib/fantasy/image.rb +12 -4
- data/lib/fantasy/includes/gravitier.rb +2 -0
- data/lib/fantasy/includes/jumper.rb +3 -2
- data/lib/fantasy/includes/log.rb +12 -0
- data/lib/fantasy/includes/move_by_cursors.rb +24 -15
- data/lib/fantasy/includes/move_by_direction.rb +2 -0
- data/lib/fantasy/includes/mover.rb +3 -0
- data/lib/fantasy/includes/user_inputs.rb +6 -0
- data/lib/fantasy/loop.rb +41 -44
- data/lib/fantasy/mouse.rb +2 -0
- data/lib/fantasy/music.rb +4 -2
- data/lib/fantasy/shape.rb +11 -2
- data/lib/fantasy/sound.rb +3 -1
- data/lib/fantasy/tilemap.rb +19 -13
- data/lib/fantasy/tween.rb +3 -1
- data/lib/fantasy/utils.rb +7 -13
- data/lib/fantasy/version.rb +1 -1
- data/lib/fantasy.rb +2 -0
- metadata +90 -4
- data/fantasy.gemspec +0 -40
- data/fonts/VT323-Regular.ttf +0 -0
data/lib/fantasy/actor.rb
CHANGED
@@ -1,3 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Represents one active entity in the game.
|
4
|
+
# An actor can be the Player sprite. Or one of the enemies. Or the bullet.
|
5
|
+
# An actor can also be a tree, or a wall
|
6
|
+
#
|
7
|
+
# @example Actor can be used directly
|
8
|
+
# player = Actor.new("warrior") # ./images/warrior.png
|
9
|
+
# player.position = Coordinates.new(100, 100)
|
10
|
+
# player.solid = true
|
11
|
+
# player.speed = 200
|
12
|
+
# player.layer = 1
|
13
|
+
# player.move_with_cursors
|
14
|
+
# player.collision_with = ["enemy", "bullets"] # default "all"
|
15
|
+
#
|
16
|
+
# player.on_collision do |other|
|
17
|
+
# if other.name == "enemy"
|
18
|
+
# player.destroy
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# player.on_after_move do
|
23
|
+
# if player.position.x > Global.screen_width
|
24
|
+
# player.position.x = Global.screen_width
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# if player.position.x < 0
|
28
|
+
# player.position.x = 0
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# @example Or in a subclass:
|
33
|
+
# class Player < Actor
|
34
|
+
# def initialize
|
35
|
+
# super("warrior") # ./images/warrior.png
|
36
|
+
# @position = Coordinates.new(100, 100)
|
37
|
+
# @solid = true
|
38
|
+
# @speed = 200
|
39
|
+
# @layer = 1
|
40
|
+
# @direction = Coordinates.zero
|
41
|
+
# @collision_with = ["enemy", "bullets"] # default "all"
|
42
|
+
# move_with_cursors
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# on_collision do |other|
|
46
|
+
# if other.name == "enemy"
|
47
|
+
# destroy
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# on_after_move do
|
52
|
+
# if @position.x > Global.screen_width
|
53
|
+
# @position.x = Global.screen_width
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# if @position.x < 0
|
57
|
+
# @position.x = 0
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
# end
|
1
61
|
class Actor
|
2
62
|
include MoveByCursor
|
3
63
|
include MoveByDirection
|
@@ -6,10 +66,173 @@ class Actor
|
|
6
66
|
include Jumper
|
7
67
|
include UserInputs
|
8
68
|
|
9
|
-
|
10
|
-
|
69
|
+
# Coordinates object where x and y represent the position of the Actor in the World (no necessarily in the Screen).
|
70
|
+
#
|
71
|
+
# Default `Coordinates.zero`.
|
72
|
+
#
|
73
|
+
# @return [Coordinates] the actual position
|
74
|
+
#
|
75
|
+
# @example Setting position
|
76
|
+
# actor = Actor.new("image")
|
77
|
+
# actor.position = Coordinates.new(10, 20)
|
78
|
+
# actor.position.x # => 10
|
79
|
+
#
|
80
|
+
# @example Modify position
|
81
|
+
# actor.position.x += 1
|
82
|
+
# actor.position.x # => 11
|
83
|
+
attr_accessor :position
|
84
|
+
|
85
|
+
# Controls the direction in which this Actor will move in the next frame.
|
86
|
+
#
|
87
|
+
# Default `Coordinates.zero`.
|
88
|
+
#
|
89
|
+
# @return [Coordinates] the actual direction
|
90
|
+
#
|
91
|
+
# @note The the pixels per second is represented by the `@speed` attribute
|
92
|
+
#
|
93
|
+
# @example Set direction
|
94
|
+
# actor = Actor.new("image")
|
95
|
+
# actor.direction = Coordinates.right
|
96
|
+
attr_accessor :direction
|
97
|
+
|
98
|
+
# Controls the pixels per second which this Actor will move in the next frame.
|
99
|
+
#
|
100
|
+
# Default `0`.
|
101
|
+
#
|
102
|
+
# @return [Float] the actual speed
|
103
|
+
#
|
104
|
+
# @note The the direction is represented by the `@direction` attribute
|
105
|
+
#
|
106
|
+
# @example Set speed
|
107
|
+
# actor = Actor.new("image")
|
108
|
+
# actor.speed = 10
|
109
|
+
attr_accessor :speed
|
110
|
+
|
111
|
+
# Controls impulse this Actor will receive when the (see #jump) is triggered.
|
112
|
+
#
|
113
|
+
# Default `0`.
|
114
|
+
#
|
115
|
+
# @return [Float] the actual jump_force
|
116
|
+
#
|
117
|
+
# @note The the direction will be `Coordinates.up`
|
118
|
+
#
|
119
|
+
# @example Set jump_force
|
120
|
+
# actor = Actor.new("image")
|
121
|
+
# actor.jump_force = 100
|
122
|
+
attr_accessor :jump_force
|
123
|
+
|
124
|
+
# Controls constant force this Actor will receive on each frame.
|
125
|
+
#
|
126
|
+
# Default `0`.
|
127
|
+
#
|
128
|
+
# @return [Float] the actual gravity
|
129
|
+
#
|
130
|
+
# @note The the direction will be `Coordinates.down`
|
131
|
+
#
|
132
|
+
# @example Set gravity
|
133
|
+
# actor = Actor.new("image")
|
134
|
+
# actor.gravity = 10
|
135
|
+
attr_accessor :gravity
|
136
|
+
|
137
|
+
# When `true` the Actor will cause and respond to collisions.
|
138
|
+
#
|
139
|
+
# When `false` the Actor won't cause neither respond to collisions.
|
140
|
+
#
|
141
|
+
# Default `true`.
|
142
|
+
#
|
143
|
+
# @return [Boolean] the actual solid value
|
144
|
+
#
|
145
|
+
# @param solid [true, false] only true or false
|
146
|
+
#
|
147
|
+
# @example Set solid
|
148
|
+
# actor = Actor.new("image")
|
149
|
+
# actor.solid = false
|
150
|
+
attr_accessor :solid
|
151
|
+
|
152
|
+
# The value to scale the image of the Actor when drawn.
|
153
|
+
# If the value is `2` the image will rendered at double of size.
|
154
|
+
# If the value is `0.5` the image will rendered at half of size.
|
155
|
+
#
|
156
|
+
# @note this value affects the attributes `width` and `height`
|
157
|
+
#
|
158
|
+
# Default `1`.
|
159
|
+
#
|
160
|
+
# @return [Float] the actual scale
|
161
|
+
#
|
162
|
+
# @example Set scale
|
163
|
+
# actor = Actor.new("image")
|
164
|
+
# actor.scale = 6
|
165
|
+
attr_accessor :scale
|
166
|
+
|
167
|
+
# The value to internal name of this Actor.
|
168
|
+
#
|
169
|
+
# It is useful for collision management for example.
|
170
|
+
#
|
171
|
+
# Default (same as `image_name`).
|
172
|
+
#
|
173
|
+
# @return [String] the actual name
|
174
|
+
#
|
175
|
+
# @example Set name
|
176
|
+
# actor = Actor.new("image")
|
177
|
+
# actor.name = "spaceship"
|
178
|
+
#
|
179
|
+
# @example Used in collision trigger
|
180
|
+
# actor = Actor.new("image")
|
181
|
+
# actor.on_collision do |other|
|
182
|
+
# if other.name == "enemy"
|
183
|
+
# # damage
|
184
|
+
# end
|
185
|
+
# end
|
186
|
+
#
|
187
|
+
# @example Used with `collision_with`
|
188
|
+
# actor = Actor.new("image")
|
189
|
+
# actor.collision_with = ["enemy", "bullet"]
|
190
|
+
attr_accessor :name
|
191
|
+
|
192
|
+
# In which layer the image of the Actor is rendered.
|
193
|
+
# Smaller numbers are rendered behind higher numbers.
|
194
|
+
#
|
195
|
+
# Default `0`.
|
196
|
+
#
|
197
|
+
# @return [Integer] the actual layer
|
198
|
+
#
|
199
|
+
# @example Set layer
|
200
|
+
# actor = Actor.new("image")
|
201
|
+
# actor.layer = -1
|
202
|
+
attr_accessor :layer
|
203
|
+
|
204
|
+
# Array of strings (or "all").
|
205
|
+
# Represents with which other solid Actors this Actor collide.
|
206
|
+
#
|
207
|
+
# Default `"all"`.
|
208
|
+
#
|
209
|
+
# @return [Array, String] the actual list of names of Actors to collide with
|
210
|
+
#
|
211
|
+
# @example Set with which other Actors this Actor is colliding:
|
212
|
+
# actor = Actor.new("image")
|
213
|
+
# actor.collision_with = ["enemy", "bullet"]
|
214
|
+
#
|
215
|
+
# @example Set this Actors collides with all other Actors
|
216
|
+
# actor = Actor.new("image")
|
217
|
+
# actor.collision_with = "all"
|
11
218
|
attr_accessor :collision_with
|
12
219
|
|
220
|
+
# Generate an Actor with all the default attribute values
|
221
|
+
# @example Generate an Actor
|
222
|
+
# actor = Actor.new("image")
|
223
|
+
# actor.position # => Coordinates.zero
|
224
|
+
# actor.direction # => Coordinates.zero
|
225
|
+
# actor.speed # => 0
|
226
|
+
# actor.scale # => 1
|
227
|
+
# actor.solid # => true
|
228
|
+
# actor.draggable_on_debug # => true
|
229
|
+
# actor.layer # => 0
|
230
|
+
# actor.gravity # => 0
|
231
|
+
# actor.jump_force # => 0
|
232
|
+
# actor.collision_with # => "all"
|
233
|
+
#
|
234
|
+
# @param image_name [string] the name of the image file from `./images/*`
|
235
|
+
# @return [Actor] the Actor
|
13
236
|
def initialize(image_name)
|
14
237
|
@image_name = image_name
|
15
238
|
@image = Image.new(image_name)
|
@@ -19,8 +242,7 @@ class Actor
|
|
19
242
|
@speed = 0
|
20
243
|
@scale = 1
|
21
244
|
|
22
|
-
@
|
23
|
-
@solid = false
|
245
|
+
@solid = true
|
24
246
|
@draggable_on_debug = true
|
25
247
|
@dragging = false
|
26
248
|
@dragging_offset = nil
|
@@ -40,59 +262,53 @@ class Actor
|
|
40
262
|
Global.actors << self
|
41
263
|
end
|
42
264
|
|
265
|
+
|
266
|
+
# Set a new image to the Actor.
|
267
|
+
# @param image_name [String] The image file from `./images/*`
|
268
|
+
#
|
269
|
+
# @example Set a new image
|
270
|
+
# actor = Actor.new("player_walk")
|
271
|
+
# actor.image_name = "player_jump"
|
43
272
|
def image=(image_name)
|
44
273
|
@image = Image.new(image_name)
|
45
274
|
end
|
46
275
|
|
276
|
+
# @return [Fixnum] the Actor width in pixels
|
47
277
|
def width
|
48
278
|
@image.width * @scale
|
49
279
|
end
|
50
280
|
|
281
|
+
# @return [Fixnum] the Actor height in pixels
|
51
282
|
def height
|
52
283
|
@image.height * @scale
|
53
284
|
end
|
54
285
|
|
286
|
+
# @return [Boolean] the value of `@solid`
|
287
|
+
def solid?
|
288
|
+
@solid
|
289
|
+
end
|
290
|
+
|
291
|
+
# @param value [Coordinates] set a new direction to the Actor.
|
292
|
+
# The vector is normalized
|
293
|
+
#
|
294
|
+
# @example Set a new direction
|
295
|
+
# actor = Actor.new("player_walking")
|
296
|
+
# actor.direction = Coordinates.up
|
55
297
|
def direction=(value)
|
56
298
|
@direction = value
|
57
299
|
@direction = @direction.normalize unless @direction.zero?
|
58
300
|
end
|
59
301
|
|
302
|
+
# @!visibility private
|
60
303
|
def draw
|
61
304
|
@image.draw(x: position_in_camera.x, y: position_in_camera.y, scale: @scale)
|
62
305
|
|
63
306
|
draw_debug if Global.debug
|
64
307
|
end
|
65
308
|
|
66
|
-
|
67
|
-
Utils.draw_frame(position_in_camera.x, position_in_camera.y, width, height, 1, Gosu::Color::RED) if solid
|
68
|
-
Global.pixel_fonts["medium"].draw_text("#{@position.x.floor},#{@position.y.floor}", position_in_camera.x, position_in_camera.y - 20, 1)
|
69
|
-
end
|
70
|
-
|
71
|
-
def position_in_camera
|
72
|
-
@position - Global.camera.position
|
73
|
-
end
|
74
|
-
|
75
|
-
# TODO: make this work optimized
|
76
|
-
# def position_top_left
|
77
|
-
# @position - position_delta
|
78
|
-
# end
|
79
|
-
|
80
|
-
# def position_delta
|
81
|
-
# case @alignment
|
82
|
-
# when "top-left"
|
83
|
-
# Coordinates.zero
|
84
|
-
# when "center"
|
85
|
-
# Coordinates.new(width/2, height/2)
|
86
|
-
# else
|
87
|
-
# raise "Actor.alignment value not valid '#{@alignment}'. Valid values: 'top-left, center'"
|
88
|
-
# end
|
89
|
-
# end
|
90
|
-
|
91
|
-
# TODO: I made more of this code while I was with Covid
|
92
|
-
# It looks horrible and it is crap
|
93
|
-
# I'll improve it some day :)
|
309
|
+
# @!visibility private
|
94
310
|
def move
|
95
|
-
mouse_position = Global.mouse_position +
|
311
|
+
mouse_position = Global.mouse_position + Camera.main.position
|
96
312
|
|
97
313
|
if @draggable_on_debug && Global.debug && !@dragging && Gosu.button_down?(Gosu::MS_LEFT) && Utils.collision_at?(self, mouse_position.x, mouse_position.y)
|
98
314
|
@dragging = true
|
@@ -126,28 +342,14 @@ class Actor
|
|
126
342
|
manage_collisions(last_position)
|
127
343
|
end
|
128
344
|
|
129
|
-
# # Jump moving
|
130
|
-
# unless @jump_force.zero?
|
131
|
-
# last_position = @position
|
132
|
-
# add_force_by_jump
|
133
|
-
# apply_forces(max_speed: @jump_speed || @jump_force)
|
134
|
-
|
135
|
-
# # Check collision after jump moving
|
136
|
-
# if @solid && @position != last_position
|
137
|
-
# if manage_collisions(last_position)
|
138
|
-
# @jumping = false
|
139
|
-
# end
|
140
|
-
# end
|
141
|
-
# end
|
142
|
-
|
143
345
|
# Gravity force
|
144
|
-
|
346
|
+
unless @gravity.zero?
|
145
347
|
add_force_by_gravity
|
146
348
|
end
|
147
349
|
|
148
350
|
# Apply forces
|
149
351
|
last_position = @position
|
150
|
-
apply_forces
|
352
|
+
apply_forces
|
151
353
|
|
152
354
|
# Check collision after gravity moving
|
153
355
|
if @solid && @position != last_position
|
@@ -159,145 +361,275 @@ class Actor
|
|
159
361
|
on_after_move_do
|
160
362
|
end
|
161
363
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
364
|
+
# Destroy this Actor is not longer, moved, or rendered, or cause any collision.
|
365
|
+
#
|
366
|
+
# @example Destroy an Actor
|
367
|
+
# actor = Actor.new("image")
|
368
|
+
# actor.destroy
|
369
|
+
def destroy
|
370
|
+
on_destroy_do
|
371
|
+
Global.actors.delete(self)
|
372
|
+
end
|
171
373
|
|
172
|
-
|
173
|
-
|
174
|
-
@velocity.y = 0
|
175
|
-
end
|
374
|
+
# rubocop:disable Metrics/MethodLength
|
375
|
+
# rubocop:disable Metrics/AbcSize
|
176
376
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
377
|
+
# @!visibility private
|
378
|
+
def clone
|
379
|
+
actor = self.class.new(@image_name)
|
380
|
+
actor.image_name = @image_name
|
381
|
+
actor.name = @name
|
382
|
+
actor.position = @position.clone
|
383
|
+
actor.direction = @direction.clone
|
181
384
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
# was_collision
|
385
|
+
actor.speed = @speed
|
386
|
+
actor.scale = @scale
|
387
|
+
actor.solid = @solid
|
388
|
+
actor.layer = @layer
|
389
|
+
actor.gravity = @gravity
|
390
|
+
actor.jump_force = @jump_force
|
391
|
+
actor.collision_with = @collision_with
|
190
392
|
|
191
|
-
|
192
|
-
|
393
|
+
actor.on_after_move_callback = @on_after_move_callback
|
394
|
+
actor.on_collision_callback = @on_collision_callback
|
395
|
+
actor.on_destroy_callback = @on_destroy_callback
|
396
|
+
actor.on_jumping_callback = @on_jumping_callback
|
397
|
+
actor.on_floor_callback = @on_floor_callback
|
193
398
|
|
194
|
-
|
195
|
-
|
399
|
+
actor.on_cursor_down_callback = @on_cursor_down_callback
|
400
|
+
actor.on_cursor_up_callback = @on_cursor_up_callback
|
401
|
+
actor.on_cursor_left_callback = @on_cursor_left_callback
|
402
|
+
actor.on_cursor_right_callback = @on_cursor_right_callback
|
403
|
+
actor.on_space_bar_callback = @on_space_bar_callback
|
404
|
+
actor.on_mouse_button_left_callback = @on_mouse_button_left_callback
|
196
405
|
|
197
|
-
|
198
|
-
end
|
406
|
+
actor.on_click_callback = @on_click_callback
|
199
407
|
|
200
|
-
|
201
|
-
@solid
|
408
|
+
actor
|
202
409
|
end
|
410
|
+
# rubocop:enable Metrics/AbcSize
|
411
|
+
# rubocop:enable Metrics/MethodLength
|
203
412
|
|
204
413
|
# Set callbacks
|
414
|
+
|
415
|
+
# The block to be executed after each frame
|
416
|
+
#
|
417
|
+
# @example Limit Actor movement horizontally
|
418
|
+
# actor = Actor.new("image")
|
419
|
+
# actor.on_after_move do
|
420
|
+
# if actor.position.x > 100
|
421
|
+
# actor.position.x = 100
|
422
|
+
# end
|
423
|
+
# end
|
205
424
|
def on_after_move(&block)
|
206
425
|
@on_after_move_callback = block
|
207
426
|
end
|
208
427
|
|
428
|
+
# The block to be executed when Actor collides with another Actor
|
429
|
+
#
|
430
|
+
# @example Collision detected with _"bullet"_
|
431
|
+
# actor = Actor.new("image")
|
432
|
+
# actor.on_collision do |other|
|
433
|
+
# if other.name == "bullet"
|
434
|
+
# actor.destroy
|
435
|
+
# end
|
436
|
+
# end
|
209
437
|
def on_collision(&block)
|
210
438
|
@on_collision_callback = block
|
211
439
|
end
|
212
440
|
|
441
|
+
# The block to be executed before the Actor is destroyed
|
442
|
+
#
|
443
|
+
# @example Executes when destroyed
|
444
|
+
# actor = Actor.new("image")
|
445
|
+
# actor.on_destroy do
|
446
|
+
# Sound.play("explosion")
|
447
|
+
# end
|
213
448
|
def on_destroy(&block)
|
214
449
|
@on_destroy_callback = block
|
215
450
|
end
|
216
451
|
|
452
|
+
# The block to be executed when the Actor starts jumping
|
453
|
+
#
|
454
|
+
# @example Change image when jumping
|
455
|
+
# actor = Actor.new("walk")
|
456
|
+
# actor.on_jumping do
|
457
|
+
# actor.image_name = "jump"
|
458
|
+
# end
|
217
459
|
def on_jumping(&block)
|
218
460
|
@on_jumping_callback = block
|
219
461
|
end
|
220
462
|
|
463
|
+
# The block to be executed when the Actor touches floor
|
464
|
+
#
|
465
|
+
# @example Change image when jumping
|
466
|
+
# actor = Actor.new("walk")
|
467
|
+
# actor.on_floor do
|
468
|
+
# actor.image_name = "land"
|
469
|
+
# Clock.new { actor.image_name = "walk" }.run_on(seconds: 0.8)
|
470
|
+
# end
|
221
471
|
def on_floor(&block)
|
222
472
|
@on_floor_callback = block
|
223
473
|
end
|
224
474
|
|
225
|
-
|
226
475
|
# Execute callbacks
|
476
|
+
|
477
|
+
# This method is triggered after each frame
|
478
|
+
#
|
479
|
+
# @example Limit Actor movement horizontally
|
480
|
+
# class Player < Actor
|
481
|
+
# def on_after_move_do
|
482
|
+
# if @position.x > 100
|
483
|
+
# @position.x = 100
|
484
|
+
# end
|
485
|
+
# end
|
486
|
+
# end
|
227
487
|
def on_after_move_do
|
228
488
|
instance_exec(&@on_after_move_callback) unless @on_after_move_callback.nil?
|
229
489
|
end
|
230
490
|
|
491
|
+
# This method is triggered when Actor collides with another Actor
|
492
|
+
#
|
493
|
+
# @example Limit Actor movement horizontally
|
494
|
+
# class Player < Actor
|
495
|
+
# def on_collision_do(other)
|
496
|
+
# if other.name == "bullet"
|
497
|
+
# destroy
|
498
|
+
# end
|
499
|
+
# end
|
500
|
+
# end
|
231
501
|
def on_collision_do(other)
|
232
502
|
instance_exec(other, &@on_collision_callback) unless @on_collision_callback.nil?
|
233
503
|
end
|
234
504
|
|
505
|
+
# This method is triggered before the Actor is destroyed
|
506
|
+
#
|
507
|
+
# @example Executes when destroyed
|
508
|
+
# class Player < Actor
|
509
|
+
# def on_destroy_do
|
510
|
+
# Sound.play("explosion")
|
511
|
+
# end
|
512
|
+
# end
|
235
513
|
def on_destroy_do
|
236
514
|
instance_exec(&@on_destroy_callback) unless @on_destroy_callback.nil?
|
237
515
|
end
|
238
516
|
|
517
|
+
# This method is triggered when the Actor starts jumping
|
518
|
+
#
|
519
|
+
# @example Change image when jumping
|
520
|
+
# class Player < Actor
|
521
|
+
# def on_jumping_do
|
522
|
+
# self.image_name = "jump"
|
523
|
+
# end
|
524
|
+
# end
|
239
525
|
def on_jumping_do
|
240
526
|
instance_exec(&@on_jumping_callback) unless @on_jumping_callback.nil?
|
241
527
|
end
|
242
528
|
|
529
|
+
# This method is triggered when the Actor touches floor
|
530
|
+
#
|
531
|
+
# @example Change image when jumping
|
532
|
+
# class Player < Actor
|
533
|
+
# def.on_floor_do
|
534
|
+
# self.image_name = "land"
|
535
|
+
# Clock.new { self.image_name = "walk" }.run_on(seconds: 0.8)
|
536
|
+
# end
|
537
|
+
# end
|
243
538
|
def on_floor_do
|
244
539
|
instance_exec(&@on_floor_callback) unless @on_floor_callback.nil?
|
245
540
|
end
|
246
541
|
|
247
542
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
543
|
+
protected
|
544
|
+
|
545
|
+
attr_accessor :image_name
|
546
|
+
|
547
|
+
# TODO: make this work optimized
|
548
|
+
# def position_top_left
|
549
|
+
# @position - position_delta
|
550
|
+
# end
|
551
|
+
|
552
|
+
# def position_delta
|
553
|
+
# case @alignment
|
554
|
+
# when "top-left"
|
555
|
+
# Coordinates.zero
|
556
|
+
# when "center"
|
557
|
+
# Coordinates.new(width/2, height/2)
|
558
|
+
# else
|
559
|
+
# raise "Actor.alignment value not valid '#{@alignment}'. Valid values: 'top-left, center'"
|
560
|
+
# end
|
561
|
+
# end
|
562
|
+
|
563
|
+
def draw_debug
|
564
|
+
Shape.rectangle(
|
565
|
+
position: position_in_camera,
|
566
|
+
width: width,
|
567
|
+
height: height,
|
568
|
+
color: Color.palette.transparent,
|
569
|
+
stroke_color: Color.palette.red,
|
570
|
+
stroke: 1
|
571
|
+
) if solid
|
572
|
+
|
573
|
+
Global.pixel_fonts["medium"].draw_text("#{@position.x.floor},#{@position.y.floor}", position_in_camera.x, position_in_camera.y - 20, 1)
|
257
574
|
end
|
258
575
|
|
259
|
-
def
|
260
|
-
|
261
|
-
Global.actors.delete(self)
|
576
|
+
def position_in_camera
|
577
|
+
@position - Camera.main.position
|
262
578
|
end
|
263
579
|
|
264
|
-
def
|
265
|
-
|
266
|
-
actor.image_name = @image_name
|
267
|
-
actor.name = @name
|
268
|
-
actor.position = @position.clone
|
269
|
-
actor.direction = @direction.clone
|
580
|
+
def manage_collisions(last_position)
|
581
|
+
@velocity ||= Coordinates.zero # In case it is not initialized yet
|
270
582
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
actor.solid = @solid
|
275
|
-
actor.layer = @layer
|
276
|
-
actor.gravity = @gravity
|
277
|
-
actor.jump_force = @jump_force
|
278
|
-
actor.collision_with = @collision_with
|
583
|
+
collisions.each do |other|
|
584
|
+
on_collision_do(other)
|
585
|
+
other.on_collision_do(self)
|
279
586
|
|
280
|
-
|
281
|
-
|
282
|
-
actor.on_destroy_callback = @on_destroy_callback
|
283
|
-
actor.on_jumping_callback = @on_jumping_callback
|
284
|
-
actor.on_floor_callback = @on_floor_callback
|
587
|
+
if other.position.y >= (last_position.y + height)
|
588
|
+
on_floor_do unless @on_floor
|
285
589
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
actor.on_space_bar_callback = @on_space_bar_callback
|
291
|
-
actor.on_mouse_button_left_callback = @on_mouse_button_left_callback
|
590
|
+
@on_floor = true
|
591
|
+
@jumping = false
|
592
|
+
@velocity.y = 0
|
593
|
+
end
|
292
594
|
|
293
|
-
|
595
|
+
if other.position.y + other.height <= last_position.y
|
596
|
+
@velocity.y = 0
|
597
|
+
end
|
598
|
+
end
|
294
599
|
|
295
|
-
|
600
|
+
# # Reset position pixel by pixel
|
601
|
+
# was_collision = false
|
602
|
+
# while collisions.any? && @position != last_position
|
603
|
+
# was_collision = true
|
604
|
+
# last_position_direction = last_position - @position
|
605
|
+
# @position += last_position_direction.normalize
|
606
|
+
# end
|
607
|
+
# was_collision
|
608
|
+
|
609
|
+
if collisions.any? # we don't cache collisions because position may be changed on collision callback
|
610
|
+
@position = last_position
|
611
|
+
|
612
|
+
return true
|
613
|
+
end
|
614
|
+
|
615
|
+
false
|
616
|
+
end
|
617
|
+
|
618
|
+
# rubocop:disable Style/Next
|
619
|
+
def collisions
|
620
|
+
Global.actors.reject { |e| e == self }.select(&:solid?).select do |other|
|
621
|
+
if(
|
622
|
+
(@collision_with == "all" || @collision_with.include?(other.name)) &&
|
623
|
+
(other.collision_with == "all" || other.collision_with.include?(name))
|
624
|
+
)
|
625
|
+
Utils.collision? self, other
|
626
|
+
end
|
627
|
+
end
|
296
628
|
end
|
629
|
+
# rubocop:enable Style/Next
|
297
630
|
|
298
631
|
protected
|
299
632
|
|
300
633
|
attr_accessor :on_after_move_callback, :on_collision_callback, :on_destroy_callback, :on_jumping_callback, :on_floor_callback
|
301
|
-
attr_accessor :on_cursor_down_callback, :on_cursor_up_callback, :on_cursor_left_callback, :on_cursor_right_callback, :on_space_bar_callback, :on_mouse_button_left_callback
|
302
634
|
attr_accessor :on_click_callback
|
303
635
|
end
|