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

但因为是用的 div,所以会遮挡场景中的元素。
我们先把它隐藏,开始打电脑之后再显示:


就像靠近飞机上飞机,靠近车辆上车一样,这里也做一个靠近电脑按 E 打电脑的提示
先在 computer.js 实现 isNearComputer、enterComputerView、exitComputerView 方法:

isNearComputer 是判断距离的,离电脑 2m 就算接近电脑了。
而 enterComputerView 是进入打电脑模式,要做的事情有:
- 隐藏玩家
- 退出鼠标锁定模式,因为要用鼠标
- 相机从玩家的 group 下移除,加到 scene 下,调整下位置。
相应的, exitComputerView 要做的事情就是:
- 显示玩家
- 隐藏浏览器 iframe
- 相机加回到玩家的 group 下
// 检查玩家是否靠近电脑
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 的逻辑

<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 模式一样,这里也单独加一个变量控制:

import { isNearComputer, enterComputerView, exitComputerView } from './computer.js';
// 电脑视角切换
export let isComputerView = false;
tip 文案也改一下:

else if (isNearPlane()) {
tipElement.textContent = '按 C 上飞机';
} else if (isNearComputer(characterModel)) {
tipElement.textContent = '按 E 打电脑';
} else {
tipElement.textContent = '靠近车辆按 X 上车 | 靠近飞机按 C 上飞机 | 靠近电脑按 E 打电脑';
}
还有键盘事件的处理:

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);
}
}
并且电脑模式下,点击屏幕不进入鼠标锁定状态。
其余事件也排除下这个模式:


试下效果:

这样,打电脑功能就完成了。
案例代码上传了小册仓库
总结
这节我们实现了打电脑模式。
其实和开车、开飞机差不多,都是单独一个模式。
只不过在这个模式下,不是控制飞机、车来跑,而是固定相机位置,展示电脑屏幕,可以用 CSS3DObject + 网页实现的电脑功能,比如 ifame 实现浏览器。
这样,我们的房屋里就有了一台电脑,并且可以靠近打电脑。