1. 角色控制器制作

一个完整的Three.js角色控制器,应该包含角色移动、物理碰撞检测和交互功能。这个实现使用Cannon.js作为物理引擎,并提供直观的UI控制。

1.1. 实现思路

  1. 创建基础Three.js场景(场景、相机、渲染器、灯光)

  2. 集成Cannon.js物理世界

  3. 创建角色控制器(WASD/方向键控制)

  4. 实现角色物理(重力、碰撞、跳跃)

  5. 添加交互对象(可拾取物品、障碍物)

  6. 实现UI控制面板

下面是完整实现代码:
html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js 角色控制器 - 包含交互与物理</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            overflow: hidden;
            background-color: #1a1a2e;
            color: #fff;
        }

        #container {
            position: relative;
            width: 100vw;
            height: 100vh;
        }

        #infoPanel {
            position: absolute;
            top: 20px;
            left: 20px;
            background-color: rgba(0, 0, 0, 0.7);
            padding: 15px;
            border-radius: 10px;
            z-index: 100;
            max-width: 300px;
            backdrop-filter: blur(5px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        #infoPanel h2 {
            color: #4cc9f0;
            margin-bottom: 10px;
            font-size: 1.4rem;
        }

        #infoPanel p {
            margin: 8px 0;
            line-height: 1.5;
            font-size: 0.95rem;
        }

        .key {
            display: inline-block;
            background-color: #333;
            padding: 3px 8px;
            border-radius: 4px;
            font-family: monospace;
            margin: 0 3px;
            border: 1px solid #555;
        }

        .controls {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 15px;
        }

        .control-group {
            background-color: rgba(40, 40, 60, 0.8);
            padding: 10px;
            border-radius: 8px;
            flex-grow: 1;
            min-width: 120px;
        }

        .control-group h3 {
            color: #f72585;
            font-size: 1rem;
            margin-bottom: 8px;
        }

        #stats {
            position: absolute;
            bottom: 20px;
            right: 20px;
            background-color: rgba(0, 0, 0, 0.5);
            padding: 10px 15px;
            border-radius: 8px;
            font-family: monospace;
            font-size: 0.9rem;
            z-index: 100;
        }

        #interactionPanel {
            position: absolute;
            top: 20px;
            right: 20px;
            background-color: rgba(0, 0, 0, 0.7);
            padding: 15px;
            border-radius: 10px;
            z-index: 100;
            max-width: 300px;
            backdrop-filter: blur(5px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .collectible {
            display: flex;
            align-items: center;
            margin: 8px 0;
            padding: 5px;
            background-color: rgba(30, 30, 50, 0.8);
            border-radius: 5px;
        }

        .collectible-icon {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            margin-right: 10px;
        }

        .red { background-color: #f72585; }
        .blue { background-color: #4361ee; }
        .green { background-color: #4cc9f0; }
        .yellow { background-color: #ffd166; }

        #gameOverlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 200;
            background-color: rgba(0, 0, 0, 0.8);
            opacity: 1; /* 初始显示为可见 */
            pointer-events: all;
            transition: opacity 0.5s;
        }

        #gameOverlay.hidden {
            opacity: 0;
            pointer-events: none;
        }

        .overlay-content {
            background-color: #162447;
            padding: 30px;
            border-radius: 15px;
            text-align: center;
            max-width: 500px;
            border: 2px solid #4cc9f0;
        }

        .overlay-content h2 {
            color: #f72585;
            margin-bottom: 20px;
            font-size: 2rem;
        }

        .overlay-content p {
            margin-bottom: 20px;
            font-size: 1.1rem;
            line-height: 1.6;
        }

        button {
            background-color: #4361ee;
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 8px;
            font-size: 1rem;
            cursor: pointer;
            transition: background-color 0.3s;
            margin-top: 10px;
        }

        button:hover {
            background-color: #3a56d4;
        }

        #canvas {
            display: block;
        }

        @media (max-width: 768px) {
            #infoPanel, #interactionPanel {
                max-width: 250px;
                padding: 10px;
                font-size: 0.9rem;
            }

            .controls {
                flex-direction: column;
            }
        }
    </style>
</head>
<body>
    <div id="container">
        <canvas id="canvas"></canvas>

        <div id="infoPanel">
            <h2>角色控制器演示</h2>
            <p>使用 <span class="key">W</span><span class="key">A</span><span class="key">S</span><span class="key">D</span> 或方向键移动角色</p>
            <p><span class="key">空格键</span> 跳跃</p>
            <p><span class="key">E</span> 与物品交互</p>
            <p><span class="key">ESC</span> 显示/隐藏菜单</p>

            <div class="controls">
                <div class="control-group">
                    <h3>移动控制</h3>
                    <p>前进: <span class="key">W</span><span class="key"></span></p>
                    <p>后退: <span class="key">S</span><span class="key"></span></p>
                    <p>左移: <span class="key">A</span><span class="key"></span></p>
                    <p>右移: <span class="key">D</span><span class="key"></span></p>
                </div>

                <div class="control-group">
                    <h3>动作控制</h3>
                    <p>跳跃: <span class="key">空格</span></p>
                    <p>交互: <span class="key">E</span></p>
                    <p>菜单: <span class="key">ESC</span></p>
                </div>
            </div>
        </div>

        <div id="interactionPanel">
            <h3>可收集物品</h3>
            <div id="collectiblesList">
                <!-- 收集物品列表将通过JS动态生成 -->
            </div>
            <p id="collectionStatus">已收集: 0/4</p>
        </div>

        <div id="stats">
            <div>FPS: <span id="fpsCounter">0</span></div>
            <div>位置: <span id="positionDisplay">(0, 0, 0)</span></div>
            <div>速度: <span id="velocityDisplay">(0, 0, 0)</span></div>
        </div>

        <div id="gameOverlay">
            <div class="overlay-content">
                <h2>Three.js 角色控制器</h2>
                <p>这是一个完整的角色控制器演示,包含物理引擎和交互功能。</p>
                <p>使用WASD键移动角色,空格键跳跃,E键与物品交互。</p>
                <p>收集所有彩色球体来完成游戏!</p>
                <button id="startButton">开始游戏</button>
                <button id="resetButton">重置游戏</button>
            </div>
        </div>
    </div>

    <!-- 使用正确的CDN链接 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/cannon-es@0.20.0/dist/cannon-es.js"></script>

    <script>
        // 主应用程序
        class CharacterController {
            constructor() {
                // 基本变量
                this.collectedItems = 0;
                this.totalItems = 4;
                this.keys = {};
                this.isJumping = false;
                this.isMenuVisible = true;
                this.clock = new THREE.Clock();
                this.prevTime = 0;
                this.fps = 0;

                // 初始化
                this.initScene();
                this.initPhysics();
                this.initCharacter();
                this.initEnvironment();
                this.initCollectibles();
                this.initUI();
                this.initEventListeners();

                // 开始动画循环
                this.animate();
            }

            initScene() {
                // 创建场景
                this.scene = new THREE.Scene();
                this.scene.background = new THREE.Color(0x1a1a2e);
                this.scene.fog = new THREE.Fog(0x1a1a2e, 10, 50);

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

                // 创建渲染器
                const canvas = document.getElementById('canvas');
                this.renderer = new THREE.WebGLRenderer({ 
                    canvas: canvas,
                    antialias: true 
                });
                this.renderer.setSize(window.innerWidth, window.innerHeight);
                this.renderer.shadowMap.enabled = true;
                this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

                // 添加环境光
                const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
                this.scene.add(ambientLight);

                // 添加方向光
                const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
                directionalLight.position.set(10, 20, 5);
                directionalLight.castShadow = true;
                directionalLight.shadow.camera.left = -30;
                directionalLight.shadow.camera.right = 30;
                directionalLight.shadow.camera.top = 30;
                directionalLight.shadow.camera.bottom = -30;
                directionalLight.shadow.mapSize.width = 2048;
                directionalLight.shadow.mapSize.height = 2048;
                this.scene.add(directionalLight);

                // 添加辅助网格
                const gridHelper = new THREE.GridHelper(100, 100, 0x444444, 0x222222);
                this.scene.add(gridHelper);
            }

            initPhysics() {
                // 创建物理世界
                this.world = new CANNON.World();
                this.world.gravity.set(0, -9.82, 0);
                this.world.broadphase = new CANNON.NaiveBroadphase();

                // 创建物理接触材料
                const defaultMaterial = new CANNON.Material('default');
                const defaultContactMaterial = new CANNON.ContactMaterial(defaultMaterial, defaultMaterial, {
                    friction: 0.1,
                    restitution: 0.3
                });
                this.world.addContactMaterial(defaultContactMaterial);
                this.world.defaultContactMaterial = defaultContactMaterial;
            }

            initCharacter() {
                // 创建角色视觉对象 - 使用【圆柱体+球】或者【three/addons/math/Capsule.js】
                const group = new THREE.Group();

                // 圆柱体部分
                const cylinderGeometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 8);
                const sphereGeometry = new THREE.SphereGeometry(0.5, 8, 8);

                const characterMaterial = new THREE.MeshStandardMaterial({ 
                    color: 0x4cc9f0,
                    roughness: 0.7,
                    metalness: 0.1
                });

                // 创建胶囊体
                const cylinder = new THREE.Mesh(cylinderGeometry, characterMaterial);
                cylinder.position.y = 0.5;

                const topSphere = new THREE.Mesh(sphereGeometry, characterMaterial);
                topSphere.position.y = 1;

                const bottomSphere = new THREE.Mesh(sphereGeometry, characterMaterial);
                bottomSphere.position.y = 0;

                group.add(cylinder);
                group.add(topSphere);
                group.add(bottomSphere);

                this.characterMesh = group;
                this.characterMesh.traverse((child) => {
                    if (child.isMesh) {
                        child.castShadow = true;
                    }
                });
                this.scene.add(this.characterMesh);

                   // 创建角色物理体 - 使用胶囊体【
                // 注意: Cannon不支持胶囊体,用圆柱体代替,或者用圆柱体+球体替代胶囊体】
                const shape = new CANNON.Cylinder(0.5, 0.5, 1, 8);
                this.characterBody = new CANNON.Body({
                    mass: 5,
                    position: new CANNON.Vec3(0, 5, 0),
                    shape: shape,
                    material: this.world.defaultContactMaterial,
                    fixedRotation: true,  // 锁定旋转,防止翻倒
                    linearDamping: 0.9,
                    angularDamping: 0.9
                });
                // 设置刚体方向,使其与世界坐标系对齐
                // this.characterBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);

                this.characterBody.linearDamping = 0.9;
                this.characterBody.angularDamping = 0.9;
                this.world.addBody(this.characterBody);

                // 角色控制器参数
                this.moveSpeed = 10;
                this.jumpForce = 8;
                this.characterBody.allowSleep = false;
            }

            initEnvironment() {
                // 创建地面
                const groundGeometry = new THREE.PlaneGeometry(100, 100);
                const groundMaterial = new THREE.MeshStandardMaterial({ 
                    color: 0x2d3047,
                    roughness: 0.8,
                    metalness: 0.2
                });
                this.groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
                this.groundMesh.rotation.x = -Math.PI / 2;
                this.groundMesh.receiveShadow = true;
                this.scene.add(this.groundMesh);

                // 创建地面物理体
                const groundShape = new CANNON.Plane();
                const groundBody = new CANNON.Body({
                    mass: 0,
                    shape: groundShape,
                    material: this.world.defaultContactMaterial
                });
                groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
                this.world.addBody(groundBody);

                // 创建一些障碍物
                this.createObstacles();
            }

            createObstacles() {
                // 障碍物配置
                const obstacles = [
                    { pos: [5, 1, 5], size: [2, 2, 2], color: 0x4361ee },
                    { pos: [-8, 1.5, 3], size: [3, 3, 1], color: 0xf72585 },
                    { pos: [0, 0.5, -8], size: [5, 1, 2], color: 0x7209b7 },
                    { pos: [10, 2, -5], size: [1, 4, 1], color: 0xffd166 }
                ];

                this.obstacles = [];

                obstacles.forEach((obs, i) => {
                    // 视觉对象
                    const geometry = new THREE.BoxGeometry(...obs.size);
                    const material = new THREE.MeshStandardMaterial({ 
                        color: obs.color,
                        roughness: 0.6,
                        metalness: 0.2
                    });
                    const mesh = new THREE.Mesh(geometry, material);
                    mesh.position.set(...obs.pos);
                    mesh.castShadow = true;
                    mesh.receiveShadow = true;
                    this.scene.add(mesh);

                    // 物理体
                    const shape = new CANNON.Box(new CANNON.Vec3(obs.size[0]/2, obs.size[1]/2, obs.size[2]/2));
                    const body = new CANNON.Body({
                        mass: 0,
                        position: new CANNON.Vec3(...obs.pos),
                        shape: shape,
                        material: this.world.defaultContactMaterial
                    });
                    this.world.addBody(body);

                    this.obstacles.push({ mesh, body });
                });
            }

            initCollectibles() {
                // 可收集物品配置
                const collectibles = [
                    { pos: [8, 1, 8], color: 0xf72585, name: "红色能量球" },
                    { pos: [-8, 1, 8], color: 0x4361ee, name: "蓝色能量球" },
                    { pos: [8, 1, -8], color: 0x4cc9f0, name: "绿色能量球" },
                    { pos: [-8, 1, -8], color: 0xffd166, name: "黄色能量球" }
                ];

                this.collectibles = [];

                collectibles.forEach((item, i) => {
                    // 视觉对象
                    const geometry = new THREE.SphereGeometry(0.5, 16, 16);
                    const material = new THREE.MeshStandardMaterial({ 
                        color: item.color,
                        emissive: item.color,
                        emissiveIntensity: 0.2,
                        roughness: 0.3,
                        metalness: 0.7
                    });
                    const mesh = new THREE.Mesh(geometry, material);
                    mesh.position.set(...item.pos);
                    mesh.castShadow = true;
                    this.scene.add(mesh);

                    // 物理体
                    const shape = new CANNON.Sphere(0.5);
                    const body = new CANNON.Body({
                        mass: 0,
                        position: new CANNON.Vec3(...item.pos),
                        shape: shape,
                        material: this.world.defaultContactMaterial
                    });
                    this.world.addBody(body);

                    this.collectibles.push({ 
                        mesh, 
                        body, 
                        collected: false,
                        name: item.name,
                        colorClass: ['red', 'blue', 'green', 'yellow'][i]
                    });
                });

                // 初始化收集物品UI
                this.updateCollectiblesUI();
            }

            initUI() {
                // 初始化UI元素
                this.fpsCounter = document.getElementById('fpsCounter');
                this.positionDisplay = document.getElementById('positionDisplay');
                this.velocityDisplay = document.getElementById('velocityDisplay');
                this.collectionStatus = document.getElementById('collectionStatus');
                this.collectiblesList = document.getElementById('collectiblesList');

                // 游戏开始/重置按钮
                document.getElementById('startButton').addEventListener('click', () => {
                    this.hideMenu();
                });

                document.getElementById('resetButton').addEventListener('click', () => {
                    this.resetGame();
                });

                // 显示初始菜单
                this.showMenu();
            }

            initEventListeners() {
                // 键盘事件
                window.addEventListener('keydown', (e) => {
                    this.keys[e.key.toLowerCase()] = true;

                    // 空格键跳跃
                    if (e.key === ' ' && !this.isJumping) {
                        this.jump();
                    }

                    // E键交互
                    if (e.key === 'e' || e.key === 'E') {
                        this.interact();
                    }

                    // ESC键显示/隐藏菜单
                    if (e.key === 'Escape') {
                        e.preventDefault();
                        this.toggleMenu();
                    }
                });

                window.addEventListener('keyup', (e) => {
                    this.keys[e.key.toLowerCase()] = false;
                });

                // 窗口大小调整
                window.addEventListener('resize', () => {
                    this.camera.aspect = window.innerWidth / window.innerHeight;
                    this.camera.updateProjectionMatrix();
                    this.renderer.setSize(window.innerWidth, window.innerHeight);
                });
            }

            jump() {
                // 简单的跳跃检测
                if (this.characterBody.position.y < 2.5) {
                    this.characterBody.velocity.y = this.jumpForce;
                    this.isJumping = true;

                    // 0.5秒后重置跳跃状态
                    setTimeout(() => {
                        this.isJumping = false;
                    }, 500);
                }
            }

            interact() {
                // 检查附近的可收集物品
                const interactionDistance = 2.5;
                const charPos = this.characterBody.position;

                this.collectibles.forEach(item => {
                    if (item.collected) return;

                    const itemPos = item.body.position;
                    const dx = charPos.x - itemPos.x;
                    const dy = charPos.y - itemPos.y;
                    const dz = charPos.z - itemPos.z;
                    const distance = Math.sqrt(dx*dx + dy*dy + dz*dz);

                    if (distance < interactionDistance) {
                        this.collectItem(item);
                    }
                });
            }

            collectItem(item) {
                if (item.collected) return;

                item.collected = true;

                // 视觉反馈:使物品消失
                item.mesh.visible = false;

                // 更新收集计数
                this.collectedItems++;

                // 更新UI
                this.updateCollectiblesUI();

                // 检查是否所有物品都已收集
                if (this.collectedItems === this.totalItems) {
                    setTimeout(() => {
                        this.showMenu(true);
                    }, 500);
                }
            }

            updateCollectiblesUI() {
                // 清空列表
                this.collectiblesList.innerHTML = '';

                // 添加每个收集物品
                this.collectibles.forEach(item => {
                    const div = document.createElement('div');
                    div.className = 'collectible';

                    const icon = document.createElement('div');
                    icon.className = `collectible-icon ${item.colorClass}`;

                    const text = document.createElement('span');
                    text.textContent = item.name;
                    text.style.textDecoration = item.collected ? 'line-through' : 'none';
                    text.style.opacity = item.collected ? '0.5' : '1';

                    div.appendChild(icon);
                    div.appendChild(text);
                    this.collectiblesList.appendChild(div);
                });

                // 更新收集状态
                this.collectionStatus.textContent = `已收集: ${this.collectedItems}/${this.totalItems}`;
            }

            showMenu(gameComplete = false) {
                const overlay = document.getElementById('gameOverlay');
                const title = overlay.querySelector('h2');
                const text = overlay.querySelector('p');
                const startButton = document.getElementById('startButton');

                if (gameComplete) {
                    title.textContent = "恭喜!";
                    text.textContent = "你已收集所有能量球!点击重置按钮重新开始游戏。";
                    startButton.style.display = 'none';
                } else {
                    title.textContent = "Three.js 角色控制器";
                    text.textContent = "这是一个完整的角色控制器演示,包含物理引擎和交互功能。使用WASD键移动角色,空格键跳跃,E键与物品交互。收集所有彩色球体来完成游戏!";
                    startButton.style.display = 'block';
                }

                overlay.classList.remove('hidden');
                this.isMenuVisible = true;
            }

            hideMenu() {
                document.getElementById('gameOverlay').classList.add('hidden');
                this.isMenuVisible = false;
            }

            toggleMenu() {
                if (this.isMenuVisible) {
                    this.hideMenu();
                } else {
                    this.showMenu();
                }
            }

            resetGame() {
                // 重置角色位置
                this.characterBody.position.set(0, 5, 0);
                this.characterBody.velocity.set(0, 0, 0);
                this.characterBody.angularVelocity.set(0, 0, 0);

                // 重置收集物品
                this.collectedItems = 0;
                this.collectibles.forEach(item => {
                    item.collected = false;
                    item.mesh.visible = true;
                });

                // 更新UI
                this.updateCollectiblesUI();

                // 隐藏菜单
                this.hideMenu();
            }

            handleMovement(deltaTime) {
                if (this.isMenuVisible) return;

                const moveForce = this.moveSpeed * deltaTime * 60; // 乘以60以补偿时间步长

                // 获取当前速度
                const velocity = this.characterBody.velocity;

                // 根据按键设置速度
                if (this.keys['w'] || this.keys['arrowup']) {
                    velocity.z = -moveForce;
                } else if (this.keys['s'] || this.keys['arrowdown']) {
                    velocity.z = moveForce;
                } else {
                    velocity.z = 0;
                }

                if (this.keys['a'] || this.keys['arrowleft']) {
                    velocity.x = -moveForce;
                } else if (this.keys['d'] || this.keys['arrowright']) {
                    velocity.x = moveForce;
                } else {
                    velocity.x = 0;
                }

                // 保持垂直速度不变
                velocity.y = this.characterBody.velocity.y;

                // 应用速度
                this.characterBody.velocity = velocity;
            }

            updateCamera() {
                // 第三人称相机跟随
                const charPos = this.characterBody.position;

                // 相机目标位置(角色后方上方)
                const targetPos = {
                    x: charPos.x,
                    y: charPos.y + 5,
                    z: charPos.z + 10
                };

                // 平滑相机移动
                this.camera.position.x += (targetPos.x - this.camera.position.x) * 0.05;
                this.camera.position.y += (targetPos.y - this.camera.position.y) * 0.05;
                this.camera.position.z += (targetPos.z - this.camera.position.z) * 0.05;

                // 相机看向角色
                this.camera.lookAt(charPos.x, charPos.y + 2, charPos.z);
            }

            updateUI() {
                // 更新FPS
                const time = this.clock.getElapsedTime();
                const delta = time - this.prevTime;
                this.prevTime = time;
                this.fps = Math.round(1 / delta);
                this.fpsCounter.textContent = this.fps;

                // 更新位置和速度
                const pos = this.characterBody.position;
                const vel = this.characterBody.velocity;

                this.positionDisplay.textContent = `(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)}, ${pos.z.toFixed(1)})`;
                this.velocityDisplay.textContent = `(${vel.x.toFixed(1)}, ${vel.y.toFixed(1)}, ${vel.z.toFixed(1)})`;
            }

            animate() {
                requestAnimationFrame(() => this.animate());

                // 计算时间增量
                const deltaTime = Math.min(this.clock.getDelta(), 0.1);

                // 处理角色移动
                this.handleMovement(deltaTime);

                // 更新物理世界
                this.world.step(1/60, deltaTime, 3);

                // 同步角色视觉对象与物理体
                this.characterMesh.position.copy(this.characterBody.position);
                this.characterMesh.quaternion.copy(this.characterBody.quaternion);

                // 更新相机
                this.updateCamera();

                // 更新UI
                this.updateUI();

                // 旋转可收集物品(如果未被收集)
                this.collectibles.forEach(item => {
                    if (!item.collected) {
                        item.mesh.rotation.y += 0.02;
                    }
                });

                // 旋转障碍物
                this.obstacles.forEach((obstacle, index) => {
                    obstacle.mesh.rotation.y += 0.005 * (index + 1);
                });

                // 渲染场景
                this.renderer.render(this.scene, this.camera);
            }
        }

        // 当页面加载时启动应用程序
        window.addEventListener('load', () => {
            try {
                new CharacterController();
                console.log("角色控制器已成功启动!");
            } catch (error) {
                console.error("启动失败:", error);
                alert("加载失败,请检查控制台获取详细信息。");
            }
        });
    </script>
</body>
</html>

2. 主要修复内容

  1. 修复了Cannon.js库链接

  2. 简化了胶囊体创建

  3. 修复了UI显示问题

  4. 改进了错误处理

  5. 优化了移动控制

3. 如何运行

  1. 将上面的完整代码复制到一个新的HTML文件中

  2. 使用现代浏览器(Chrome/Firefox/Edge)打开该文件

  3. 点击"开始游戏"按钮

  4. 使用WASD键移动角色,空格键跳跃,E键收集物品

如果仍然无法运行,请按F12打开浏览器开发者工具,查看控制台是否有错误信息,这将帮助我们进一步诊断问题。