Skip to content

218. 综合实战:开放世界(十九)

Published:

上节把电脑屏幕画了出来:

2026-01-11 21.49.51.gif

但因为是用的 div,所以会遮挡场景中的元素。

我们先把它隐藏,开始打电脑之后再显示:

image.png

image.png

就像靠近飞机上飞机,靠近车辆上车一样,这里也做一个靠近电脑按 E 打电脑的提示

先在 computer.js 实现 isNearComputer、enterComputerView、exitComputerView 方法:

image.png

isNearComputer 是判断距离的,离电脑 2m 就算接近电脑了。

而 enterComputerView 是进入打电脑模式,要做的事情有:

相应的, exitComputerView 要做的事情就是:

// 检查玩家是否靠近电脑
export function isNearComputer(characterModel) {
    if (!characterModel || !monitorPosition) return false;
    
    const characterPos = characterModel.position;
    
    const distance = Math.sqrt(
        Math.pow(characterPos.x - monitorPosition.x, 2) +
        Math.pow(characterPos.z - monitorPosition.z, 2)
    );
    
    return distance < 2; // 2米范围内
}

// 进入电脑模式
export function enterComputerView(camera, scene, css3Renderer, characterModel) {
    if (css3dObj) {
        css3dObj.visible = true;
    }
    
    // 隐藏玩家
    if (characterModel) {
        characterModel.visible = false;
    }
    
    // 退出鼠标锁定模式
    if (document.pointerLockElement === document.body) {
        document.exitPointerLock();
    }
    
    // 将相机从人物组下移除,添加到场景下
    if (camera.parent) {
        camera.parent.remove(camera);
    }
    scene.add(camera);
    
    // 设置相机固定位置(在屏幕正前方)
    if (monitorPosition && css3dObj) {
        // CSS3D对象的位置是相对于显示器模型的局部坐标
        // position.y = 2, position.x = -0.16, scale = 0.01
        // 屏幕中心的世界坐标
        const screenCenterX = monitorPosition.x - 0.16 * 0.01;
        const screenCenterY = monitorPosition.y + 2 * 0.01;
        const screenCenterZ = monitorPosition.z;
        
        // 相机位置:屏幕右侧,距离约0.6米,再向上一点
        camera.position.set(
            screenCenterX + 0.6,  // 屏幕右侧(X轴正方向)
            screenCenterY + 0.3,  // 再向上一点
            screenCenterZ        // 与屏幕Z坐标相同
        );
        // 朝左看向屏幕中心上方一点
        camera.lookAt(screenCenterX, screenCenterY + 0.2, screenCenterZ);
    }
}

// 退出电脑模式
export function exitComputerView(camera, characterModel, css3Renderer) {
    if (css3dObj) {
        css3dObj.visible = false;
    }
    
    // 显示玩家
    if (characterModel) {
        characterModel.visible = true;
    }
    
    // 隐藏浏览器 iframe
    const browserIframe = document.querySelector('#desktop .browser');
    if (browserIframe) {
        browserIframe.style.display = 'none';
    }
    
    // CSS3DRenderer 的 domElement 应该保持 pointer-events: 'none'
    // 不需要手动设置
    
    // 将相机从场景下移除,添加回人物组
    if (camera.parent) {
        camera.parent.remove(camera);
    }
    if (characterModel) {
        characterModel.add(camera);
        // 恢复为人物行走时的相机设置
        camera.position.set(0, 1.5, 2.5);
        camera.rotation.set(0, 0, 0);
        camera.up.set(0, 1, 0);
    }
}

在 html 里加一下双击图标,显示 ifame 的逻辑

image.png

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const appElement = document.querySelector('#desktop .app');
    const browserElement = document.querySelector('#desktop .browser');
    if (appElement && browserElement) {
      appElement.addEventListener('dblclick', function() {
        browserElement.style.display = browserElement.style.display === 'none' ? 'block' : 'none';
      });
    }
  });
</script>

和之前 car 和 plane 模式一样,这里也单独加一个变量控制:

image.png

import { isNearComputer, enterComputerView, exitComputerView } from './computer.js';
// 电脑视角切换
export let isComputerView = false;

tip 文案也改一下:

image.png

else if (isNearPlane()) {
    tipElement.textContent = '按 C 上飞机';
} else if (isNearComputer(characterModel)) {
    tipElement.textContent = '按 E 打电脑';
} else {
    tipElement.textContent = '靠近车辆按 X 上车 | 靠近飞机按 C 上飞机 | 靠近电脑按 E 打电脑';
}

还有键盘事件的处理: image.png

else if (event.key === 'e' || event.key === 'E') {
    if (isComputerView) {
        // 退出电脑模式
        isComputerView = false;
        exitComputerView(camera, characterModel, css3Renderer);
    } else if (isNearComputer(characterModel)) {
        // 进入电脑模式
        isComputerView = true;
        enterComputerView(camera, scene, css3Renderer, characterModel);
    }
}

并且电脑模式下,点击屏幕不进入鼠标锁定状态。

其余事件也排除下这个模式:

image.png

image.png

试下效果:

2026-01-11 23.30.18.gif

这样,打电脑功能就完成了。

案例代码上传了小册仓库

总结

这节我们实现了打电脑模式。

其实和开车、开飞机差不多,都是单独一个模式。

只不过在这个模式下,不是控制飞机、车来跑,而是固定相机位置,展示电脑屏幕,可以用 CSS3DObject + 网页实现的电脑功能,比如 ifame 实现浏览器。

这样,我们的房屋里就有了一台电脑,并且可以靠近打电脑。

评论