lib/minigl/movement.rb in minigl-1.2.5 vs lib/minigl/movement.rb in minigl-1.2.6

- old
+ new

@@ -1,30 +1,95 @@ require_relative 'global' module AGL + # Represents an object with a rectangular bounding box and the +passable+ + # property. It is the simplest structure that can be passed as an element of + # the +obst+ array parameter of the +move+ method. class Block - attr_reader :x, :y, :w, :h, :passable + # The x-coordinate of the top left corner of the bounding box. + attr_reader :x + # The y-coordinate of the top left corner of the bounding box. + attr_reader :y + + # The width of the bounding box. + attr_reader :w + + # The height of the bounding box. + attr_reader :h + + # Whether a moving object can pass through this block when coming from + # below. This is a common feature of platforms in platform games. + attr_reader :passable + + # Creates a new block. + # Parameters: + # [x] The x-coordinate of the top left corner of the bounding box. + # [y] The y-coordinate of the top left corner of the bounding box. + # [w] The width of the bounding box. + # [h] The height of the bounding box. + # [passable] Whether a moving object can pass through this block when + # coming from below. This is a common feature of platforms in platform + # games. def initialize x, y, w, h, passable @x = x; @y = y; @w = w; @h = h @passable = passable end + # Returns the bounding box of this block as a Rectangle. def bounds Rectangle.new @x, @y, @w, @h end end + # Represents a ramp, i.e., an inclined structure which allows walking over + # it while automatically going up or down. It can be imagined as a right + # triangle, with a side parallel to the x axis and another one parallel to + # the y axis. You must provide instances of this class (or derived classes) + # to the +ramps+ array parameter of the +move+ method. class Ramp + # Creates a new ramp. + # Parameters: + # [x] The x-coordinate of the top left corner of a rectangle that + # completely (and precisely) encloses the ramp (thought of as a right + # triangle). + # [y] The y-coordinate of the top left corner of the rectangle described + # above. + # [w] The width of the ramp (which corresponds to the width of the + # rectangle described above). + # [h] The height of the ramp (which corresponds to the height of the + # rectangle described above, and to the difference between the lowest + # point of the ramp, where it usually meets the floor, and the + # highest). + # [left] Whether the height of the ramp increases from left to right. Use + # +false+ for a ramp that goes down from left to right. def initialize x, y, w, h, left @x = x @y = y @w = w @h = h @left = left end + # Checks if an object is in contact with this ramp (standing over it). + # Parameters: + # [obj] The object to check contact with. It must have the +x+, +y+, +w+ + # and +h+ accessible attributes determining its bounding box. + def contact? obj + obj.x.round(6) == get_x(obj).round(6) && obj.y.round(6) == get_y(obj).round(6) + end + + # Checks if an object is intersecting this ramp (inside the corresponding + # right triangle and at the floor level or above). + # Parameters: + # [obj] The object to check intersection with. It must have the +x+, +y+, + # +w+ and +h+ accessible attributes determining its bounding box. + def intersects obj + obj.x + obj.w > @x && obj.x < @x + @w && obj.y > get_y(obj) && obj.y <= @y + @h - obj.h + end + + # :nodoc: def can_collide? obj @can_collide = (obj.speed.y >= 0 and not intersects(obj)) end def check_intersection obj @@ -44,18 +109,10 @@ # obj.speed.x *= (@w / (@w + @h)) # obj.speed.y = 0 end end - def contact? obj - obj.x.round(6) == get_x(obj).round(6) && obj.y.round(6) == get_y(obj).round(6) - end - - def intersects obj - obj.x + obj.w > @x && obj.x < @x + @w && obj.y > get_y(obj) && obj.y <= @y + @h - obj.h - end - def get_x obj return @x + (1.0 * (@y + @h - obj.y - obj.h) * @w / @h) - obj.w if @left @x + (1.0 * (obj.y + obj.h - @y) * @w / @h) end @@ -65,18 +122,70 @@ return @y - obj.h if obj.x < @x @y + (1.0 * (obj.x - @x) * @h / @w) - obj.h end end + # 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. module Movement - attr_reader :speed, :w, :h, :passable, :top, :bottom, :left, :right - attr_accessor :x, :y, :stored_forces - + # A Vector with the current speed of the object (x: horizontal component, + # y: vertical component). + attr_reader :speed + + # Width of the bounding box. + attr_reader :w + + # Height of the bounding box. + attr_reader :h + + # Whether a moving object can pass through this block when coming from + # below. This is a common feature of platforms in platform games. + attr_reader :passable + + # The object that is making contact with this from above. If there's no + # contact, returns +nil+. + attr_reader :top + + # The object that is making contact with this from below. If there's no + # contact, returns +nil+. + attr_reader :bottom + + # The object that is making contact with this from the left. If there's no + # contact, returns +nil+. + attr_reader :left + + # The object that is making contact with this from the right. If there's + # no contact, returns +nil+. + attr_reader :right + + # The x-coordinate of the top left corner of the bounding box. + attr_accessor :x + + # The y-coordinate of the top left corner of the bounding box. + attr_accessor :y + + # A Vector with the horizontal and vertical components of a force that + # be applied in the next time +move+ is called. + attr_accessor :stored_forces + + # Returns the bounding box as a Rectangle. def bounds Rectangle.new @x, @y, @w, @h end - + + # 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 <code>include Movement</code>. + # [ramps] An array of ramps to be considered in the collision checking. + # Ramps must be instances of Ramp (or derived classes). def move forces, obst, ramps forces.x += Game.gravity.x; forces.y += Game.gravity.y forces.x += @stored_forces.x; forces.y += @stored_forces.y @stored_forces.x = @stored_forces.y = 0 @@ -171,11 +280,21 @@ ramps.each do |r| r.check_intersection self end check_contact obst, ramps end - + + # Moves this object as an elevator (i.e., potentially carrying other + # objects) towards a given point. + # Parameters: + # [aim] A Vector specifying where the object will move to. + # [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 <code>include Movement</code>. def move_carrying aim, speed, obstacles x_d = aim.x - @x; y_d = aim.y - @y distance = Math.sqrt(x_d**2 + y_d**2) @speed.x = 1.0 * x_d * speed / distance @speed.y = 1.0 * y_d * speed / distance @@ -204,11 +323,17 @@ @y = y_aim end passengers.each do |p| p.y = @y - p.h end end - + + # Moves this object, without performing any collision checking, towards + # the specified point. + # Parameters: + # [aim] A Vector specifying where the object will move to. + # [speed] The constant speed at which the object will move. This must be + # provided as a scalar, not a vector. def move_free aim, speed x_d = aim.x - @x; y_d = aim.y - @y distance = Math.sqrt(x_d**2 + y_d**2) @speed.x = 1.0 * x_d * speed / distance @speed.y = 1.0 * y_d * speed / distance @@ -225,10 +350,27 @@ @speed.y = 0 else @y += @speed.y end end - + + # Causes the object to move in cycles across multiple given points (the + # method must be called repeatedly, and it returns the value that must be + # provided to +cur_point+ after the first call). 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. + # [cur_point] The index of the point in the path that the object is + # currently moving to. In the first call, it is a good idea to + # provide 0, while in the subsequent calls, you must provide + # the return value of this method. + # [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 <code>include Movement</code>. def cycle points, cur_point, speed, obstacles = nil if obstacles move_carrying points[cur_point], speed, obstacles else move_free points[cur_point], speed