Skip to content

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

Published:

上节实现了车辆的行驶:

2025-12-20 18.22.02.gif

切换到车辆视角之后,就可以开车了。

但是现在是按 x 切换的视角。

实际上应该是人走到车辆附近,按 x 上车,之后就是车辆行驶。

然后按 x 下车,这样就是人物行走。

应该这样切换。

我们加一下这个功能:

在那之前我发现一个问题:

2025-12-20 18.35.38.gif

之前的平台的刚体大小设置的不对,导致能够穿过它。

我们改一下:

image.png

// 平台的物理碰撞体(从地面到顶部的实心结构)
const platformBody = new CANNON.Body({
    mass: 0,
    position: new CANNON.Vec3(
        x,
        y + totalStairsHeight / 2, // 平台中心高度
        z + stepCount * stepDepth + platformDepth / 2
    )
});
platformBody.addShape(new CANNON.Box(new CANNON.Vec3(stepWidth / 2, totalStairsHeight / 2, platformDepth / 2)));
world.addBody(platformBody);

2025-12-20 18.38.37.gif

这样就不会穿过了。

说回正题,继续来做上车功能。

首先改下文案:

image.png

<div id="viewTip">靠近车辆,按 X 上车</div>

然后加个判断逻辑,靠近车的时候按 X 才会上车:

并且加一下提示文案的更新:

image.png

image.png

两点之间的距离公式,大家应该知道:

image.png

import './style.css';
import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh, { characterModel } from './mesh.js';
import car, { carModel, carBody } from './car.js';

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb);

scene.add(mesh);
scene.add(car);

scene.add(new THREE.AmbientLight(0xffffff, 0.6));
const sun = new THREE.DirectionalLight(0xffffff, 0.8);
sun.position.set(20, 30, 10);
sun.castShadow = true;
sun.shadow.camera.left = -30;
sun.shadow.camera.right = 30;
sun.shadow.camera.top = 30;
sun.shadow.camera.bottom = -30;
sun.shadow.mapSize.width = 2048;
sun.shadow.mapSize.height = 2048;
scene.add(sun);

const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 200);
camera.position.set(0, 1.6, 5);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(width, height)
renderer.shadowMap.enabled = true;

document.body.append(renderer.domElement);

// const controls = new OrbitControls(camera, renderer.domElement);

// 车辆视角切换
export let isCarView = false;

// 检查人物是否在车辆附近
function isNearCar() {
    if (!characterModel || !carBody) return false;

    const characterPos = characterModel.position;
    const carPos = carBody.position;

    const distance = Math.sqrt(
        Math.pow(characterPos.x - carPos.x, 2) +
        Math.pow(characterPos.z - carPos.z, 2)
    );

    return distance < 3; // 3米范围内
}

// 更新提示文本
function updateViewTip() {
    const tipElement = document.getElementById('viewTip');
    if (!tipElement) return;

    if (isCarView) {
        tipElement.textContent = '按 X 下车';
    } else if (isNearCar()) {
        tipElement.textContent = '按 X 上车';
    } else {
        tipElement.textContent = '靠近车辆按 X 上车';
    }
}

// 渲染循环
function render() {
    renderer.render(scene, camera);
    updateViewTip();
    requestAnimationFrame(render);
}

render();

// 监听键盘事件
window.addEventListener('keydown', (event) => {
    if (event.key === 'x' || event.key === 'X') {
        if (isCarView) {
            // 在车辆视角时,可以随时下车
            isCarView = false;
            if (carModel && characterModel) {
                carModel.remove(camera);

                characterModel.add(camera);
                // 恢复为人物行走时的相机设置
                camera.position.set(0, 1.5, 2.5);
                camera.rotation.set(0, 0, 0);
                camera.up.set(0, 1, 0);

            }
        } else if (isNearCar()) {
            // 只有在车辆附近才能上车
            isCarView = true;
            if (carModel && characterModel) {
                characterModel.remove(camera);
                carModel.add(camera);
                // 相机在车后面,看向车辆前进方向
                camera.position.set(0, 3, -6);
                camera.rotation.set(-0.1, Math.PI, 0); // 稍微向下看约6度,主要朝向前方
            }
        }
    }
});

export { camera }

试下效果:

2025-12-20 18.56.56.gif

走到车附近,提示按 x 上车,按 x 之后就切换为车辆行驶模式,之后按 x 下车,就切换为了人物行走模式。

这样,上车、下车功能就完成了!

案例代码上传了小册仓库

总结

这节我们加上了上车下车功能。

首先,人物行走的时候,判断人和车的距离,用两点的距离公式。

当人走到车附近的时候,可以按 x 上车,之后切换为车辆行驶模式。

按 x 下车,之后又切换为了人物行走模式。

当然,上车之后应该把人物从场景中去掉,下节我们继续完善。

评论