minigl 1.3.10 → 2.0.0

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.
data/lib/minigl/map.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  require_relative 'global'
2
2
 
3
- module AGL
3
+ module MiniGL
4
4
  # This class provides easy control of a tile map, i.e., a map consisting of
5
5
  # a grid of equally sized tiles. It also provides viewport control, through
6
6
  # its camera property and methods.
7
7
  class Map
8
- Sqrt2Div2 = Math.sqrt(2) / 2 # :nodoc:
9
- MinusPiDiv4 = -Math::PI / 4 # :nodoc:
8
+ SQRT_2_DIV_2 = Math.sqrt(2) / 2 # :nodoc:
9
+ MINUS_PI_DIV_4 = -Math::PI / 4 # :nodoc:
10
10
 
11
11
  # A Vector where x is the tile width and y is the tile height.
12
12
  attr_reader :tile_size
@@ -34,7 +34,7 @@ module AGL
34
34
  # outside the map to appear in the screen, the camera would
35
35
  # move to the nearest position where only the map shows up
36
36
  # in the screen).
37
- def initialize t_w, t_h, t_x_count, t_y_count, scr_w = 800, scr_h = 600, isometric = false, limit_cam = true
37
+ def initialize(t_w, t_h, t_x_count, t_y_count, scr_w = 800, scr_h = 600, isometric = false, limit_cam = true)
38
38
  @tile_size = Vector.new t_w, t_h
39
39
  @size = Vector.new t_x_count, t_y_count
40
40
  @cam = Rectangle.new 0, 0, scr_w, scr_h
@@ -71,7 +71,7 @@ module AGL
71
71
  # the interval <code>0..t_x_count</code>.
72
72
  # [map_y] The index of the tile in the vertical direction. It must be in
73
73
  # the interval <code>0..t_y_count</code>.
74
- def get_screen_pos map_x, map_y
74
+ def get_screen_pos(map_x, map_y)
75
75
  return Vector.new(map_x * @tile_size.x - @cam.x, map_y * @tile_size.y - @cam.y) unless @isometric
76
76
  Vector.new ((map_x - map_y - 1) * @tile_size.x * 0.5) - @cam.x + @x_offset,
77
77
  ((map_x + map_y) * @tile_size.y * 0.5) - @cam.y
@@ -84,7 +84,7 @@ module AGL
84
84
  # Parameters:
85
85
  # [scr_x] The x-coordinate in the screen.
86
86
  # [scr_y] The y-coordinate in the screen.
87
- def get_map_pos scr_x, scr_y
87
+ def get_map_pos(scr_x, scr_y)
88
88
  return Vector.new((scr_x + @cam.x) / @tile_size.x, (scr_y + @cam.y) / @tile_size.y) unless @isometric
89
89
 
90
90
  # Obtém a posição transformada para as coordenadas isométricas
@@ -99,7 +99,7 @@ module AGL
99
99
  # Parameters:
100
100
  # [v] A Vector representing the tile, with x as the horizontal index and
101
101
  # y as the vertical index.
102
- def is_in_map v
102
+ def is_in_map(v)
103
103
  v.x >= 0 && v.y >= 0 && v.x < @size.x && v.y < @size.y
104
104
  end
105
105
 
@@ -109,7 +109,7 @@ module AGL
109
109
  # Parameters:
110
110
  # [cam_x] The x-coordinate inside the map, in pixels (not a tile index).
111
111
  # [cam_y] The y-coordinate inside the map, in pixels (not a tile index).
112
- def set_camera cam_x, cam_y
112
+ def set_camera(cam_x, cam_y)
113
113
  @cam.x = cam_x
114
114
  @cam.y = cam_y
115
115
  set_bounds
@@ -122,7 +122,7 @@ module AGL
122
122
  # cause the camera to move to the left.
123
123
  # [y] The amount of pixels to move vertically. Negative values will cause
124
124
  # the camera to move up.
125
- def move_camera x, y
125
+ def move_camera(x, y)
126
126
  @cam.x += x
127
127
  @cam.y += y
128
128
  set_bounds
@@ -158,26 +158,26 @@ module AGL
158
158
  v4 = get_isometric_position(0, @cam.h - 1)
159
159
  if v1.x < -@max_offset
160
160
  offset = -(v1.x + @max_offset)
161
- @cam.x += offset * Sqrt2Div2
162
- @cam.y += offset * Sqrt2Div2 / @tile_ratio
161
+ @cam.x += offset * SQRT_2_DIV_2
162
+ @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio
163
163
  v1.x = -@max_offset
164
164
  end
165
165
  if v2.y < -@max_offset
166
166
  offset = -(v2.y + @max_offset)
167
- @cam.x -= offset * Sqrt2Div2
168
- @cam.y += offset * Sqrt2Div2 / @tile_ratio
167
+ @cam.x -= offset * SQRT_2_DIV_2
168
+ @cam.y += offset * SQRT_2_DIV_2 / @tile_ratio
169
169
  v2.y = -@max_offset
170
170
  end
171
171
  if v3.x > @iso_abs_size.x + @max_offset
172
172
  offset = v3.x - @iso_abs_size.x - @max_offset
173
- @cam.x -= offset * Sqrt2Div2
174
- @cam.y -= offset * Sqrt2Div2 / @tile_ratio
173
+ @cam.x -= offset * SQRT_2_DIV_2
174
+ @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio
175
175
  v3.x = @iso_abs_size.x + @max_offset
176
176
  end
177
177
  if v4.y > @iso_abs_size.y + @max_offset
178
178
  offset = v4.y - @iso_abs_size.y - @max_offset
179
- @cam.x += offset * Sqrt2Div2
180
- @cam.y -= offset * Sqrt2Div2 / @tile_ratio
179
+ @cam.x += offset * SQRT_2_DIV_2
180
+ @cam.y -= offset * SQRT_2_DIV_2 / @tile_ratio
181
181
  v4.y = @iso_abs_size.y + @max_offset
182
182
  end
183
183
  else
@@ -218,7 +218,7 @@ module AGL
218
218
  def initialize_isometric
219
219
  @x_offset = (@size.y * 0.5 * @tile_size.x).round
220
220
  @tile_ratio = @tile_size.x.to_f / @tile_size.y
221
- square_size = @tile_size.x * Sqrt2Div2
221
+ square_size = @tile_size.x * SQRT_2_DIV_2
222
222
  @inverse_square_size = 1 / square_size
223
223
  @iso_abs_size = Vector.new(square_size * @size.x, square_size * @size.y)
224
224
  a = (@size.x + @size.y) * 0.5 * @tile_size.x
@@ -227,11 +227,11 @@ module AGL
227
227
  if @limit_cam
228
228
  actual_cam_h = @cam.h * @tile_ratio
229
229
  @max_offset = actual_cam_h < @cam.w ? actual_cam_h : @cam.w
230
- @max_offset *= Sqrt2Div2
230
+ @max_offset *= SQRT_2_DIV_2
231
231
  end
232
232
  end
233
233
 
234
- def get_isometric_position scr_x, scr_y
234
+ def get_isometric_position(scr_x, scr_y)
235
235
  # Escreve a posição em relação a origem (no centro do mapa)
236
236
  center = get_center
237
237
  position = Vector.new scr_x + @cam.x - center.x, scr_y + @cam.y - center.y
@@ -243,7 +243,7 @@ module AGL
243
243
  center.y *= @tile_ratio
244
244
 
245
245
  # Rotaciona o vetor posição -45°
246
- position.rotate! MinusPiDiv4
246
+ position.rotate! MINUS_PI_DIV_4
247
247
 
248
248
  # Retorna a referência da posição para o canto da tela
249
249
  position += center
@@ -1,6 +1,6 @@
1
1
  require_relative 'global'
2
2
 
3
- module AGL
3
+ module MiniGL
4
4
  # Represents an object with a rectangular bounding box and the +passable+
5
5
  # property. It is the simplest structure that can be passed as an element of
6
6
  # the +obst+ array parameter of the +move+ method.
@@ -31,7 +31,7 @@ module AGL
31
31
  # [passable] Whether a moving object can pass through this block when
32
32
  # coming from below. This is a common feature of platforms in platform
33
33
  # games.
34
- def initialize x, y, w, h, passable
34
+ def initialize(x, y, w, h, passable)
35
35
  @x = x; @y = y; @w = w; @h = h
36
36
  @passable = passable
37
37
  end
@@ -66,6 +66,8 @@ module AGL
66
66
  # from left to right when +false+).
67
67
  attr_reader :left
68
68
 
69
+ attr_reader :ratio # :nodoc:
70
+
69
71
  # Creates a new ramp.
70
72
  #
71
73
  # Parameters:
@@ -82,12 +84,14 @@ module AGL
82
84
  # highest).
83
85
  # [left] Whether the height of the ramp increases from left to right. Use
84
86
  # +false+ for a ramp that goes down from left to right.
85
- def initialize x, y, w, h, left
87
+ def initialize(x, y, w, h, left)
86
88
  @x = x
87
89
  @y = y
88
90
  @w = w
89
91
  @h = h
90
92
  @left = left
93
+ @ratio = @h.to_f / @w
94
+ @factor = Math.cos(@w / Math.sqrt(@w**2 + @h**2))
91
95
  end
92
96
 
93
97
  # Checks if an object is in contact with this ramp (standing over it).
@@ -95,8 +99,8 @@ module AGL
95
99
  # Parameters:
96
100
  # [obj] The object to check contact with. It must have the +x+, +y+, +w+
97
101
  # and +h+ accessible attributes determining its bounding box.
98
- def contact? obj
99
- obj.x.round(6) == get_x(obj).round(6) && obj.y.round(6) == get_y(obj).round(6)
102
+ def contact?(obj)
103
+ obj.x + obj.w > @x && obj.x < @x + @w && obj.x.round(6) == get_x(obj).round(6) && obj.y.round(6) == get_y(obj).round(6)
100
104
  end
101
105
 
102
106
  # Checks if an object is intersecting this ramp (inside the corresponding
@@ -105,40 +109,41 @@ module AGL
105
109
  # Parameters:
106
110
  # [obj] The object to check intersection with. It must have the +x+, +y+,
107
111
  # +w+ and +h+ accessible attributes determining its bounding box.
108
- def intersects obj
112
+ def intersect?(obj)
109
113
  obj.x + obj.w > @x && obj.x < @x + @w && obj.y > get_y(obj) && obj.y <= @y + @h - obj.h
110
114
  end
111
115
 
112
116
  # :nodoc:
113
- def can_collide? obj
114
- @can_collide = (obj.speed.y >= 0 and not intersects(obj))
117
+ def check_can_collide(m)
118
+ y = get_y(m) + m.h
119
+ @can_collide = m.x + m.w > @x && @x + @w > m.x && m.y < y && m.y + m.h > y
115
120
  end
116
121
 
117
- def check_intersection obj
118
- if @can_collide and intersects obj
122
+ def check_intersection(obj)
123
+ if @can_collide and intersect? obj
124
+ counter = @left && obj.prev_speed.x > 0 || !@left && obj.prev_speed.x < 0
125
+ if obj.prev_speed.y > 0 && counter
126
+ dx = get_x(obj) - obj.x
127
+ s = (obj.prev_speed.y.to_f / obj.prev_speed.x).abs
128
+ dx /= s + @ratio
129
+ obj.x += dx
130
+ end
119
131
  obj.y = get_y obj
132
+ if counter
133
+ obj.speed.x *= @factor
134
+ end
120
135
  obj.speed.y = 0
121
- # a = @w / @h
122
- # x = get_x(obj)
123
- # y = get_y(obj)
124
- # w = obj.x - x
125
- # h = obj.y - y
126
- # dx = w * h / (w * a + h)
127
- # dy = dx * a
128
- #
129
- # obj.x -= dx
130
- # obj.y -= dy
131
- # obj.speed.x *= (@w / (@w + @h))
132
- # obj.speed.y = 0
133
136
  end
134
137
  end
135
138
 
136
- def get_x obj
139
+ def get_x(obj)
140
+ return obj.x if @left && obj.x + obj.w > @x + @w
137
141
  return @x + (1.0 * (@y + @h - obj.y - obj.h) * @w / @h) - obj.w if @left
142
+ return obj.x if obj.x < @x
138
143
  @x + (1.0 * (obj.y + obj.h - @y) * @w / @h)
139
144
  end
140
145
 
141
- def get_y obj
146
+ def get_y(obj)
142
147
  return @y - obj.h if @left && obj.x + obj.w > @x + @w
143
148
  return @y + (1.0 * (@x + @w - obj.x - obj.w) * @h / @w) - obj.h if @left
144
149
  return @y - obj.h if obj.x < @x
@@ -161,6 +166,10 @@ module AGL
161
166
  # y: vertical component).
162
167
  attr_reader :speed
163
168
 
169
+ # A Vector with the speed limits for the object (x: horizontal component,
170
+ # y: vertical component).
171
+ attr_reader :max_speed
172
+
164
173
  # Width of the bounding box.
165
174
  attr_reader :w
166
175
 
@@ -197,6 +206,8 @@ module AGL
197
206
  # be applied in the next time +move+ is called.
198
207
  attr_accessor :stored_forces
199
208
 
209
+ attr_reader :prev_speed # :nodoc:
210
+
200
211
  # Returns the bounding box as a Rectangle.
201
212
  def bounds
202
213
  Rectangle.new @x, @y, @w, @h
@@ -213,24 +224,25 @@ module AGL
213
224
  # objects that <code>include Movement</code>.
214
225
  # [ramps] An array of ramps to be considered in the collision checking.
215
226
  # Ramps must be instances of Ramp (or derived classes).
216
- def move forces, obst, ramps
217
- forces.x += Game.gravity.x; forces.y += Game.gravity.y
227
+ def move(forces, obst, ramps)
228
+ forces.x += G.gravity.x; forces.y += G.gravity.y
218
229
  forces.x += @stored_forces.x; forces.y += @stored_forces.y
219
230
  @stored_forces.x = @stored_forces.y = 0
220
231
 
221
- # check_contact obst, ramps
222
232
  forces.x = 0 if (forces.x < 0 and @left) or (forces.x > 0 and @right)
223
233
  forces.y = 0 if (forces.y < 0 and @top) or (forces.y > 0 and @bottom)
224
234
 
235
+ if @bottom.is_a? Ramp and @bottom.ratio >= G.ramp_slip_threshold
236
+ forces.x = (@bottom.left ? -1 : 1) * 0.1
237
+ end
238
+
225
239
  @speed.x += forces.x / @mass; @speed.y += forces.y / @mass
226
- @speed.x = 0 if @speed.x.abs < @min_speed.x
227
- @speed.y = 0 if @speed.y.abs < @min_speed.y
240
+ @speed.x = 0 if @speed.x.abs < G.min_speed.x
241
+ @speed.y = 0 if @speed.y.abs < G.min_speed.y
228
242
  @speed.x = (@speed.x <=> 0) * @max_speed.x if @speed.x.abs > @max_speed.x
229
243
  @speed.y = (@speed.y <=> 0) * @max_speed.y if @speed.y.abs > @max_speed.y
230
244
 
231
- ramps.each do |r|
232
- r.can_collide? self
233
- end
245
+ @prev_speed = @speed.clone
234
246
 
235
247
  x = @speed.x < 0 ? @x + @speed.x : @x
236
248
  y = @speed.y < 0 ? @y + @speed.y : @y
@@ -239,7 +251,10 @@ module AGL
239
251
  move_bounds = Rectangle.new x, y, w, h
240
252
  coll_list = []
241
253
  obst.each do |o|
242
- coll_list << o if move_bounds.intersects o.bounds
254
+ coll_list << o if move_bounds.intersect? o.bounds
255
+ end
256
+ ramps.each do |r|
257
+ r.check_can_collide move_bounds
243
258
  end
244
259
 
245
260
  if coll_list.length > 0
@@ -251,8 +266,12 @@ module AGL
251
266
  elsif dn; y_lim = find_down_limit coll_list
252
267
  elsif up; y_lim = find_up_limit coll_list
253
268
  end
254
- if rt && @x + @w + @speed.x > x_lim; @x = x_lim - @w; @speed.x = 0
255
- elsif lf && @x + @speed.x < x_lim; @x = x_lim; @speed.x = 0
269
+ if rt && @x + @w + @speed.x > x_lim
270
+ @x = x_lim - @w
271
+ @speed.x = 0
272
+ elsif lf && @x + @speed.x < x_lim
273
+ @x = x_lim
274
+ @speed.x = 0
256
275
  elsif dn && @y + @h + @speed.y > y_lim; @y = y_lim - @h; @speed.y = 0
257
276
  elsif up && @y + @speed.y < y_lim; @y = y_lim; @speed.y = 0
258
277
  end
@@ -306,6 +325,11 @@ module AGL
306
325
  @x += @speed.x
307
326
  @y += @speed.y
308
327
 
328
+ # Keeping contact with ramp
329
+ # if @speed.y == 0 and @speed.x.abs <= G.ramp_contact_threshold and @bottom.is_a? Ramp
330
+ # @y = @bottom.get_y(self)
331
+ # puts 'aqui'
332
+ # end
309
333
  ramps.each do |r|
310
334
  r.check_intersection self
311
335
  end
@@ -323,9 +347,15 @@ module AGL
323
347
  # checking, and carried along when colliding from above.
324
348
  # Obstacles must be instances of Block (or derived classes),
325
349
  # or objects that <code>include Movement</code>.
326
- def move_carrying aim, speed, obstacles
350
+ def move_carrying(aim, speed, obstacles)
327
351
  x_d = aim.x - @x; y_d = aim.y - @y
328
352
  distance = Math.sqrt(x_d**2 + y_d**2)
353
+
354
+ if distance == 0
355
+ @speed.x = @speed.y = 0
356
+ return
357
+ end
358
+
329
359
  @speed.x = 1.0 * x_d * speed / distance
330
360
  @speed.y = 1.0 * y_d * speed / distance
331
361
 
@@ -363,9 +393,15 @@ module AGL
363
393
  # [aim] A Vector specifying where the object will move to.
364
394
  # [speed] The constant speed at which the object will move. This must be
365
395
  # provided as a scalar, not a vector.
366
- def move_free aim, speed
396
+ def move_free(aim, speed)
367
397
  x_d = aim.x - @x; y_d = aim.y - @y
368
398
  distance = Math.sqrt(x_d**2 + y_d**2)
399
+
400
+ if distance == 0
401
+ @speed.x = @speed.y = 0
402
+ return
403
+ end
404
+
369
405
  @speed.x = 1.0 * x_d * speed / distance
370
406
  @speed.y = 1.0 * y_d * speed / distance
371
407
 
@@ -385,39 +421,37 @@ module AGL
385
421
  end
386
422
 
387
423
  # Causes the object to move in cycles across multiple given points (the
388
- # method must be called repeatedly, and it returns the value that must be
389
- # provided to +cur_point+ after the first call). If obstacles are
390
- # provided, it will behave as an elevator (as in +move_carrying+).
424
+ # first point in the array is the first point the object will move towards,
425
+ # so it doesn't need to be equal to the current/initial position). If
426
+ # obstacles are provided, it will behave as an elevator (as in
427
+ # +move_carrying+).
391
428
  #
392
429
  # Parameters:
393
430
  # [points] An array of Vectors representing the path that the object will
394
431
  # perform.
395
- # [cur_point] The index of the point in the path that the object is
396
- # currently moving to. In the first call, it is a good idea to
397
- # provide 0, while in the subsequent calls, you must provide
398
- # the return value of this method.
399
432
  # [speed] The constant speed at which the object will move. This must be
400
433
  # provided as a scalar, not a vector.
401
434
  # [obstacles] An array of obstacles to be considered in the collision
402
435
  # checking, and carried along when colliding from above.
403
436
  # Obstacles must be instances of Block (or derived classes),
404
437
  # or objects that <code>include Movement</code>.
405
- def cycle points, cur_point, speed, obstacles = nil
438
+ def cycle(points, speed, obstacles = nil)
439
+ @cur_point = 0 if @cur_point.nil?
406
440
  if obstacles
407
- move_carrying points[cur_point], speed, obstacles
441
+ move_carrying points[@cur_point], speed, obstacles
408
442
  else
409
- move_free points[cur_point], speed
443
+ move_free points[@cur_point], speed
410
444
  end
411
445
  if @speed.x == 0 and @speed.y == 0
412
- if cur_point == points.length - 1; cur_point = 0
413
- else; cur_point += 1; end
446
+ if @cur_point == points.length - 1; @cur_point = 0
447
+ else; @cur_point += 1; end
414
448
  end
415
- cur_point
416
449
  end
417
450
 
418
451
  private
419
452
 
420
- def check_contact obst, ramps
453
+ def check_contact(obst, ramps)
454
+ prev_bottom = @bottom
421
455
  @top = @bottom = @left = @right = nil
422
456
  obst.each do |o|
423
457
  x2 = @x + @w; y2 = @y + @h; x2o = o.x + o.w; y2o = o.y + o.h
@@ -433,10 +467,21 @@ module AGL
433
467
  break
434
468
  end
435
469
  end
470
+ if @bottom.nil?
471
+ ramps.each do |r|
472
+ if r == prev_bottom && @x + @w > r.x && r.x + r.w > @x &&
473
+ @prev_speed.x.abs <= G.ramp_contact_threshold &&
474
+ @prev_speed.y >= 0
475
+ @y = r.get_y self
476
+ @bottom = r
477
+ break
478
+ end
479
+ end
480
+ end
436
481
  end
437
482
  end
438
483
 
439
- def find_right_limit coll_list
484
+ def find_right_limit(coll_list)
440
485
  limit = @x + @w + @speed.x
441
486
  coll_list.each do |c|
442
487
  limit = c.x if !c.passable && c.x < limit
@@ -444,7 +489,7 @@ module AGL
444
489
  limit
445
490
  end
446
491
 
447
- def find_left_limit coll_list
492
+ def find_left_limit(coll_list)
448
493
  limit = @x + @speed.x
449
494
  coll_list.each do |c|
450
495
  limit = c.x + c.w if !c.passable && c.x + c.w > limit
@@ -452,7 +497,7 @@ module AGL
452
497
  limit
453
498
  end
454
499
 
455
- def find_down_limit coll_list
500
+ def find_down_limit(coll_list)
456
501
  limit = @y + @h + @speed.y
457
502
  coll_list.each do |c|
458
503
  limit = c.y if c.y < limit && c.y >= @y + @h
@@ -460,7 +505,7 @@ module AGL
460
505
  limit
461
506
  end
462
507
 
463
- def find_up_limit coll_list
508
+ def find_up_limit(coll_list)
464
509
  limit = @y + @speed.y
465
510
  coll_list.each do |c|
466
511
  limit = c.y + c.h if !c.passable && c.y + c.h > limit