forms

Urban Havoc 3D is a high-octane, endless driving and combat game

Urban Havoc 3D is a high-octane, endless driving and combat game set on a sprawling, sun-drenched coastal highway. You take control of a high-performance sports car, maneuvering through heavy traffic and urban obstacles while fending off waves of hostile vehicles.

Key Features:

Dynamic Combat: Use your vehicle's mounted weaponry to clear the road and rack up points.

Responsive Controls: Includes a custom virtual joystick for smooth mobile steering and physical keyboard support for desktop play.

Persistent Challenge: Navigate a procedurally generated environment where the density of enemies and obstacles increases as you progress.

Sleek UI: Monitor your "Hull Integrity" and score via a minimalist, modern glassmorphism HUD.

Immersive Atmosphere: Features 3D lighting, real-time shadows, and a stylish low-poly urban aesthetic.

How to Play:

Desktop: Use W/A/S/D or Arrow Keys to drive and Spacebar to fire.

Mobile: Use the left-hand Joystick to steer and the right-hand Gas, ABS (Brake), and Fire buttons to control your speed and weapons.

Code Snippet

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Urban Havoc 3D: High Noon</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        body { margin: 0; overflow: hidden; background: #87CEEB; font-family: 'Segoe UI', sans-serif; touch-action: none; }
        canvas { display: block; }
        #ui-layer { pointer-events: none; }
        .touch-btn { pointer-events: auto; user-select: none; -webkit-tap-highlight-color: transparent; }
        #joystick-container { width: 140px; height: 140px; background: rgba(0,0,0,0.05); border-radius: 50%; border: 2px solid rgba(0,0,0,0.1); position: relative; }
        #joystick-knob { width: 50px; height: 50px; background: rgba(30,58,138,0.4); border-radius: 50%; position: absolute; left: 45px; top: 45px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); }
    </style>
</head>
<body>

    <!-- HUD Overlay -->
    <div id="ui-layer" class="absolute inset-0 flex flex-col justify-between p-4 z-10 text-slate-800">
        <div class="flex justify-between items-start pt-2">
            <div class="bg-white/80 backdrop-blur-md p-3 rounded-xl border border-white shadow-lg">
                <div class="text-[10px] uppercase tracking-widest text-blue-600 mb-1 font-bold">Hull Integrity</div>
                <div class="w-40 h-2 bg-slate-200 rounded-full overflow-hidden">
                    <div id="health-bar" class="h-full bg-gradient-to-r from-green-400 to-blue-500 transition-all duration-300" style="width: 100%"></div>
                </div>
            </div>
            <div class="bg-white/80 backdrop-blur-md p-3 rounded-xl border border-white shadow-lg text-right">
                <div class="text-[10px] uppercase tracking-widest text-blue-600 font-bold">Score</div>
                <div id="score-val" class="text-2xl font-black font-mono tracking-tighter italic">00000</div>
            </div>
        </div>

        <!-- Mobile Controls -->
        <div class="flex justify-between items-end mb-8 px-2">
            <div class="touch-btn" id="joystick-zone">
                <div id="joystick-container">
                    <div id="joystick-knob"></div>
                </div>
            </div>

            <div class="flex flex-col gap-3">
                <div class="flex gap-3">
                    <div id="btn-brake" class="touch-btn w-16 h-16 rounded-full bg-red-500/80 border-4 border-white flex items-center justify-center text-[10px] font-black text-white active:bg-red-700 shadow-md">ABS</div>
                    <div id="btn-gas" class="touch-btn w-20 h-20 rounded-full bg-blue-600/80 border-4 border-white flex items-center justify-center text-lg font-black italic text-white active:scale-95 shadow-lg">GAS</div>
                </div>
                <div id="btn-fire" class="touch-btn w-full h-16 rounded-xl bg-amber-500/90 border-4 border-white flex items-center justify-center text-xl font-black italic text-white active:bg-amber-600 shadow-lg">FIRE</div>
            </div>
        </div>
    </div>

    <script>
        let scene, camera, renderer, clock;
        let player, worldGroup;
        let enemies = [], bullets = [], scenery = [];
        let score = 0, health = 100;
        
        const input = { forward: 0, brake: 0, turn: 0, fire: false };
        const MAX_SPEED = 1.4, ACCEL = 0.04, FRICTION = 0.97;
        let velocity = 0;

        window.onload = init;

        function init() {
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x87CEEB); // Sky Blue
            scene.fog = new THREE.Fog(0x87CEEB, 20, 300);

            camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.shadowMap.enabled = true;
            document.body.appendChild(renderer.domElement);

            clock = new THREE.Clock();
            worldGroup = new THREE.Group();
            scene.add(worldGroup);

            // Bright Sunlight
            const ambient = new THREE.AmbientLight(0xffffff, 0.6);
            scene.add(ambient);
            const sun = new THREE.DirectionalLight(0xfff5e1, 1.2);
            sun.position.set(100, 200, 100);
            sun.castShadow = true;
            scene.add(sun);

            createGround();
            createPlayer();
            setupControls();
            
            for(let i=0; i<30; i++) spawnScenery(i * -20);

            animate();
        }

        function createGround() {
            // Main Road
            const roadGeo = new THREE.PlaneGeometry(30, 4000);
            const roadMat = new THREE.MeshPhongMaterial({ color: 0x444444 });
            const road = new THREE.Mesh(roadGeo, roadMat);
            road.rotation.x = -Math.PI / 2;
            road.receiveShadow = true;
            worldGroup.add(road);

            // Sidewalks/Sand
            const sandGeo = new THREE.PlaneGeometry(1000, 4000);
            const sandMat = new THREE.MeshPhongMaterial({ color: 0xdbca9a });
            const sand = new THREE.Mesh(sandGeo, sandMat);
            sand.rotation.x = -Math.PI / 2;
            sand.position.y = -0.1;
            worldGroup.add(sand);

            // Markings
            for(let i=0; i<150; i++) {
                const markGeo = new THREE.PlaneGeometry(0.6, 5);
                const markMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
                const mark = new THREE.Mesh(markGeo, markMat);
                mark.rotation.x = -Math.PI / 2;
                mark.position.set(0, 0.05, i * -25);
                worldGroup.add(mark);
            }
        }

        function spawnScenery(zPos) {
            const side = Math.random() > 0.5 ? 1 : -1;
            const xOffset = side * (25 + Math.random() * 15);
            
            if (Math.random() > 0.4) {
                // Modern Building
                const h = 15 + Math.random() * 50;
                const w = 10 + Math.random() * 10;
                const geo = new THREE.BoxGeometry(w, h, w);
                const colors = [0xffffff, 0xe0e0e0, 0xd0d0d0, 0xb0c4de];
                const mat = new THREE.MeshPhongMaterial({ color: colors[Math.floor(Math.random()*colors.length)] });
                const b = new THREE.Mesh(geo, mat);
                b.position.set(xOffset, h/2, zPos);
                b.castShadow = true;
                b.receiveShadow = true;
                
                // Windows (Indented boxes or simple dark panes)
                const winRowGeo = new THREE.BoxGeometry(w + 0.2, 1, w + 0.2);
                const winMat = new THREE.MeshPhongMaterial({ color: 0x333344 });
                for(let j=1; j<h/5; j++) {
                    const win = new THREE.Mesh(winRowGeo, winMat);
                    win.position.y = (j * 5) - h/2;
                    b.add(win);
                }
                scenery.push(b);
                worldGroup.add(b);
            } else {
                // Summer Tree
                const tree = new THREE.Group();
                const trunkGeo = new THREE.CylinderGeometry(0.5, 0.7, 5);
                const trunkMat = new THREE.MeshPhongMaterial({ color: 0x5d4037 });
                const trunk = new THREE.Mesh(trunkGeo, trunkMat);
                trunk.castShadow = true;
                tree.add(trunk);

                const foliageGeo = new THREE.SphereGeometry(3, 8, 8);
                const foliageMat = new THREE.MeshPhongMaterial({ color: 0x2e7d32 });
                const fol = new THREE.Mesh(foliageGeo, foliageMat);
                fol.position.y = 4;
                fol.castShadow = true;
                tree.add(fol);

                tree.position.set(side * 20, 2.5, zPos);
                scenery.push(tree);
                worldGroup.add(tree);
            }
        }

        function createPlayer() {
            player = new THREE.Group();
            
            // Sports Car Body
            const body = new THREE.Mesh(
                new THREE.BoxGeometry(1.8, 0.7, 3.5),
                new THREE.MeshPhongMaterial({ color: 0xcc0000, shininess: 100 })
            );
            body.castShadow = true;
            player.add(body);

            // Roof
            const roof = new THREE.Mesh(
                new THREE.BoxGeometry(1.4, 0.5, 1.8),
                new THREE.MeshPhongMaterial({ color: 0x111111 })
            );
            roof.position.set(0, 0.5, 0.2);
            player.add(roof);

            // Wheels
            const wheelGeo = new THREE.CylinderGeometry(0.5, 0.5, 0.5, 16);
            const wheelMat = new THREE.MeshPhongMaterial({ color: 0x222222 });
            const wheelPos = [[1, -0.2, 1.2], [-1, -0.2, 1.2], [1, -0.2, -1.2], [-1, -0.2, -1.2]];
            wheelPos.forEach(p => {
                const w = new THREE.Mesh(wheelGeo, wheelMat);
                w.rotation.z = Math.PI / 2;
                w.position.set(...p);
                player.add(w);
            });

            player.position.y = 0.7;
            scene.add(player);
        }

        function spawnEnemy() {
            if (enemies.length < 15 && Math.random() < 0.04) {
                const e = new THREE.Group();
                const body = new THREE.Mesh(
                    new THREE.BoxGeometry(2, 1.2, 4),
                    new THREE.MeshPhongMaterial({ color: 0xffffff })
                );
                e.add(body);
                // Windows
                const glass = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.5, 2), new THREE.MeshPhongMaterial({color: 0x333333}));
                glass.position.set(0, 0.6, 0.5);
                e.add(glass);

                e.position.set((Math.random() - 0.5) * 25, 0.8, player.position.z - 200);
                e.speed = 0.4 + Math.random() * 0.6;
                e.castShadow = true;
                enemies.push(e);
                scene.add(e);
            }
        }

        function setupControls() {
            // Keyboard
            window.addEventListener('keydown', (e) => {
                if (e.code === 'KeyW' || e.code === 'ArrowUp') input.forward = 1;
                if (e.code === 'KeyS' || e.code === 'ArrowDown') input.brake = 1;
                if (e.code === 'KeyA' || e.code === 'ArrowLeft') input.turn = -1;
                if (e.code === 'KeyD' || e.code === 'ArrowRight') input.turn = 1;
                if (e.code === 'Space') input.fire = true;
            });
            window.addEventListener('keyup', (e) => {
                if (e.code === 'KeyW' || e.code === 'ArrowUp') input.forward = 0;
                if (e.code === 'KeyS' || e.code === 'ArrowDown') input.brake = 0;
                if (e.code === 'KeyA' || e.code === 'ArrowLeft' || e.code === 'KeyD' || e.code === 'ArrowRight') input.turn = 0;
                if (e.code === 'Space') input.fire = false;
            });

            // Touch
            const btnGas = document.getElementById('btn-gas');
            const btnBrake = document.getElementById('btn-brake');
            const btnFire = document.getElementById('btn-fire');

            btnGas.ontouchstart = (e) => { e.preventDefault(); input.forward = 1; };
            btnGas.ontouchend = () => input.forward = 0;
            btnBrake.ontouchstart = (e) => { e.preventDefault(); input.brake = 1; };
            btnBrake.ontouchend = () => input.brake = 0;
            btnFire.ontouchstart = (e) => { e.preventDefault(); input.fire = true; };
            btnFire.ontouchend = () => input.fire = false;

            const knob = document.getElementById('joystick-knob');
            const container = document.getElementById('joystick-container');
            const rect = container.getBoundingClientRect();
            const center = rect.width / 2;

            container.addEventListener('touchmove', (e) => {
                e.preventDefault();
                const touch = e.touches[0];
                const dx = Math.max(-50, Math.min(50, touch.clientX - (rect.left + center)));
                knob.style.transform = `translateX(${dx}px)`;
                input.turn = dx / 50;
            });
            container.addEventListener('touchend', () => {
                knob.style.transform = `translateX(0px)`;
                input.turn = 0;
            });
        }

        function animate() {
            requestAnimationFrame(animate);
            const dt = clock.getDelta();

            // Physics
            if (input.forward) velocity += ACCEL;
            if (input.brake) velocity -= ACCEL * 2;
            velocity *= FRICTION;
            velocity = Math.max(-0.2, Math.min(velocity, MAX_SPEED));

            player.rotation.y -= input.turn * 0.05 * (velocity + 0.1);
            player.translateZ(-velocity);

            // Camera
            const targetCam = player.position.clone().add(new THREE.Vector3(0, 4, 10).applyQuaternion(player.quaternion));
            camera.position.lerp(targetCam, 0.1);
            camera.lookAt(player.position.clone().add(new THREE.Vector3(0, 1, -5).applyQuaternion(player.quaternion)));

            spawnEnemy();
            if (Math.random() < 0.1) spawnScenery(player.position.z - 250);

            // Projectiles
            if (input.fire && clock.elapsedTime % 0.2 < 0.05) {
                const b = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.3, 1), new THREE.MeshBasicMaterial({color: 0x000000}));
                b.position.copy(player.position).add(new THREE.Vector3(0, 0.5, -2).applyQuaternion(player.quaternion));
                b.velocity = new THREE.Vector3(0,0,-4).applyQuaternion(player.quaternion);
                bullets.push(b);
                scene.add(b);
            }

            for(let i=bullets.length-1; i>=0; i--) {
                bullets[i].position.add(bullets[i].velocity);
                if (bullets[i].position.distanceTo(player.position) > 200) {
                    scene.remove(bullets[i]);
                    bullets.splice(i, 1);
                }
            }

            // Enemies
            for(let i=enemies.length-1; i>=0; i--) {
                const e = enemies[i];
                e.position.z += e.speed;
                
                bullets.forEach((b, bi) => {
                    if (b.position.distanceTo(e.position) < 3) {
                        scene.remove(e);
                        enemies.splice(i, 1);
                        scene.remove(b);
                        bullets.splice(bi, 1);
                        score += 100;
                        document.getElementById('score-val').innerText = score.toString().padStart(5, '0');
                    }
                });

                if (e.position.distanceTo(player.position) < 2.5) {
                    health -= 1;
                    document.getElementById('health-bar').style.width = Math.max(0, health) + '%';
                    
                    // Simple knockback away from player
                    const bounceDir = e.position.x > player.position.x ? 1 : -1;
                    e.position.x += bounceDir * 5;
                }

                if (e.position.z > player.position.z + 100) {
                    scene.remove(e);
                    enemies.splice(i, 1);
                }
            }

            // Cleanup scenery
            for(let i=scenery.length-1; i>=0; i--) {
                if (scenery[i].position.z > player.position.z + 100) {
                    worldGroup.remove(scenery[i]);
                    scenery.splice(i, 1);
                }
            }

            renderer.render(scene, camera);
        }

        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
    </script>
</body>
</html>

Media & Assets

Urban Havoc 3D is a high-octane, endless driving and combat game
Resource ID: #00029
Posted: December 22, 2025
© 2025 Your Code Library.