引言
在之前的代码中,我们借助 Three.js 达成了小车在公路上行驶的动画效果。现在,我们要在此基础上,在公路两侧随机添加障碍物模型,从而提升模拟的真实感与趣味性。
原代码功能回顾
原代码实现了如下主要功能:
- 场景、相机和渲染器的创建 :使用
THREE.Scene()
创建场景,THREE.PerspectiveCamera
创建相机并设置视野角度、宽高比、近裁剪面和远裁剪面,THREE.WebGLRenderer
创建渲染器并将其添加到页面中。 - 公路的生成 :通过循环创建多个公路段,每个公路段的宽度随着路段的增加而逐渐变窄,使用
THREE.PlaneGeometry
和THREE.MeshBasicMaterial
构建公路网格。 - 小车的创建 :使用
THREE.BoxGeometry
和THREE.MeshBasicMaterial
创建一个简单的长方体作为小车的模型,并设置其初始位置。 - 动画循环 :在
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.BoxGeometry
和THREE.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 场景开发。