3D坦克大战网页游戏完整源码

Vibe Coding 使用 AI 回答

代码问题:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>🎮 3D 坦克大战</title>
<!-- 使用 importmap 引入 Three.js -->
<script type="importmap">
    {
        "imports": {
            "three": "https://unpkg.com/[email protected]/build/three.module.js",
            "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
        }
    }
</script>

<style>
    /* ---------- 全局样式 ---------- */
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        user-select: none;
    }

    body {
        background: #0a0a1a;
        overflow: hidden;
        font-family: 'Segoe UI', Arial, sans-serif;
    }

    /* ---------- 游戏画布容器 ---------- */
    #game-container {
        width: 100vw;
        height: 100vh;
        display: block;
    }

    /* --------
代码解决方案:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>🎮 3D 坦克大战</title>

    <!-- 使用 importmap 引入 Three.js -->
    <script type="importmap">
        {
            "imports": {
                "three": "https://unpkg.com/[email protected]/build/three.module.js",
                "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
            }
        }
    </script>

    <style>
        /* ---------- 全局样式 ---------- */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            user-select: none;
        }

        body {
            background: #0a0a1a;
            overflow: hidden;
            font-family: 'Segoe UI', Arial, sans-serif;
        }

        /* ---------- 游戏画布容器 ---------- */
        #game-container {
            width: 100vw;
            height: 100vh;
            display: block;
            position: relative;
        }

        /* ---------- HUD 覆盖层 ---------- */
        #hud {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 10;
            color: #ffffff;
            font-family: 'Orbitron', /* 未来感字体 */
            text-shadow: 0 0 5px rgba(0, 255, 255, 0.7);
        }

        #hud-top {
            position: absolute;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 20px;
            align-items: center;
            font-size: 1.5rem;
        }

        #hud-bottom {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 20px;
            align-items: center;
            font-size: 1.2rem;
        }

        .hud-box {
            background: rgba(0, 0, 0, 0.5);
            padding: 10px 20px;
            border: 2px solid #00ffff;
            border-radius: 5px;
            text-align: center;
        }

        #health-bar {
            width: 200px;
            height: 20px;
            background: #333;
            border: 1px solid #00ffff;
            border-radius: 10px;
            overflow: hidden;
        }

        #health-fill {
            height: 100%;
            background: linear-gradient(to right, #ff0000, #ffff00, #00ff00);
            width: 100%;
            transition: width 0.3s ease-out;
        }

        #ammo-count {
            font-size: 1.5rem;
            color: #00ffff;
        }

        #crosshair {
            position: absolute;
            top: 50%;
            left: 50%;
            width: 30px;
            height: 30px;
            transform: translate(-50%, -50%);
            pointer-events: none;
            border: 2px solid rgba(255, 255, 255, 0.7);
            border-radius: 50%;
            box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
        }

        #crosshair::before,
        #crosshair::after {
            content: '';
            position: absolute;
            background: rgba(255, 255, 255, 0.7);
        }

        #crosshair::before {
            top: 50%;
            left: 50%;
            width: 2px;
            height: 10px;
            transform: translate(-50%, -50%);
        }

        #crosshair::after {
            top: 50%;
            left: 50%;
            width: 10px;
            height: 2px;
            transform: translate(-50%, -50%);
        }

        /* ---------- 加载动画 ---------- */
        #loading-screen {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: #0a0a1a;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            color: #00ffff;
            font-family: 'Orbitron', sans-serif;
        }

        #loading-text {
            font-size: 3rem;
            margin-bottom: 20px;
            text-shadow: 0 0 20px #00ffff;
            animation: pulse 1.5s infinite alternate;
        }

        #loading-bar {
            width: 300px;
            height: 10px;
            background: #333;
            border-radius: 5px;
            overflow: hidden;
        }

        #loading-progress {
            height: 100%;
            background: linear-gradient(to right, #00ffff, #ff00ff);
            width: 0%;
            transition: width 0.3s ease-in-out;
        }

        @keyframes pulse {
            0% {
                transform: scale(1);
                opacity: 0.7;
            }
            100% {
                transform: scale(1.1);
                opacity: 1;
            }
        }

        /* ---------- 游戏结束屏幕 ---------- */
        #game-over-screen {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            display: none;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            color: #ffffff;
            font-family: 'Orbitron', sans-serif;
        }

        #game-over-title {
            font-size: 4rem;
            margin-bottom: 20px;
            color: #ff0000;
            text-shadow: 0 0 20px #ff0000;
        }

        #game-over-score {
            font-size: 2rem;
            margin-bottom: 30px;
            color: #00ffff;
        }

        #restart-button {
            padding: 15px 30px;
            font-size: 1.5rem;
            background: rgba(0, 255, 255, 0.2);
            border: 2px solid #00ffff;
            color: #ffffff;
            cursor: pointer;
            border-radius: 5px;
            transition: background 0.3s ease;
        }

        #restart-button:hover {
            background: rgba(0, 255, 255, 0.5);
        }

        /* ---------- 控制提示 ---------- */
        #controls-hint {
            position: absolute;
            bottom: 20px;
            right: 20px;
            color: rgba(255, 255, 255, 0.5);
            font-size: 0.9rem;
            pointer-events: none;
        }
    </style>
</head>
<body>
    <!-- 游戏画布容器 -->
    <div id="game-container">
        <!-- HUD 覆盖层 -->
        <div id="hud">
            <div id="hud-top">
                <div class="hud-box">
                    <div>得分: <span id="score">0</span></div>
                </div>
                <div class="hud-box">
                    <div>生命值</div>
                    <div id="health-bar">
                        <div id="health-fill"></div>
                    </div>
                </div>
            </div>
            <div id="crosshair"></div>
            <div id="hud-bottom">
                <div class="hud-box">
                    <div>弹药: <span id="ammo-count">30 / 90</span></div>
                </div>
            </div>
            <div id="controls-hint">
                WASD 移动 | 鼠标瞄准射击 | R 装弹 | ESC 暂停
            </div>
        </div>
    </div>

    <!-- 加载屏幕 -->
    <div id="loading-screen">
        <div id="loading-text">加载中...</div>
        <div id="loading-bar">
            <div id="loading-progress"></div>
        </div>
    </div>

    <!-- 游戏结束屏幕 -->
    <div id="game-over-screen">
        <div id="game-over-title">游戏结束</div>
        <div id="game-over-score">最终得分: <span id="final-score">0</span></div>
        <button id="restart-button">重新开始</button>
    </div>

    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

        // ---------- 全局变量 ----------
        let scene, camera, renderer;
        let tank, ground;
        let bullets = [];
        let enemies = [];
        let particles = [];
        let score = 0;
        let health = 100;
        let ammo = 30;
        let maxAmmo = 30;
        let totalAmmo = 90;
        let gameRunning = true;

        const keys = {
            w: false,
            a: false,
            s: false,
            d: false,
            space: false,
            r: false,
        };

        // ---------- 初始化 ----------
        init();
        animate();

        function init() {
            // 场景
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x0a0a1a);
            scene.fog = new THREE.Fog(0x0a0a1a, 50, 200);

            // 相机
            camera = new THREE.PerspectiveCamera(
                75,
                window.innerWidth / window.innerHeight,
                0.1,
                1000
            );
            camera.position.set(0, 5, 10);

            // 渲染器
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = true;
            document.getElementById('game-container').appendChild(renderer.domElement);

            // 灯光
            const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
            scene.add(ambientLight);

            const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
            directionalLight.position.set(50, 50, 50);
            directionalLight.castShadow = true;
            directionalLight.shadow.mapSize.width = 2048;
            directionalLight.shadow.mapSize.height = 2048;
            scene.add(directionalLight);

            // 地面
            const groundGeometry = new THREE.PlaneGeometry(200, 200);
            const groundMaterial = new THREE.MeshStandardMaterial({
                color: 0x228b22,
                roughness: 0.8,
                metalness: 0.2,
            });
            ground = new THREE.Mesh(groundGeometry, groundMaterial);
            ground.rotation.x = -Math.PI / 2;
            ground.receiveShadow = true;
            scene.add(ground);

            // 坦克
            createTank();

            // 敌人
            for (let i = 0; i < 5; i++) {
                createEnemy();
            }

            // 事件监听
            window.addEventListener('resize', onWindowResize);
            window.addEventListener('keydown', onKeyDown);
);
            window.addEventListener('keyup', onKeyUp);
            window.addEventListener('mousemove', onMouseMove);
            window.addEventListener('click', onMouseClick);

            // 隐藏加载屏幕
            setTimeout(() => {
                document.getElementById('loading-screen').style.display = 'none';
            }, 2000);
        }

        function createTank() {
            // 简单坦克模型 (车体 + 炮塔)
            const tankGroup = new THREE.Group();

            // 车体
            const bodyGeometry = new THREE.BoxGeometry(4, 1.5, 2);
            const bodyMaterial = new THREE.MeshStandardMaterial({ color: 0x556b2f });
            const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
            body.castShadow = true;
            body.receiveShadow = true;
            tankGroup.add(body);

            // 炮塔
            const turretGeometry = new THREE.CylinderGeometry(1, 1.5, 1, 8);
            const turretMaterial = new THREE.MeshStandardMaterial({ color: 0x556b2f });
            const turret = new THREE.Mesh(turretGeometry, turretMaterial);
            turret.position.y = 1.5;
            turret.castShadow = true;
            tankGroup.add(turret);

            // 炮管
            const barrelGeometry = new THREE.CylinderGeometry(0.2, 0.2, 3, 8);
            const barrelMaterial = new THREE.MeshStandardMaterial({ color: 0x444444 });
            const barrel = new THREE.Mesh(barrelGeometry, barrelMaterial);
            barrel.rotation.z = Math.PI / 2;
            barrel.position.set(1.5, 1.5, 0);
            barrel.castShadow = true;
            tankGroup.add(barrel);

            tank = tankGroup;
            tank.position.y = 1;
            scene.add(tank);
        }

        function createBullet() {
            const bulletGeometry = new THREE.SphereGeometry(0.2, 8, 8);
            const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
            const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
            bullet.position.copy(tank.position);
            bullet.position.y += 1.5; // 炮塔高度
            bullet.velocity = new THREE.Vector3(0, 0, -1);
            bullet.velocity.applyQuaternion(tank.quaternion);
            bullet.velocity.multiplyScalar(50); // 子弹速度
            scene.add(bullet);
            bullets.push(bullet);
        }

        function createEnemy() {
            const enemyGroup = new THREE.Group();

            // 敌人车体
            const enemyBodyGeometry = new THREE.BoxGeometry(3, 1.5, 2);
            const enemyBodyMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
            const enemyBody = new THREE.Mesh(enemyBodyGeometry, enemyBodyMaterial);
            enemyBody.castShadow = true;
            enemyBody.receiveShadow = true;
            enemyGroup.add(enemyBody);

            // 敌人炮塔
            const enemyTurretGeometry = new THREE.CylinderGeometry(0.8, 1.2, 1, 8);
            const enemyTurretMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
            const enemyTurret = new THREE.Mesh(enemyTurretGeometry, enemyTurretMaterial);
            enemyTurret.position.y = 1.5;
            enemyTurret.castShadow = true;
            enemyGroup.add(enemyTurret);

            // 随机位置
            enemyGroup.position.set(
                (Math.random() - 0.5) * 100,
                1,
                (Math.random() - 0.5) * 100
            );
            enemyGroup.lookAt(tank.position); // 朝向玩家
            enemyGroup.userData = { health: 100 };
            scene.add(enemyGroup);
            enemies.push(enemyGroup);
        }

        function createExplosion(position) {
            const particleCount = 50;
            for (let i = 0; i < particleCount; i++) {
                const particleGeometry = new THREE.SphereGeometry(0.1, 4, 4);
                const particleMaterial = new THREE.MeshBasicMaterial({
                    color: new THREE.Color(
                        Math.random() * 0.5 + 0.5,
                        Math.random() * 0.3,
                        0
                    ),
                });
                const particle = new THREE.Mesh(particleGeometry, particleMaterial);
                particle.position.copy(position);
                particle.velocity = new THREE.Vector3(
                    (Math.random() - 0.5) * 10,
                    Math.random() * 5 + 2,
                    (Math.random() - 0.5) * 10
                );
                scene.add(particle);
                particles.push(particle);
            }
        }

        function updateBullets() {
            for (let i = bullets.length - 1; i >= 0; i--) {
                const bullet = bullets[i];
                bullet.position.add(bullet.velocity.clone().multiplyScalar(0.016)); // 60fps

                // 检查碰撞
                for (let j = enemies.length - 1; j >= 0; j--) {
                    const enemy = enemies[j];
                    if (
                        bullet.position.distanceTo(enemy.position) <
                        3 /* 敌人半径 */
                    ) {
                        createExplosion(enemy.position);
                        scene.remove(enemy);
                        enemies.splice(j, 1);
                        scene.remove(bullet);
                        bullets.splice(i, 1);
                        score += 100;
                        document.getElementById('score').textContent = score;
                        break;
                    }
                }

                // 超出范围
                if (bullet.position.length() > 100) {
                    scene.remove(bullet);
                    bullets.splice(i, 1);
                }
            }
        }

        function updateEnemies() {
            for (let i = enemies.length - 1; i >= 0; i--) {
                const enemy = enemies[i];
                enemy.lookAt(tank.position);
                enemy.position.add(
                    new THREE.Vector3(0, 0, -0.05).applyQuaternion(enemy.quaternion)
                );

                // 简单 AI:每隔一段时间射击
                if (Math.random() < 0.001) {
                    // 敌人射击逻辑可以在这里添加
                }

                // 检查与玩家碰撞
                if (enemy.position.distanceTo(tank.position) < 5) {
                    health -= 1;
                    document.getElementById('health-fill').style.width = health + '%';
                    if (health <= 0) {
                        gameOver();
                    }
                }
            }

            // 补充敌人
            if (enemies.length < 5 && Math.random() < 0.01) {
                createEnemy();
            }
        }

        function updateParticles() {
            for (let i = particles.length - 1; i >= 0; i--) {
                const particle = particles[i];
                particle.position.add(particle.velocity.clone().multiplyScalar(0.016));
                particle.velocity.y -= 0.1; // 重力
                particle.material.opacity -= 0.02;
                if (particle.material.opacity <= 0) {
                    scene.remove(particle);
                    particles.splice(i, 1);
                }
            }
        }

        function updateTank() {
            const speed = 0.5;
            const rotSpeed = 0.05;

            if (keys.w) {
                tank.position.add(
                    new THREE.Vector3(0, 0, -speed).applyQuaternion(tank.quaternion)
                );
            }
            if (keys.s) {
                tank.position.add(
                    new THREE.Vector3(0, 0, speed).applyQuaternion(tank.quaternion)
                );
            }
            if (keys.a) {
                tank.rotation.y += rotSpeed;
            }
            if (keys.d) {
                tank.rotation.y -= rotSpeed;
            }

            // 限制在地面
            if (tank.position.x > 95) tank.position.x = 95;
            if (tank.position.x < -95) tank.position.x = -95;
            if (tank.position.z > 95) tank.position.z = 95;
            if (tank.position.z < -95) tank.position.z = -95;

            // 相机跟随
            camera.position.x = tank.position.x;
            camera.position.z = tank.position.z + 10;
            camera.position.y = tank.position.y + 5;
            camera.lookAt(tank.position);
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        function onKeyDown(event) {
            if (!gameRunning) return;
            switch (event.key.toLowerCase()) {
                case 'w':
                    keys.w = true;
                    break;
                case 'a':
                    keys.a = true;
                    break;
                case 's':
                    keys.s = true;
                    break;
                case 'd':
                    keys.d = true;
                    break;
                case ' ':
                    keys.space = true;
                    fireBullet();
                    break;
                case 'r':
                    reload();
                    break;
            }
        }

        function onKeyUp(event) {
            switch (event.key.toLowerCase()) {
                case 'w':
                    keys.w = false;
                    break;
                case 'a':
                    keys.a = false;
                    break;
                case 's':
                    keys.s = false;
                    break;
                case 'd':
                    keys.d = false;
                    break;
                case ' ':
                    keys.space = false;
                    break;
            }
        }

        function onMouseMove(event) {
            if (!gameRunning) return;
            const mouseX = (event.clientX / window.innerWidth) * 2 - 1;
            const mouseY = -(event.clientY / window.innerHeight) * 2 + 1;
            // 简单的炮塔跟随鼠标 (这里简化处理)
            tank.rotation.y = -mouseX * Math.PI;
        }

        function onMouseClick() {
            if (!gameRunning) return;
            fireBullet();
        }

        function fireBullet() {
            if (ammo > 0) {
                createBullet();
                ammo--;
                updateAmmoDisplay();
            } else {
                reload();
            }
        }

        function reload() {
            if (totalAmmo > 0) {
                const needed = maxAmmo - ammo;
                const toLoad = Math.min(needed, totalAmmo);
                ammo += toLoad;
                totalAmmo -= toLoad;
                updateAmmoDisplay();
            }
        }

        function updateAmmoDisplay() {
            document.getElementById('ammo-count').textContent = `${ammo} / ${totalAmmo}`;
        }

        function gameOver() {
            gameRunning = false;
            document.getElementById('final-score').textContent = score;
            document.getElementById('game-over-screen').style.display = 'flex';
        }

        function restartGame() {
            // 重置游戏状态
            score = 0;
            health = 100;
            ammo = 30;
            totalAmmo = 90;
            gameRunning = true;
            document.getElementById('score').textContent = score;
            document.getElementById('health-fill').style.width = '100%';
            updateAmmoDisplay();

            // 清除战场
            for (const bullet of bullets) {
                scene.remove(bullet);
            }
            bullets = [];
            for (const enemy of enemies) {
                scene.remove(enemy);
            }
            enemies = [];
            for (const particle of particles) {
                scene.remove(particle);
            }
            particles = [];

            // 重置坦克位置
            tank.position.set(0, 1, 0);
            tank.rotation.set(0, 0, 0);

            // 重新创建敌人
            for (let i = 0; i < 5; i++) {
                createEnemy();
            }

            document.getElementById('game-over-screen').style.display = 'none';
        }

        document.getElementById('restart-button').addEventListener('click', restartGame);

        function animate() {
            if (!gameRunning) return;
            requestAnimationFrame(animate);

            updateTank();
            updateBullets();
            updateEnemies();
            updateParticles();

            renderer.render(scene, camera);
        }
    </script>
</body>
</html>