利用threejs實現(xiàn)一個簡易的泊車功能
簡易版小車
因為之前用的模型比較大,加載很慢,這里就先自己簡單實現(xiàn)一輛小車(后面統(tǒng)稱自車),如下圖:

將車輪、車體和邊框組合成一個 Group,便于后面做自車的一些操作,實現(xiàn)代碼如下:
// 自車車體
const geometry = new THREE.BoxGeometry(2, 0.6, 3);
const material = new THREE.MeshBasicMaterial({
color: 0x00ffff,
side: THREE.DoubleSide,
});
const vehicle = new THREE.Mesh(geometry, material);
vehicle.position.set(0, 1, 0);
scene.add(vehicle);
// 增加自車邊框
const box = geometry.clone();
const edges = new THREE.EdgesGeometry(box);
const edgesMaterial = new THREE.LineBasicMaterial({
color: 0x333333,
});
const line = new THREE.LineSegments(edges, edgesMaterial);
line.position.x = 0;
line.position.y = 1;
line.position.z = 0;
scene.add(line);
// 組成一個Group
const egoCar = new THREE.Group();
egoCar.name = "自車";
egoCar.add(vehicle, line);
scene.add(egoCar);
// 車輪
const axlewidth = 0.7;
const radius = 0.4;
const wheels: any[] = [];
const wheelObjects: any[] = [];
wheels.push({ position: [axlewidth, 0.4, -1], radius });
wheels.push({
position: [-axlewidth, 0.4, -1],
radius,
});
wheels.push({ position: [axlewidth, 0.4, 1], radius });
wheels.push({ position: [-axlewidth, 0.4, 1], radius });
wheels.forEach(function (wheel) {
const geometry = new THREE.CylinderGeometry(
wheel.radius,
wheel.radius,
0.4,
32
);
const material = new THREE.MeshPhongMaterial({
color: 0xd0901d,
emissive: 0xee0000,
side: THREE.DoubleSide,
flatShading: true,
});
const cylinder = new THREE.Mesh(geometry, material);
cylinder.geometry.rotateZ(Math.PI / 2);
cylinder.position.set(
wheel.position[0],
wheel.position[1],
wheel.position[2]
);
egoCar.add(cylinder);
// 后面修改車輪方向會用到
wheelObjects.push(cylinder);
});
跟車相機
讓相機一直跟著自車,體驗更好一點
// ...
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 800);
// 設置攝像機位置,并將其朝向場景中心
camera.position.x = 0;
// camera.position.y = 10;
// camera.position.z = 20;
// camera.lookAt(scene.position);
camera.lookAt(egoCar.position);
// ...
function animate() {
stats.begin();
controls.update();
// 相機跟隨自車
camera.position.y = egoCar.position.y + 15;
camera.position.z = egoCar.position.z + 25;
camera.lookAt(egoCar.position);
renderer.render(scene, camera);
stats.end();
requestAnimationFrame(animate);
}
// ...
自車行駛
實現(xiàn)自車前行后退和左右轉向
// ...
// 記錄開始按下的時間
let startTime = 0;
const activeKeys = new Set();
let t = 0;
document.addEventListener("keydown", (e) => {
activeKeys.add(e.key);
if (startTime === 0) {
startTime = Date.now();
}
t = (Date.now() - startTime) / 1000;
if (t > 10) {
t = 10;
}
});
document.addEventListener("keyup", (e) => {
activeKeys.delete(e.key);
if (activeKeys.size === 0) {
startTime = 0;
}
});
function animate() {
stats.begin();
controls.update();
// 相機跟隨自車
camera.position.y = egoCar.position.y + 15;
camera.position.z = egoCar.position.z + 25;
camera.lookAt(egoCar.position);
if (activeKeys.has("ArrowUp")) {
// 估算對應方向的移動距離
egoCar.position.z -= t * 0.1 * Math.cos(egoCar.rotation.y);
egoCar.position.x -= t * 0.1 * Math.sin(egoCar.rotation.y);
}
if (activeKeys.has("ArrowDown")) {
egoCar.position.z += t * 0.1 * Math.cos(egoCar.rotation.y);
egoCar.position.x += t * 0.1 * Math.sin(egoCar.rotation.y);
}
if (activeKeys.has("ArrowLeft")) {
egoCar.rotation.y += 0.01;
}
if (activeKeys.has("ArrowRight")) {
egoCar.rotation.y -= 0.01;
}
renderer.render(scene, camera);
stats.end();
requestAnimationFrame(animate);
}
//...
車輪轉動
遍歷車輪對象,動態(tài)修改車輪的偏轉角 rotation,以車頭方向為基準偏轉固定的角度
function animate() {
// ...
if (activeKeys.has("ArrowLeft")) {
egoCar.rotation.y += 0.01;
wheelObjects.forEach((wheel) => {
wheel.rotation.y = egoCar.rotation.y + Math.PI / 4;
});
}
if (activeKeys.has("ArrowRight")) {
egoCar.rotation.y -= 0.01;
wheelObjects.forEach((wheel) => {
wheel.rotation.y = egoCar.rotation.y - Math.PI / 4;
});
}
// ...
}
行進效果還是有點僵硬(能用就行),這里的問題是行進方向應該是按車頭方向,而不是固定按某個坐標軸方向,不過這里也只是簡單模擬這個行進效果,后面再引入物理庫 cannon.js優(yōu)化下這塊控制邏輯

泊車功能
車位實現(xiàn)
做一個貼地面的矩形框來模擬車位,可以使用 THREE.PlaneGeometry 來創(chuàng)建平面幾何體
createParkingSpace() {
const plane = new THREE.PlaneGeometry(8, 5);
const material = new THREE.MeshPhongMaterial({
color: 0x666666,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(plane, material);
mesh.rotation.x = -Math.PI / 2;
mesh.position.set(10, 0.12, -20);
this.scene?.add(mesh);
// 增加自定義type,便于后面處理車位的選中邏輯
mesh.userData.type = "parkingSpace";
}
現(xiàn)在咱們把小車開過去停到那個位置

自動泊車
需要實現(xiàn)點擊車位后高亮對應的車位,之后小車自動行駛到對應的位置并停好。點擊原理是用射線的方式采集第一個碰到的車位物體,當點擊鼠標時,會發(fā)生以下步驟:
- 基于屏幕上的點擊位置創(chuàng)建一個
THREE.Vector3向量 - 使用
vector.unproject方法將屏幕上點擊位置的坐標轉換成 three.js 場景中的坐標 - 創(chuàng)建
THREE.Raycaster可以從攝像機的位置向場景中鼠標的點擊位置發(fā)出一條射線 raycaster.intersectObjects返回包含了所有被射線穿過的對象信息的數(shù)組(從攝像機位置開始由短到長)
function handleParkSpaceClick(event: any) {
let vector = new THREE.Vector3(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
vector = vector.unproject(camera);
const raycaster = new THREE.Raycaster(
camera.position,
vector.sub(camera.position).normalize()
);
const intersects = raycaster.intersectObjects(scene.children);
for (let i = 0; i < intersects.length; i++) {
const obj = intersects[i];
// @ts-ignore
if (obj.object.userData.type === "parkingSpace")
// @ts-ignore
obj.object.material.color.set(0x00ff00);
}
}
document.addEventListener("click", handleParkSpaceClick);
自動泊車的實現(xiàn)邏輯也比較簡單,這里簡單記住了車位的位置信息,然后讓小車按一定的偏移駛入,其實實際場景可能還要考慮躲避障礙物、加減速、偏轉角等,一般也不由前端操心這些。實現(xiàn)代碼參考 three-gta v0.1.1 -- 在線體驗

到此這篇關于利用threejs實現(xiàn)一個簡易的泊車功能的文章就介紹到這了,更多相關threejs泊車功能內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
js組件SlotMachine實現(xiàn)圖片切換效果制作抽獎系統(tǒng)
這篇文章主要介紹了js組件SlotMachine實現(xiàn)圖片切換效果制作抽獎系統(tǒng)的相關資料,需要的朋友可以參考下2016-04-04
javascript引用類型之時間Date和數(shù)組Array
引用類型的值(對象)其實就是引用類型的一個實例,接下來,通過本篇文章給大家介紹javascript引用類型之時間Date和數(shù)組Array,需要的朋友可以參考下2015-08-08

