Module: MiniGL::Movement

Included in:
GameObject
Defined in:
lib/minigl/movement.rb

Overview

This module provides objects with physical properties and methods for moving. It allows moving with or without collision checking (based on rectangular bounding boxes), including a method to behave as an elevator, affecting other objects’ positions as it moves.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#bottomObject (readonly)

The object that is making contact with this from below. If there’s no contact, returns nil.



186
187
188
# File 'lib/minigl/movement.rb', line 186

def bottom
  @bottom
end

#hObject (readonly)

Height of the bounding box.



178
179
180
# File 'lib/minigl/movement.rb', line 178

def h
  @h
end

#leftObject (readonly)

The object that is making contact with this from the left. If there’s no contact, returns nil.



190
191
192
# File 'lib/minigl/movement.rb', line 190

def left
  @left
end

#massObject (readonly)

The mass of the object, in arbitrary units. The default value for GameObject instances, for example, is 1. The larger the mass (i.e., the heavier the object), the more intense the forces applied to the object have to be in order to move it.



164
165
166
# File 'lib/minigl/movement.rb', line 164

def mass
  @mass
end

#max_speedObject (readonly)

A Vector with the speed limits for the object (x: horizontal component, y: vertical component).



172
173
174
# File 'lib/minigl/movement.rb', line 172

def max_speed
  @max_speed
end

#passableObject

Whether a moving object can pass through this block when coming from below. This is a common feature of platforms in platform games.



204
205
206
# File 'lib/minigl/movement.rb', line 204

def passable
  @passable
end

#prev_speedObject (readonly)

A Vector containing the speed of the object in the previous frame.



211
212
213
# File 'lib/minigl/movement.rb', line 211

def prev_speed
  @prev_speed
end

#rightObject (readonly)

The object that is making contact with this from the right. If there’s no contact, returns nil.



194
195
196
# File 'lib/minigl/movement.rb', line 194

def right
  @right
end

#speedObject (readonly)

A Vector with the current speed of the object (x: horizontal component, y: vertical component).



168
169
170
# File 'lib/minigl/movement.rb', line 168

def speed
  @speed
end

#stored_forcesObject

A Vector with the horizontal and vertical components of a force that be applied in the next time move is called.



208
209
210
# File 'lib/minigl/movement.rb', line 208

def stored_forces
  @stored_forces
end

#topObject (readonly)

The object that is making contact with this from above. If there’s no contact, returns nil.



182
183
184
# File 'lib/minigl/movement.rb', line 182

def top
  @top
end

#wObject (readonly)

Width of the bounding box.



175
176
177
# File 'lib/minigl/movement.rb', line 175

def w
  @w
end

#xObject

The x-coordinate of the top left corner of the bounding box.



197
198
199
# File 'lib/minigl/movement.rb', line 197

def x
  @x
end

#yObject

The y-coordinate of the top left corner of the bounding box.



200
201
202
# File 'lib/minigl/movement.rb', line 200

def y
  @y
end

Instance Method Details

#boundsObject

Returns the bounding box as a Rectangle.



214
215
216
# File 'lib/minigl/movement.rb', line 214

def bounds
  Rectangle.new @x, @y, @w, @h
end

#cycle(points, speed, obstacles = nil, obst_obstacles = nil, obst_ramps = nil, stop_time = 0) ⇒ Object

Causes the object to move in cycles across multiple given points (the first point in the array is the first point the object will move towards, so it doesn’t need to be equal to the current/initial position). If obstacles are provided, it will behave as an elevator (as in move_carrying).

Parameters:

points

An array of Vectors representing the path that the object will perform.

speed

The constant speed at which the object will move. This must be provided as a scalar, not a vector.

obstacles

An array of obstacles to be considered in the collision checking, and carried along when colliding from above. Obstacles must be instances of Block (or derived classes), or objects that include Movement.

obst_obstacles

Obstacles that should be considered when moving objects from the obstacles array, i.e., these obstacles won’t interfere in the elevator’s movement, but in the movement of the objects being carried.

obst_ramps

Ramps to consider when moving objects from the obstacles array, as described for obst_obstacles.

stop_time

Optional stop time (in frames) when the object reaches each of the points.



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/minigl/movement.rb', line 512

def cycle(points, speed, obstacles = nil, obst_obstacles = nil, obst_ramps = nil, stop_time = 0)
  unless @cycle_setup
    @cur_point = 0 if @cur_point.nil?
    if obstacles
      obst_obstacles = [] if obst_obstacles.nil?
      obst_ramps = [] if obst_ramps.nil?
      move_carrying points[@cur_point], speed, obstacles, obst_obstacles, obst_ramps
    else
      move_free points[@cur_point], speed
    end
  end
  if @speed.x == 0 and @speed.y == 0
    unless @cycle_setup
      @cycle_timer = 0
      @cycle_setup = true
    end
    if @cycle_timer >= stop_time
      if @cur_point == points.length - 1
        @cur_point = 0
      else
        @cur_point += 1
      end
      @cycle_setup = false
    else
      @cycle_timer += 1
    end
  end
end

#move(forces, obst, ramps, set_speed = false) ⇒ Object

Moves this object, based on the forces being applied to it, and performing collision checking.

Parameters:

forces

A Vector where x is the horizontal component of the resulting force and y is the vertical component.

obst

An array of obstacles to be considered in the collision checking. Obstacles must be instances of Block (or derived classes), or objects that include Movement.

ramps

An array of ramps to be considered in the collision checking. Ramps must be instances of Ramp (or derived classes).

set_speed

Set this flag to true to cause the forces vector to be treated as a speed vector, i.e., the object’s speed will be directly set to the given values. The force of gravity will also be ignored in this case.



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/minigl/movement.rb', line 233

def move(forces, obst, ramps, set_speed = false)
  if set_speed
    @speed.x = forces.x
    @speed.y = forces.y
  else
    forces.x += G.gravity.x; forces.y += G.gravity.y
    forces.x += @stored_forces.x; forces.y += @stored_forces.y
    @stored_forces.x = @stored_forces.y = 0

    forces.x = 0 if (forces.x < 0 and @left) or (forces.x > 0 and @right)
    forces.y = 0 if (forces.y < 0 and @top) or (forces.y > 0 and @bottom)

    if @bottom.is_a? Ramp
      if @bottom.ratio > G.ramp_slip_threshold
        forces.x += (@bottom.left ? -1 : 1) * (@bottom.ratio - G.ramp_slip_threshold) * G.ramp_slip_force / G.ramp_slip_threshold
      elsif forces.x > 0 && @bottom.left || forces.x < 0 && !@bottom.left
        forces.x *= @bottom.factor
      end
    end

    @speed.x += forces.x / @mass; @speed.y += forces.y / @mass
  end

  @speed.x = 0 if @speed.x.abs < G.min_speed.x
  @speed.y = 0 if @speed.y.abs < G.min_speed.y
  @speed.x = (@speed.x <=> 0) * @max_speed.x if @speed.x.abs > @max_speed.x
  @speed.y = (@speed.y <=> 0) * @max_speed.y if @speed.y.abs > @max_speed.y
  @prev_speed = @speed.clone

  x = @speed.x < 0 ? @x + @speed.x : @x
  y = @speed.y < 0 ? @y + @speed.y : @y
  w = @w + (@speed.x < 0 ? -@speed.x : @speed.x)
  h = @h + (@speed.y < 0 ? -@speed.y : @speed.y)
  move_bounds = Rectangle.new x, y, w, h
  coll_list = []
  obst.each do |o|
    coll_list << o if o != self && move_bounds.intersect?(o.bounds)
  end
  ramps.each do |r|
    r.check_can_collide move_bounds
  end

  if coll_list.length > 0
    up = @speed.y < 0; rt = @speed.x > 0; dn = @speed.y > 0; lf = @speed.x < 0
    if @speed.x == 0 || @speed.y == 0
      # Ortogonal
      if rt; x_lim = find_right_limit coll_list
      elsif lf; x_lim = find_left_limit coll_list
      elsif dn; y_lim = find_down_limit coll_list
      elsif up; y_lim = find_up_limit coll_list
      end
      if rt && @x + @w + @speed.x > x_lim
        @x = x_lim - @w
        @speed.x = 0
      elsif lf && @x + @speed.x < x_lim
        @x = x_lim
        @speed.x = 0
      elsif dn && @y + @h + @speed.y > y_lim; @y = y_lim - @h; @speed.y = 0
      elsif up && @y + @speed.y < y_lim; @y = y_lim; @speed.y = 0
      end
    else
      # Diagonal
      x_aim = @x + @speed.x + (rt ? @w : 0); x_lim_def = x_aim
      y_aim = @y + @speed.y + (dn ? @h : 0); y_lim_def = y_aim
      coll_list.each do |c|
        if c.passable; x_lim = x_aim
        elsif rt; x_lim = c.x
        else; x_lim = c.x + c.w
        end
        if dn; y_lim = c.y
        elsif c.passable; y_lim = y_aim
        else; y_lim = c.y + c.h
        end

        if c.passable
          y_lim_def = y_lim if dn && @y + @h <= y_lim && y_lim < y_lim_def
        elsif (rt && @x + @w > x_lim) || (lf && @x < x_lim)
          # Can't limit by x, will limit by y
          y_lim_def = y_lim if (dn && y_lim < y_lim_def) || (up && y_lim > y_lim_def)
        elsif (dn && @y + @h > y_lim) || (up && @y < y_lim)
          # Can't limit by y, will limit by x
          x_lim_def = x_lim if (rt && x_lim < x_lim_def) || (lf && x_lim > x_lim_def)
        else
          x_time = 1.0 * (x_lim - @x - (@speed.x < 0 ? 0 : @w)) / @speed.x
          y_time = 1.0 * (y_lim - @y - (@speed.y < 0 ? 0 : @h)) / @speed.y
          if x_time > y_time
            # Will limit by x
            x_lim_def = x_lim if (rt && x_lim < x_lim_def) || (lf && x_lim > x_lim_def)
          elsif (dn && y_lim < y_lim_def) || (up && y_lim > y_lim_def)
            y_lim_def = y_lim
          end
        end
      end
      if x_lim_def != x_aim
        @speed.x = 0
        if lf; @x = x_lim_def
        else; @x = x_lim_def - @w
        end
      end
      if y_lim_def != y_aim
        @speed.y = 0
        if up; @y = y_lim_def
        else; @y = y_lim_def - @h
        end
      end
    end
  end
  @x += @speed.x
  @y += @speed.y

  # Keeping contact with ramp
  # if @speed.y == 0 and @speed.x.abs <= G.ramp_contact_threshold and @bottom.is_a? Ramp
  #   @y = @bottom.get_y(self)
  #   puts 'aqui'
  # end
  ramps.each do |r|
    r.check_intersection self
  end
  check_contact obst, ramps
end

#move_carrying(arg, speed, carried_objs, obstacles, ramps, ignore_collision = false) ⇒ Object

Moves this object as an elevator (i.e., potentially carrying other objects) with the specified forces or towards a given point.

Parameters:

arg

A Vector specifying either the forces acting on this object or a point towards the object should move.

speed

If the first argument is a forces vector, then this should be nil. If it is a point, then this is the constant speed at which the object will move (provided as a scalar, not a vector).

carried_objs

An array of objects that can potentially be carried by this object while it moves. The objects must respond to x, y, w and h.

obstacles

Obstacles that should be considered for collision checking with the carried objects, if they include the Movement module, and with this object too, if moving with forces and the ignore_collision flag is false.

ramps

Ramps that should be considered for the carried objects, if they include the Movement module, and for this object too, if moving with forces and ignore_collision is false.

ignore_collision

Set to true to make this object ignore collision even when moving with forces.



375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/minigl/movement.rb', line 375

def move_carrying(arg, speed, carried_objs, obstacles, ramps, ignore_collision = false)
  if speed
    x_d = arg.x - @x; y_d = arg.y - @y
    distance = Math.sqrt(x_d**2 + y_d**2)

    if distance == 0
      @speed.x = @speed.y = 0
      return
    end

    @speed.x = 1.0 * x_d * speed / distance
    @speed.y = 1.0 * y_d * speed / distance
    x_aim = @x + @speed.x; y_aim = @y + @speed.y
  else
    x_aim = @x + @speed.x + G.gravity.x + arg.x
    y_aim = @y + @speed.y + G.gravity.y + arg.y
  end

  passengers = []
  carried_objs.each do |o|
    if @x + @w > o.x && o.x + o.w > @x
      foot = o.y + o.h
      if foot.round(6) == @y.round(6) || @speed.y < 0 && foot < @y && foot > y_aim
        passengers << o
      end
    end
  end

  prev_x = @x; prev_y = @y
  if speed
    if @speed.x > 0 && x_aim >= arg.x || @speed.x < 0 && x_aim <= arg.x
      @x = arg.x; @speed.x = 0
    else
      @x = x_aim
    end
    if @speed.y > 0 && y_aim >= arg.y || @speed.y < 0 && y_aim <= arg.y
      @y = arg.y; @speed.y = 0
    else
      @y = y_aim
    end
  else
    move(arg, ignore_collision ? [] : obstacles, ignore_collision ? [] : ramps)
  end

  forces = Vector.new @x - prev_x, @y - prev_y
  prev_g = G.gravity.clone
  G.gravity.x = G.gravity.y = 0
  passengers.each do |p|
    if p.class.included_modules.include?(Movement)
      prev_speed = p.speed.clone
      prev_forces = p.stored_forces.clone
      prev_bottom = p.bottom
      p.speed.x = p.speed.y = 0
      p.stored_forces.x = p.stored_forces.y = 0
      p.instance_exec { @bottom = nil }
      p.move(forces * p.mass, obstacles, ramps)
      p.speed.x = prev_speed.x
      p.speed.y = prev_speed.y
      p.stored_forces.x = prev_forces.x
      p.stored_forces.y = prev_forces.y
      p.instance_exec(prev_bottom) { |b| @bottom = b }
    else
      p.x += forces.x
      p.y += forces.y
    end
  end
  G.gravity = prev_g
end

#move_free(aim, speed) ⇒ Object

Moves this object, without performing any collision checking, towards a specified point or in a specified direction.

Parameters:

aim

A Vector specifying where the object will move to or an angle (in degrees) indicating the direction of the movement. Angles are measured starting from the right (i.e., to move to the right, the angle must be 0) and raising clockwise.

speed

The constant speed at which the object will move. This must be provided as a scalar, not a vector.



454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/minigl/movement.rb', line 454

def move_free(aim, speed)
  if aim.is_a? Vector
    x_d = aim.x - @x; y_d = aim.y - @y
    distance = Math.sqrt(x_d**2 + y_d**2)

    if distance == 0
      @speed.x = @speed.y = 0
      return
    end

    @speed.x = 1.0 * x_d * speed / distance
    @speed.y = 1.0 * y_d * speed / distance

    if (@speed.x < 0 and @x + @speed.x <= aim.x) or (@speed.x >= 0 and @x + @speed.x >= aim.x)
      @x = aim.x
      @speed.x = 0
    else
      @x += @speed.x
    end

    if (@speed.y < 0 and @y + @speed.y <= aim.y) or (@speed.y >= 0 and @y + @speed.y >= aim.y)
      @y = aim.y
      @speed.y = 0
    else
      @y += @speed.y
    end
  else
    rads = aim * Math::PI / 180
    @speed.x = speed * Math.cos(rads)
    @speed.y = speed * Math.sin(rads)
    @x += @speed.x
    @y += @speed.y
  end
end