Building a 3D Indian Racing Game using JavaScript & Three.js 🚀

 BASIC CODE:-

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Indian Pro Racer 3D - Ultra</title>

    <style>

        body { margin: 0; overflow: hidden; background: #1a1a1a; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }

        #ui {

            position: absolute; top: 20px; left: 20px; color: white;

            background: rgba(0, 0, 0, 0.7); padding: 20px; border-radius: 12px;

            border-left: 5px solid #ff4500; pointer-events: none;

        }

        .stats { font-size: 24px; font-weight: bold; color: #00ffcc; }

        #speedometer { position: absolute; bottom: 30px; right: 30px; color: white; font-size: 40px; font-weight: bold; text-shadow: 2px 2px #000; }

    </style>

</head>

<body>


    <div id="ui">

        <div style="font-size: 1.5em; margin-bottom: 10px;">🇮🇳 Indian Road Sim v2.0</div>

        <p><b>W/A/S/D</b> or <b>Arrows</b> to Drive</p>

        <div class="stats" id="score">Distance: 0m</div>

    </div>

    <div id="speedometer"><span id="speed-val">0</span> km/h</div>


    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

    <script>

        // --- CONFIG & GLOBALS ---

        let scene, camera, renderer, playerCar, clock;

        let npcVehicles = [];

        let environmentObjects = [];

        let keys = {};

        let speed = 0;

        const maxSpeed = 0.8;

        const accel = 0.015;

        const friction = 0.97;

        let distanceTravelled = 0;


        init();

        animate();


        function init() {

            scene = new THREE.Scene();

            scene.background = new THREE.Color(0x87CEEB); 

            scene.fog = new THREE.FogExp2(0x87CEEB, 0.015);


            camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);

            renderer = new THREE.WebGLRenderer({ antialias: true });

            renderer.setSize(window.innerWidth, window.innerHeight);

            renderer.shadowMap.enabled = true;

            document.body.appendChild(renderer.domElement);


            clock = new THREE.Clock();


            // Lighting

            const ambient = new THREE.AmbientLight(0xffffff, 0.7);

            scene.add(ambient);

            const sun = new THREE.DirectionalLight(0xffffff, 1.0);

            sun.position.set(50, 100, 50);

            sun.castShadow = true;

            scene.add(sun);


            // Create Player Car

            playerCar = createVehicle('car', 0xff0000, true);

            scene.add(playerCar);


            // Create Initial Ground/Road

            createRoad(0);

            createRoad(200);


            // Event Listeners

            window.addEventListener('keydown', e => keys[e.code] = true);

            window.addEventListener('keyup', e => keys[e.code] = false);

            window.addEventListener('resize', onWindowResize);

        }


        // --- MODEL CREATION FUNCTIONS ---


        function createVehicle(type, color, hasDriver = false) {

            const group = new THREE.Group();


            // Chassis

            const body = new THREE.Mesh(

                new THREE.BoxGeometry(type==='truck'? 2.5 : 1.8, type==='truck'? 2 : 0.8, type==='truck'? 6 : 4),

                new THREE.MeshStandardMaterial({ color: color })

            );

            body.position.y = type==='truck'? 1.5 : 0.6;

            body.castShadow = true;

            group.add(body);


            // Cabin

            if(type === 'car') {

                const cabin = new THREE.Mesh(

                    new THREE.BoxGeometry(1.4, 0.7, 1.8),

                    new THREE.MeshStandardMaterial({ color: 0x333333, transparent: true, opacity: 0.8 })

                );

                cabin.position.set(0, 1.3, -0.2);

                group.add(cabin);


                if(hasDriver) {

                    const head = new THREE.Mesh(new THREE.SphereGeometry(0.2), new THREE.MeshStandardMaterial({color: 0xffdbac}));

                    head.position.set(0.3, 1.3, 0); // Indian driver seat (Right side)

                    group.add(head);

                }

            } else if (type === 'truck') {

                const cargo = new THREE.Mesh(

                    new THREE.BoxGeometry(2.5, 1.5, 4),

                    new THREE.MeshStandardMaterial({ color: 0x2255ff }) // Blue Cargo

                );

                cargo.position.set(0, 1.8, -1);

                group.add(cargo);

            }


            // Wheels

            const wheelGeo = new THREE.CylinderGeometry(0.4, 0.4, 0.4, 12);

            const wheelMat = new THREE.MeshStandardMaterial({ color: 0x111111 });

            const wheelPos = type === 'truck' ? [1.3, -1.3, 1, -1, 2.5, -2.5] : [1, -1, 1.5, -1.5];

            

            for(let i=0; i < wheelPos.length; i+=2) {

                const w1 = new THREE.Mesh(wheelGeo, wheelMat);

                w1.rotation.z = Math.PI/2;

                w1.position.set(wheelPos[0], 0.4, wheelPos[i+2] || wheelPos[i+1]);

                group.add(w1);

                const w2 = w1.clone();

                w2.position.x *= -1;

                group.add(w2);

            }


            return group;

        }


        function createHouse(x, z) {

            const colors = [0xffcc00, 0xff66aa, 0x00ccff, 0x88ff88];

            const house = new THREE.Group();

            const base = new THREE.Mesh(

                new THREE.BoxGeometry(6, 5, 6),

                new THREE.MeshStandardMaterial({ color: colors[Math.floor(Math.random()*colors.length)] })

            );

            base.position.set(x, 2.5, z);

            house.add(base);


            const roof = new THREE.Mesh(

                new THREE.ConeGeometry(5, 3, 4),

                new THREE.MeshStandardMaterial({ color: 0x882222 })

            );

            roof.position.set(x, 6.5, z);

            roof.rotation.y = Math.PI/4;

            house.add(roof);

            

            scene.add(house);

            environmentObjects.push(house);

        }


        function createTree(x, z) {

            const tree = new THREE.Group();

            const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.3, 0.3, 2), new THREE.MeshStandardMaterial({color: 0x4d2600}));

            trunk.position.set(x, 1, z);

            const leaves = new THREE.Mesh(new THREE.SphereGeometry(1.5), new THREE.MeshStandardMaterial({color: 0x005500}));

            leaves.position.set(x, 3, z);

            tree.add(trunk, leaves);

            scene.add(tree);

            environmentObjects.push(tree);

        }


        function createRoad(zPos) {

            const roadGeo = new THREE.PlaneGeometry(20, 200);

            const roadMat = new THREE.MeshStandardMaterial({ color: 0x333333 });

            const road = new THREE.Mesh(roadGeo, roadMat);

            road.rotation.x = -Math.PI / 2;

            road.position.z = zPos;

            road.receiveShadow = true;

            scene.add(road);


            // Road Lines

            const lineGeo = new THREE.PlaneGeometry(0.5, 10);

            const lineMat = new THREE.MeshStandardMaterial({ color: 0xffffff });

            for(let i=0; i<10; i++) {

                const line = new THREE.Mesh(lineGeo, lineMat);

                line.rotation.x = -Math.PI/2;

                line.position.set(0, 0.02, zPos + (i*20) - 90);

                scene.add(line);

            }


            // Side Grass

            const grassGeo = new THREE.PlaneGeometry(100, 200);

            const grassMat = new THREE.MeshStandardMaterial({ color: 0x228B22 });

            const grass = new THREE.Mesh(grassGeo, grassMat);

            grass.rotation.x = -Math.PI/2;

            grass.position.set(0, -0.1, zPos);

            scene.add(grass);


            // Add Houses and Trees on sides

            for(let i=0; i<5; i++) {

                createHouse(15 + Math.random()*10, zPos - 100 + (i*40));

                createHouse(-15 - Math.random()*10, zPos - 100 + (i*40));

                createTree(10, zPos - 80 + (i*40));

                createTree(-10, zPos - 80 + (i*40));

            }

        }


        // --- TRAFFIC ENGINE ---


        function spawnTraffic() {

            if(npcVehicles.length < 15) {

                const types = ['car', 'truck', 'car'];

                const type = types[Math.floor(Math.random()*types.length)];

                const color = Math.random() * 0xffffff;

                const v = createVehicle(type, color);

                

                v.position.z = playerCar.position.z - 150 - (Math.random() * 100);

                v.position.x = (Math.random() > 0.5 ? 4.5 : -4.5); // Lane logic

                v.userData = { speed: 0.2 + Math.random() * 0.3 };

                

                scene.add(v);

                npcVehicles.push(v);

            }

        }


        // --- CORE LOOP ---


        function updatePhysics() {

            // Player Movement

            if (keys['ArrowUp'] || keys['KeyW']) speed += accel;

            if (keys['ArrowDown'] || keys['KeyS']) speed -= accel;

            

            speed *= friction;

            if (speed > maxSpeed) speed = maxSpeed;

            if (speed < -0.1) speed = -0.1;


            if (Math.abs(speed) > 0.01) {

                if (keys['ArrowLeft'] || keys['KeyA']) playerCar.rotation.y += 0.04;

                if (keys['ArrowRight'] || keys['KeyD']) playerCar.rotation.y -= 0.04;

            }


            playerCar.translateZ(-speed);

            distanceTravelled += speed;


            // Camera follow

            const offset = new THREE.Vector3(0, 4, 10).applyMatrix4(playerCar.matrixWorld);

            camera.position.lerp(offset, 0.1);

            camera.lookAt(playerCar.position.x, playerCar.position.y + 1, playerCar.position.z - 5);


            // NPC Movement

            npcVehicles.forEach((v, index) => {

                v.position.z -= v.userData.speed;

                // Cleanup

                if(v.position.z > playerCar.position.z + 50) {

                    scene.remove(v);

                    npcVehicles.splice(index, 1);

                }

                // Collision Simple

                if(playerCar.position.distanceTo(v.position) < 2.5) {

                    speed = -0.2; // Crash effect

                }

            });


            // Infinite World Logic

            if(Math.floor(distanceTravelled / 200) > (window.lastChunk || 0)) {

                window.lastChunk = Math.floor(distanceTravelled / 200);

                createRoad(playerCar.position.z - 400);

            }


            spawnTraffic();

            

            // UI Updates

            document.getElementById('score').innerText = `Distance: ${Math.floor(distanceTravelled)}m`;

            document.getElementById('speed-val').innerText = Math.floor(speed * 250);

        }


        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;

            camera.updateProjectionMatrix();

            renderer.setSize(window.innerWidth, window.innerHeight);

        }


        function animate() {

            requestAnimationFrame(animate);

            updatePhysics();

            renderer.render(scene, camera);

        }

    </script>*/

</body>

</html>

Post a Comment

Previous Post Next Post