ObstacleManager

Author
  • Mark Mayes / mm-dev
License
  • GPL-3.0-only

Methods

(static) addAllBackground()

Source
Add all the groups of background obstacles
  • Background obstacles appear underneath everything else and don't interact with the player
  • Calls addGroup() for each group

(static) addAllCollectAndAvoid()

Source
Add all the groups of collectable and avoidable obstacles
  • Both of these obstacle types are drawn in the same 'layer' between background and floating obstacles
  • Calls addGroup() for each group

(static) addAllFloating()

Source
Add all the groups of floating obstacles
  • Floating obstacles appear above everything else and don't interact with the player
  • Calls addGroup() for each group

(static) addGroup(_data)

Source
Add a group of obstacles to the game

Loops through the group, calling spawn() repeatedly until _data.total obstacles of this group type have been added.

Parameters
Name Type Description
_data object

An object describing a group of obstacles, originating from a LEVEL_DATA/*.js file

(static) addGroupToRadiusRanges(_data)

Source
Check the range of radii for a group of obstacles

Over the entire game we want to know what the smallest and highest radii of obstacles is. This info is then used to decide which sound effects to add to an obstacle:

  • Smallest obstacles will have the highest-pitch version of SFX
  • Largest obstacles will have the lowest-pitch version of SFX
Parameters
Name Type Description
_data object

An object describing a group of obstacles, originating from a LEVEL_DATA/*.js file

(static) bounceInRectangle(_obstacle, _rect)

Source
Bounce an obstacle off the walls of a rectangle
Parameters
Name Type Description
_obstacle object
_rect object

A rectangle with top, right, bottom, left properties

(static) bounceOffPlayer(_obstacle) → {object}

Source
Calculate effects of a collision between the player and an obstacle

This is an elastic collision, no energy is lost in the collision ie its a 'perfect'/unrealistic collision.

Conservation of momentum

Momentum is the product of mass and velocity: p = m * v

(Player.radius * Player.speed) + (_obstacle.radius * Obstacle.speed)
=
(Player.radius * Player.speedAfter) + (_obstacle.radius * Obstacle.speedAfter)
Conservation of kinetic energy

Kinetic energy of an object is half its mass times the square of its velocity: ke = (0.5 * m) * (v * v)

((0.5 * Player.radius) * (Player.speed * Player.speed)) + ((0.5 * _obstacle.radius) * (Obstacle.speed * Obstacle.speed))
=
((0.5 * Player.radius) * (Player.speedAfter * Player.speedAfter)) + ((0.5 * _obstacle.radius) * (Obstacle.speedAfter * Obstacle.speedAfter))

So after collision:

Player.speedAfter = (Player.speed * (Player.radius - _obstacle.radius)) + (2 * _obstacle.radius * Obstacle.speed) / (Player.radius + _obstacle.radius)
Obstacle.speedAfter = (Obstacle.speed * (_obstacle.radius - Player.radius)) + (2 * Player.radius * Player.speed) / (Player.radius + _obstacle.radius)

Project the velocity vectors of the 2 objects (player and obstacle) onto the vectors which are normal (perpendicular) and tangent to the surface of the collision.

So for each velocity (player and obstacle) we have 2 components:

  • Normal component: These undergo a 1-dimensional collison, computed using the formulas above
  • Tangential component: Unchanged by the collision, as there is no force along the line tangent to the collision surface

Then the unit normal vector is multiplied by the scalar normal velocity after the collision, to get a vector which has a direction normal to the collision surface, and a magnitude which is the normal component of the velocity after the collision.

The same is done with the unit tangent vector and the scalar tangential velocity

Finally the new velocity vectors are found by adding the normal velocity and tangential velocity for each object.

  1. Find unit normal and unit tangent vectors Unit normal:
  • Has magnitude of 1
  • Direction is normal (perpendicular) to the surfaces of the objects at the point of collision Unit tangent:
  • Has magnitude of 1
  • Direction is tangent to the surfaces of the objects at the point of collision

First find a normal vector:

  • A vector whose components are the difference between the coordinates of the centres of the circles normalVector = { x: _obstacle.pos.x - Player.pos.x, y: _obstacle.pos.y - Player.pos.y };

Next get the unit vector of normalVector:

normalVectorMagnitude = Math.sqrt(
  (normalVector.x * normalVector.x) +
  (normalVector.y * normalVector.y)
);
unitNormalVector = {
  x: normalVector.x / normalVectorMagnitude,
  y: normalVector.y / normalVectorMagnitude
}
// OR
normalVectorMagnitude = vectorGetMagnitude(normalVector);
unitNormalVector = vectorScalarMultiply(normalVector, normalVectorMagnitude);

Next get the unit vector of tangentVector:

  • x component is equal to the negative of the y component of the unit normal vector
  • y component is equal to the xcomponent of the unit normal vector
unitTangentVector = {
  x: -1 * unitNormalVector.y,
  y: unitNormalVector.x
}
  1. Create the initial (before the collision) velocity vectors ALREADY DONE (Player/obstacle vectors)

  2. After the collision:

  • The tangential component of the velocities is unchanged
  • The normal component of the velocities can be found using the 1D collision formulas above

So for the player and obstacle velocity vectors we need to resolve them into normal and tangential components. To do this, project the velocity vectors onto the unit normal and unit tangent vectors by computing the dot product.

normalVectorPlayerMagnitude = vectorGetDotProduct(unitNormalVector, Player.velocityVector);
tangentVectorPlayerMagnitude = vectorGetDotProduct(unitTangentVector, Player.velocityVector);

normalVectorObstacleMagnitude = vectorGetDotProduct(unitNormalVector, _obstacle.velocityVector);
tangentVectorObstacleMagnitude = vectorGetDotProduct(unitTangentVector, _obstacle.velocityVector);
  1. Find new (after collision) tangential velocities, which are simply equal to the old ones:
tangentVectorPlayerMagnitudeAfter = tangentVectorPlayerMagnitude;
tangentVectorObstacleMagnitudeAfter = tangentVectorObstacleMagnitude;
  1. Find the new normal velocities using the 1D collision formulas from earlier
normalVectorPlayerMagnitudeAfter =
  ((normalVectorPlayerMagnitude * (Player.radius - _obstacle.radius)) +
  (2 * _obstacle.radius * normalVectorObstacleMagnitude)) /
  (Player.radius + _obstacle.radius)

normalVectorObstacleMagnitudeAfter =
  ((normalVectorObstacleMagnitude * (_obstacle.radius - Player.radius)) +
  (2 * Player.radius * normalVectorPlayerMagnitude)) /
  (Player.radius + _obstacle.radius)
  1. Convert the scalar normal and tangential velocities into vectors:
  • Multiply the unit normal vector by the scalar normal velocity (magnitude) to get a vector which is normal to the surfaces at the point of collision with a magnitude equal to the normal component of the velocity.
  • Multiply the unit tangential vector by the scalar tangential velocity (magnitude) to get a vector which is tangential to the surfaces at the point of collision with a magnitude equal to the tangential component of the velocity.
normalVectorPlayerAfter = vectorScalarMultiply(unitNormalVector, normalVectorPlayerMagnitudeAfter);
tangentVectorPlayerAfter = vectorScalarMultiply(unitTangentVector, tangentVectorPlayerMagnitudeAfter);
normalVectorObstacleAfter = vectorScalarMultiply(unitNormalVector, normalVectorObstacleMagnitudeAfter);
tangentVectorObstacleAfter = vectorScalarMultiply(unitTangentVector, tangentVectorObstacleMagnitudeAfter);
  1. Find the final velocity vectors by adding the normal and tangential components for each object
playerVelocityVectorAfter = vectorAdd(normalVectorPlayerAfter, tangentVectorPlayerAfter);
playerVectorMagnitudeAfter = vectorGetMagnitude(playerVelocityVectorAfter);
playerUnitVectorAfter = {
  x: playerVelocityVectorAfter.x / playerVectorMagnitudeAfter,
  y: playerVelocityVectorAfter.y / playerVectorMagnitudeAfter,
};

obstacleVelocityVectorAfter = vectorAdd(normalVectorObstacleAfter, tangentVectorObstacleAfter);
obstacleVectorMagnitudeAfter = vectorGetMagnitude(obstacleVelocityVectorAfter);
obstacleUnitVectorAfter = {
  x: obstacleVelocityVectorAfter.x / obstacleVectorMagnitudeAfter,
  y: obstacleVelocityVectorAfter.y / obstacleVectorMagnitudeAfter,
};

These are velocity vectors (including magnitude) so need to be broken down into unit vectors and magnitude for use on the player and obstacle

Parameters
Name Type Description
_obstacle object

The collided-with obstacle

Returns
  object

A vector which is used to move the control stick and give the effect of the player being knocked

(static) deleteObstacles()

Source
Empty the obstacles array so that old obstacles can be garbage collected

(static) explodeAllAvoids()

Source
Starts explosion animations for all avoidable objects

Used at the end of a completed level to wipe out the objects.

(static) getRandomVector(_degreesMin, _degreesMax) → {object}

Source
Get a random vector for an obstacle, based on its allowable range of movement as described in its group data
Parameters
Name Type Description
_degreesMin number

Smallest possible angle of direction in degrees

_degreesMax number

Largest possible angle of direction in degrees

Returns
  object

A normalised vector

(static) getSoundIDFromRadius(_r, _orderedSfx_ar) → {string}

Source
Choose sound based on size of obstacle
Parameters
Name Type Description
_r number

Obstacle radius

_orderedSfx_ar array

An array of IDs for sounds which may represent this obstacle. The sounds will all be the same SFX, each processed to a different pitch. They should be ordered in descending pitch order.

Returns
  string

A sound ID

(static) increaseVectorAngle(_obstacle, _inc)

Source
Adjust an obstacle's vector to incrementally spin the obstacle

Directly adjusts the vector property of the object.

Parameters
Name Type Description
_obstacle object
_inc number

A negative or positive number, in degrees, by which to adjust the vector

(static) incrementExplosionAnimation(_obstacle)

Source
Process the next frame of the obstacle explosion animation

At the end of the animation, mark the obstacle as deleted.

Parameters
Name Type Description
_obstacle object

(static) reset()

Source

(static) spawn(_data)

Source
Creates an individual obstacle object and adds it to ObstacleManager.obstacles array

Sets various properties depending on the type of obstacle.

Parameters
Name Type Description
_data object

An object describing a group of obstacles, originating from a LEVEL_DATA/*.js file

(static) update(_frames)

Source
Loop through all obstacles, updating their positions and other properties
Parameters
Name Type Description
_frames number

Number of frames passed since last update, in case the engine isn't keeping up with our desired frame rate and we need perform multiple operations to keep things as smooth/consistent as possible

(static) wrapAroundRectangle(_obstacle, _rect)

Source
Wrap an obstacle at the boundaries of a rectangle

Behaviour varies based on obstacle type.

Parameters
Name Type Description
_obstacle object
_rect object

A rectangle with top, right, bottom, left properties