Code Your Own Car Racing Game in HTML: A Beginner’s Guide

Dive into the exciting world of game development by learning how to create a simple car racing game directly in your web browser using HTML, CSS, and JavaScript. This guide breaks down the fundamental code structure, allowing you to understand the core mechanics behind “Car Racing Coding In Html”. Whether you’re a budding developer or just curious about game programming, this step-by-step explanation will set you on the right track.

Let’s explore the code that brings a basic car racing game to life. We’ll dissect the HTML structure, the styling, and most importantly, the JavaScript logic that drives the game.

<!DOCTYPE html>
<html>
<head>
<title>Car Racer</title>
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
<script type="text/javascript">

This initial part sets up the HTML document. The <title>Car Racer</title> defines the title that appears in the browser tab. Crucially, it includes jQuery library (jquery-1.7.1.min.js). While not strictly necessary for basic canvas operations, jQuery was likely used in the original context for DOM manipulation or event handling. For simplicity and modern JavaScript practices, we will focus on vanilla JavaScript for the core game logic explanation.

$(document).ready(function() {
    var then = Date.now();
    function init() {
        setInterval(main, 20);
    }

This JavaScript code block starts when the document is fully loaded, thanks to $(document).ready(). It initializes the game by setting up a game loop using setInterval(main, 20). setInterval repeatedly calls the main function every 20 milliseconds, creating the animation effect. then = Date.now(); is used for frame rate calculation.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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);
        }
    }

The canvas() function is designed to handle canvas operations. It gets the HTML canvas element with the ID “race-track” and obtains the 2D rendering context (ctx). This context is essential for drawing shapes, images, and text on the canvas. The function also stores and provides access to canvas dimensions and includes a clear() function to erase the canvas in each frame, preparing it for the next render.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * MAIN
     */
    function main() {
        var now = Date.now();
        var fps = 1000 / (now - then);
        update();
        render(fps);
        then = now;
    };

The main() function is the heart of the game loop. It’s called repeatedly by setInterval. Inside main(), it calculates the frames per second (FPS) to monitor performance, calls the update() function to handle game logic and object positions, and then calls the render() function to draw everything on the canvas. then = now; updates the timestamp for the next FPS calculation.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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());
    }

The update() function is where game state changes are processed. In this basic example, it calls car1.calcPos() to calculate the car’s new position based on inputs and game physics. It also gets the current tile the car is on, which could be used for collision detection or track interaction in a more complex game. The modifier parameter is present but not used in this basic code, it’s typically used for time-based animation to ensure consistent speed across different frame rates.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * DRAW
     */
    function render(fps) {
        // *** Clear canvas
        canvas.clear();

        // *** Draw/render map
        renderMap(trackObj);

        // *** Draw the car
        car1.drawCar(ctx);

        // *** Draw the car hud
        hud(ctx, fps);
    }

The render() function is responsible for drawing everything visible in the game in each frame. It starts by clearing the canvas using canvas.clear(). Then it renders the game map using renderMap(trackObj), draws the car car1.drawCar(ctx), and finally draws the Heads-Up Display (HUD) using hud(ctx, fps) to show the current 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());
            }
        }
    }

Alt text: Tile-based map rendering process in the HTML car racing game, illustrating how tiles are arranged to form the track

The renderMap() function handles drawing the race track. It iterates through the mapObj.data() which is a 2D array representing the map layout. For each element in the array, it extracts tile coordinates from the split(',') operation. It calculates the source sx, sy and destination dx, dy coordinates to draw tiles from a tileset image (mapObj.mapTilesImg()) onto the canvas using ctx.drawImage(). This creates the visual track by piecing together tiles.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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);
        }
    }

Alt text: Red car image sprite used in the HTML car racing game, a top-down view of a red race car

The car() function acts as a constructor for creating car objects. It takes initial x, y positions, starting degrees direction, and a carObj (which defines car properties like max_speed, max_torque, etc.) as parameters. It initializes various properties related to car physics: MAX_TORQUE, MAX_ACCEL, TURN_RADIUS, DRAG, position, velocity, rotation, and loads the car image (carImage.src = carObj.image).

The drawCar() method handles drawing the car image on the canvas. It calculates the transformation to center the car image at its xPos, yPos, and then applies rotation using ctx.rotate(heading) before drawing the image.

The calcPos() method is the physics engine for the car. It simulates car movement by:

  1. Applying drag to reduce speed over time.
  2. Calculating the heading based on rotation.
  3. Applying acceleration based on user input (_accel).
  4. Calculating spin based on velocity and turn input (_turn).
  5. Calculating torque to simulate how turning changes the car’s direction.
  6. Limiting torque to simulate spinning out.
  7. Applying torque to update velocity.
  8. Updating car position based on velocity and heading.

The rest of the car() function defines setter and getter methods for car properties and input handling functions like forwardOn, forwardOff, rightTurnOn, rightTurnOff which are called in response to keyboard events.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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);
    }

The hud() function is responsible for drawing the Heads-Up Display. In this simple game, it only displays the current FPS counter at the top-left corner of the canvas. It sets the fill color, font, text alignment, and baseline for the text before using ctx.fillText() to draw the FPS value.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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;
        }
    }

The onKeyDown() and onKeyUp() functions handle keyboard input. They are event listeners attached to the document to detect when keys are pressed down and released. A switch statement checks the evt.keyCode to identify which key was pressed (arrow keys in this case). Based on the key, it calls the corresponding car control functions (car1.forwardOn(), car1.leftTurnOn(), etc.) to initiate car actions or stop them when the key is released.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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); 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];
        }
    }

Alt text: Race track map tileset image used in the HTML car racing game, showing different tile types

This section defines the game map and related functions. track1 is a JavaScript object that holds the map data:

  • tileset: Path to the tileset image (images/tileset-g-100.jpg).
  • tileWidth, tileHeight: Dimensions of each tile in pixels.
  • startTile: Starting tile coordinates for the car.
  • startFacing: Initial direction the car is facing (‘n’, ‘s’, ‘w’, ‘e’).
  • rgt: A 2D array representing the map layout. Each element is a string like ‘x,y’ referencing a tile in the tileset.
  • trackMarkers: Array of tile coordinates for track markers (potentially for checkpoints or AI pathfinding).

The track() function is a constructor for track objects. It takes a track data object (like track1) and sets up the track. It loads the tileset image and provides methods to access track properties and calculate the starting position of the car based on startTile and startFacing.

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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
    }

This section defines car properties. car1 and car2 are JavaScript objects defining properties for different car types (though car2 is not actually used in the game code provided). Properties include:

  • image: Path to the car image (images/carred.png).
  • max_speed: Maximum acceleration.
  • max_torque: Maximum turning force (influences spin-out).
  • turn_radius: Turning sharpness (smaller value = sharper turn).
  • drag: Speed deceleration factor.
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     * 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();
    init();

    // *** Bind keys
    $(document).keydown(onKeyDown);
    $(document).keyup(onKeyUp);
});

This final “RUN” section initializes and starts the game.

  1. It creates a canvas object using the canvas() constructor.
  2. Gets the 2D rendering context ctx.
  3. Clears the canvas initially.
  4. Creates a trackObj using the track() constructor with track1 data.
  5. Gets the starting x, y position and direction for the car from trackObj.setStartPos().
  6. Creates the car1 object using the car() constructor, passing in the starting position, direction, and car1 properties.
  7. Calls init() to start the game loop.
  8. Attaches the onKeyDown and onKeyUp functions as event listeners to the document for keyboard input using jQuery.
</script>
</head>
<body>
    <canvas id="race-track" width="768" height="640" style="border:1px solid black;"></canvas>
</body>
</html>

Finally, the HTML body contains the <canvas> element with the ID “race-track”, sets its width and height, and includes inline CSS to add a border for visual clarity. This <canvas> is where all the game graphics are rendered.

Conclusion

This code provides a foundational understanding of “car racing coding in HTML”. It demonstrates how to set up a canvas, create a game loop for animation, render a tile-based map and a controllable car, handle keyboard input, and implement basic game physics. While simple, it encapsulates the core principles of 2D game development in the browser and serves as an excellent starting point for anyone interested in creating web-based racing games. You can expand upon this foundation by adding features like collision detection, more complex tracks, AI opponents, scoring systems, and more refined game mechanics to create a richer and more engaging car racing experience directly in HTML.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *