Skip to content

Commit

Permalink
Added TopDownVehicle class, Body.prototype.getVelocityAtPoint
Browse files Browse the repository at this point in the history
  • Loading branch information
schteppe committed May 9, 2015
1 parent 2e149b2 commit 33846a5
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 0 deletions.
90 changes: 90 additions & 0 deletions demos/topDownVehicle.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html>
<head>
<title>Top down vehicle demo - p2.js physics engine</title>
<script src="../build/p2.js"></script>
<script src="../build/p2.renderer.js"></script>
<link href="css/demo.css" rel="stylesheet"/>
<meta name="description" content="">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<script>

// Create demo application
var app = new p2.WebGLRenderer(function(){

// Create a World
var world = new p2.World({
gravity : [0,0]
});
this.setWorld(world);

// Create a dynamic body for the chassis
chassisBody = new p2.Body({
mass: 1
});
var boxShape = new p2.Rectangle(0.5,1);
chassisBody.addShape(boxShape);
world.addBody(chassisBody);

// Create the vehicle
vehicle = new p2.TopDownVehicle(chassisBody);

// Add one front wheel and one back wheel - we don't actually need four :)
frontWheel = vehicle.addWheel({
localPosition: [0, 0.5] // front
});
frontWheel.setSideFriction(4);

// Back wheel
backWheel = vehicle.addWheel({
localPosition: [0, -0.5] // back
});
backWheel.setSideFriction(3); // Less side friction on back wheel makes it easier to drift

vehicle.addToWorld(world);

// Key controls
var keys = {
'37': 0, // left
'39': 0, // right
'38': 0, // up
'40': 0 // down
};
var maxSteer = Math.PI / 5;
this.on("keydown",function (evt){
keys[evt.keyCode] = 1;
onInputChange();
});
this.on("keyup",function (evt){
keys[evt.keyCode] = 0;
onInputChange();
});
function onInputChange(){

// Steer value zero means straight forward. Positive is left and negative right.
frontWheel.steerValue = maxSteer * (keys[37] - keys[39]);

// Engine force forward
backWheel.engineForce = keys[38] * 7;

backWheel.setBrakeForce(0);
if(keys[40]){
if(backWheel.getSpeed() > 0.1){
// Moving forward - add some brake force to slow down
backWheel.setBrakeForce(5);
} else {
// Moving backwards - reverse the engine force
backWheel.setBrakeForce(0);
backWheel.engineForce = -2;
}
}
}

});

</script>
</body>
</html>
13 changes: 13 additions & 0 deletions src/objects/Body.js
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,19 @@ Body.prototype.integrateToTimeOfImpact = function(dt){
return true;
};

/**
* Get velocity of a point in the body.
* @method getVelocityAtPoint
* @param {Array} result A vector to store the result in
* @param {Array} relativePoint A world oriented vector, indicating the position of the point to get the velocity from
* @return {Array} The result vector
*/
Body.prototype.getVelocityAtPoint = function(result, relativePoint){
vec2.crossVZ(result, relativePoint, this.angularVelocity);
vec2.add(result, result, this.velocity);
return result;
};

/**
* @event sleepy
*/
Expand Down
199 changes: 199 additions & 0 deletions src/objects/TopDownVehicle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
var vec2 = require('../math/vec2');
var Utils = require('../utils/Utils');
var Constraint = require('../constraints/Constraint');
var FrictionEquation = require('../equations/FrictionEquation');
var Body = require('../objects/Body');

module.exports = TopDownVehicle;

/**
* @class TopDownVehicle
* @constructor
* @param {Body} chassisBody A dynamic body, already added to the world.
* @param {Object} [options]
*/
function TopDownVehicle(chassisBody, options){
options = options || {};

/**
* @property {Body} chassisBody
*/
this.chassisBody = chassisBody;

/**
* @property {Array} wheels
*/
this.wheels = [];

// A dummy body to constrain the chassis to
this.groundBody = new Body({ mass: 0 });

this.world = null;

var that = this;
this.preStepCallback = function(){
that.update();
};
}

/**
* @method addToWorld
* @param {World} world
*/
TopDownVehicle.prototype.addToWorld = function(world){
this.world = world;
world.addBody(this.groundBody);
world.on('preStep', this.preStepCallback);
for (var i = 0; i < this.wheels.length; i++) {
var wheel = this.wheels[i];
world.addConstraint(wheel);
}
};

/**
* @method removeFromWorld
* @param {World} world
*/
TopDownVehicle.prototype.removeFromWorld = function(){
var world = this.world;
world.removeBody(this.groundBody);
world.off('preStep', this.preStepCallback);
for (var i = 0; i < this.wheels.length; i++) {
var wheel = this.wheels[i];
world.removeConstraint(wheel);
}
this.world = null;
};

/**
* @method addWheel
* @param {object} [wheelOptions]
* @return {WheelConstraint}
*/
TopDownVehicle.prototype.addWheel = function(wheelOptions){
var wheel = new WheelConstraint(this,wheelOptions);
this.wheels.push(wheel);
return wheel;
};

/**
* @method update
*/
TopDownVehicle.prototype.update = function(){
for (var i = 0; i < this.wheels.length; i++) {
this.wheels[i].update();
}
};

/**
* @class WheelConstraint
* @constructor
* @extends {Constraint}
* @param {Vehicle} vehicle
* @param {object} [options]
* @param {Array} [options.localForwardVector] The local wheel forward vector in local body space. Default is [0,1].
* @param {Array} [options.localPosition] The local position of the wheen in the chassis body. Default is [0,0] - the center of the body.
* @param {Array} [options.sideFriction=5] The max friction force in the sideways direction.
*/
function WheelConstraint(vehicle, options){
options = options || {};

this.vehicle = vehicle;

this.forwardEquation = new FrictionEquation(vehicle.chassisBody, vehicle.groundBody);

this.sideEquation = new FrictionEquation(vehicle.chassisBody, vehicle.groundBody);

/**
* @property {number} steerValue
*/
this.steerValue = 0;

/**
* @property {number} engineForce
*/
this.engineForce = 0;

this.setSideFriction(options.sideFriction !== undefined ? options.sideFriction : 5);

/**
* @property {Array} localForwardVector
*/
this.localForwardVector = vec2.fromValues(0, 1);
if(options.localForwardVector){
vec2.copy(this.localForwardVector, options.localForwardVector);
}

/**
* @property {Array} localPosition
*/
this.localPosition = vec2.fromValues(0, 0);
if(options.localPosition){
vec2.copy(this.localPosition, options.localPosition);
}

Constraint.apply(this, vehicle.chassisBody, vehicle.groundBody);

this.equations.push(
this.forwardEquation,
this.sideEquation
);

this.setBrakeForce(0);
}
WheelConstraint.prototype = new Constraint();

/**
* @method setForwardFriction
*/
WheelConstraint.prototype.setBrakeForce = function(force){
this.forwardEquation.setSlipForce(force);
};

/**
* @method setSideFriction
*/
WheelConstraint.prototype.setSideFriction = function(force){
this.sideEquation.setSlipForce(force);
};

var worldVelocity = vec2.create();
var relativePoint = vec2.create();

/**
* @method getSpeed
*/
WheelConstraint.prototype.getSpeed = function(){
this.vehicle.chassisBody.vectorToWorldFrame(relativePoint, this.localForwardVector);
this.vehicle.chassisBody.getVelocityAtPoint(worldVelocity, relativePoint);
return vec2.dot(worldVelocity, relativePoint);
};

var tmpVec = vec2.create();

/**
* @method update
*/
WheelConstraint.prototype.update = function(){

// Directional
this.vehicle.chassisBody.vectorToWorldFrame(this.forwardEquation.t, this.localForwardVector);
vec2.rotate(this.sideEquation.t, this.localForwardVector, Math.PI / 2);
this.vehicle.chassisBody.vectorToWorldFrame(this.sideEquation.t, this.sideEquation.t);

vec2.rotate(this.forwardEquation.t, this.forwardEquation.t, this.steerValue);
vec2.rotate(this.sideEquation.t, this.sideEquation.t, this.steerValue);

// Attachment point
this.vehicle.chassisBody.toWorldFrame(this.forwardEquation.contactPointB, this.localPosition);
vec2.copy(this.sideEquation.contactPointB, this.forwardEquation.contactPointB);

this.vehicle.chassisBody.vectorToWorldFrame(this.forwardEquation.contactPointA, this.localPosition);
vec2.copy(this.sideEquation.contactPointA, this.forwardEquation.contactPointA);

// Add engine force
vec2.normalize(tmpVec, this.forwardEquation.t);
vec2.scale(tmpVec, tmpVec, this.engineForce);

this.vehicle.chassisBody.applyForce(tmpVec, this.forwardEquation.contactPointA);
};
1 change: 1 addition & 0 deletions src/p2.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var p2 = module.exports = {
Shape : require('./shapes/Shape'),
Solver : require('./solver/Solver'),
Spring : require('./objects/Spring'),
TopDownVehicle : require('./objects/TopDownVehicle'),
LinearSpring : require('./objects/LinearSpring'),
RotationalSpring : require('./objects/RotationalSpring'),
Utils : require('./utils/Utils'),
Expand Down

0 comments on commit 33846a5

Please sign in to comment.