Skip to content

Instantly share code, notes, and snippets.

@richtaur
Created January 1, 2012 01:08
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save richtaur/1545853 to your computer and use it in GitHub Desktop.
Save richtaur/1545853 to your computer and use it in GitHub Desktop.
Racing simulation sent in by one of the listeners of our HTML5 game dev podcast
<!DOCTYPE html>
<html>
<head>
<title>Car Racer</title>
<meta charset="utf-8">
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var then = Date.now();
function init() {
setInterval(main, 20);
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* CANVAS
*/
function canvas()
{
// *** Private vars
var canvas = document.getElementById("race-track");
var ctx = canvas.getContext("2d");
var canvasWidth = ctx.canvas.clientWidth;
var canvasHeight = ctx.canvas.clientHeight;
// *** Add public pointer
this.getCtx = getCtx;
this.getCanvasWidth = getCanvasWidth;
this.getCanvasHeight = getCanvasHeight;
// *** Private -> public
function getCtx () {
return ctx;
}
// *** Private -> public
function getCanvasWidth () {
return canvasWidth;
}
// *** Private -> public
function getCanvasHeight () {
return canvasHeight;
}
// *** Public
this.clear = function() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
}
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* MAIN
*/
function main()
{
var now = Date.now();
var fps = 1000 / (now - then);
update();
render(fps);
then = now;
};
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* RENDER
*/
function update(modifier)
{
// *** Calculatge the cars new x,y pos
car1.calcPos(modifier);
// *** Get the current tile the car is on
currentTileArray = car1.getTile(trackObj.tileWidth(), trackObj.tileHeight());
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* DRAW
*/
function render(fps)
{
// *** Clear canvas
canvas.clear();
// *** Draw/render map
renderMap(trackObj);
// *** Draw the car
car1.drawCar(ctx);
// *** Draw the car
hud(ctx, fps);
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* Draw Map
*/
function renderMap(mapObj)
{
var rgt = mapObj.data();
for (matrixY in rgt) {
for (matrixX in rgt[matrixY]) {
var matrixArray = rgt[matrixY][matrixX].split(',');
var x = matrixArray[1];
var y = matrixArray[0];
var sx = (x-1) * mapObj.tileWidth();
var sy = (y-1) * mapObj.tileHeight();
var dx = (matrixX) * mapObj.tileWidth();
var dy = (matrixY) * mapObj.tileHeight();
ctx.drawImage(mapObj.mapTilesImg(), sx, sy, mapObj.tileWidth(), mapObj.tileHeight(), dx, dy, mapObj.tileWidth(), mapObj.tileHeight());
}
}
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* Cars
*/
function car(x, y, degrees, carObj)
{
var MAX_TORQUE = carObj.max_torque; //(cause the car to spin out if value is reached (see IF statement below)) //1
var MAX_ACCEL = carObj.max_speed; // px. per frame. squared (maximum speed) //1
var TURN_RADIUS = carObj.turn_radius; // px (The smaller this number is, the sharper the turn the car will make.)
var DRAG = carObj.drag; // ratio (the larger the value, the faster the car loses speed)
var spinDrag = 0.2;
// *** Initialise
var xPos = x;
var yPos = y;
var _turn = 0; // 1, 0, -1
var _accel = 0; // 1, 0, -1
var _rotation = degrees;
var xVel = 0; // px. per frame (point)
var yVel = 0;
var heading = degrees; // radians
var spin = 0; // radians per frame
var spinImpact = 0;
// *** Car image
var carImage = new Image(); // Create new img element
carImage.src = carObj.image; // Set source path
var rightKey = false;
var leftKey = false;
var upKey = false;
var downKey = false;
this.drawCar = function(ctx) {
// *** Draw car (find center)
var transX = xPos-(carImage.width/2);
var transY = yPos-(carImage.height/2);
ctx.save();
ctx.translate(transX, transY);
// *** Rotate car
ctx.rotate(heading);
ctx.translate(-xPos, -yPos);
ctx.drawImage(carImage, transX, transY);
ctx.restore();
}
this.calcPos = function() {
// *** Add drag. Slows car down when not accelerating (also reduces spin)
xVel *= (1 - DRAG);
yVel *= (1 - DRAG);
spinImpact *= spinDrag;
if ( Math.abs(spinImpact) <= 1 ) {
spinImpact = 0;
}
// *** Direction
heading = (_rotation * Math.PI / 180);
// *** Add acceleration
xVel += MAX_ACCEL * _accel * Math.sin(heading) //* modifier;
yVel -= MAX_ACCEL * _accel * Math.cos(heading) //* modifier;
var velMag = Math.sqrt(xVel * xVel + yVel * yVel);
spin = velMag / TURN_RADIUS * _turn; // something like (_turn * 3) is good for simulating oil slip
/* calculate torque (turning the car changes the direction it travels).
* "First we calculate how much the velocity will change to get the car moving in the direction
* it is pointed. I'm calling this value torque"
*/
var torqueX = 0;
var torqueY = 0;
var newHeading = heading + spin + spinImpact;
torqueX = Math.sin(newHeading) * velMag - xVel;
torqueY = -Math.cos(newHeading) * velMag - yVel;
var torqueMag = Math.sqrt(Math.pow(torqueX,2) + Math.pow(torqueY,2));
// *** Limit torque, so the car will "spin out" if turning too fast
if (torqueMag > MAX_TORQUE) {
torqueX = MAX_TORQUE * torqueX / torqueMag;
torqueY = MAX_TORQUE * torqueY / torqueMag;
}
// *** Apply torque to velocity
xVel += torqueX;
yVel += torqueY;
// *** Set the new position and values
xPos += xVel;
yPos += yVel;
heading = newHeading;
_rotation = heading * 180 / Math.PI;
}
// *** Setters and getters
this.forwardOn = function (value) { _accel = 1; }
this.forwardOff = function (value) { _accel = 0; }
this.reverseOn = function (value) { _accel = -1; }
this.reverseOff = function (value) { _accel = 0; }
this.rightTurnOn = function (value) { _turn = 1 }
this.rightTurnOff = function (value) { _turn = 0 }
this.leftTurnOn = function (value) { _turn = -1; }
this.leftTurnOff = function (value) { _turn = 0; }
this.setX = function (x) { xPos = x; }
this.setY = function (y) { yPos = y; }
this.setDirection = function (degreesx) { direction = degreesx; }
this.setSpeed = function (speed) { speed = speed; }
this.getX = function () { return xPos; }
this.getY = function () { return yPos; }
this.getDirection = function () { return direction; }
this.getSpeed = function () { return speed; }
this.getTile = function (tileWidth, tileHeight) {
var tileX = Math.ceil(xPos / tileWidth);
var tileY = Math.ceil(yPos / tileHeight);
}
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* Draw HUD
*/
function hud(ctx, fps)
{
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "24px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("FPS: " + fps, 32, 32);
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* Keys
*/
function onKeyDown(evt)
{
switch (evt.keyCode) {
case 38: // Up arrow
car1.forwardOn();
break;
case 40: // Down arrow
car1.reverseOn();
break;
case 37: // Left arrow
car1.leftTurnOn();
break;
case 39: // Right arrow
car1.rightTurnOn();
break;
}
}
function onKeyUp(evt)
{
switch (evt.keyCode) {
case 38: // Up arrow
car1.forwardOff();
break;
case 40: // Down arrow
car1.reverseOff();
break;
case 37: // Left arrow
car1.leftTurnOff();
break;
case 39: // Right arrow
car1.rightTurnOff();
break;
}
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* MAPS
*/
var track1 = {
tileset: "images/tileset-g-100.jpg",
tileWidth: 128,
tileHeight: 128,
startTile: '3,1',
startFacing: 'n',
rgt: [ ['2,1', '1,2', '4,4', '1,2', '4,2', '2,2'],
['4,3', '5,4', '7,1', '7,1', '13,4', '4,1'],
['3,4', '7,1', '7,3', '7,1', '2,1', '6,2'],
['1,1', '15,1', '7,1', '7,2', '4,1', '7,1'],
['6,1', '1,2', '4,2', '1,4', '6,2', '7,1']
],
trackMarkers: ['1,1', '1,6', '3,6', '4,5', '5,5', '5,1']
}
function track(track) // CAN PROBABLY REMOVE THIS AND METHOD setStartPos CAN BE PART OF RENDER MAP.
{
var mapTilesImg = new Image();
mapTilesImg.src = track.tileset;
this.mapTilesImg = function() {
return mapTilesImg;
}
this.tileset = function() {
return track.tileset;
}
this.tileWidth = function() {
return track.tileWidth;
}
this.tileHeight = function() {
return track.tileHeight;
}
this.data = function() {
return track.rgt;
}
this.trackMarkers = function() {
return track.trackMarkers;
}
this.setStartPos = function()
{
var startTileArray = track.startTile.split(',');
var startTileX = startTileArray[1];
var startTileY = startTileArray[0];
var startX = (startTileX * track.tileWidth) - (track.tileWidth/2);
var startY = (startTileY * track.tileHeight) - (track.tileHeight/2)
var rotation = 0;
switch (track.startFacing) {
case 'n':
startY += 50;
rotation = 0;
break;
case 's':
startY -= (50 - 34); // 34 is the length of the car
rotation = 180;
break;
case 'w':
startX += 50;
rotation = 270;
break;
case 'e':
startX -= (50 - 20);
rotation = 90;
break;
}
// *** Stored as x,y
return posArray = [startX, startY, rotation];
}
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* CARS
*/
var car1 = {
image: "images/carred.png",
max_speed: 0.5,
max_torque: 0.4,
turn_radius: 100,
drag: 0.05
}
var car2 = {
image: "images/carred.png",
max_speed: 0.4,
max_torque: 0.5,
turn_radius: 90,
drag: 0.05
}
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* RUN
*/
// *** Create the canvas object
var canvas = new canvas();
// *** Get the canvas
var ctx = canvas.getCtx();
// *** Clear canvas
canvas.clear();
// *** Create map object
var trackObj = new track(track1);
x = trackObj.setStartPos()[0];
y = trackObj.setStartPos()[1];
direction = trackObj.setStartPos()[2];
// *** Create car object
car1 = new car(x, y, direction, car1);
// *** Run
init();
// *** Bind keys
$(document).keydown(onKeyDown);
$(document).keyup(onKeyUp);
});
</script>
</head>
<body>
<canvas id="race-track" width="768" height="640" style="border:1px solid black;"></canvas>
</body>
</html>
@roninderawan
Copy link

can i get tileset-g-100.jpg and carred.png ?

@singh-beep
Copy link

this does not work

@Whitestarlines
Copy link

It does not work

@has34212
Copy link

didnt work :(

@noblockhit
Copy link

Yes, empty field showing up. too bad

@Lynx29O6
Copy link

hi

@Lynx29O6
Copy link

HI

@Enderpie
Copy link

It ain't work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment