# 使用 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 场景开发。

相关推荐
程序猿阿伟2 分钟前
《社交应用动态表情:RN与Flutter实战解码》
javascript·flutter·react native
明似水4 分钟前
Flutter 开发入门:从一个简单的计数器应用开始
前端·javascript·flutter
沐土Arvin9 分钟前
前端图片上传组件实战:从动态销毁Input到全屏预览的全功能实现
开发语言·前端·javascript
Zww08911 小时前
el-dialog鼠标在遮罩层松开会意外关闭,教程图文并茂
javascript·vue.js·计算机外设
爱编程的鱼1 小时前
C#接口(Interface)全方位讲解:定义、特性、应用与实践
java·前端·c#
sunbyte1 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | 页面布局 与 Vue Router 路由配置
前端·javascript·vue.js·tailwindcss
saadiya~2 小时前
Vue 3 实现后端 Excel 文件流导出功能(Blob 下载详解)
前端·vue.js·excel
摇摇奶昔x2 小时前
webpack 学习
前端·学习·webpack
阿珊和她的猫3 小时前
Vue Router中的路由嵌套:主子路由
前端·javascript·vue.js