# 使用 Three.js 实现带随机障碍物的小车行驶模拟

引言

在之前的代码中,我们借助 Three.js 达成了小车在公路上行驶的动画效果。现在,我们要在此基础上,在公路两侧随机添加障碍物模型,从而提升模拟的真实感与趣味性。

原代码功能回顾

原代码实现了如下主要功能:

  1. 场景、相机和渲染器的创建 :使用 THREE.Scene() 创建场景,THREE.PerspectiveCamera 创建相机并设置视野角度、宽高比、近裁剪面和远裁剪面,THREE.WebGLRenderer 创建渲染器并将其添加到页面中。
  2. 公路的生成 :通过循环创建多个公路段,每个公路段的宽度随着路段的增加而逐渐变窄,使用 THREE.PlaneGeometryTHREE.MeshBasicMaterial 构建公路网格。
  3. 小车的创建 :使用 THREE.BoxGeometryTHREE.MeshBasicMaterial 创建一个简单的长方体作为小车的模型,并设置其初始位置。
  4. 动画循环 :在 animate 函数中,更新小车的位置、相机的位置和方向,同时更新公路段的位置,并在公路段即将移出视野时进行重置,最后渲染场景。

新增代码解析

1. 定义障碍物参数

javascript 复制代码
// 定义障碍物的参数
const numObstacles = 10; // 障碍物数量
const obstacleSizeRange = [0.5, 1]; // 障碍物大小范围
const obstacleColors = [0x00ff00, 0x0000ff, 0xff00ff]; // 障碍物颜色

这里定义了障碍物的数量、大小范围以及可能的颜色。这使得障碍物的外观和分布具有一定的随机性。

2. 存储障碍物的数组

javascript 复制代码
    // 存储障碍物的数组
    const obstacles = [];

创建一个数组 obstacles 来存储所有生成的障碍物,方便后续管理。

3. 随机生成障碍物函数

ini 复制代码
// 随机生成障碍物
function generateObstacles() {
    for (let i = 0; i < numObstacles; i++) {
        const size = obstacleSizeRange[0] + (obstacleSizeRange[1] - obstacleSizeRange[0]) * Math.random();
        const geometry = new THREE.BoxGeometry(size, size, size);
        const color = obstacleColors[Math.floor(Math.random() * obstacleColors.length)];
        const material = new THREE.MeshBasicMaterial({ color });
        const obstacle = new THREE.Mesh(geometry, material);

        // 随机生成障碍物在公路两侧的位置
        const side = Math.random() < 0.5? -1 : 1; // -1 表示左侧,1 表示右侧
        const x = (maxRoadWidth / 2 - size / 2) * side;
        const z = Math.random() * roadLength * numRoadSegments;
        obstacle.position.set(x, size / 2, z);

        scene.add(obstacle);
        obstacles.push(obstacle);
    }
}
  • 此函数通过循环 numObstacles 次来生成指定数量的障碍物。
  • 随机生成障碍物的大小,范围在 obstacleSizeRange 内。
  • 随机选择一种颜色 obstacleColors 作为障碍物的颜色。
  • 使用 THREE.BoxGeometryTHREE.MeshBasicMaterial 创建障碍物的网格。
  • 随机决定障碍物出现在公路的左侧或右侧,计算其 x 坐标。
  • 随机生成障碍物在公路长度方向上的 z 坐标。
  • 将障碍物添加到场景中,并将其存入 obstacles 数组。

4. 调用生成障碍物函数

scss 复制代码
generateObstacles();

在合适的位置调用 generateObstacles 函数,以生成障碍物。

完整代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js Car Animation</title>
    <style>
        body {
            margin: 0;
        }

        canvas {
            display: block;
        }
    </style>
</head>

<body>
    <!-- 使用替代 CDN -->
    <script src="https://cdn.jsdelivr.net/npm/three/build/three.min.js"></script>
    <script>
        // 创建场景
        const scene = new THREE.Scene();

        // 创建相机
        const camera = new THREE.PerspectiveCamera(
            75, // 视野角度
            window.innerWidth / window.innerHeight, // 宽高比
            0.1, // 近裁剪面
            1000 // 远裁剪面
        );
        camera.position.set(0, 1.5, 10); // 将相机放置在汽车后面,稍微高一点

        // 创建渲染器
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const numRoadSegments = 5; // 公路段数量
        const roadLength = 20; // 每段公路长度
        const maxRoadWidth = 10; // 公路最大宽度
        const minRoadWidth = 2; // 公路最小宽度
        const roadSegments = [];

        // 创建多个公路段
        for (let i = 0; i < numRoadSegments; i++) {
            const width = maxRoadWidth - (maxRoadWidth - minRoadWidth) * (i / numRoadSegments);
            const roadGeometry = new THREE.PlaneGeometry(width, roadLength);
            const roadMaterial = new THREE.MeshBasicMaterial({ color: 0x666666 });
            const road = new THREE.Mesh(roadGeometry, roadMaterial);
            road.rotation.x = -Math.PI / 2;
            road.position.z = i * roadLength;
            scene.add(road);
            roadSegments.push(road);
        }

        // 创建汽车几何体
        const carGeometry = new THREE.BoxGeometry(1, 0.5, 2);

        // 创建汽车材质
        const carMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });

        // 创建汽车网格
        const car = new THREE.Mesh(carGeometry, carMaterial);
        car.position.z = 0; // 将汽车放置在公路的起始位置
        car.position.y = 0.25; // 将汽车稍微抬高一点
        scene.add(car);

        let carPosition = 0; // 汽车的初始位置
        const carSpeed = 0.1; // 汽车的移动速度

        // 定义障碍物的参数
        const numObstacles = 10; // 障碍物数量
        const obstacleSizeRange = [0.5, 1]; // 障碍物大小范围
        const obstacleColors = [0x00ff00, 0x0000ff, 0xff00ff]; // 障碍物颜色

        // 存储障碍物的数组
        const obstacles = [];

        // 随机生成障碍物
        function generateObstacles() {
            for (let i = 0; i < numObstacles; i++) {
                const size = obstacleSizeRange[0] + (obstacleSizeRange[1] - obstacleSizeRange[0]) * Math.random();
                const geometry = new THREE.BoxGeometry(size, size, size);
                const color = obstacleColors[Math.floor(Math.random() * obstacleColors.length)];
                const material = new THREE.MeshBasicMaterial({ color });
                const obstacle = new THREE.Mesh(geometry, material);

                // 随机生成障碍物在公路两侧的位置
                const side = Math.random() < 0.5? -1 : 1; // -1 表示左侧,1 表示右侧
                const x = (maxRoadWidth / 2 - size / 2) * side;
                const z = Math.random() * roadLength * numRoadSegments;
                obstacle.position.set(x, size / 2, z);

                scene.add(obstacle);
                obstacles.push(obstacle);
            }
        }

        generateObstacles();

        // 检查公路是否即将移出视野,提前进行重置
        function checkAndResetRoad(road, index) {
            if (road.position.z + roadLength < camera.position.z) {
                const lastRoad = roadSegments[(index - 1 + numRoadSegments) % numRoadSegments];
                road.position.z = lastRoad.position.z + roadLength;
                const width = maxRoadWidth - (maxRoadWidth - minRoadWidth) * (index / numRoadSegments);
                road.geometry = new THREE.PlaneGeometry(width, roadLength);
            }
        }

        function animate() {
            requestAnimationFrame(animate);

            // 更新汽车位置
            carPosition += carSpeed;
            car.position.z = carPosition;

            // 更新相机位置和方向
            camera.position.z = carPosition - 5; // 相机始终在汽车后面 5 个单位
            camera.lookAt(car.position); // 相机始终看向汽车

            // 更新公路位置
            roadSegments.forEach(road => {
                road.position.z -= carSpeed;
            });

            // 检查并重置公路位置
            roadSegments.forEach((road, index) => {
                checkAndResetRoad(road, index);
            });

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

        animate();
    </script>
</body>

</html>
    

总结

通过上述步骤,我们在原有的小车行驶模拟中成功添加了随机障碍物。这些障碍物增加了场景的复杂性和趣味性,使模拟更接近现实情况。在实际应用中,还可进一步扩展,例如添加碰撞检测功能,让小车与障碍物碰撞时产生相应效果。希望本文有助于你理解和使用 Three.js 进行 3D 场景开发。

相关推荐
brzhang3 分钟前
告别『上线裸奔』!一文带你配齐生产级 Web 应用的 10 大核心组件
前端·后端·架构
程序员Bears3 分钟前
深入理解CSS3:Flex/Grid布局、动画与媒体查询实战指南
前端·css3·媒体·visual studio code
工呈士10 分钟前
CSS in JS:机遇与挑战的思考
javascript·css
至尊童12 分钟前
五个JavaScript 应用技巧
javascript
David凉宸15 分钟前
凉宸推荐给大家的一些开源项目
前端
举个栗子dhy17 分钟前
编辑器、代码块、大模型AI对话中代码复制功能实现
javascript
袋鱼不重17 分钟前
Cursor 最简易上手体验:谷歌浏览器插件开发3s搞定!
前端·后端·cursor
hyyyyy!17 分钟前
《从分遗产说起:JS 原型与继承详解》
前端·javascript·原型模式
竹苓18 分钟前
从一个想法到上线,一万字记录我开发浏览器插件的全过程
前端
小桥风满袖19 分钟前
Three.js-硬要自学系列19 (曲线颜色渐变、渐变插值、查看设置gltf顶点、山脉高度可视化)
前端·css·three.js